diff options
43 files changed, 887 insertions, 406 deletions
diff --git a/core/api/system-current.txt b/core/api/system-current.txt index dffa0cc315ae..bfdce373a47e 100644 --- a/core/api/system-current.txt +++ b/core/api/system-current.txt @@ -338,9 +338,8 @@ package android { field public static final int config_helpPackageNameKey = 17039387; // 0x104001b field public static final int config_helpPackageNameValue = 17039388; // 0x104001c field public static final int config_systemAutomotiveCluster = 17039400; // 0x1040028 - field public static final int config_systemAutomotiveProjection = 17039402; // 0x104002a + field public static final int config_systemAutomotiveProjection = 17039401; // 0x1040029 field public static final int config_systemGallery = 17039399; // 0x1040027 - field public static final int config_systemVideoCall = 17039401; // 0x1040029 } public static final class R.style { diff --git a/core/api/test-current.txt b/core/api/test-current.txt index 5d0cea56a2d4..06110319cb35 100644 --- a/core/api/test-current.txt +++ b/core/api/test-current.txt @@ -54,9 +54,8 @@ package android { field public static final int config_defaultAssistant = 17039393; // 0x1040021 field public static final int config_defaultDialer = 17039395; // 0x1040023 field public static final int config_systemAutomotiveCluster = 17039400; // 0x1040028 - field public static final int config_systemAutomotiveProjection = 17039402; // 0x104002a + field public static final int config_systemAutomotiveProjection = 17039401; // 0x1040029 field public static final int config_systemGallery = 17039399; // 0x1040027 - field public static final int config_systemVideoCall = 17039401; // 0x1040029 } } diff --git a/core/java/android/app/ActivityManager.java b/core/java/android/app/ActivityManager.java index 43d0269beae2..e2426d116319 100644 --- a/core/java/android/app/ActivityManager.java +++ b/core/java/android/app/ActivityManager.java @@ -50,7 +50,9 @@ import android.graphics.Canvas; import android.graphics.Color; import android.graphics.Matrix; import android.graphics.Point; +import android.graphics.Rect; import android.graphics.drawable.Icon; +import android.hardware.HardwareBuffer; import android.os.BatteryStats; import android.os.Binder; import android.os.Build; @@ -76,6 +78,7 @@ import android.util.Singleton; import android.util.Size; import android.util.TypedXmlPullParser; import android.util.TypedXmlSerializer; +import android.window.TaskSnapshot; import com.android.internal.app.LocalePicker; import com.android.internal.app.procstats.ProcessStats; @@ -1740,6 +1743,62 @@ public class ActivityManager { */ public static class RecentTaskInfo extends TaskInfo implements Parcelable { /** + * @hide + */ + public static class PersistedTaskSnapshotData { + /** + * The bounds of the task when the last snapshot was taken, may be null if the task is + * not yet attached to the hierarchy. + * @see {@link android.window.TaskSnapshot#mTaskSize}. + * @hide + */ + public @Nullable Point taskSize; + + /** + * The content insets of the task when the task snapshot was taken. + * @see {@link android.window.TaskSnapshot#mContentInsets}. + * @hide + */ + public @Nullable Rect contentInsets; + + /** + * The size of the last snapshot taken, may be null if there is no associated snapshot. + * @see {@link android.window.TaskSnapshot#mSnapshot}. + * @hide + */ + public @Nullable Point bufferSize; + + /** + * Sets the data from the other data. + * @hide + */ + public void set(PersistedTaskSnapshotData other) { + taskSize = other.taskSize; + contentInsets = other.contentInsets; + bufferSize = other.bufferSize; + } + + /** + * Sets the data from the provided {@param snapshot}. + * @hide + */ + public void set(TaskSnapshot snapshot) { + if (snapshot == null) { + taskSize = null; + contentInsets = null; + bufferSize = null; + return; + } + final HardwareBuffer buffer = snapshot.getHardwareBuffer(); + taskSize = new Point(snapshot.getTaskSize()); + contentInsets = new Rect(snapshot.getContentInsets()); + bufferSize = buffer != null + ? new Point(buffer.getWidth(), buffer.getHeight()) + : null; + } + } + + /** * If this task is currently running, this is the identifier for it. * If it is not running, this will be -1. * @@ -1782,6 +1841,12 @@ public class ActivityManager { */ public ArrayList<RecentTaskInfo> childrenTaskInfos = new ArrayList<>(); + /** + * Information about the last snapshot taken for this task. + * @hide + */ + public PersistedTaskSnapshotData lastSnapshotData = new PersistedTaskSnapshotData(); + public RecentTaskInfo() { } @@ -1798,6 +1863,9 @@ public class ActivityManager { id = source.readInt(); persistentId = source.readInt(); childrenTaskInfos = source.readArrayList(RecentTaskInfo.class.getClassLoader()); + lastSnapshotData.taskSize = source.readTypedObject(Point.CREATOR); + lastSnapshotData.contentInsets = source.readTypedObject(Rect.CREATOR); + lastSnapshotData.bufferSize = source.readTypedObject(Point.CREATOR); super.readFromParcel(source); } @@ -1806,6 +1874,9 @@ public class ActivityManager { dest.writeInt(id); dest.writeInt(persistentId); dest.writeList(childrenTaskInfos); + dest.writeTypedObject(lastSnapshotData.taskSize, flags); + dest.writeTypedObject(lastSnapshotData.contentInsets, flags); + dest.writeTypedObject(lastSnapshotData.bufferSize, flags); super.writeToParcel(dest, flags); } @@ -1825,7 +1896,6 @@ public class ActivityManager { public void dump(PrintWriter pw, String indent) { pw.println(); pw.print(" "); pw.print(" id="); pw.print(persistentId); - pw.print(" stackId="); pw.print(stackId); pw.print(" userId="); pw.print(userId); pw.print(" hasTask="); pw.print((id != -1)); pw.print(" lastActiveTime="); pw.println(lastActiveTime); @@ -1872,6 +1942,12 @@ public class ActivityManager { pw.print(Integer.toHexString(td.getBackgroundColorFloating())); pw.println(" }"); } + pw.print(" "); + pw.print(" lastSnapshotData {"); + pw.print(" taskSize=" + lastSnapshotData.taskSize); + pw.print(" contentInsets=" + lastSnapshotData.contentInsets); + pw.print(" bufferSize=" + lastSnapshotData.bufferSize); + pw.println(" }"); } } diff --git a/core/java/android/app/TaskInfo.java b/core/java/android/app/TaskInfo.java index 390d9219ef2a..938ce0d56933 100644 --- a/core/java/android/app/TaskInfo.java +++ b/core/java/android/app/TaskInfo.java @@ -53,13 +53,6 @@ public class TaskInfo { public int userId; /** - * The id of the ActivityStack that currently contains this task. - * @hide - */ - @UnsupportedAppUsage - public int stackId; - - /** * The identifier for this task. */ public int taskId; @@ -358,7 +351,6 @@ public class TaskInfo { */ void readFromParcel(Parcel source) { userId = source.readInt(); - stackId = source.readInt(); taskId = source.readInt(); displayId = source.readInt(); isRunning = source.readBoolean(); @@ -394,7 +386,6 @@ public class TaskInfo { */ void writeToParcel(Parcel dest, int flags) { dest.writeInt(userId); - dest.writeInt(stackId); dest.writeInt(taskId); dest.writeInt(displayId); dest.writeBoolean(isRunning); @@ -428,7 +419,7 @@ public class TaskInfo { @Override public String toString() { - return "TaskInfo{userId=" + userId + " stackId=" + stackId + " taskId=" + taskId + return "TaskInfo{userId=" + userId + " taskId=" + taskId + " displayId=" + displayId + " isRunning=" + isRunning + " baseIntent=" + baseIntent + " baseActivity=" + baseActivity diff --git a/core/java/android/os/Environment.java b/core/java/android/os/Environment.java index 62951246a43b..124c0b00b2b2 100644 --- a/core/java/android/os/Environment.java +++ b/core/java/android/os/Environment.java @@ -1359,8 +1359,17 @@ public class Environment { } final AppOpsManager appOps = context.getSystemService(AppOpsManager.class); - return appOps.checkOpNoThrow(AppOpsManager.OP_LEGACY_STORAGE, - uid, context.getOpPackageName()) == AppOpsManager.MODE_ALLOWED; + final String opPackageName = context.getOpPackageName(); + + if (appOps.noteOpNoThrow(AppOpsManager.OP_LEGACY_STORAGE, uid, + opPackageName) == AppOpsManager.MODE_ALLOWED) { + return true; + } + + // Legacy external storage access is granted to instrumentations invoked with + // "--no-isolated-storage" flag. + return appOps.noteOpNoThrow(AppOpsManager.OP_NO_ISOLATED_STORAGE, uid, + opPackageName) == AppOpsManager.MODE_ALLOWED; } private static boolean isScopedStorageEnforced(boolean defaultScopedStorage, diff --git a/core/java/android/view/inputmethod/InputMethodManager.java b/core/java/android/view/inputmethod/InputMethodManager.java index 90c8e17a6984..7b2bb73ff562 100644 --- a/core/java/android/view/inputmethod/InputMethodManager.java +++ b/core/java/android/view/inputmethod/InputMethodManager.java @@ -115,7 +115,6 @@ import java.util.List; import java.util.Map; import java.util.Objects; import java.util.concurrent.CountDownLatch; -import java.util.concurrent.ThreadFactory; import java.util.concurrent.TimeUnit; /** @@ -1083,19 +1082,6 @@ public final class InputMethodManager { } } - private static class ImeThreadFactory implements ThreadFactory { - private final String mThreadName; - - ImeThreadFactory(String name) { - mThreadName = name; - } - - @Override - public Thread newThread(Runnable r) { - return new Thread(r, mThreadName); - } - } - final IInputMethodClient.Stub mClient = new IInputMethodClient.Stub() { @Override protected void dump(FileDescriptor fd, PrintWriter fout, String[] args) { diff --git a/core/java/android/widget/ToastPresenter.java b/core/java/android/widget/ToastPresenter.java index b484dfacbf6c..2904a8c889a2 100644 --- a/core/java/android/widget/ToastPresenter.java +++ b/core/java/android/widget/ToastPresenter.java @@ -172,6 +172,22 @@ public class ToastPresenter { } /** + * Update the LayoutParameters of the currently showing toast view. This is used for layout + * updates based on orientation changes. + */ + public void updateLayoutParams(int xOffset, int yOffset, float horizontalMargin, + float verticalMargin, int gravity) { + checkState(mView != null, "Toast must be showing to update its layout parameters."); + Configuration config = mResources.getConfiguration(); + mParams.gravity = Gravity.getAbsoluteGravity(gravity, config.getLayoutDirection()); + mParams.x = xOffset; + mParams.y = yOffset; + mParams.horizontalMargin = horizontalMargin; + mParams.verticalMargin = verticalMargin; + addToastView(); + } + + /** * Sets {@link WindowManager.LayoutParams#SYSTEM_FLAG_SHOW_FOR_ALL_USERS} flag if {@code * packageName} is a cross-user package. * @@ -221,18 +237,7 @@ public class ToastPresenter { adjustLayoutParams(mParams, windowToken, duration, gravity, xOffset, yOffset, horizontalMargin, verticalMargin, removeWindowAnimations); - if (mView.getParent() != null) { - mWindowManager.removeView(mView); - } - try { - mWindowManager.addView(mView, mParams); - } catch (WindowManager.BadTokenException e) { - // Since the notification manager service cancels the token right after it notifies us - // to cancel the toast there is an inherent race and we may attempt to add a window - // after the token has been invalidated. Let us hedge against that. - Log.w(TAG, "Error while attempting to show toast from " + mPackageName, e); - return; - } + addToastView(); trySendAccessibilityEvent(mView, mPackageName); if (callback != null) { try { @@ -288,4 +293,19 @@ public class ToastPresenter { view.dispatchPopulateAccessibilityEvent(event); mAccessibilityManager.sendAccessibilityEvent(event); } + + private void addToastView() { + if (mView.getParent() != null) { + mWindowManager.removeView(mView); + } + try { + mWindowManager.addView(mView, mParams); + } catch (WindowManager.BadTokenException e) { + // Since the notification manager service cancels the token right after it notifies us + // to cancel the toast there is an inherent race and we may attempt to add a window + // after the token has been invalidated. Let us hedge against that. + Log.w(TAG, "Error while attempting to show toast from " + mPackageName, e); + return; + } + } } diff --git a/core/jni/android_content_res_ApkAssets.cpp b/core/jni/android_content_res_ApkAssets.cpp index af06e2e80cec..c7439f1b32d4 100644 --- a/core/jni/android_content_res_ApkAssets.cpp +++ b/core/jni/android_content_res_ApkAssets.cpp @@ -354,7 +354,7 @@ static void NativeDestroy(void* ptr) { } static jlong NativeGetFinalizer(JNIEnv* /*env*/, jclass /*clazz*/) { - return reinterpret_cast<jlong>(&NativeDestroy); + return static_cast<jlong>(reinterpret_cast<uintptr_t>(&NativeDestroy)); } static jstring NativeGetAssetPath(JNIEnv* env, jclass /*clazz*/, jlong ptr) { diff --git a/core/jni/android_view_InputEventSender.cpp b/core/jni/android_view_InputEventSender.cpp index ac680f6e2611..9746a07a1b77 100644 --- a/core/jni/android_view_InputEventSender.cpp +++ b/core/jni/android_view_InputEventSender.cpp @@ -199,7 +199,13 @@ status_t NativeInputEventSender::receiveFinishedSignals(JNIEnv* env) { for (;;) { uint32_t publishedSeq; bool handled; - status_t status = mInputPublisher.receiveFinishedSignal(&publishedSeq, &handled); + std::function<void(uint32_t seq, bool handled, nsecs_t consumeTime)> callback = + [&publishedSeq, &handled](uint32_t inSeq, bool inHandled, + nsecs_t inConsumeTime) -> void { + publishedSeq = inSeq; + handled = inHandled; + }; + status_t status = mInputPublisher.receiveFinishedSignal(callback); if (status) { if (status == WOULD_BLOCK) { return OK; diff --git a/core/res/res/values/config.xml b/core/res/res/values/config.xml index beae9353a10f..09ca12aa9744 100644 --- a/core/res/res/values/config.xml +++ b/core/res/res/values/config.xml @@ -1946,8 +1946,6 @@ <string name="config_systemAutomotiveProjection" translatable="false"></string> <!-- The name of the package that will hold the system cluster service role. --> <string name="config_systemAutomotiveCluster" translatable="false"></string> - <!-- The name of the package that will hold the system video call role. --> - <string name="config_systemVideoCall" translatable="false"></string> <!-- The name of the package that will be allowed to change its components' label/icon. --> <string name="config_overrideComponentUiPackage" translatable="false"></string> diff --git a/core/res/res/values/public.xml b/core/res/res/values/public.xml index 40c5206b86d8..46f928df323c 100644 --- a/core/res/res/values/public.xml +++ b/core/res/res/values/public.xml @@ -3118,8 +3118,6 @@ <!-- @hide @SystemApi @TestApi --> <public name="config_systemAutomotiveCluster" /> <!-- @hide @SystemApi @TestApi --> - <public name="config_systemVideoCall" /> - <!-- @hide @SystemApi @TestApi --> <public name="config_systemAutomotiveProjection" /> </public-group> diff --git a/data/etc/car/com.android.car.developeroptions.xml b/data/etc/car/com.android.car.developeroptions.xml index cf0199bd1b85..c9405748d7de 100644 --- a/data/etc/car/com.android.car.developeroptions.xml +++ b/data/etc/car/com.android.car.developeroptions.xml @@ -26,6 +26,7 @@ <permission name="android.permission.CONTROL_DISPLAY_COLOR_TRANSFORMS"/> <permission name="android.permission.DELETE_PACKAGES"/> <permission name="android.permission.FORCE_STOP_PACKAGES"/> + <permission name="android.permission.INSTALL_DYNAMIC_SYSTEM"/> <permission name="android.permission.LOCAL_MAC_ADDRESS"/> <permission name="android.permission.MANAGE_DEBUGGING"/> <permission name="android.permission.MANAGE_FINGERPRINT"/> @@ -40,10 +41,12 @@ <permission name="android.permission.MOVE_PACKAGE"/> <permission name="android.permission.OVERRIDE_WIFI_CONFIG"/> <permission name="android.permission.PACKAGE_USAGE_STATS"/> + <permission name="android.permission.READ_DREAM_STATE"/> <permission name="android.permission.READ_PRIVILEGED_PHONE_STATE"/> <permission name="android.permission.READ_SEARCH_INDEXABLES"/> <permission name="android.permission.REBOOT"/> <permission name="android.permission.REQUEST_NETWORK_SCORES"/> + <permission name="android.permission.RESTART_WIFI_SUBSYSTEM"/> <permission name="android.permission.SET_TIME"/> <permission name="android.permission.STATUS_BAR"/> <permission name="android.permission.TETHER_PRIVILEGED"/> diff --git a/data/etc/car/com.android.car.xml b/data/etc/car/com.android.car.xml index 19548bc404d1..48f6ab376c82 100644 --- a/data/etc/car/com.android.car.xml +++ b/data/etc/car/com.android.car.xml @@ -25,6 +25,7 @@ <permission name="android.permission.REAL_GET_TASKS"/> <permission name="android.permission.REBOOT"/> <permission name="android.permission.READ_LOGS"/> + <permission name="android.permission.SUBSTITUTE_NOTIFICATION_APP_NAME"/> <permission name="android.permission.WRITE_SECURE_SETTINGS"/> </privapp-permissions> </permissions> diff --git a/data/etc/car/com.google.android.car.kitchensink.xml b/data/etc/car/com.google.android.car.kitchensink.xml index 3a20a9cb1695..bd30d7a61517 100644 --- a/data/etc/car/com.google.android.car.kitchensink.xml +++ b/data/etc/car/com.google.android.car.kitchensink.xml @@ -27,8 +27,10 @@ <permission name="android.permission.INTERACT_ACROSS_USERS_FULL"/> <permission name="android.permission.CONNECTIVITY_USE_RESTRICTED_NETWORKS"/> <permission name="android.permission.LOCATION_HARDWARE"/> + <permission name="android.permission.LOCK_DEVICE"/> <permission name="android.permission.MANAGE_USB"/> <permission name="android.permission.MANAGE_USERS"/> + <permission name="android.permission.MASTER_CLEAR"/> <!-- use for CarServiceTest --> <permission name="android.permission.MEDIA_CONTENT_CONTROL"/> <permission name="android.permission.MODIFY_AUDIO_ROUTING"/> @@ -40,9 +42,11 @@ <permission name="android.permission.REAL_GET_TASKS"/> <permission name="android.permission.READ_LOGS"/> <permission name="android.permission.REBOOT"/> + <permission name="android.permission.RESET_PASSWORD"/> <permission name="android.permission.SEND_CATEGORY_CAR_NOTIFICATIONS"/> <!-- use for CarServiceTest --> <permission name="android.permission.SET_ACTIVITY_WATCHER"/> + <permission name="android.permission.SUBSTITUTE_NOTIFICATION_APP_NAME"/> <permission name="android.permission.WRITE_SECURE_SETTINGS"/> <!-- use for rotary fragment to enable/disable packages related to rotary --> diff --git a/graphics/java/android/graphics/Point.java b/graphics/java/android/graphics/Point.java index cf2f970e3d58..25f76f6f6da7 100644 --- a/graphics/java/android/graphics/Point.java +++ b/graphics/java/android/graphics/Point.java @@ -17,6 +17,7 @@ package android.graphics; import android.annotation.NonNull; +import android.annotation.Nullable; import android.os.Parcel; import android.os.Parcelable; @@ -96,6 +97,27 @@ public class Point implements Parcelable { } /** + * @return Returns a {@link String} that represents this point which can be parsed with + * {@link #unflattenFromString(String)}. + * @hide + */ + @NonNull + public String flattenToString() { + return x + "x" + y; + } + + /** + * @return Returns a {@link Point} from a short string created from {@link #flattenToString()}. + * @hide + */ + @Nullable + public static Point unflattenFromString(String s) throws NumberFormatException { + final int sep_ix = s.indexOf("x"); + return new Point(Integer.parseInt(s.substring(0, sep_ix)), + Integer.parseInt(s.substring(sep_ix + 1))); + } + + /** * Parcelable interface methods */ @Override diff --git a/packages/SystemUI/plugin/src/com/android/systemui/plugins/ToastPlugin.java b/packages/SystemUI/plugin/src/com/android/systemui/plugins/ToastPlugin.java index 0831e0ef7795..da079cf04403 100644 --- a/packages/SystemUI/plugin/src/com/android/systemui/plugins/ToastPlugin.java +++ b/packages/SystemUI/plugin/src/com/android/systemui/plugins/ToastPlugin.java @@ -103,5 +103,10 @@ public interface ToastPlugin extends Plugin { default Animator getOutAnimation() { return null; } + + /** + * Called on orientation changes. + */ + default void onOrientationChange(int orientation) { } } } diff --git a/packages/SystemUI/res/drawable/toast_background.xml b/packages/SystemUI/res/drawable/toast_background.xml new file mode 100644 index 000000000000..5c45e8346e3c --- /dev/null +++ b/packages/SystemUI/res/drawable/toast_background.xml @@ -0,0 +1,21 @@ +<?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. + --> +<shape xmlns:android="http://schemas.android.com/apk/res/android" + android:shape="rectangle"> + <solid android:color="#FFFFFFFF" /> + <corners android:radius="@dimen/toast_bg_radius" /> +</shape> diff --git a/packages/SystemUI/res/layout/text_toast.xml b/packages/SystemUI/res/layout/text_toast.xml new file mode 100644 index 000000000000..de4e062805fe --- /dev/null +++ b/packages/SystemUI/res/layout/text_toast.xml @@ -0,0 +1,51 @@ +<?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. + --> + +<LinearLayout + xmlns:android="http://schemas.android.com/apk/res/android" + android:layout_width="wrap_content" + android:layout_height="wrap_content" + android:maxWidth="@dimen/toast_width" + android:orientation="horizontal" + android:background="@drawable/toast_background" + android:backgroundTint="?android:attr/colorBackground" + android:layout_marginEnd="16dp" + android:layout_marginStart="16dp" + android:gravity="center_vertical"> + + <!-- Icon should be 24x24, make slightly larger to allow for shadowing, adjust via padding --> + <ImageView + android:id="@+id/icon" + android:alpha="@dimen/toast_icon_alpha" + android:padding="11.5dp" + android:layout_width="@dimen/toast_icon_size" + android:layout_height="@dimen/toast_icon_size"/> + <TextView + android:id="@+id/text" + android:ellipsize="end" + android:maxLines="2" + android:paddingTop="12dp" + android:paddingBottom="12dp" + android:paddingStart="0dp" + android:paddingEnd="22dp" + android:textSize="@dimen/toast_text_size" + android:textColor="?android:attr/textColorPrimary" + android:fontFamily="@*android:string/config_headlineFontFamily" + android:layout_width="match_parent" + android:layout_height="wrap_content"/> +</LinearLayout> diff --git a/packages/SystemUI/res/values-land/dimens.xml b/packages/SystemUI/res/values-land/dimens.xml index 51d7b8eff5fc..24c7655e5ae4 100644 --- a/packages/SystemUI/res/values-land/dimens.xml +++ b/packages/SystemUI/res/values-land/dimens.xml @@ -52,4 +52,6 @@ <!-- (footer_height -48dp)/2 --> <dimen name="controls_management_footer_top_margin">4dp</dimen> <dimen name="controls_management_favorites_top_margin">8dp</dimen> + + <dimen name="toast_y_offset">24dp</dimen> </resources> diff --git a/packages/SystemUI/res/values/dimens.xml b/packages/SystemUI/res/values/dimens.xml index 79471e6dacd2..08cd6553e252 100644 --- a/packages/SystemUI/res/values/dimens.xml +++ b/packages/SystemUI/res/values/dimens.xml @@ -1354,4 +1354,11 @@ <dimen name="rounded_slider_icon_size">24dp</dimen> <!-- rounded_slider_icon_size / 2 --> <dimen name="rounded_slider_icon_inset">12dp</dimen> + + <dimen name="toast_width">296dp</dimen> + <item name="toast_icon_alpha" format="float" type="dimen">1</item> + <dimen name="toast_text_size">14sp</dimen> + <dimen name="toast_y_offset">48dp</dimen> + <dimen name="toast_icon_size">48dp</dimen> + <dimen name="toast_bg_radius">28dp</dimen> </resources> diff --git a/packages/SystemUI/res/values/flags.xml b/packages/SystemUI/res/values/flags.xml index ded8a2e66aea..6196e4a6613a 100644 --- a/packages/SystemUI/res/values/flags.xml +++ b/packages/SystemUI/res/values/flags.xml @@ -39,4 +39,6 @@ <!-- People Tile flag --> <bool name="flag_conversations">false</bool> + + <bool name="flag_toast_style">false</bool> </resources> diff --git a/packages/SystemUI/shared/src/com/android/systemui/shared/pip/PipSurfaceTransactionHelper.java b/packages/SystemUI/shared/src/com/android/systemui/shared/pip/PipSurfaceTransactionHelper.java index 7f04f28198c0..72e4061829fa 100644 --- a/packages/SystemUI/shared/src/com/android/systemui/shared/pip/PipSurfaceTransactionHelper.java +++ b/packages/SystemUI/shared/src/com/android/systemui/shared/pip/PipSurfaceTransactionHelper.java @@ -32,8 +32,29 @@ public class PipSurfaceTransactionHelper { private final Matrix mTmpTransform = new Matrix(); private final float[] mTmpFloat9 = new float[9]; private final RectF mTmpSourceRectF = new RectF(); + private final RectF mTmpDestinationRectF = new RectF(); private final Rect mTmpDestinationRect = new Rect(); + public void scale(SurfaceControl.Transaction tx, SurfaceControl leash, + Rect sourceBounds, Rect destinationBounds) { + mTmpSourceRectF.set(sourceBounds); + mTmpDestinationRectF.set(destinationBounds); + mTmpTransform.setRectToRect(mTmpSourceRectF, mTmpDestinationRectF, Matrix.ScaleToFit.FILL); + tx.setMatrix(leash, mTmpTransform, mTmpFloat9) + .setPosition(leash, mTmpDestinationRectF.left, mTmpDestinationRectF.top); + } + + public void scale(SurfaceControl.Transaction tx, SurfaceControl leash, + Rect sourceBounds, Rect destinationBounds, + float degree, float positionX, float positionY) { + mTmpSourceRectF.set(sourceBounds); + mTmpDestinationRectF.set(destinationBounds); + mTmpTransform.setRectToRect(mTmpSourceRectF, mTmpDestinationRectF, Matrix.ScaleToFit.FILL); + mTmpTransform.postRotate(degree, 0, 0); + tx.setMatrix(leash, mTmpTransform, mTmpFloat9) + .setPosition(leash, positionX, positionY); + } + public void scaleAndCrop(SurfaceControl.Transaction tx, SurfaceControl leash, Rect sourceBounds, Rect destinationBounds, Rect insets) { mTmpSourceRectF.set(sourceBounds); diff --git a/packages/SystemUI/shared/src/com/android/systemui/shared/recents/model/Task.java b/packages/SystemUI/shared/src/com/android/systemui/shared/recents/model/Task.java index 186379af4b1d..f6b239e31e99 100644 --- a/packages/SystemUI/shared/src/com/android/systemui/shared/recents/model/Task.java +++ b/packages/SystemUI/shared/src/com/android/systemui/shared/recents/model/Task.java @@ -43,17 +43,6 @@ public class Task { public static final String TAG = "Task"; - /* Task callbacks */ - @Deprecated - public interface TaskCallbacks { - /* Notifies when a task has been bound */ - void onTaskDataLoaded(Task task, ThumbnailData thumbnailData); - /* Notifies when a task has been unbound */ - void onTaskDataUnloaded(); - /* Notifies when a task's windowing mode has changed. */ - void onTaskWindowingModeChanged(); - } - /** * The Task Key represents the unique primary key for the task */ @@ -209,12 +198,6 @@ public class Task { public TaskKey key; /** - * The temporary sort index in the stack, used when ordering the stack. - */ - @Deprecated - public int temporarySortIndexInStack; - - /** * The icon is the task description icon (if provided), which falls back to the activity icon, * which can then fall back to the application icon. */ @@ -229,45 +212,24 @@ public class Task { public int colorPrimary; @ViewDebug.ExportedProperty(category="recents") public int colorBackground; - @ViewDebug.ExportedProperty(category="recents") - @Deprecated - public boolean useLightOnPrimaryColor; /** * The task description for this task, only used to reload task icons. */ public TaskDescription taskDescription; - /** - * The state isLaunchTarget will be set for the correct task upon launching Recents. - */ - @ViewDebug.ExportedProperty(category="recents") - @Deprecated - public boolean isLaunchTarget; - @ViewDebug.ExportedProperty(category="recents") - @Deprecated - public boolean isStackTask; - @ViewDebug.ExportedProperty(category="recents") - @Deprecated - public boolean isSystemApp; @ViewDebug.ExportedProperty(category="recents") public boolean isDockable; - /** - * Resize mode. See {@link ActivityInfo#resizeMode}. - */ - @ViewDebug.ExportedProperty(category="recents") - @Deprecated - public int resizeMode; - @ViewDebug.ExportedProperty(category="recents") public ComponentName topActivity; @ViewDebug.ExportedProperty(category="recents") public boolean isLocked; - @Deprecated - private ArrayList<TaskCallbacks> mCallbacks = new ArrayList<>(); + // Last snapshot data, only used for recent tasks + public ActivityManager.RecentTaskInfo.PersistedTaskSnapshotData lastSnapshotData = + new ActivityManager.RecentTaskInfo.PersistedTaskSnapshotData(); public Task() { // Do nothing @@ -289,6 +251,15 @@ public class Task { this.taskDescription = new TaskDescription(); } + public Task(Task other) { + this(other.key, other.colorPrimary, other.colorBackground, other.isDockable, + other.isLocked, other.taskDescription, other.topActivity); + } + + /** + * Use {@link Task#Task(Task)}. + */ + @Deprecated public Task(TaskKey key, int colorPrimary, int colorBackground, boolean isDockable, boolean isLocked, TaskDescription taskDescription, ComponentName topActivity) { @@ -301,103 +272,6 @@ public class Task { this.topActivity = topActivity; } - @Deprecated - public Task(TaskKey key, Drawable icon, ThumbnailData thumbnail, String title, - String titleDescription, int colorPrimary, int colorBackground, boolean isLaunchTarget, - boolean isStackTask, boolean isSystemApp, boolean isDockable, - TaskDescription taskDescription, int resizeMode, ComponentName topActivity, - boolean isLocked) { - this.key = key; - this.icon = icon; - this.thumbnail = thumbnail; - this.title = title; - this.titleDescription = titleDescription; - this.colorPrimary = colorPrimary; - this.colorBackground = colorBackground; - this.useLightOnPrimaryColor = Utilities.computeContrastBetweenColors(this.colorPrimary, - Color.WHITE) > 3f; - this.taskDescription = taskDescription; - this.isLaunchTarget = isLaunchTarget; - this.isStackTask = isStackTask; - this.isSystemApp = isSystemApp; - this.isDockable = isDockable; - this.resizeMode = resizeMode; - this.topActivity = topActivity; - this.isLocked = isLocked; - } - - /** - * Copies the metadata from another task, but retains the current callbacks. - */ - @Deprecated - public void copyFrom(Task o) { - this.key = o.key; - this.icon = o.icon; - this.thumbnail = o.thumbnail; - this.title = o.title; - this.titleDescription = o.titleDescription; - this.colorPrimary = o.colorPrimary; - this.colorBackground = o.colorBackground; - this.useLightOnPrimaryColor = o.useLightOnPrimaryColor; - this.taskDescription = o.taskDescription; - this.isLaunchTarget = o.isLaunchTarget; - this.isStackTask = o.isStackTask; - this.isSystemApp = o.isSystemApp; - this.isDockable = o.isDockable; - this.resizeMode = o.resizeMode; - this.isLocked = o.isLocked; - this.topActivity = o.topActivity; - } - - /** - * Add a callback. - */ - @Deprecated - public void addCallback(TaskCallbacks cb) { - if (!mCallbacks.contains(cb)) { - mCallbacks.add(cb); - } - } - - /** - * Remove a callback. - */ - @Deprecated - public void removeCallback(TaskCallbacks cb) { - mCallbacks.remove(cb); - } - - /** Updates the task's windowing mode. */ - @Deprecated - public void setWindowingMode(int windowingMode) { - key.setWindowingMode(windowingMode); - int callbackCount = mCallbacks.size(); - for (int i = 0; i < callbackCount; i++) { - mCallbacks.get(i).onTaskWindowingModeChanged(); - } - } - - /** Notifies the callback listeners that this task has been loaded */ - @Deprecated - public void notifyTaskDataLoaded(ThumbnailData thumbnailData, Drawable applicationIcon) { - this.icon = applicationIcon; - this.thumbnail = thumbnailData; - int callbackCount = mCallbacks.size(); - for (int i = 0; i < callbackCount; i++) { - mCallbacks.get(i).onTaskDataLoaded(this, thumbnailData); - } - } - - /** Notifies the callback listeners that this task has been unloaded */ - @Deprecated - public void notifyTaskDataUnloaded(Drawable defaultApplicationIcon) { - icon = defaultApplicationIcon; - thumbnail = null; - for (int i = mCallbacks.size() - 1; i >= 0; i--) { - mCallbacks.get(i).onTaskDataUnloaded(); - } - } - /** * Returns the top activity component. */ @@ -424,9 +298,6 @@ public class Task { if (!isDockable) { writer.print(" dockable=N"); } - if (isLaunchTarget) { - writer.print(" launchTarget=Y"); - } if (isLocked) { writer.print(" locked=Y"); } diff --git a/packages/SystemUI/src/com/android/systemui/BatteryMeterView.java b/packages/SystemUI/src/com/android/systemui/BatteryMeterView.java index caaee5fd3f37..176562799838 100644 --- a/packages/SystemUI/src/com/android/systemui/BatteryMeterView.java +++ b/packages/SystemUI/src/com/android/systemui/BatteryMeterView.java @@ -49,7 +49,6 @@ import android.widget.TextView; import androidx.annotation.StyleRes; -import com.android.settingslib.Utils; import com.android.settingslib.graph.ThemedBatteryDrawable; import com.android.systemui.broadcast.BroadcastDispatcher; import com.android.systemui.plugins.DarkIconDispatcher; @@ -105,11 +104,6 @@ public class BatteryMeterView extends LinearLayout implements private DualToneHandler mDualToneHandler; private int mUser; - /** - * Whether we should use colors that adapt based on wallpaper/the scrim behind quick settings. - */ - private boolean mUseWallpaperTextColors; - private int mNonAdaptedSingleToneColor; private int mNonAdaptedForegroundColor; private int mNonAdaptedBackgroundColor; @@ -242,31 +236,6 @@ public class BatteryMeterView extends LinearLayout implements mIsSubscribedForTunerUpdates = false; } - /** - * Sets whether the battery meter view uses the wallpaperTextColor. If we're not using it, we'll - * revert back to dark-mode-based/tinted colors. - * - * @param shouldUseWallpaperTextColor whether we should use wallpaperTextColor for all - * components - */ - public void useWallpaperTextColor(boolean shouldUseWallpaperTextColor) { - if (shouldUseWallpaperTextColor == mUseWallpaperTextColors) { - return; - } - - mUseWallpaperTextColors = shouldUseWallpaperTextColor; - - if (mUseWallpaperTextColors) { - updateColors( - Utils.getColorAttrDefaultColor(mContext, R.attr.wallpaperTextColor), - Utils.getColorAttrDefaultColor(mContext, R.attr.wallpaperTextColorSecondary), - Utils.getColorAttrDefaultColor(mContext, R.attr.wallpaperTextColor)); - } else { - updateColors(mNonAdaptedForegroundColor, mNonAdaptedBackgroundColor, - mNonAdaptedSingleToneColor); - } - } - public void setColorsFromContext(Context context) { if (context == null) { return; @@ -476,13 +445,19 @@ public class BatteryMeterView extends LinearLayout implements mNonAdaptedForegroundColor = mDualToneHandler.getFillColor(intensity); mNonAdaptedBackgroundColor = mDualToneHandler.getBackgroundColor(intensity); - if (!mUseWallpaperTextColors) { - updateColors(mNonAdaptedForegroundColor, mNonAdaptedBackgroundColor, - mNonAdaptedSingleToneColor); - } + updateColors(mNonAdaptedForegroundColor, mNonAdaptedBackgroundColor, + mNonAdaptedSingleToneColor); } - private void updateColors(int foregroundColor, int backgroundColor, int singleToneColor) { + /** + * Sets icon and text colors. This will be overridden by {@code onDarkChanged} events, + * if registered. + * + * @param foregroundColor + * @param backgroundColor + * @param singleToneColor + */ + public void updateColors(int foregroundColor, int backgroundColor, int singleToneColor) { mDrawable.setColors(foregroundColor, backgroundColor, singleToneColor); mTextColor = singleToneColor; if (mBatteryPercentView != null) { diff --git a/packages/SystemUI/src/com/android/systemui/qs/QuickStatusBarHeader.java b/packages/SystemUI/src/com/android/systemui/qs/QuickStatusBarHeader.java index 7776c12f587e..87252ff2b908 100644 --- a/packages/SystemUI/src/com/android/systemui/qs/QuickStatusBarHeader.java +++ b/packages/SystemUI/src/com/android/systemui/qs/QuickStatusBarHeader.java @@ -29,7 +29,6 @@ import android.media.AudioManager; import android.util.AttributeSet; import android.util.MathUtils; import android.util.Pair; -import android.view.ContextThemeWrapper; import android.view.DisplayCutout; import android.view.View; import android.view.ViewGroup; @@ -48,7 +47,6 @@ import androidx.lifecycle.LifecycleRegistry; import com.android.settingslib.Utils; import com.android.systemui.BatteryMeterView; -import com.android.systemui.DualToneHandler; import com.android.systemui.Interpolators; import com.android.systemui.R; import com.android.systemui.privacy.OngoingPrivacyChip; @@ -75,7 +73,6 @@ public class QuickStatusBarHeader extends RelativeLayout implements LifecycleOwn protected QuickQSPanel mHeaderQsPanel; private TouchAnimator mStatusIconsAlphaAnimator; private TouchAnimator mHeaderTextContainerAlphaAnimator; - private DualToneHandler mDualToneHandler; private View mSystemIconsView; private View mQuickQsStatusIcons; @@ -110,8 +107,6 @@ public class QuickStatusBarHeader extends RelativeLayout implements LifecycleOwn public QuickStatusBarHeader(Context context, AttributeSet attrs) { super(context, attrs); - mDualToneHandler = new DualToneHandler( - new ContextThemeWrapper(context, R.style.QSHeaderTheme)); } @Override @@ -149,10 +144,8 @@ public class QuickStatusBarHeader extends RelativeLayout implements LifecycleOwn } void onAttach(TintedIconManager iconManager) { - int colorForeground = Utils.getColorAttrDefaultColor(getContext(), - android.R.attr.colorForeground); - float intensity = getColorIntensity(colorForeground); - int fillColor = mDualToneHandler.getSingleColor(intensity); + int fillColor = Utils.getColorAttrDefaultColor(getContext(), + android.R.attr.textColorPrimary); // Set the correct tint for the status icons so they contrast iconManager.setTint(fillColor); @@ -271,14 +264,12 @@ public class QuickStatusBarHeader extends RelativeLayout implements LifecycleOwn int textColor = Utils.getColorAttrDefaultColor(mContext, android.R.attr.textColorPrimary); if (textColor != mTextColorPrimary) { + int textColorSecondary = Utils.getColorAttrDefaultColor(mContext, + android.R.attr.textColorSecondary); mTextColorPrimary = textColor; mClockView.setTextColor(textColor); - - float intensity = getColorIntensity(textColor); - int fillColor = mDualToneHandler.getSingleColor(intensity); - - Rect tintArea = new Rect(0, 0, 0, 0); - mBatteryRemainingIcon.onDarkChanged(tintArea, intensity, fillColor); + mBatteryRemainingIcon.updateColors(mTextColorPrimary, textColorSecondary, + mTextColorPrimary); } updateStatusIconAlphaAnimator(); diff --git a/packages/SystemUI/src/com/android/systemui/qs/carrier/QSCarrier.java b/packages/SystemUI/src/com/android/systemui/qs/carrier/QSCarrier.java index 7d8d86fe691e..eddcf8c1e9ae 100644 --- a/packages/SystemUI/src/com/android/systemui/qs/carrier/QSCarrier.java +++ b/packages/SystemUI/src/com/android/systemui/qs/carrier/QSCarrier.java @@ -28,9 +28,7 @@ import android.widget.TextView; import com.android.settingslib.Utils; import com.android.settingslib.graph.SignalDrawable; -import com.android.systemui.DualToneHandler; import com.android.systemui.R; -import com.android.systemui.qs.QuickStatusBarHeader; import java.util.Objects; @@ -40,9 +38,6 @@ public class QSCarrier extends LinearLayout { private TextView mCarrierText; private ImageView mMobileSignal; private ImageView mMobileRoaming; - private DualToneHandler mDualToneHandler; - private ColorStateList mColorForegroundStateList; - private float mColorForegroundIntensity; private CellSignalState mLastSignalState; public QSCarrier(Context context) { @@ -64,7 +59,6 @@ public class QSCarrier extends LinearLayout { @Override protected void onFinishInflate() { super.onFinishInflate(); - mDualToneHandler = new DualToneHandler(getContext()); mMobileGroup = findViewById(R.id.mobile_combo); if (FeatureFlagUtils.isEnabled(mContext, FeatureFlagUtils.SETTINGS_PROVIDER_MODEL)) { mMobileRoaming = findViewById(R.id.mobile_roaming_large); @@ -74,11 +68,6 @@ public class QSCarrier extends LinearLayout { mMobileSignal = findViewById(R.id.mobile_signal); mCarrierText = findViewById(R.id.qs_carrier_text); mMobileSignal.setImageDrawable(new SignalDrawable(mContext)); - - int colorForeground = Utils.getColorAttrDefaultColor(mContext, - android.R.attr.colorForeground); - mColorForegroundStateList = ColorStateList.valueOf(colorForeground); - mColorForegroundIntensity = QuickStatusBarHeader.getColorIntensity(colorForeground); } /** @@ -92,8 +81,8 @@ public class QSCarrier extends LinearLayout { mMobileGroup.setVisibility(state.visible ? View.VISIBLE : View.GONE); if (state.visible) { mMobileRoaming.setVisibility(state.roaming ? View.VISIBLE : View.GONE); - ColorStateList colorStateList = ColorStateList.valueOf( - mDualToneHandler.getSingleColor(mColorForegroundIntensity)); + ColorStateList colorStateList = Utils.getColorAttr(mContext, + android.R.attr.textColorPrimary); mMobileRoaming.setImageTintList(colorStateList); mMobileSignal.setImageTintList(colorStateList); mMobileSignal.setImageLevel(state.mobileSignalIconId); diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/FeatureFlags.java b/packages/SystemUI/src/com/android/systemui/statusbar/FeatureFlags.java index 2dd85e9bb98d..778f813b8d07 100644 --- a/packages/SystemUI/src/com/android/systemui/statusbar/FeatureFlags.java +++ b/packages/SystemUI/src/com/android/systemui/statusbar/FeatureFlags.java @@ -74,4 +74,8 @@ public class FeatureFlags { public boolean isPeopleTileEnabled() { return mFlagReader.isEnabled(R.bool.flag_conversations); } + + public boolean isToastStyleEnabled() { + return mFlagReader.isEnabled(R.bool.flag_toast_style); + } } diff --git a/packages/SystemUI/src/com/android/systemui/toast/SystemUIToast.java b/packages/SystemUI/src/com/android/systemui/toast/SystemUIToast.java index e9fcf1aa9598..365cd2a5d20b 100644 --- a/packages/SystemUI/src/com/android/systemui/toast/SystemUIToast.java +++ b/packages/SystemUI/src/com/android/systemui/toast/SystemUIToast.java @@ -20,10 +20,21 @@ import android.animation.Animator; import android.annotation.NonNull; import android.annotation.Nullable; import android.content.Context; +import android.content.pm.ApplicationInfo; +import android.content.pm.PackageManager; +import android.graphics.Bitmap; +import android.graphics.drawable.BitmapDrawable; +import android.graphics.drawable.Drawable; +import android.os.UserHandle; +import android.util.Log; +import android.view.LayoutInflater; import android.view.View; +import android.widget.ImageView; +import android.widget.TextView; import android.widget.ToastPresenter; import com.android.internal.R; +import com.android.launcher3.icons.IconFactory; import com.android.systemui.plugins.ToastPlugin; /** @@ -35,23 +46,43 @@ public class SystemUIToast implements ToastPlugin.Toast { final CharSequence mText; final ToastPlugin.Toast mPluginToast; - final int mDefaultGravity; - final int mDefaultY; + private final String mPackageName; + private final int mUserId; + private final LayoutInflater mLayoutInflater; + private final boolean mToastStyleEnabled; + final int mDefaultX = 0; final int mDefaultHorizontalMargin = 0; final int mDefaultVerticalMargin = 0; - SystemUIToast(Context context, CharSequence text) { - this(context, text, null); + private int mDefaultY; + private int mDefaultGravity; + + @NonNull private final View mToastView; + @Nullable private final Animator mInAnimator; + @Nullable private final Animator mOutAnimator; + + SystemUIToast(LayoutInflater layoutInflater, Context context, CharSequence text, + String packageName, int userId, boolean toastStyleEnabled, int orientation) { + this(layoutInflater, context, text, null, packageName, userId, + toastStyleEnabled, orientation); } - SystemUIToast(Context context, CharSequence text, ToastPlugin.Toast pluginToast) { + SystemUIToast(LayoutInflater layoutInflater, Context context, CharSequence text, + ToastPlugin.Toast pluginToast, String packageName, int userId, + boolean toastStyleEnabled, int orientation) { + mToastStyleEnabled = toastStyleEnabled; + mLayoutInflater = layoutInflater; mContext = context; mText = text; mPluginToast = pluginToast; + mPackageName = packageName; + mUserId = userId; + mToastView = inflateToastView(); + mInAnimator = createInAnimator(); + mOutAnimator = createOutAnimator(); - mDefaultGravity = context.getResources().getInteger(R.integer.config_toastDefaultGravity); - mDefaultY = context.getResources().getDimensionPixelSize(R.dimen.toast_y_offset); + onOrientationChange(orientation); } @Override @@ -102,28 +133,19 @@ public class SystemUIToast implements ToastPlugin.Toast { @Override @NonNull public View getView() { - if (isPluginToast() && mPluginToast.getView() != null) { - return mPluginToast.getView(); - } - return ToastPresenter.getTextToastView(mContext, mText); + return mToastView; } @Override @Nullable public Animator getInAnimation() { - if (isPluginToast() && mPluginToast.getInAnimation() != null) { - return mPluginToast.getInAnimation(); - } - return null; + return mInAnimator; } @Override @Nullable public Animator getOutAnimation() { - if (isPluginToast() && mPluginToast.getOutAnimation() != null) { - return mPluginToast.getOutAnimation(); - } - return null; + return mOutAnimator; } /** @@ -136,4 +158,80 @@ public class SystemUIToast implements ToastPlugin.Toast { private boolean isPluginToast() { return mPluginToast != null; } + + private View inflateToastView() { + if (isPluginToast() && mPluginToast.getView() != null) { + return mPluginToast.getView(); + } + + View toastView; + if (mToastStyleEnabled) { + toastView = mLayoutInflater.inflate( + com.android.systemui.R.layout.text_toast, null); + ((TextView) toastView.findViewById(com.android.systemui.R.id.text)).setText(mText); + + ((ImageView) toastView.findViewById(com.android.systemui.R.id.icon)) + .setImageDrawable(getBadgedIcon(mContext, mPackageName, mUserId)); + } else { + toastView = ToastPresenter.getTextToastView(mContext, mText); + } + + return toastView; + } + + /** + * Called on orientation changes to update parameters associated with the toast placement. + */ + public void onOrientationChange(int orientation) { + if (mPluginToast != null) { + mPluginToast.onOrientationChange(orientation); + } + + mDefaultY = mContext.getResources().getDimensionPixelSize( + mToastStyleEnabled + ? com.android.systemui.R.dimen.toast_y_offset + : R.dimen.toast_y_offset); + mDefaultGravity = + mContext.getResources().getInteger(R.integer.config_toastDefaultGravity); + } + + private Animator createInAnimator() { + if (isPluginToast() && mPluginToast.getInAnimation() != null) { + return mPluginToast.getInAnimation(); + } + + return mToastStyleEnabled + ? ToastDefaultAnimation.Companion.toastIn(getView()) + : null; + } + + private Animator createOutAnimator() { + if (isPluginToast() && mPluginToast.getOutAnimation() != null) { + return mPluginToast.getOutAnimation(); + } + return mToastStyleEnabled + ? ToastDefaultAnimation.Companion.toastOut(getView()) + : null; + } + + /** + * Get badged app icon if necessary, similar as used in the Settings UI. + * @return The icon to use + */ + public static Drawable getBadgedIcon(@NonNull Context context, String packageName, + int userId) { + final PackageManager packageManager = context.getPackageManager(); + try { + final ApplicationInfo appInfo = packageManager.getApplicationInfoAsUser( + packageName, PackageManager.GET_META_DATA, userId); + UserHandle user = UserHandle.getUserHandleForUid(appInfo.uid); + IconFactory iconFactory = IconFactory.obtain(context); + Bitmap iconBmp = iconFactory.createBadgedIconBitmap( + appInfo.loadUnbadgedIcon(packageManager), user, false).icon; + return new BitmapDrawable(context.getResources(), iconBmp); + } catch (PackageManager.NameNotFoundException e) { + Log.e("SystemUIToast", "could not load icon for package=" + packageName + " e=" + e); + return null; + } + } } diff --git a/packages/SystemUI/src/com/android/systemui/toast/ToastDefaultAnimation.kt b/packages/SystemUI/src/com/android/systemui/toast/ToastDefaultAnimation.kt new file mode 100644 index 000000000000..603d69057e5c --- /dev/null +++ b/packages/SystemUI/src/com/android/systemui/toast/ToastDefaultAnimation.kt @@ -0,0 +1,105 @@ +/* + * 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.systemui.toast + +import android.animation.ObjectAnimator +import android.view.View +import android.view.animation.LinearInterpolator +import android.view.animation.PathInterpolator +import android.animation.AnimatorSet + +class ToastDefaultAnimation { + /** + * sum of the in and out animation durations cannot exceed + * [com.android.server.policy.PhoneWindowManager.TOAST_WINDOW_ANIM_BUFFER] to prevent the toast + * window from being removed before animations are completed + */ + companion object { + // total duration shouldn't exceed NotificationManagerService's delay for "in" animation + fun toastIn(view: View): AnimatorSet? { + val icon: View? = view.findViewById(com.android.systemui.R.id.icon) + val text: View? = view.findViewById(com.android.systemui.R.id.text) + if (icon == null || text == null) { + return null + } + val linearInterp = LinearInterpolator() + val scaleInterp = PathInterpolator(0f, 0f, 0f, 1f) + val sX = ObjectAnimator.ofFloat(view, "scaleX", 0.9f, 1f).apply { + interpolator = scaleInterp + duration = 333 + } + val sY = ObjectAnimator.ofFloat(view, "scaleY", 0.9f, 1f).apply { + interpolator = scaleInterp + duration = 333 + } + val vA = ObjectAnimator.ofFloat(view, "alpha", 0f, 1f).apply { + interpolator = linearInterp + duration = 66 + } + text.alpha = 0f // Set now otherwise won't apply until start delay + val tA = ObjectAnimator.ofFloat(text, "alpha", 0f, 1f).apply { + interpolator = linearInterp + duration = 283 + startDelay = 50 + } + icon.alpha = 0f // Set now otherwise won't apply until start delay + val iA = ObjectAnimator.ofFloat(icon, "alpha", 0f, 1f).apply { + interpolator = linearInterp + duration = 283 + startDelay = 50 + } + return AnimatorSet().apply { + playTogether(sX, sY, vA, tA, iA) + } + } + + fun toastOut(view: View): AnimatorSet? { + // total duration shouldn't exceed NotificationManagerService's delay for "out" anim + val icon: View? = view.findViewById(com.android.systemui.R.id.icon) + val text: View? = view.findViewById(com.android.systemui.R.id.text) + if (icon == null || text == null) { + return null + } + val linearInterp = LinearInterpolator() + val scaleInterp = PathInterpolator(0.3f, 0f, 1f, 1f) + val sX = ObjectAnimator.ofFloat(view, "scaleX", 1f, 0.9f).apply { + interpolator = scaleInterp + duration = 250 + } + val sY = ObjectAnimator.ofFloat(view, "scaleY", 1f, 0.9f).apply { + interpolator = scaleInterp + duration = 250 + } + val vA = ObjectAnimator.ofFloat(view, "alpha", 1f, 0f).apply { + interpolator = linearInterp + duration = 100 + startDelay = 150 + } + val tA = ObjectAnimator.ofFloat(text, "alpha", 1f, 0f).apply { + interpolator = linearInterp + duration = 166 + } + val iA = ObjectAnimator.ofFloat(icon, "alpha", 1f, 0f).apply { + interpolator = linearInterp + duration = 166 + } + return AnimatorSet().apply { + playTogether(sX, sY, vA, tA, iA) + } + } + } +} diff --git a/packages/SystemUI/src/com/android/systemui/toast/ToastFactory.java b/packages/SystemUI/src/com/android/systemui/toast/ToastFactory.java index d8cb61c6b349..8b782d4b7923 100644 --- a/packages/SystemUI/src/com/android/systemui/toast/ToastFactory.java +++ b/packages/SystemUI/src/com/android/systemui/toast/ToastFactory.java @@ -17,6 +17,7 @@ package com.android.systemui.toast; import android.content.Context; +import android.view.LayoutInflater; import androidx.annotation.NonNull; @@ -26,6 +27,7 @@ import com.android.systemui.dump.DumpManager; import com.android.systemui.plugins.PluginListener; import com.android.systemui.plugins.ToastPlugin; import com.android.systemui.shared.plugins.PluginManager; +import com.android.systemui.statusbar.FeatureFlags; import java.io.FileDescriptor; import java.io.PrintWriter; @@ -40,10 +42,18 @@ import javax.inject.Inject; public class ToastFactory implements Dumpable { // only one ToastPlugin can be connected at a time. private ToastPlugin mPlugin; + private final LayoutInflater mLayoutInflater; + private final boolean mToastStyleEnabled; @Inject - public ToastFactory(PluginManager pluginManager, DumpManager dumpManager) { + public ToastFactory( + LayoutInflater layoutInflater, + PluginManager pluginManager, + DumpManager dumpManager, + FeatureFlags featureFlags) { + mLayoutInflater = layoutInflater; dumpManager.registerDumpable("ToastFactory", this); + mToastStyleEnabled = featureFlags.isToastStyleEnabled(); pluginManager.addPluginListener( new PluginListener<ToastPlugin>() { @Override @@ -64,11 +74,13 @@ public class ToastFactory implements Dumpable { * Create a toast to be shown by ToastUI. */ public SystemUIToast createToast(Context context, CharSequence text, String packageName, - int userId) { + int userId, int orientation) { if (isPluginAvailable()) { - return new SystemUIToast(context, text, mPlugin.createToast(text, packageName, userId)); + return new SystemUIToast(mLayoutInflater, context, text, mPlugin.createToast(text, + packageName, userId), packageName, userId, mToastStyleEnabled, orientation); } - return new SystemUIToast(context, text); + return new SystemUIToast(mLayoutInflater, context, text, packageName, userId, + mToastStyleEnabled, orientation); } private boolean isPluginAvailable() { @@ -79,5 +91,6 @@ public class ToastFactory implements Dumpable { public void dump(@NonNull FileDescriptor fd, @NonNull PrintWriter pw, @NonNull String[] args) { pw.println("ToastFactory:"); pw.println(" mAttachedPlugin=" + mPlugin); + pw.println(" mToastStyleEnabled=" + mToastStyleEnabled); } } diff --git a/packages/SystemUI/src/com/android/systemui/toast/ToastLogger.kt b/packages/SystemUI/src/com/android/systemui/toast/ToastLogger.kt index 78173cf62a93..51541bd3032e 100644 --- a/packages/SystemUI/src/com/android/systemui/toast/ToastLogger.kt +++ b/packages/SystemUI/src/com/android/systemui/toast/ToastLogger.kt @@ -49,6 +49,15 @@ class ToastLogger @Inject constructor( }) } + fun logOrientationChange(text: String, isPortrait: Boolean) { + log(DEBUG, { + str1 = text + bool1 = isPortrait + }, { + "Orientation change for toast. msg=\'$str1\' isPortrait=$bool1" + }) + } + private inline fun log( logLevel: LogLevel, initializer: LogMessage.() -> Unit, diff --git a/packages/SystemUI/src/com/android/systemui/toast/ToastUI.java b/packages/SystemUI/src/com/android/systemui/toast/ToastUI.java index 409d1361223c..92ea1d0e5fbd 100644 --- a/packages/SystemUI/src/com/android/systemui/toast/ToastUI.java +++ b/packages/SystemUI/src/com/android/systemui/toast/ToastUI.java @@ -16,27 +16,28 @@ package com.android.systemui.toast; +import static android.content.res.Configuration.ORIENTATION_PORTRAIT; + import android.animation.Animator; import android.annotation.MainThread; +import android.annotation.NonNull; import android.annotation.Nullable; import android.app.INotificationManager; import android.app.ITransientNotificationCallback; import android.content.Context; +import android.content.res.Configuration; import android.os.IBinder; import android.os.ServiceManager; import android.os.UserHandle; import android.util.Log; import android.view.accessibility.AccessibilityManager; import android.view.accessibility.IAccessibilityManager; -import android.widget.Toast; import android.widget.ToastPresenter; import com.android.internal.annotations.VisibleForTesting; import com.android.systemui.SystemUI; import com.android.systemui.dagger.SysUISingleton; -import com.android.systemui.dagger.qualifiers.Main; import com.android.systemui.statusbar.CommandQueue; -import com.android.systemui.util.concurrency.DelayableExecutor; import java.util.Objects; @@ -58,18 +59,19 @@ public class ToastUI extends SystemUI implements CommandQueue.Callbacks { private final IAccessibilityManager mIAccessibilityManager; private final AccessibilityManager mAccessibilityManager; private final ToastFactory mToastFactory; - private final DelayableExecutor mMainExecutor; private final ToastLogger mToastLogger; private SystemUIToast mToast; @Nullable private ToastPresenter mPresenter; @Nullable private ITransientNotificationCallback mCallback; + private ToastOutAnimatorListener mToastOutAnimatorListener; + + private int mOrientation = ORIENTATION_PORTRAIT; @Inject public ToastUI( Context context, CommandQueue commandQueue, ToastFactory toastFactory, - @Main DelayableExecutor mainExecutor, ToastLogger toastLogger) { this(context, commandQueue, INotificationManager.Stub.asInterface( @@ -77,21 +79,19 @@ public class ToastUI extends SystemUI implements CommandQueue.Callbacks { IAccessibilityManager.Stub.asInterface( ServiceManager.getService(Context.ACCESSIBILITY_SERVICE)), toastFactory, - mainExecutor, toastLogger); } @VisibleForTesting ToastUI(Context context, CommandQueue commandQueue, INotificationManager notificationManager, @Nullable IAccessibilityManager accessibilityManager, - ToastFactory toastFactory, DelayableExecutor mainExecutor, ToastLogger toastLogger + ToastFactory toastFactory, ToastLogger toastLogger ) { super(context); mCommandQueue = commandQueue; mNotificationManager = notificationManager; mIAccessibilityManager = accessibilityManager; mToastFactory = toastFactory; - mMainExecutor = mainExecutor; mAccessibilityManager = mContext.getSystemService(AccessibilityManager.class); mToastLogger = toastLogger; } @@ -105,36 +105,38 @@ public class ToastUI extends SystemUI implements CommandQueue.Callbacks { @MainThread public void showToast(int uid, String packageName, IBinder token, CharSequence text, IBinder windowToken, int duration, @Nullable ITransientNotificationCallback callback) { - if (mPresenter != null) { - hideCurrentToast(); - } - UserHandle userHandle = UserHandle.getUserHandleForUid(uid); - Context context = mContext.createContextAsUser(userHandle, 0); - mToast = mToastFactory.createToast(context, text, packageName, userHandle.getIdentifier()); + Runnable showToastRunnable = () -> { + UserHandle userHandle = UserHandle.getUserHandleForUid(uid); + Context context = mContext.createContextAsUser(userHandle, 0); + mToast = mToastFactory.createToast(mContext /* sysuiContext */, text, packageName, + userHandle.getIdentifier(), mOrientation); - if (mToast.hasCustomAnimation()) { if (mToast.getInAnimation() != null) { mToast.getInAnimation().start(); } - final Animator hideAnimator = mToast.getOutAnimation(); - if (hideAnimator != null) { - final long durationMillis = duration == Toast.LENGTH_LONG - ? TOAST_LONG_TIME : TOAST_SHORT_TIME; - final long updatedDuration = mAccessibilityManager.getRecommendedTimeoutMillis( - (int) durationMillis, AccessibilityManager.FLAG_CONTENT_TEXT); - mMainExecutor.executeDelayed(() -> hideAnimator.start(), - updatedDuration - hideAnimator.getTotalDuration()); - } + + mCallback = callback; + mPresenter = new ToastPresenter(context, mIAccessibilityManager, + mNotificationManager, packageName); + // Set as trusted overlay so touches can pass through toasts + mPresenter.getLayoutParams().setTrustedOverlay(); + mToastLogger.logOnShowToast(uid, packageName, text.toString(), token.toString()); + mPresenter.show(mToast.getView(), token, windowToken, duration, mToast.getGravity(), + mToast.getXOffset(), mToast.getYOffset(), mToast.getHorizontalMargin(), + mToast.getVerticalMargin(), mCallback, mToast.hasCustomAnimation()); + }; + + if (mToastOutAnimatorListener != null) { + // if we're currently animating out a toast, show new toast after prev toast is hidden + mToastOutAnimatorListener.setShowNextToastRunnable(showToastRunnable); + } else if (mPresenter != null) { + // if there's a toast already showing that we haven't tried hiding yet, hide it and + // then show the next toast after its hidden animation is done + hideCurrentToast(showToastRunnable); + } else { + // else, show this next toast immediately + showToastRunnable.run(); } - mCallback = callback; - mPresenter = new ToastPresenter(context, mIAccessibilityManager, mNotificationManager, - packageName); - // Set as trusted overlay so touches can pass through toasts - mPresenter.getLayoutParams().setTrustedOverlay(); - mToastLogger.logOnShowToast(uid, packageName, text.toString(), token.toString()); - mPresenter.show(mToast.getView(), token, windowToken, duration, mToast.getGravity(), - mToast.getXOffset(), mToast.getYOffset(), mToast.getHorizontalMargin(), - mToast.getVerticalMargin(), mCallback, mToast.hasCustomAnimation()); } @Override @@ -146,12 +148,88 @@ public class ToastUI extends SystemUI implements CommandQueue.Callbacks { return; } mToastLogger.logOnHideToast(packageName, token.toString()); - hideCurrentToast(); + hideCurrentToast(null); } @MainThread - private void hideCurrentToast() { - mPresenter.hide(mCallback); + private void hideCurrentToast(Runnable runnable) { + if (mToast.getOutAnimation() != null) { + Animator animator = mToast.getOutAnimation(); + mToastOutAnimatorListener = new ToastOutAnimatorListener(mPresenter, mCallback, + runnable); + animator.addListener(mToastOutAnimatorListener); + animator.start(); + } else { + mPresenter.hide(mCallback); + if (runnable != null) { + runnable.run(); + } + } + mToast = null; mPresenter = null; + mCallback = null; + } + + @Override + protected void onConfigurationChanged(Configuration newConfig) { + if (newConfig.orientation != mOrientation) { + mOrientation = newConfig.orientation; + if (mToast != null) { + mToastLogger.logOrientationChange(mToast.mText.toString(), + mOrientation == ORIENTATION_PORTRAIT); + mToast.onOrientationChange(mOrientation); + mPresenter.updateLayoutParams( + mToast.getXOffset(), + mToast.getYOffset(), + mToast.getHorizontalMargin(), + mToast.getVerticalMargin(), + mToast.getGravity()); + } + } + } + + /** + * Once the out animation for a toast is finished, start showing the next toast. + */ + class ToastOutAnimatorListener implements Animator.AnimatorListener { + final ToastPresenter mPrevPresenter; + final ITransientNotificationCallback mPrevCallback; + @Nullable Runnable mShowNextToastRunnable; + + ToastOutAnimatorListener( + @NonNull ToastPresenter presenter, + @NonNull ITransientNotificationCallback callback, + @Nullable Runnable runnable) { + mPrevPresenter = presenter; + mPrevCallback = callback; + mShowNextToastRunnable = runnable; + } + + void setShowNextToastRunnable(Runnable runnable) { + mShowNextToastRunnable = runnable; + } + + @Override + public void onAnimationStart(Animator animation) { + } + + @Override + public void onAnimationEnd(Animator animation) { + mPrevPresenter.hide(mPrevCallback); + if (mShowNextToastRunnable != null) { + mShowNextToastRunnable.run(); + } + mToastOutAnimatorListener = null; + } + + @Override + public void onAnimationCancel(Animator animation) { + onAnimationEnd(animation); + } + + @Override + public void onAnimationRepeat(Animator animation) { + + } } } diff --git a/packages/SystemUI/tests/src/com/android/systemui/toast/ToastUITest.java b/packages/SystemUI/tests/src/com/android/systemui/toast/ToastUITest.java index c743fd07c492..365c62cddbdf 100644 --- a/packages/SystemUI/tests/src/com/android/systemui/toast/ToastUITest.java +++ b/packages/SystemUI/tests/src/com/android/systemui/toast/ToastUITest.java @@ -57,8 +57,7 @@ import com.android.systemui.SysuiTestCase; import com.android.systemui.dump.DumpManager; import com.android.systemui.shared.plugins.PluginManager; import com.android.systemui.statusbar.CommandQueue; -import com.android.systemui.util.concurrency.FakeExecutor; -import com.android.systemui.util.time.FakeSystemClock; +import com.android.systemui.statusbar.FeatureFlags; import org.junit.Before; import org.junit.Test; @@ -88,7 +87,6 @@ public class ToastUITest extends SysuiTestCase { private static final String TEXT = "Hello World"; private static final int MESSAGE_RES_ID = R.id.message; - private FakeExecutor mFakeDelayableExecutor = new FakeExecutor(new FakeSystemClock()); private Context mContextSpy; private ToastUI mToastUI; @Mock private LayoutInflater mLayoutInflater; @@ -99,6 +97,7 @@ public class ToastUITest extends SysuiTestCase { @Mock private PluginManager mPluginManager; @Mock private DumpManager mDumpManager; @Mock private ToastLogger mToastLogger; + @Mock private FeatureFlags mFeatureFlags; @Mock private ITransientNotificationCallback mCallback; @Captor private ArgumentCaptor<View> mViewCaptor; @@ -107,12 +106,9 @@ public class ToastUITest extends SysuiTestCase { @Before public void setUp() throws Exception { MockitoAnnotations.initMocks(this); - - // This is because inflate will result in WindowManager (WM) calls, which will fail since we - // are mocking it, so we mock LayoutInflater with the view obtained before mocking WM. - View view = ToastPresenter.getTextToastView(mContext, TEXT); - when(mLayoutInflater.inflate(eq(TEXT_TOAST_LAYOUT), any())).thenReturn(view); - mContext.addMockSystemService(LayoutInflater.class, mLayoutInflater); + when(mLayoutInflater.inflate(eq(TEXT_TOAST_LAYOUT), any())).thenReturn( + ToastPresenter.getTextToastView(mContext, TEXT)); + when(mFeatureFlags.isToastStyleEnabled()).thenReturn(false); mContext.addMockSystemService(WindowManager.class, mWindowManager); mContextSpy = spy(mContext); @@ -120,8 +116,8 @@ public class ToastUITest extends SysuiTestCase { doReturn(mContextSpy).when(mContextSpy).createContextAsUser(any(), anyInt()); mToastUI = new ToastUI(mContextSpy, mCommandQueue, mNotificationManager, - mAccessibilityManager, new ToastFactory(mPluginManager, mDumpManager), - mFakeDelayableExecutor, mToastLogger); + mAccessibilityManager, new ToastFactory(mLayoutInflater, mPluginManager, + mDumpManager, mFeatureFlags), mToastLogger); } @Test diff --git a/services/core/java/com/android/server/am/BroadcastQueue.java b/services/core/java/com/android/server/am/BroadcastQueue.java index 7e65434b8189..8b6fabd7faff 100644 --- a/services/core/java/com/android/server/am/BroadcastQueue.java +++ b/services/core/java/com/android/server/am/BroadcastQueue.java @@ -967,6 +967,12 @@ public final class BroadcastQueue { } } + static String broadcastDescription(BroadcastRecord r, ComponentName component) { + return r.intent.toString() + + " from " + r.callerPackage + " (pid=" + r.callingPid + + ", uid=" + r.callingUid + ") to " + component.flattenToShortString(); + } + final void processNextBroadcastLocked(boolean fromMsg, boolean skipOomAdj) { BroadcastRecord r; @@ -1349,14 +1355,18 @@ public final class BroadcastQueue { < brOptions.getMinManifestReceiverApiLevel() || info.activityInfo.applicationInfo.targetSdkVersion > brOptions.getMaxManifestReceiverApiLevel())) { + Slog.w(TAG, "Target SDK mismatch: receiver " + info.activityInfo + + " targets " + info.activityInfo.applicationInfo.targetSdkVersion + + " but delivery restricted to [" + + brOptions.getMinManifestReceiverApiLevel() + ", " + + brOptions.getMaxManifestReceiverApiLevel() + + "] broadcasting " + broadcastDescription(r, component)); skip = true; } if (!skip && !mService.validateAssociationAllowedLocked(r.callerPackage, r.callingUid, component.getPackageName(), info.activityInfo.applicationInfo.uid)) { Slog.w(TAG, "Association not allowed: broadcasting " - + r.intent.toString() - + " from " + r.callerPackage + " (pid=" + r.callingPid - + ", uid=" + r.callingUid + ") to " + component.flattenToShortString()); + + broadcastDescription(r, component)); skip = true; } if (!skip) { @@ -1364,9 +1374,7 @@ public final class BroadcastQueue { r.callingPid, r.resolvedType, info.activityInfo.applicationInfo.uid); if (skip) { Slog.w(TAG, "Firewall blocked: broadcasting " - + r.intent.toString() - + " from " + r.callerPackage + " (pid=" + r.callingPid - + ", uid=" + r.callingUid + ") to " + component.flattenToShortString()); + + broadcastDescription(r, component)); } } int perm = mService.checkComponentPermission(info.activityInfo.permission, @@ -1375,18 +1383,12 @@ public final class BroadcastQueue { if (!skip && perm != PackageManager.PERMISSION_GRANTED) { if (!info.activityInfo.exported) { Slog.w(TAG, "Permission Denial: broadcasting " - + r.intent.toString() - + " from " + r.callerPackage + " (pid=" + r.callingPid - + ", uid=" + r.callingUid + ")" - + " is not exported from uid " + info.activityInfo.applicationInfo.uid - + " due to receiver " + component.flattenToShortString()); + + broadcastDescription(r, component) + + " is not exported from uid " + info.activityInfo.applicationInfo.uid); } else { Slog.w(TAG, "Permission Denial: broadcasting " - + r.intent.toString() - + " from " + r.callerPackage + " (pid=" + r.callingPid - + ", uid=" + r.callingUid + ")" - + " requires " + info.activityInfo.permission - + " due to receiver " + component.flattenToShortString()); + + broadcastDescription(r, component) + + " requires " + info.activityInfo.permission); } skip = true; } else if (!skip && info.activityInfo.permission != null) { @@ -1396,13 +1398,9 @@ public final class BroadcastQueue { "Broadcast delivered to " + info.activityInfo.name) != AppOpsManager.MODE_ALLOWED) { Slog.w(TAG, "Appop Denial: broadcasting " - + r.intent.toString() - + " from " + r.callerPackage + " (pid=" - + r.callingPid + ", uid=" + r.callingUid + ")" + + broadcastDescription(r, component) + " requires appop " + AppOpsManager.permissionToOp( - info.activityInfo.permission) - + " due to registered receiver " - + component.flattenToShortString()); + info.activityInfo.permission)); skip = true; } } @@ -1520,7 +1518,7 @@ public final class BroadcastQueue { + info.activityInfo.packageName, e); } if (!isAvailable) { - if (DEBUG_BROADCAST) Slog.v(TAG_BROADCAST, + Slog.w(TAG_BROADCAST, "Skipping delivery to " + info.activityInfo.packageName + " / " + info.activityInfo.applicationInfo.uid + " : package no longer available"); @@ -1536,6 +1534,9 @@ public final class BroadcastQueue { if (!requestStartTargetPermissionsReviewIfNeededLocked(r, info.activityInfo.packageName, UserHandle.getUserId( info.activityInfo.applicationInfo.uid))) { + Slog.w(TAG_BROADCAST, + "Skipping delivery: permission review required for " + + broadcastDescription(r, component)); skip = true; } } diff --git a/services/core/java/com/android/server/inputmethod/InputMethodManagerService.java b/services/core/java/com/android/server/inputmethod/InputMethodManagerService.java index 1a4c8b7d6571..00ae30e2aa0e 100644 --- a/services/core/java/com/android/server/inputmethod/InputMethodManagerService.java +++ b/services/core/java/com/android/server/inputmethod/InputMethodManagerService.java @@ -5246,12 +5246,7 @@ public class InputMethodManagerService extends IInputMethodManager.Stub imeTracing.startTrace(null); }); } - } - doDump(fd, pw, args, asProto); - } - private void doDump(FileDescriptor fd, PrintWriter pw, String[] args, boolean useProto) { - if (useProto) { final ProtoOutputStream proto = new ProtoOutputStream(fd); dumpDebug(proto, InputMethodManagerServiceTraceProto.INPUT_METHOD_MANAGER_SERVICE); proto.flush(); diff --git a/services/core/java/com/android/server/locksettings/ResumeOnRebootServiceProvider.java b/services/core/java/com/android/server/locksettings/ResumeOnRebootServiceProvider.java index a1e18bd5a6bd..5bd3f6455ecb 100644 --- a/services/core/java/com/android/server/locksettings/ResumeOnRebootServiceProvider.java +++ b/services/core/java/com/android/server/locksettings/ResumeOnRebootServiceProvider.java @@ -120,6 +120,7 @@ public class ResumeOnRebootServiceProvider { if (mServiceConnection != null) { mContext.unbindService(mServiceConnection); } + mBinder = null; } /** Bind to the service */ diff --git a/services/core/java/com/android/server/notification/NotificationManagerService.java b/services/core/java/com/android/server/notification/NotificationManagerService.java index 4c3dfbff3f87..5703ffec21a0 100755 --- a/services/core/java/com/android/server/notification/NotificationManagerService.java +++ b/services/core/java/com/android/server/notification/NotificationManagerService.java @@ -110,6 +110,8 @@ import static com.android.internal.util.Preconditions.checkArgument; import static com.android.server.am.PendingIntentRecord.FLAG_ACTIVITY_SENDER; import static com.android.server.am.PendingIntentRecord.FLAG_BROADCAST_SENDER; import static com.android.server.am.PendingIntentRecord.FLAG_SERVICE_SENDER; +import static com.android.server.policy.PhoneWindowManager.TOAST_WINDOW_ANIM_BUFFER; +import static com.android.server.policy.PhoneWindowManager.TOAST_WINDOW_TIMEOUT; import static com.android.server.utils.PriorityDump.PRIORITY_ARG; import static com.android.server.utils.PriorityDump.PRIORITY_ARG_CRITICAL; import static com.android.server.utils.PriorityDump.PRIORITY_ARG_NORMAL; @@ -284,7 +286,6 @@ import com.android.server.notification.toast.CustomToastRecord; import com.android.server.notification.toast.TextToastRecord; import com.android.server.notification.toast.ToastRecord; import com.android.server.pm.PackageManagerService; -import com.android.server.policy.PhoneWindowManager; import com.android.server.statusbar.StatusBarManagerInternal; import com.android.server.uri.UriGrantsManagerInternal; import com.android.server.utils.quota.MultiRateLimiter; @@ -358,7 +359,7 @@ public class NotificationManagerService extends SystemService { private static final int MESSAGE_RECONSIDER_RANKING = 1000; private static final int MESSAGE_RANKING_SORT = 1001; - static final int LONG_DELAY = PhoneWindowManager.TOAST_WINDOW_TIMEOUT; + static final int LONG_DELAY = TOAST_WINDOW_TIMEOUT - TOAST_WINDOW_ANIM_BUFFER; // 3.5 seconds static final int SHORT_DELAY = 2000; // 2 seconds // 1 second past the ANR timeout. @@ -3056,7 +3057,7 @@ public class NotificationManagerService extends SystemService { // If the callback fails, this will remove it from the list, so don't // assume that it's valid after this. if (index == 0) { - showNextToastLocked(); + showNextToastLocked(false); } } finally { Binder.restoreCallingIdentity(callingId); @@ -7392,7 +7393,7 @@ public class NotificationManagerService extends SystemService { } @GuardedBy("mToastQueue") - void showNextToastLocked() { + void showNextToastLocked(boolean lastToastWasTextRecord) { if (mIsCurrentToastShown) { return; // Don't show the same toast twice. } @@ -7406,7 +7407,7 @@ public class NotificationManagerService extends SystemService { mToastRateLimiter.isWithinQuota(userId, record.pkg, TOAST_QUOTA_TAG); if (tryShowToast(record, rateLimitingEnabled, isWithinQuota)) { - scheduleDurationReachedLocked(record); + scheduleDurationReachedLocked(record, lastToastWasTextRecord); mIsCurrentToastShown = true; if (rateLimitingEnabled) { mToastRateLimiter.noteEvent(userId, record.pkg, TOAST_QUOTA_TAG); @@ -7477,7 +7478,7 @@ public class NotificationManagerService extends SystemService { // Show the next one. If the callback fails, this will remove // it from the list, so don't assume that the list hasn't changed // after this point. - showNextToastLocked(); + showNextToastLocked(lastToast instanceof TextToastRecord); } } @@ -7491,7 +7492,7 @@ public class NotificationManagerService extends SystemService { } @GuardedBy("mToastQueue") - private void scheduleDurationReachedLocked(ToastRecord r) + private void scheduleDurationReachedLocked(ToastRecord r, boolean lastToastWasTextRecord) { mHandler.removeCallbacksAndMessages(r); Message m = Message.obtain(mHandler, MESSAGE_DURATION_REACHED, r); @@ -7501,6 +7502,14 @@ public class NotificationManagerService extends SystemService { // preference. delay = mAccessibilityManager.getRecommendedTimeoutMillis(delay, AccessibilityManager.FLAG_CONTENT_TEXT); + + if (lastToastWasTextRecord) { + delay += 250; // delay to account for previous toast's "out" animation + } + if (r instanceof TextToastRecord) { + delay += 333; // delay to account for this toast's "in" animation + } + mHandler.sendMessageDelayed(m, delay); } diff --git a/services/core/java/com/android/server/policy/PhoneWindowManager.java b/services/core/java/com/android/server/policy/PhoneWindowManager.java index bc819617332d..6a441f1830d1 100644 --- a/services/core/java/com/android/server/policy/PhoneWindowManager.java +++ b/services/core/java/com/android/server/policy/PhoneWindowManager.java @@ -346,8 +346,21 @@ public class PhoneWindowManager implements WindowManagerPolicy { /** Amount of time (in milliseconds) to wait for windows drawn before powering on. */ static final int WAITING_FOR_DRAWN_TIMEOUT = 1000; - /** Amount of time (in milliseconds) a toast window can be shown. */ - public static final int TOAST_WINDOW_TIMEOUT = 3500; // 3.5 seconds + /** + * Extra time for additional SystemUI animations. + * <p>Since legacy apps can add Toast windows directly instead of using Toast APIs, + * {@link DisplayPolicy} ensures that the window manager removes toast windows after + * TOAST_WINDOW_TIMEOUT. We increase this timeout by TOAST_WINDOW_ANIM_BUFFER to account for + * SystemUI's in/out toast animations, so that the toast text is still shown for a minimum + * of 3.5 seconds and the animations are finished before window manager removes the window. + */ + public static final int TOAST_WINDOW_ANIM_BUFFER = 600; + + /** + * Amount of time (in milliseconds) a toast window can be shown before it's automatically + * removed by window manager. + */ + public static final int TOAST_WINDOW_TIMEOUT = 3500 + TOAST_WINDOW_ANIM_BUFFER; /** * Lock protecting internal state. Must not call out into window diff --git a/services/core/java/com/android/server/wm/RecentTasks.java b/services/core/java/com/android/server/wm/RecentTasks.java index 6e8110e9c36e..d81181d46417 100644 --- a/services/core/java/com/android/server/wm/RecentTasks.java +++ b/services/core/java/com/android/server/wm/RecentTasks.java @@ -58,6 +58,8 @@ import android.content.pm.ParceledListSlice; import android.content.pm.UserInfo; import android.content.res.Resources; import android.graphics.Bitmap; +import android.graphics.Point; +import android.graphics.Rect; import android.os.Environment; import android.os.IBinder; import android.os.RemoteException; @@ -1886,6 +1888,7 @@ class RecentTasks { // Fill in some deprecated values. rti.id = rti.isRunning ? rti.taskId : INVALID_TASK_ID; rti.persistentId = rti.taskId; + rti.lastSnapshotData.set(tr.mLastTaskSnapshotData); // Fill in organized child task info for the task created by organizer. if (tr.mCreatedByOrganizer) { diff --git a/services/core/java/com/android/server/wm/Task.java b/services/core/java/com/android/server/wm/Task.java index 9bbbbe0a8535..9c8a997ec098 100644 --- a/services/core/java/com/android/server/wm/Task.java +++ b/services/core/java/com/android/server/wm/Task.java @@ -158,6 +158,7 @@ import android.annotation.NonNull; import android.annotation.Nullable; import android.app.Activity; import android.app.ActivityManager; +import android.app.ActivityManager.RecentTaskInfo.PersistedTaskSnapshotData; import android.app.ActivityManager.TaskDescription; import android.app.ActivityOptions; import android.app.ActivityTaskManager; @@ -293,6 +294,9 @@ class Task extends WindowContainer<WindowContainer> { private static final String ATTR_MIN_HEIGHT = "min_height"; private static final String ATTR_PERSIST_TASK_VERSION = "persist_task_version"; private static final String ATTR_WINDOW_LAYOUT_AFFINITY = "window_layout_affinity"; + private static final String ATTR_LAST_SNAPSHOT_TASK_SIZE = "last_snapshot_task_size"; + private static final String ATTR_LAST_SNAPSHOT_CONTENT_INSETS = "last_snapshot_content_insets"; + private static final String ATTR_LAST_SNAPSHOT_BUFFER_SIZE = "last_snapshot_buffer_size"; // Set to false to disable the preview that is shown while a new activity // is being started. @@ -540,6 +544,10 @@ class Task extends WindowContainer<WindowContainer> { // NOTE: This value needs to be persisted with each task private TaskDescription mTaskDescription; + // Information about the last snapshot that should be persisted with the task to allow SystemUI + // to layout without loading all the task snapshots + final PersistedTaskSnapshotData mLastTaskSnapshotData; + // If set to true, the task will report that it is not in the floating // state regardless of it's root task affiliation. As the floating state drives // production of content insets this can be used to preserve them across @@ -613,8 +621,6 @@ class Task extends WindowContainer<WindowContainer> { SurfaceControl.Transaction mMainWindowSizeChangeTransaction; Task mMainWindowSizeChangeTask; - Rect mPreAnimationBounds = new Rect(); - private final AnimatingActivityRegistry mAnimatingActivityRegistry = new AnimatingActivityRegistry(); @@ -839,13 +845,13 @@ class Task extends WindowContainer<WindowContainer> { ComponentName _realActivity, ComponentName _origActivity, boolean _rootWasReset, boolean _autoRemoveRecents, boolean _askedCompatMode, int _userId, int _effectiveUid, String _lastDescription, long lastTimeMoved, boolean neverRelinquishIdentity, - TaskDescription _lastTaskDescription, int taskAffiliation, int prevTaskId, - int nextTaskId, int callingUid, String callingPackage, - @Nullable String callingFeatureId, int resizeMode, boolean supportsPictureInPicture, - boolean _realActivitySuspended, boolean userSetupComplete, int minWidth, int minHeight, - ActivityInfo info, IVoiceInteractionSession _voiceSession, - IVoiceInteractor _voiceInteractor, boolean _createdByOrganizer, - IBinder _launchCookie, boolean _deferTaskAppear) { + TaskDescription _lastTaskDescription, PersistedTaskSnapshotData _lastSnapshotData, + int taskAffiliation, int prevTaskId, int nextTaskId, int callingUid, + String callingPackage, @Nullable String callingFeatureId, int resizeMode, + boolean supportsPictureInPicture, boolean _realActivitySuspended, + boolean userSetupComplete, int minWidth, int minHeight, ActivityInfo info, + IVoiceInteractionSession _voiceSession, IVoiceInteractor _voiceInteractor, + boolean _createdByOrganizer, IBinder _launchCookie, boolean _deferTaskAppear) { super(atmService.mWindowManager); mAtmService = atmService; @@ -855,7 +861,12 @@ class Task extends WindowContainer<WindowContainer> { mUserId = _userId; mResizeMode = resizeMode; mSupportsPictureInPicture = supportsPictureInPicture; - mTaskDescription = _lastTaskDescription; + mTaskDescription = _lastTaskDescription != null + ? _lastTaskDescription + : new TaskDescription(); + mLastTaskSnapshotData = _lastSnapshotData != null + ? _lastSnapshotData + : new PersistedTaskSnapshotData(); // Tasks have no set orientation value (including SCREEN_ORIENTATION_UNSPECIFIED). setOrientation(SCREEN_ORIENTATION_UNSET); mRemoteToken = new RemoteToken(this); @@ -3899,6 +3910,7 @@ class Task extends WindowContainer<WindowContainer> { } void onSnapshotChanged(TaskSnapshot snapshot) { + mLastTaskSnapshotData.set(snapshot); mAtmService.getTaskChangeNotificationController().notifyTaskSnapshotChanged( mTaskId, snapshot); } @@ -4157,7 +4169,6 @@ class Task extends WindowContainer<WindowContainer> { void fillTaskInfo(TaskInfo info, boolean stripExtras) { getNumRunningActivities(mReuseActivitiesReport); info.userId = isLeafTask() ? mUserId : mCurrentUser; - info.stackId = getRootTaskId(); info.taskId = mTaskId; info.displayId = getDisplayId(); info.isRunning = getTopNonFinishingActivity() != null; @@ -4707,6 +4718,19 @@ class Task extends WindowContainer<WindowContainer> { out.attributeInt(null, ATTR_MIN_HEIGHT, mMinHeight); out.attributeInt(null, ATTR_PERSIST_TASK_VERSION, PERSIST_TASK_VERSION); + if (mLastTaskSnapshotData.taskSize != null) { + out.attribute(null, ATTR_LAST_SNAPSHOT_TASK_SIZE, + mLastTaskSnapshotData.taskSize.flattenToString()); + } + if (mLastTaskSnapshotData.contentInsets != null) { + out.attribute(null, ATTR_LAST_SNAPSHOT_CONTENT_INSETS, + mLastTaskSnapshotData.contentInsets.flattenToString()); + } + if (mLastTaskSnapshotData.bufferSize != null) { + out.attribute(null, ATTR_LAST_SNAPSHOT_BUFFER_SIZE, + mLastTaskSnapshotData.bufferSize.flattenToString()); + } + if (affinityIntent != null) { out.startTag(null, TAG_AFFINITYINTENT); affinityIntent.saveToXml(out); @@ -4774,6 +4798,7 @@ class Task extends WindowContainer<WindowContainer> { int taskId = INVALID_TASK_ID; final int outerDepth = in.getDepth(); TaskDescription taskDescription = new TaskDescription(); + PersistedTaskSnapshotData lastSnapshotData = new PersistedTaskSnapshotData(); int taskAffiliation = INVALID_TASK_ID; int prevTaskId = INVALID_TASK_ID; int nextTaskId = INVALID_TASK_ID; @@ -4883,6 +4908,15 @@ class Task extends WindowContainer<WindowContainer> { case ATTR_PERSIST_TASK_VERSION: persistTaskVersion = Integer.parseInt(attrValue); break; + case ATTR_LAST_SNAPSHOT_TASK_SIZE: + lastSnapshotData.taskSize = Point.unflattenFromString(attrValue); + break; + case ATTR_LAST_SNAPSHOT_CONTENT_INSETS: + lastSnapshotData.contentInsets = Rect.unflattenFromString(attrValue); + break; + case ATTR_LAST_SNAPSHOT_BUFFER_SIZE: + lastSnapshotData.bufferSize = Point.unflattenFromString(attrValue); + break; default: if (!attrName.startsWith(TaskDescription.ATTR_TASKDESCRIPTION_PREFIX)) { Slog.w(TAG, "Task: Unknown attribute=" + attrName); @@ -4977,6 +5011,7 @@ class Task extends WindowContainer<WindowContainer> { .setLastTimeMoved(lastTimeOnTop) .setNeverRelinquishIdentity(neverRelinquishIdentity) .setLastTaskDescription(taskDescription) + .setLastSnapshotData(lastSnapshotData) .setTaskAffiliation(taskAffiliation) .setPrevAffiliateTaskId(prevTaskId) .setNextAffiliateTaskId(nextTaskId) @@ -7904,6 +7939,7 @@ class Task extends WindowContainer<WindowContainer> { private long mLastTimeMoved; private boolean mNeverRelinquishIdentity; private TaskDescription mLastTaskDescription; + private PersistedTaskSnapshotData mLastSnapshotData; private int mTaskAffiliation; private int mPrevAffiliateTaskId = INVALID_TASK_ID; private int mNextAffiliateTaskId = INVALID_TASK_ID; @@ -8104,6 +8140,11 @@ class Task extends WindowContainer<WindowContainer> { return this; } + private Builder setLastSnapshotData(PersistedTaskSnapshotData lastSnapshotData) { + mLastSnapshotData = lastSnapshotData; + return this; + } + private Builder setOrigActivity(ComponentName origActivity) { mOrigActivity = origActivity; return this; @@ -8217,9 +8258,6 @@ class Task extends WindowContainer<WindowContainer> { mCallingPackage = mActivityInfo.packageName; mResizeMode = mActivityInfo.resizeMode; mSupportsPictureInPicture = mActivityInfo.supportsPictureInPicture(); - if (mLastTaskDescription == null) { - mLastTaskDescription = new TaskDescription(); - } final Task task = buildInner(); task.mHasBeenVisible = mHasBeenVisible; @@ -8253,9 +8291,9 @@ class Task extends WindowContainer<WindowContainer> { return new Task(mAtmService, mTaskId, mIntent, mAffinityIntent, mAffinity, mRootAffinity, mRealActivity, mOrigActivity, mRootWasReset, mAutoRemoveRecents, mAskedCompatMode, mUserId, mEffectiveUid, mLastDescription, mLastTimeMoved, - mNeverRelinquishIdentity, mLastTaskDescription, mTaskAffiliation, - mPrevAffiliateTaskId, mNextAffiliateTaskId, mCallingUid, mCallingPackage, - mCallingFeatureId, mResizeMode, mSupportsPictureInPicture, + mNeverRelinquishIdentity, mLastTaskDescription, mLastSnapshotData, + mTaskAffiliation, mPrevAffiliateTaskId, mNextAffiliateTaskId, mCallingUid, + mCallingPackage, mCallingFeatureId, mResizeMode, mSupportsPictureInPicture, mRealActivitySuspended, mUserSetupComplete, mMinWidth, mMinHeight, mActivityInfo, mVoiceSession, mVoiceInteractor, mCreatedByOrganizer, mLaunchCookie, mDeferTaskAppear); diff --git a/services/devicepolicy/java/com/android/server/devicepolicy/DevicePolicyManagerService.java b/services/devicepolicy/java/com/android/server/devicepolicy/DevicePolicyManagerService.java index bd109922cb42..07eb7bf8f9a0 100644 --- a/services/devicepolicy/java/com/android/server/devicepolicy/DevicePolicyManagerService.java +++ b/services/devicepolicy/java/com/android/server/devicepolicy/DevicePolicyManagerService.java @@ -16328,8 +16328,10 @@ public class DevicePolicyManagerService extends BaseIDevicePolicyManager { setTimeAndTimezone(provisioningParams.getTimeZone(), provisioningParams.getLocalTime()); setLocale(provisioningParams.getLocale()); + final int deviceOwnerUserId = mInjector.userManagerIsHeadlessSystemUserMode() + ? UserHandle.USER_SYSTEM : caller.getUserId(); if (!removeNonRequiredAppsForManagedDevice( - caller.getUserId(), + deviceOwnerUserId, provisioningParams.isLeaveAllSystemAppsEnabled(), deviceAdmin)) { throw new ServiceSpecificException( @@ -16337,15 +16339,16 @@ public class DevicePolicyManagerService extends BaseIDevicePolicyManager { "PackageManager failed to remove non required apps."); } + if (!setActiveAdminAndDeviceOwner( - caller.getUserId(), deviceAdmin, provisioningParams.getOwnerName())) { + deviceOwnerUserId, deviceAdmin, provisioningParams.getOwnerName())) { throw new ServiceSpecificException( PROVISIONING_RESULT_SET_DEVICE_OWNER_FAILED, "Failed to set device owner."); } disallowAddUser(); - setAdminCanGrantSensorsPermissionForUserUnchecked(caller.getUserId(), + setAdminCanGrantSensorsPermissionForUserUnchecked(deviceOwnerUserId, provisioningParams.canDeviceOwnerGrantSensorsPermissions()); } catch (Exception e) { DevicePolicyEventLogger @@ -16388,30 +16391,42 @@ public class DevicePolicyManagerService extends BaseIDevicePolicyManager { } private boolean removeNonRequiredAppsForManagedDevice( - int userId, boolean leaveAllSystemAppsEnabled, ComponentName admin) { + @UserIdInt int userId, boolean leaveAllSystemAppsEnabled, ComponentName admin) { Set<String> packagesToDelete = leaveAllSystemAppsEnabled ? Collections.emptySet() : mOverlayPackagesProvider.getNonRequiredApps( admin, userId, ACTION_PROVISION_MANAGED_DEVICE); + + removeNonInstalledPackages(packagesToDelete, userId); if (packagesToDelete.isEmpty()) { + Slog.i(LOG_TAG, "No packages to delete on user " + userId); return true; } + NonRequiredPackageDeleteObserver packageDeleteObserver = new NonRequiredPackageDeleteObserver(packagesToDelete.size()); for (String packageName : packagesToDelete) { - if (isPackageInstalledForUser(packageName, userId)) { - Slog.i(LOG_TAG, "Deleting package [" + packageName + "] as user " + userId); - mContext.getPackageManager().deletePackageAsUser( - packageName, - packageDeleteObserver, - PackageManager.DELETE_SYSTEM_APP, - userId); - } + Slog.i(LOG_TAG, "Deleting package [" + packageName + "] as user " + userId); + mContext.getPackageManager().deletePackageAsUser( + packageName, + packageDeleteObserver, + PackageManager.DELETE_SYSTEM_APP, + userId); } Slog.i(LOG_TAG, "Waiting for non required apps to be deleted"); return packageDeleteObserver.awaitPackagesDeletion(); } + private void removeNonInstalledPackages(Set<String> packages, @UserIdInt int userId) { + final Set<String> toBeRemoved = new HashSet<>(); + for (String packageName : packages) { + if (!isPackageInstalledForUser(packageName, userId)) { + toBeRemoved.add(packageName); + } + } + packages.removeAll(toBeRemoved); + } + private void disallowAddUser() { if (mInjector.userManagerIsHeadlessSystemUserMode()) { Slog.i(LOG_TAG, "Not setting DISALLOW_ADD_USER on headless system user mode."); diff --git a/services/java/com/android/server/SystemServer.java b/services/java/com/android/server/SystemServer.java index 98c3b99124ee..97e75828ed91 100644 --- a/services/java/com/android/server/SystemServer.java +++ b/services/java/com/android/server/SystemServer.java @@ -525,8 +525,7 @@ public final class SystemServer implements Dumpable { String filename = "/data/system/heapdump/fdtrack-" + date + ".hprof"; Debug.dumpHprofData(filename); } catch (IOException ex) { - Slog.e("System", "Failed to dump fdtrack hprof"); - ex.printStackTrace(); + Slog.e("System", "Failed to dump fdtrack hprof", ex); } } diff --git a/services/tests/wmtests/src/com/android/server/wm/RecentTasksTest.java b/services/tests/wmtests/src/com/android/server/wm/RecentTasksTest.java index 21fd04ee3ae9..925b6f9601be 100644 --- a/services/tests/wmtests/src/com/android/server/wm/RecentTasksTest.java +++ b/services/tests/wmtests/src/com/android/server/wm/RecentTasksTest.java @@ -29,6 +29,7 @@ import static android.content.Intent.FLAG_ACTIVITY_NEW_DOCUMENT; import static android.content.Intent.FLAG_ACTIVITY_NEW_TASK; import static android.content.pm.ActivityInfo.LAUNCH_MULTIPLE; import static android.content.pm.ActivityInfo.LAUNCH_SINGLE_INSTANCE; +import static android.content.res.Configuration.ORIENTATION_PORTRAIT; import static com.android.dx.mockito.inline.extended.ExtendedMockito.doNothing; import static com.android.dx.mockito.inline.extended.ExtendedMockito.doReturn; @@ -59,6 +60,10 @@ import android.app.ActivityTaskManager; import android.content.ComponentName; import android.content.pm.ParceledListSlice; import android.content.pm.UserInfo; +import android.graphics.ColorSpace; +import android.graphics.Point; +import android.graphics.Rect; +import android.hardware.HardwareBuffer; import android.os.Bundle; import android.os.RemoteException; import android.os.SystemClock; @@ -66,6 +71,8 @@ import android.os.UserManager; import android.platform.test.annotations.Presubmit; import android.util.ArraySet; import android.util.SparseBooleanArray; +import android.view.Surface; +import android.window.TaskSnapshot; import androidx.test.filters.MediumTest; @@ -446,12 +453,8 @@ public class RecentTasksTest extends WindowTestsBase { doReturn(false).when(child2).isOrganized(); mRecentTasks.add(root); - doNothing().when(mRecentTasks).loadUserRecentsLocked(anyInt()); - doReturn(true).when(mRecentTasks).isUserRunning(anyInt(), anyInt()); - final List<RecentTaskInfo> infos = mRecentTasks.getRecentTasks(MAX_VALUE, 0 /* flags */, - true /* getTasksAllowed */, TEST_USER_0_ID, 0 /* callingUid */).getList(); - // Make sure only organized child will be appended. + final List<RecentTaskInfo> infos = getRecentTasks(0 /* flags */); final List<RecentTaskInfo> childrenTaskInfos = infos.get(0).childrenTaskInfos; assertEquals(childrenTaskInfos.size(), 1); assertEquals(childrenTaskInfos.get(0).taskId, child1.mTaskId); @@ -1051,9 +1054,6 @@ public class RecentTasksTest extends WindowTestsBase { @Test public void testTaskInfo_expectNoExtras() { - doNothing().when(mRecentTasks).loadUserRecentsLocked(anyInt()); - doReturn(true).when(mRecentTasks).isUserRunning(anyInt(), anyInt()); - final Bundle data = new Bundle(); data.putInt("key", 100); final Task task1 = createTaskBuilder(".Task").build(); @@ -1063,8 +1063,7 @@ public class RecentTasksTest extends WindowTestsBase { .build(); mRecentTasks.add(r1.getTask()); - final List<RecentTaskInfo> infos = mRecentTasks.getRecentTasks(MAX_VALUE, 0 /* flags */, - true /* getTasksAllowed */, TEST_USER_0_ID, 0).getList(); + final List<RecentTaskInfo> infos = getRecentTasks(0 /* flags */); assertTrue(infos.size() == 1); for (int i = 0; i < infos.size(); i++) { final Bundle extras = infos.get(i).baseIntent.getExtras(); @@ -1072,6 +1071,60 @@ public class RecentTasksTest extends WindowTestsBase { } } + @Test + public void testLastSnapshotData_snapshotSaved() { + final TaskSnapshot snapshot = createSnapshot(new Point(100, 100), new Point(80, 80)); + final Task task1 = createTaskBuilder(".Task").build(); + task1.onSnapshotChanged(snapshot); + + mRecentTasks.add(task1); + final List<RecentTaskInfo> infos = getRecentTasks(0 /* flags */); + final RecentTaskInfo.PersistedTaskSnapshotData lastSnapshotData = + infos.get(0).lastSnapshotData; + assertTrue(lastSnapshotData.taskSize.equals(100, 100)); + assertTrue(lastSnapshotData.bufferSize.equals(80, 80)); + } + + @Test + public void testLastSnapshotData_noBuffer() { + final Task task1 = createTaskBuilder(".Task").build(); + final TaskSnapshot snapshot = createSnapshot(new Point(100, 100), null); + task1.onSnapshotChanged(snapshot); + + mRecentTasks.add(task1); + final List<RecentTaskInfo> infos = getRecentTasks(0 /* flags */); + final RecentTaskInfo.PersistedTaskSnapshotData lastSnapshotData = + infos.get(0).lastSnapshotData; + assertTrue(lastSnapshotData.taskSize.equals(100, 100)); + assertNull(lastSnapshotData.bufferSize); + } + + @Test + public void testLastSnapshotData_notSet() { + final Task task1 = createTaskBuilder(".Task").build(); + + mRecentTasks.add(task1); + final List<RecentTaskInfo> infos = getRecentTasks(0 /* flags */); + final RecentTaskInfo.PersistedTaskSnapshotData lastSnapshotData = + infos.get(0).lastSnapshotData; + assertNull(lastSnapshotData.taskSize); + assertNull(lastSnapshotData.bufferSize); + } + + private TaskSnapshot createSnapshot(Point taskSize, Point bufferSize) { + HardwareBuffer buffer = null; + if (bufferSize != null) { + buffer = mock(HardwareBuffer.class); + doReturn(bufferSize.x).when(buffer).getWidth(); + doReturn(bufferSize.y).when(buffer).getHeight(); + } + return new TaskSnapshot(1, new ComponentName("", ""), buffer, + ColorSpace.get(ColorSpace.Named.SRGB), ORIENTATION_PORTRAIT, + Surface.ROTATION_0, taskSize, new Rect() /* insets */, false /* isLowResolution */, + true /* isRealSnapshot */, WINDOWING_MODE_FULLSCREEN, 0 /* mSystemUiVisibility */, + false /* isTranslucent */, false /* hasImeSurface */); + } + /** * Ensures that the raw recent tasks list is in the provided order. Note that the expected tasks * should be ordered from least to most recent. @@ -1084,15 +1137,19 @@ public class RecentTasksTest extends WindowTestsBase { } } + private List<RecentTaskInfo> getRecentTasks(int flags) { + doNothing().when(mRecentTasks).loadUserRecentsLocked(anyInt()); + doReturn(true).when(mRecentTasks).isUserRunning(anyInt(), anyInt()); + return mRecentTasks.getRecentTasks(MAX_VALUE, flags, true /* getTasksAllowed */, + TEST_USER_0_ID, 0 /* callingUid */).getList(); + } + /** * Ensures that the recent tasks list is in the provided order. Note that the expected tasks * should be ordered from least to most recent. */ private void assertGetRecentTasksOrder(int getRecentTaskFlags, Task... expectedTasks) { - doNothing().when(mRecentTasks).loadUserRecentsLocked(anyInt()); - doReturn(true).when(mRecentTasks).isUserRunning(anyInt(), anyInt()); - List<RecentTaskInfo> infos = mRecentTasks.getRecentTasks(MAX_VALUE, getRecentTaskFlags, - true /* getTasksAllowed */, TEST_USER_0_ID, 0).getList(); + List<RecentTaskInfo> infos = getRecentTasks(getRecentTaskFlags); assertTrue(expectedTasks.length == infos.size()); for (int i = 0; i < infos.size(); i++) { assertTrue(expectedTasks[i].mTaskId == infos.get(i).taskId); |