diff options
168 files changed, 5028 insertions, 3691 deletions
diff --git a/Android.bp b/Android.bp index 11f5c41c73bc..4a1f96ece71d 100644 --- a/Android.bp +++ b/Android.bp @@ -496,7 +496,6 @@ java_library { "//frameworks/base/apex/appsearch/framework", "//frameworks/base/apex/blobstore/framework", "//frameworks/base/apex/jobscheduler/framework", - "//frameworks/base/apex/statsd/service", "//frameworks/base/packages/Tethering/tests/unit", ], } diff --git a/apct-tests/perftests/packagemanager/src/android/os/PackageManagerPerfTest.java b/apct-tests/perftests/packagemanager/src/android/os/PackageManagerPerfTest.java index d7428cf0ab8a..761e9300398a 100644 --- a/apct-tests/perftests/packagemanager/src/android/os/PackageManagerPerfTest.java +++ b/apct-tests/perftests/packagemanager/src/android/os/PackageManagerPerfTest.java @@ -31,6 +31,7 @@ import androidx.test.ext.junit.runners.AndroidJUnit4; import androidx.test.filters.LargeTest; import androidx.test.platform.app.InstrumentationRegistry; +import org.junit.Before; import org.junit.Rule; import org.junit.Test; import org.junit.runner.RunWith; @@ -58,6 +59,12 @@ public class PackageManagerPerfTest { final Context context = InstrumentationRegistry.getInstrumentation().getContext(); } + @Before + public void setup() { + PackageManager.disableApplicationInfoCache(); + PackageManager.disablePackageInfoCache(); + } + @Test @DisableCompatChanges(PackageManager.FILTER_APPLICATION_QUERY) public void testCheckPermissionExists() { diff --git a/apex/jobscheduler/service/java/com/android/server/usage/AppStandbyController.java b/apex/jobscheduler/service/java/com/android/server/usage/AppStandbyController.java index a4ab31d7b49a..7a1b4f275ab0 100644 --- a/apex/jobscheduler/service/java/com/android/server/usage/AppStandbyController.java +++ b/apex/jobscheduler/service/java/com/android/server/usage/AppStandbyController.java @@ -1844,7 +1844,7 @@ public class AppStandbyController implements AppStandbyInternal { break; case MSG_REPORT_SYNC_SCHEDULED: - final boolean exempted = msg.arg1 > 0 ? true : false; + final boolean exempted = msg.arg2 > 0 ? true : false; if (exempted) { reportExemptedSyncScheduled((String) msg.obj, msg.arg1); } else { diff --git a/apex/media/framework/java/android/media/MediaParser.java b/apex/media/framework/java/android/media/MediaParser.java index 11d3a6867ac3..566f4cdca6da 100644 --- a/apex/media/framework/java/android/media/MediaParser.java +++ b/apex/media/framework/java/android/media/MediaParser.java @@ -1114,20 +1114,22 @@ public final class MediaParser { static { // Using a LinkedHashMap to keep the insertion order when iterating over the keys. LinkedHashMap<String, ExtractorFactory> extractorFactoriesByName = new LinkedHashMap<>(); - extractorFactoriesByName.put("exo.Ac3Parser", Ac3Extractor::new); - extractorFactoriesByName.put("exo.Ac4Parser", Ac4Extractor::new); - extractorFactoriesByName.put("exo.AdtsParser", AdtsExtractor::new); - extractorFactoriesByName.put("exo.AmrParser", AmrExtractor::new); - extractorFactoriesByName.put("exo.FlacParser", FlacExtractor::new); - extractorFactoriesByName.put("exo.FlvParser", FlvExtractor::new); - extractorFactoriesByName.put("exo.FragmentedMp4Parser", FragmentedMp4Extractor::new); + // Parsers are ordered to match ExoPlayer's DefaultExtractorsFactory extractor ordering, + // which in turn aims to minimize the chances of incorrect extractor selections. extractorFactoriesByName.put("exo.MatroskaParser", MatroskaExtractor::new); - extractorFactoriesByName.put("exo.Mp3Parser", Mp3Extractor::new); + extractorFactoriesByName.put("exo.FragmentedMp4Parser", FragmentedMp4Extractor::new); extractorFactoriesByName.put("exo.Mp4Parser", Mp4Extractor::new); + extractorFactoriesByName.put("exo.Mp3Parser", Mp3Extractor::new); + extractorFactoriesByName.put("exo.AdtsParser", AdtsExtractor::new); + extractorFactoriesByName.put("exo.Ac3Parser", Ac3Extractor::new); + extractorFactoriesByName.put("exo.TsParser", TsExtractor::new); + extractorFactoriesByName.put("exo.FlvParser", FlvExtractor::new); extractorFactoriesByName.put("exo.OggParser", OggExtractor::new); extractorFactoriesByName.put("exo.PsParser", PsExtractor::new); - extractorFactoriesByName.put("exo.TsParser", TsExtractor::new); extractorFactoriesByName.put("exo.WavParser", WavExtractor::new); + extractorFactoriesByName.put("exo.AmrParser", AmrExtractor::new); + extractorFactoriesByName.put("exo.Ac4Parser", Ac4Extractor::new); + extractorFactoriesByName.put("exo.FlacParser", FlacExtractor::new); EXTRACTOR_FACTORIES_BY_NAME = Collections.unmodifiableMap(extractorFactoriesByName); HashMap<String, Class> expectedTypeByParameterName = new HashMap<>(); diff --git a/api/current.txt b/api/current.txt index 1227006dbe04..93b3784c3d2b 100644 --- a/api/current.txt +++ b/api/current.txt @@ -29356,9 +29356,9 @@ package android.media.tv { public abstract class TvInputService extends android.app.Service { ctor public TvInputService(); method public final android.os.IBinder onBind(android.content.Intent); - method @Nullable public android.media.tv.TvInputService.RecordingSession onCreateRecordingSession(String); + method @Nullable public android.media.tv.TvInputService.RecordingSession onCreateRecordingSession(@NonNull String); method @Nullable public android.media.tv.TvInputService.RecordingSession onCreateRecordingSession(@NonNull String, @NonNull String); - method @Nullable public abstract android.media.tv.TvInputService.Session onCreateSession(String); + method @Nullable public abstract android.media.tv.TvInputService.Session onCreateSession(@NonNull String); method @Nullable public android.media.tv.TvInputService.Session onCreateSession(@NonNull String, @NonNull String); field public static final int PRIORITY_HINT_USE_CASE_TYPE_BACKGROUND = 100; // 0x64 field public static final int PRIORITY_HINT_USE_CASE_TYPE_LIVE = 400; // 0x190 @@ -48146,6 +48146,7 @@ package android.telephony { method public String getMmsUserAgent(); method @RequiresPermission("android.permission.READ_PRIVILEGED_PHONE_STATE") public String getNai(); method public String getNetworkCountryIso(); + method @NonNull public String getNetworkCountryIso(int); method public String getNetworkOperator(); method public String getNetworkOperatorName(); method @RequiresPermission(anyOf={"android.permission.READ_PRIVILEGED_PHONE_STATE", android.Manifest.permission.READ_PRECISE_PHONE_STATE}) public int getNetworkSelectionMode(); diff --git a/api/system-current.txt b/api/system-current.txt index f84b4158da08..3b95f3226341 100755 --- a/api/system-current.txt +++ b/api/system-current.txt @@ -11738,7 +11738,6 @@ package android.telephony { method public int getMaxNumberOfSimultaneouslyActiveSims(); method public static long getMaxNumberVerificationTimeoutMillis(); method @NonNull @RequiresPermission(android.Manifest.permission.READ_PRIVILEGED_PHONE_STATE) public String[] getMergedImsisFromGroup(); - method @NonNull @RequiresPermission(android.Manifest.permission.READ_PRIVILEGED_PHONE_STATE) public String getNetworkCountryIso(int); method @RequiresPermission(android.Manifest.permission.READ_PRIVILEGED_PHONE_STATE) public long getPreferredNetworkTypeBitmask(); method @RequiresPermission(anyOf={android.Manifest.permission.READ_PRIVILEGED_PHONE_STATE, android.Manifest.permission.READ_PHONE_STATE}) public int getRadioPowerState(); method public int getSimApplicationState(); diff --git a/api/test-current.txt b/api/test-current.txt index 2d15c0ea0ffb..0f8694f7435b 100644 --- a/api/test-current.txt +++ b/api/test-current.txt @@ -118,7 +118,6 @@ package android.app { method @RequiresPermission(android.Manifest.permission.MANAGE_ACTIVITY_STACKS) public void removeStacksWithActivityTypes(int[]) throws java.lang.SecurityException; method @RequiresPermission(android.Manifest.permission.MANAGE_ACTIVITY_STACKS) public void requestPictureInPictureMode(@NonNull android.os.IBinder); method @RequiresPermission(android.Manifest.permission.MANAGE_ACTIVITY_STACKS) public void resizeDockedStack(android.graphics.Rect, android.graphics.Rect); - method @RequiresPermission(android.Manifest.permission.MANAGE_ACTIVITY_STACKS) public void resizePinnedStack(int, android.graphics.Rect, boolean); method @RequiresPermission(android.Manifest.permission.MANAGE_ACTIVITY_STACKS) public void resizeTask(int, android.graphics.Rect); method @RequiresPermission(android.Manifest.permission.MANAGE_ACTIVITY_STACKS) public void setDisplayToSingleTaskInstance(int); method @RequiresPermission(android.Manifest.permission.MANAGE_ACTIVITY_STACKS) public boolean setTaskWindowingMode(int, int, boolean) throws java.lang.SecurityException; @@ -413,6 +412,12 @@ package android.app { field public static final String COLUMN_MEDIASTORE_URI = "mediastore_uri"; } + public class DreamManager { + method @RequiresPermission("android.permission.WRITE_DREAM_STATE") public void setActiveDream(@NonNull android.content.ComponentName); + method @RequiresPermission("android.permission.WRITE_DREAM_STATE") public void startDream(@NonNull android.content.ComponentName); + method @RequiresPermission("android.permission.WRITE_DREAM_STATE") public void stopDream(); + } + public final class NotificationChannel implements android.os.Parcelable { method public int getOriginalImportance(); method public boolean isBlockableSystem(); @@ -792,6 +797,7 @@ package android.content { field public static final String BUGREPORT_SERVICE = "bugreport"; field public static final String CONTENT_CAPTURE_MANAGER_SERVICE = "content_capture"; field public static final String DEVICE_IDLE_CONTROLLER = "deviceidle"; + field public static final String DREAM_SERVICE = "dream"; field public static final String ETHERNET_SERVICE = "ethernet"; field public static final String NETWORK_STACK_SERVICE = "network_stack"; field public static final String PERMISSION_SERVICE = "permission"; @@ -975,6 +981,14 @@ package android.content.pm { field @Nullable public final String backgroundPermission; } + public final class ProviderInfoList implements android.os.Parcelable { + method public int describeContents(); + method @NonNull public static android.content.pm.ProviderInfoList fromList(@NonNull java.util.List<android.content.pm.ProviderInfo>); + method @NonNull public java.util.List<android.content.pm.ProviderInfo> getList(); + method public void writeToParcel(@NonNull android.os.Parcel, int); + field @NonNull public static final android.os.Parcelable.Creator<android.content.pm.ProviderInfoList> CREATOR; + } + public final class ShortcutInfo implements android.os.Parcelable { method public boolean isVisibleToPublisher(); } @@ -2463,7 +2477,9 @@ package android.os { } public final class Parcel { + method public boolean allowSquashing(); method public int readExceptionCode(); + method public void restoreAllowSquashing(boolean); } public class ParcelFileDescriptor implements java.io.Closeable android.os.Parcelable { @@ -3753,7 +3769,6 @@ package android.telephony { method @NonNull public java.util.List<android.telephony.data.ApnSetting> getDevicePolicyOverrideApns(@NonNull android.content.Context); method public int getEmergencyNumberDbVersion(); method @RequiresPermission(android.Manifest.permission.READ_PHONE_STATE) public String getLine1AlphaTag(); - method @NonNull @RequiresPermission("android.permission.READ_PRIVILEGED_PHONE_STATE") public String getNetworkCountryIso(int); method public android.util.Pair<java.lang.Integer,java.lang.Integer> getRadioHalVersion(); method public boolean modifyDevicePolicyOverrideApn(@NonNull android.content.Context, int, @NonNull android.telephony.data.ApnSetting); method @RequiresPermission(android.Manifest.permission.MODIFY_PHONE_STATE) public void refreshUiccProfile(); diff --git a/core/java/android/app/ActivityManager.java b/core/java/android/app/ActivityManager.java index 82fdb90be165..b51bbdf62286 100644 --- a/core/java/android/app/ActivityManager.java +++ b/core/java/android/app/ActivityManager.java @@ -2012,15 +2012,16 @@ public class ActivityManager { /** See {@link android.view.Surface.Rotation} */ @Surface.Rotation private int mRotation; + /** The size of the snapshot before scaling */ + private final Point mTaskSize; private final Rect mContentInsets; - // Whether this snapshot is a down-sampled version of the full resolution, used mainly for - // low-ram devices + // Whether this snapshot is a down-sampled version of the high resolution snapshot, used + // mainly for loading snapshots quickly from disk when user is flinging fast private final boolean mIsLowResolution; // Whether or not the snapshot is a real snapshot or an app-theme generated snapshot due to // the task having a secure window or having previews disabled private final boolean mIsRealSnapshot; private final int mWindowingMode; - private final float mScale; private final int mSystemUiVisibility; private final boolean mIsTranslucent; // Must be one of the named color spaces, otherwise, always use SRGB color space. @@ -2028,9 +2029,9 @@ public class ActivityManager { public TaskSnapshot(long id, @NonNull ComponentName topActivityComponent, GraphicBuffer snapshot, - @NonNull ColorSpace colorSpace, int orientation, int rotation, Rect contentInsets, - boolean isLowResolution, float scale, boolean isRealSnapshot, int windowingMode, - int systemUiVisibility, boolean isTranslucent) { + @NonNull ColorSpace colorSpace, int orientation, int rotation, Point taskSize, + Rect contentInsets, boolean isLowResolution, boolean isRealSnapshot, + int windowingMode, int systemUiVisibility, boolean isTranslucent) { mId = id; mTopActivityComponent = topActivityComponent; mSnapshot = snapshot; @@ -2038,9 +2039,9 @@ public class ActivityManager { ? ColorSpace.get(ColorSpace.Named.SRGB) : colorSpace; mOrientation = orientation; mRotation = rotation; + mTaskSize = new Point(taskSize); mContentInsets = new Rect(contentInsets); mIsLowResolution = isLowResolution; - mScale = scale; mIsRealSnapshot = isRealSnapshot; mWindowingMode = windowingMode; mSystemUiVisibility = systemUiVisibility; @@ -2057,9 +2058,9 @@ public class ActivityManager { : ColorSpace.get(ColorSpace.Named.SRGB); mOrientation = source.readInt(); mRotation = source.readInt(); + mTaskSize = source.readParcelable(null /* classLoader */); mContentInsets = source.readParcelable(null /* classLoader */); mIsLowResolution = source.readBoolean(); - mScale = source.readFloat(); mIsRealSnapshot = source.readBoolean(); mWindowingMode = source.readInt(); mSystemUiVisibility = source.readInt(); @@ -2111,6 +2112,14 @@ public class ActivityManager { } /** + * @return The size of the task at the point this snapshot was taken. + */ + @UnsupportedAppUsage + public Point getTaskSize() { + return mTaskSize; + } + + /** * @return The system/content insets on the snapshot. These can be clipped off in order to * remove any areas behind system bars in the snapshot. */ @@ -2159,14 +2168,6 @@ public class ActivityManager { return mSystemUiVisibility; } - /** - * @return The scale this snapshot was taken in. - */ - @UnsupportedAppUsage - public float getScale() { - return mScale; - } - @Override public int describeContents() { return 0; @@ -2180,9 +2181,9 @@ public class ActivityManager { dest.writeInt(mColorSpace.getId()); dest.writeInt(mOrientation); dest.writeInt(mRotation); + dest.writeParcelable(mTaskSize, 0); dest.writeParcelable(mContentInsets, 0); dest.writeBoolean(mIsLowResolution); - dest.writeFloat(mScale); dest.writeBoolean(mIsRealSnapshot); dest.writeInt(mWindowingMode); dest.writeInt(mSystemUiVisibility); @@ -2200,9 +2201,11 @@ public class ActivityManager { + " mColorSpace=" + mColorSpace.toString() + " mOrientation=" + mOrientation + " mRotation=" + mRotation + + " mTaskSize=" + mTaskSize.toString() + " mContentInsets=" + mContentInsets.toShortString() - + " mIsLowResolution=" + mIsLowResolution + " mScale=" + mScale - + " mIsRealSnapshot=" + mIsRealSnapshot + " mWindowingMode=" + mWindowingMode + + " mIsLowResolution=" + mIsLowResolution + + " mIsRealSnapshot=" + mIsRealSnapshot + + " mWindowingMode=" + mWindowingMode + " mSystemUiVisibility=" + mSystemUiVisibility + " mIsTranslucent=" + mIsTranslucent; } @@ -2224,9 +2227,8 @@ public class ActivityManager { private ColorSpace mColorSpace; private int mOrientation; private int mRotation; + private Point mTaskSize; private Rect mContentInsets; - private boolean mIsLowResolution; - private float mScaleFraction; private boolean mIsRealSnapshot; private int mWindowingMode; private int mSystemUiVisibility; @@ -2263,25 +2265,16 @@ public class ActivityManager { return this; } - public Builder setContentInsets(Rect contentInsets) { - mContentInsets = contentInsets; - return this; - } - /** - * Set to true if this is a low-resolution snapshot stored in *_reduced.jpg. + * Sets the original size of the task */ - public Builder setIsLowResolution(boolean isLowResolution) { - mIsLowResolution = isLowResolution; + public Builder setTaskSize(Point size) { + mTaskSize = size; return this; } - public float getScaleFraction() { - return mScaleFraction; - } - - public Builder setScaleFraction(float scaleFraction) { - mScaleFraction = scaleFraction; + public Builder setContentInsets(Rect contentInsets) { + mContentInsets = contentInsets; return this; } @@ -2322,9 +2315,12 @@ public class ActivityManager { mColorSpace, mOrientation, mRotation, + mTaskSize, mContentInsets, - mIsLowResolution, - mScaleFraction, + // When building a TaskSnapshot with the Builder class, isLowResolution + // is always false. Low-res snapshots are only created when loading from + // disk. + false /* isLowResolution */, mIsRealSnapshot, mWindowingMode, mSystemUiVisibility, diff --git a/core/java/android/app/ActivityTaskManager.java b/core/java/android/app/ActivityTaskManager.java index 9ba56cf40161..d48b35b6f0c8 100644 --- a/core/java/android/app/ActivityTaskManager.java +++ b/core/java/android/app/ActivityTaskManager.java @@ -362,25 +362,6 @@ public class ActivityTaskManager { } /** - * Resize the input stack id to the given bounds with animate setting. - * @param stackId Id of the stack to resize. - * @param bounds Bounds to resize the stack to or {@code null} for fullscreen. - * @param animate Whether we should play an animation for resizing stack. - */ - @RequiresPermission(android.Manifest.permission.MANAGE_ACTIVITY_STACKS) - public void resizePinnedStack(int stackId, Rect bounds, boolean animate) { - try { - if (animate) { - getService().animateResizePinnedStack(stackId, bounds, -1 /* animationDuration */); - } else { - getService().resizePinnedStack(bounds, null /* tempPinnedTaskBounds */); - } - } catch (RemoteException e) { - throw e.rethrowFromSystemServer(); - } - } - - /** * Resize task to given bounds. * @param taskId Id of task to resize. * @param bounds Bounds to resize task. diff --git a/core/java/android/app/ActivityThread.java b/core/java/android/app/ActivityThread.java index 92dd91a877a9..0ed5aec58924 100644 --- a/core/java/android/app/ActivityThread.java +++ b/core/java/android/app/ActivityThread.java @@ -68,6 +68,7 @@ import android.content.pm.PackageManager; import android.content.pm.PackageManager.NameNotFoundException; import android.content.pm.ParceledListSlice; import android.content.pm.ProviderInfo; +import android.content.pm.ProviderInfoList; import android.content.pm.ServiceInfo; import android.content.res.AssetManager; import android.content.res.CompatibilityInfo; @@ -1011,8 +1012,9 @@ public final class ActivityThread extends ClientTransactionHandler { sendMessage(H.STOP_SERVICE, token); } + @Override public final void bindApplication(String processName, ApplicationInfo appInfo, - List<ProviderInfo> providers, ComponentName instrumentationName, + ProviderInfoList providerList, ComponentName instrumentationName, ProfilerInfo profilerInfo, Bundle instrumentationArgs, IInstrumentationWatcher instrumentationWatcher, IUiAutomationConnection instrumentationUiConnection, int debugMode, @@ -1052,7 +1054,7 @@ public final class ActivityThread extends ClientTransactionHandler { AppBindData data = new AppBindData(); data.processName = processName; data.appInfo = appInfo; - data.providers = providers; + data.providers = providerList.getList(); data.instrumentationName = instrumentationName; data.instrumentationArgs = instrumentationArgs; data.instrumentationWatcher = instrumentationWatcher; diff --git a/core/java/android/app/DreamManager.java b/core/java/android/app/DreamManager.java new file mode 100644 index 000000000000..fe13b8f26d78 --- /dev/null +++ b/core/java/android/app/DreamManager.java @@ -0,0 +1,102 @@ +/* + * Copyright (C) 2020 The Android Open Source Project + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +package android.app; + +import android.annotation.NonNull; +import android.annotation.RequiresPermission; +import android.annotation.SystemService; +import android.annotation.TestApi; +import android.annotation.UserHandleAware; +import android.content.ComponentName; +import android.content.Context; +import android.os.RemoteException; +import android.os.ServiceManager; +import android.service.dreams.DreamService; +import android.service.dreams.IDreamManager; + +/** + * @hide + */ +@SystemService(Context.DREAM_SERVICE) +@TestApi +public class DreamManager { + private final IDreamManager mService; + private final Context mContext; + + /** + * @hide + */ + public DreamManager(Context context) throws ServiceManager.ServiceNotFoundException { + mService = IDreamManager.Stub.asInterface( + ServiceManager.getServiceOrThrow(DreamService.DREAM_SERVICE)); + mContext = context; + } + + /** + * Starts dream service with name "name". + * + * <p>This is only used for testing the dream service APIs. + * + * @hide + */ + @TestApi + @UserHandleAware + @RequiresPermission(android.Manifest.permission.WRITE_DREAM_STATE) + public void startDream(@NonNull ComponentName name) { + try { + mService.testDream(mContext.getUserId(), name); + } catch (RemoteException e) { + e.rethrowFromSystemServer(); + } + } + + /** + * Stops the dream service on the device if one is started. + * + * <p> This is only used for testing the dream service APIs. + * + * @hide + */ + @TestApi + @RequiresPermission(android.Manifest.permission.WRITE_DREAM_STATE) + public void stopDream() { + try { + mService.awaken(); + } catch (RemoteException e) { + e.rethrowFromSystemServer(); + } + } + + /** + * Sets the active dream on the device to be "dreamComponent". + * + * <p>This is only used for testing the dream service APIs. + * + * @hide + */ + @TestApi + @UserHandleAware + @RequiresPermission(android.Manifest.permission.WRITE_DREAM_STATE) + public void setActiveDream(@NonNull ComponentName dreamComponent) { + ComponentName[] dreams = {dreamComponent}; + try { + mService.setDreamComponentsForUser(mContext.getUserId(), dreams); + } catch (RemoteException e) { + e.rethrowFromSystemServer(); + } + } +} diff --git a/core/java/android/app/IActivityTaskManager.aidl b/core/java/android/app/IActivityTaskManager.aidl index 5b61402314c4..266a06a7aac4 100644 --- a/core/java/android/app/IActivityTaskManager.aidl +++ b/core/java/android/app/IActivityTaskManager.aidl @@ -98,6 +98,14 @@ interface IActivityTaskManager { in ProfilerInfo profilerInfo, in Bundle options, int userId); boolean startNextMatchingActivity(in IBinder callingActivity, in Intent intent, in Bundle options); + + /** + * The DreamActivity has to be started in a special way that does not involve the PackageParser. + * The DreamActivity is a framework component inserted in the dream application process. Hence, + * it is not declared in the application's manifest and cannot be parsed. startDreamActivity + * creates the activity and starts it without reaching out to the PackageParser. + */ + boolean startDreamActivity(in Intent intent); int startActivityIntentSender(in IApplicationThread caller, in IIntentSender target, in IBinder whitelistToken, in Intent fillInIntent, in String resolvedType, in IBinder resultTo, in String resultWho, int requestCode, @@ -236,33 +244,9 @@ interface IActivityTaskManager { */ boolean setTaskWindowingMode(int taskId, int windowingMode, boolean toTop); void moveTaskToStack(int taskId, int stackId, boolean toTop); - /** - * Resizes the input pinned stack to the given bounds with animation. - * - * @param stackId Id of the pinned stack to resize. - * @param bounds Bounds to resize the stack to or {@code null} for fullscreen. - * @param animationDuration The duration of the resize animation in milliseconds or -1 if the - * default animation duration should be used. - * @throws RemoteException - */ - void animateResizePinnedStack(int stackId, in Rect bounds, int animationDuration); boolean setTaskWindowingModeSplitScreenPrimary(int taskId, int createMode, boolean toTop, boolean animate, in Rect initialBounds, boolean showRecents); /** - * Use the offset to adjust the stack boundary with animation. - * - * @param stackId Id of the stack to adjust. - * @param compareBounds Offset is only applied if the current pinned stack bounds is equal to - * the compareBounds. - * @param xOffset The horizontal offset. - * @param yOffset The vertical offset. - * @param animationDuration The duration of the resize animation in milliseconds or -1 if the - * default animation duration should be used. - * @throws RemoteException - */ - void offsetPinnedStackBounds(int stackId, in Rect compareBounds, int xOffset, int yOffset, - int animationDuration); - /** * Removes stacks in the input windowing modes from the system if they are of activity type * ACTIVITY_TYPE_STANDARD or ACTIVITY_TYPE_UNDEFINED */ @@ -371,23 +355,10 @@ interface IActivityTaskManager { void startLocalVoiceInteraction(in IBinder token, in Bundle options); void stopLocalVoiceInteraction(in IBinder token); boolean supportsLocalVoiceInteraction(); - void notifyPinnedStackAnimationStarted(); - void notifyPinnedStackAnimationEnded(); // Get device configuration ConfigurationInfo getDeviceConfigurationInfo(); - /** - * Resizes the pinned stack. - * - * @param pinnedBounds The bounds for the pinned stack. - * @param tempPinnedTaskBounds The temporary bounds for the tasks in the pinned stack, which - * might be different from the stack bounds to allow more - * flexibility while resizing, or {@code null} if they should be the - * same as the stack bounds. - */ - void resizePinnedStack(in Rect pinnedBounds, in Rect tempPinnedTaskBounds); - void dismissKeyguard(in IBinder token, in IKeyguardDismissCallback callback, in CharSequence message); diff --git a/core/java/android/app/IApplicationThread.aidl b/core/java/android/app/IApplicationThread.aidl index c33c515f062c..1f6e4cac199a 100644 --- a/core/java/android/app/IApplicationThread.aidl +++ b/core/java/android/app/IApplicationThread.aidl @@ -30,6 +30,7 @@ import android.content.pm.ActivityInfo; import android.content.pm.ApplicationInfo; import android.content.pm.ParceledListSlice; import android.content.pm.ProviderInfo; +import android.content.pm.ProviderInfoList; import android.content.pm.ServiceInfo; import android.content.res.CompatibilityInfo; import android.content.res.Configuration; @@ -66,7 +67,7 @@ oneway interface IApplicationThread { @UnsupportedAppUsage void scheduleStopService(IBinder token); void bindApplication(in String packageName, in ApplicationInfo info, - in List<ProviderInfo> providers, in ComponentName testName, + in ProviderInfoList providerList, in ComponentName testName, in ProfilerInfo profilerInfo, in Bundle testArguments, IInstrumentationWatcher testWatcher, IUiAutomationConnection uiAutomationConnection, int debugMode, boolean enableBinderTracking, boolean trackAllocation, diff --git a/core/java/android/app/ITaskStackListener.aidl b/core/java/android/app/ITaskStackListener.aidl index 37bdda0b0393..28b28dad5b82 100644 --- a/core/java/android/app/ITaskStackListener.aidl +++ b/core/java/android/app/ITaskStackListener.aidl @@ -46,16 +46,6 @@ oneway interface ITaskStackListener { void onPinnedActivityRestartAttempt(boolean clearedTask); /** - * Called whenever the pinned stack is starting animating a resize. - */ - void onPinnedStackAnimationStarted(); - - /** - * Called whenever the pinned stack is done animating a resize. - */ - void onPinnedStackAnimationEnded(); - - /** * Called when we launched an activity that we forced to be resizable. * * @param packageName Package name of the top activity in the task. diff --git a/core/java/android/app/SystemServiceRegistry.java b/core/java/android/app/SystemServiceRegistry.java index 4369680e7781..d04630c747a3 100644 --- a/core/java/android/app/SystemServiceRegistry.java +++ b/core/java/android/app/SystemServiceRegistry.java @@ -186,7 +186,6 @@ import android.telephony.TelephonyFrameworkInitializer; import android.telephony.TelephonyRegistryManager; import android.util.ArrayMap; import android.util.Log; -import android.util.Slog; import android.view.ContextThemeWrapper; import android.view.LayoutInflater; import android.view.WindowManager; @@ -223,8 +222,6 @@ import java.util.Objects; public final class SystemServiceRegistry { private static final String TAG = "SystemServiceRegistry"; - private static final boolean ENABLE_SERVICE_NOT_FOUND_WTF = true; - // Service registry information. // This information is never changed once static initialization has completed. private static final Map<Class<?>, String> SYSTEM_SERVICE_NAMES = @@ -1334,6 +1331,13 @@ public final class SystemServiceRegistry { IBinder b = ServiceManager.getServiceOrThrow(Context.APP_INTEGRITY_SERVICE); return new AppIntegrityManager(IAppIntegrityManager.Stub.asInterface(b)); }}); + registerService(Context.DREAM_SERVICE, DreamManager.class, + new CachedServiceFetcher<DreamManager>() { + @Override + public DreamManager createService(ContextImpl ctx) + throws ServiceNotFoundException { + return new DreamManager(ctx); + }}); sInitializing = true; try { @@ -1370,29 +1374,8 @@ public final class SystemServiceRegistry { * @hide */ public static Object getSystemService(ContextImpl ctx, String name) { - if (name == null) { - return null; - } - final ServiceFetcher<?> fetcher = SYSTEM_SERVICE_FETCHERS.get(name); - if (ENABLE_SERVICE_NOT_FOUND_WTF && fetcher == null) { - // This should be a caller bug. - Slog.wtf(TAG, "Unknown manager requested: " + name); - return null; - } - - final Object ret = fetcher.getService(ctx); - if (ENABLE_SERVICE_NOT_FOUND_WTF && ret == null) { - // Some services do return null in certain situations, so don't do WTF for them. - switch (name) { - case Context.CONTENT_CAPTURE_MANAGER_SERVICE: - case Context.APP_PREDICTION_SERVICE: - case Context.INCREMENTAL_SERVICE: - return null; - } - Slog.wtf(TAG, "Manager wrapper not available: " + name); - return null; - } - return ret; + ServiceFetcher<?> fetcher = SYSTEM_SERVICE_FETCHERS.get(name); + return fetcher != null ? fetcher.getService(ctx) : null; } /** @@ -1400,15 +1383,7 @@ public final class SystemServiceRegistry { * @hide */ public static String getSystemServiceName(Class<?> serviceClass) { - if (serviceClass == null) { - return null; - } - final String serviceName = SYSTEM_SERVICE_NAMES.get(serviceClass); - if (ENABLE_SERVICE_NOT_FOUND_WTF && serviceName == null) { - // This should be a caller bug. - Slog.wtf(TAG, "Unknown manager requested: " + serviceClass.getCanonicalName()); - } - return serviceName; + return SYSTEM_SERVICE_NAMES.get(serviceClass); } /** @@ -1705,9 +1680,7 @@ public final class SystemServiceRegistry { try { cache.wait(); } catch (InterruptedException e) { - // This shouldn't normally happen, but if someone interrupts the - // thread, it will. - Slog.wtf(TAG, "getService() interrupted"); + Log.w(TAG, "getService() interrupted"); Thread.currentThread().interrupt(); return null; } diff --git a/core/java/android/app/TaskStackListener.java b/core/java/android/app/TaskStackListener.java index da0aadb3ea48..b892b8e51c88 100644 --- a/core/java/android/app/TaskStackListener.java +++ b/core/java/android/app/TaskStackListener.java @@ -58,16 +58,6 @@ public abstract class TaskStackListener extends ITaskStackListener.Stub { @Override @UnsupportedAppUsage - public void onPinnedStackAnimationStarted() throws RemoteException { - } - - @Override - @UnsupportedAppUsage - public void onPinnedStackAnimationEnded() throws RemoteException { - } - - @Override - @UnsupportedAppUsage public void onActivityForcedResizable(String packageName, int taskId, int reason) throws RemoteException { } diff --git a/core/java/android/app/prediction/AppPredictor.java b/core/java/android/app/prediction/AppPredictor.java index f0eedf3d18f2..7f436401dbf4 100644 --- a/core/java/android/app/prediction/AppPredictor.java +++ b/core/java/android/app/prediction/AppPredictor.java @@ -260,6 +260,7 @@ public final class AppPredictor { Log.e(TAG, "Failed to notify app target event", e); e.rethrowAsRuntimeException(); } + mRegisteredCallbacks.clear(); } else { throw new IllegalStateException("This client has already been destroyed."); } diff --git a/core/java/android/content/Context.java b/core/java/android/content/Context.java index c6e84b7e46b0..536b6c33b142 100644 --- a/core/java/android/content/Context.java +++ b/core/java/android/content/Context.java @@ -5157,6 +5157,17 @@ public abstract class Context { public static final String LIGHTS_SERVICE = "lights"; /** + * Use with {@link #getSystemService(String)} to retrieve a + * {@link android.app.DreamManager} for controlling Dream states. + * + * @see #getSystemService(String) + + * @hide + */ + @TestApi + public static final String DREAM_SERVICE = "dream"; + + /** * Determine whether the given permission is allowed for a particular * process and user ID running in the system. * diff --git a/core/java/android/content/pm/ApplicationInfo.java b/core/java/android/content/pm/ApplicationInfo.java index 9d1c677f35c6..4c6fef2e1856 100644 --- a/core/java/android/content/pm/ApplicationInfo.java +++ b/core/java/android/content/pm/ApplicationInfo.java @@ -1630,6 +1630,9 @@ public class ApplicationInfo extends PackageItemInfo implements Parcelable { @SuppressWarnings("unchecked") public void writeToParcel(Parcel dest, int parcelableFlags) { + if (dest.maybeWriteSquashed(this)) { + return; + } super.writeToParcel(dest, parcelableFlags); dest.writeString(taskAffinity); dest.writeString(permission); @@ -1700,9 +1703,12 @@ public class ApplicationInfo extends PackageItemInfo implements Parcelable { public static final @android.annotation.NonNull Parcelable.Creator<ApplicationInfo> CREATOR = new Parcelable.Creator<ApplicationInfo>() { + @Override public ApplicationInfo createFromParcel(Parcel source) { - return new ApplicationInfo(source); + return source.readSquashed(ApplicationInfo::new); } + + @Override public ApplicationInfo[] newArray(int size) { return new ApplicationInfo[size]; } diff --git a/core/java/android/content/pm/ComponentInfo.java b/core/java/android/content/pm/ComponentInfo.java index 8b41c04b3549..362098c447ce 100644 --- a/core/java/android/content/pm/ComponentInfo.java +++ b/core/java/android/content/pm/ComponentInfo.java @@ -20,7 +20,6 @@ import android.compat.annotation.UnsupportedAppUsage; import android.content.ComponentName; import android.graphics.drawable.Drawable; import android.os.Parcel; -import android.os.Parcelable; import android.util.Printer; /** @@ -197,12 +196,7 @@ public class ComponentInfo extends PackageItemInfo { public void writeToParcel(Parcel dest, int parcelableFlags) { super.writeToParcel(dest, parcelableFlags); - if ((parcelableFlags & Parcelable.PARCELABLE_ELIDE_DUPLICATES) != 0) { - dest.writeInt(0); - } else { - dest.writeInt(1); - applicationInfo.writeToParcel(dest, parcelableFlags); - } + applicationInfo.writeToParcel(dest, parcelableFlags); dest.writeString(processName); dest.writeString(splitName); dest.writeInt(descriptionRes); @@ -213,10 +207,7 @@ public class ComponentInfo extends PackageItemInfo { protected ComponentInfo(Parcel source) { super(source); - final boolean hasApplicationInfo = (source.readInt() != 0); - if (hasApplicationInfo) { - applicationInfo = ApplicationInfo.CREATOR.createFromParcel(source); - } + applicationInfo = ApplicationInfo.CREATOR.createFromParcel(source); processName = source.readString(); splitName = source.readString(); descriptionRes = source.readInt(); diff --git a/core/java/android/content/pm/PackageInfo.java b/core/java/android/content/pm/PackageInfo.java index 36fa5728b34e..85c698f3fb0c 100644 --- a/core/java/android/content/pm/PackageInfo.java +++ b/core/java/android/content/pm/PackageInfo.java @@ -439,6 +439,8 @@ public class PackageInfo implements Parcelable { @Override public void writeToParcel(Parcel dest, int parcelableFlags) { + // Allow ApplicationInfo to be squashed. + final boolean prevAllowSquashing = dest.allowSquashing(); dest.writeString(packageName); dest.writeStringArray(splitNames); dest.writeInt(versionCode); @@ -457,10 +459,10 @@ public class PackageInfo implements Parcelable { dest.writeLong(firstInstallTime); dest.writeLong(lastUpdateTime); dest.writeIntArray(gids); - dest.writeTypedArray(activities, parcelableFlags | Parcelable.PARCELABLE_ELIDE_DUPLICATES); - dest.writeTypedArray(receivers, parcelableFlags | Parcelable.PARCELABLE_ELIDE_DUPLICATES); - dest.writeTypedArray(services, parcelableFlags | Parcelable.PARCELABLE_ELIDE_DUPLICATES); - dest.writeTypedArray(providers, parcelableFlags | Parcelable.PARCELABLE_ELIDE_DUPLICATES); + dest.writeTypedArray(activities, parcelableFlags); + dest.writeTypedArray(receivers, parcelableFlags); + dest.writeTypedArray(services, parcelableFlags); + dest.writeTypedArray(providers, parcelableFlags); dest.writeTypedArray(instrumentation, parcelableFlags); dest.writeTypedArray(permissions, parcelableFlags); dest.writeStringArray(requestedPermissions); @@ -488,6 +490,7 @@ public class PackageInfo implements Parcelable { dest.writeInt(0); } dest.writeBoolean(isApex); + dest.restoreAllowSquashing(prevAllowSquashing); } public static final @android.annotation.NonNull Parcelable.Creator<PackageInfo> CREATOR @@ -550,21 +553,5 @@ public class PackageInfo implements Parcelable { signingInfo = SigningInfo.CREATOR.createFromParcel(source); } isApex = source.readBoolean(); - // The component lists were flattened with the redundant ApplicationInfo - // instances omitted. Distribute the canonical one here as appropriate. - if (applicationInfo != null) { - propagateApplicationInfo(applicationInfo, activities); - propagateApplicationInfo(applicationInfo, receivers); - propagateApplicationInfo(applicationInfo, services); - propagateApplicationInfo(applicationInfo, providers); - } - } - - private void propagateApplicationInfo(ApplicationInfo appInfo, ComponentInfo[] components) { - if (components != null) { - for (ComponentInfo ci : components) { - ci.applicationInfo = appInfo; - } - } } } diff --git a/core/java/android/content/pm/PackageManager.java b/core/java/android/content/pm/PackageManager.java index f2ec938b3d9d..fa751d380580 100644 --- a/core/java/android/content/pm/PackageManager.java +++ b/core/java/android/content/pm/PackageManager.java @@ -7853,11 +7853,16 @@ public abstract class PackageManager { /** * Returns if the provided drawable represents the default activity icon provided by the system. * - * PackageManager provides a default icon for any package/activity if the app itself does not - * define one or if the system encountered any error when loading the icon. + * PackageManager silently returns a default application icon for any package/activity if the + * app itself does not define one or if the system encountered any error when loading the icon. + * + * Developers can use this to check implement app specific logic around retrying or caching. * * @return true if the drawable represents the default activity icon, false otherwise * @see #getDefaultActivityIcon() + * @see PackageItemInfo#loadDefaultIcon(PackageManager) + * @see #getActivityIcon + * @see LauncherActivityInfo#getIcon(int) */ public boolean isDefaultApplicationIcon(@NonNull Drawable drawable) { int resId = drawable instanceof AdaptiveIconDrawable diff --git a/core/java/android/content/pm/ProviderInfoList.aidl b/core/java/android/content/pm/ProviderInfoList.aidl new file mode 100644 index 000000000000..bb576d77003d --- /dev/null +++ b/core/java/android/content/pm/ProviderInfoList.aidl @@ -0,0 +1,19 @@ +/* + * Copyright (C) 2020 The Android Open Source Project + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +package android.content.pm; + +parcelable ProviderInfoList; diff --git a/core/java/android/content/pm/ProviderInfoList.java b/core/java/android/content/pm/ProviderInfoList.java new file mode 100644 index 000000000000..566be2e32fe0 --- /dev/null +++ b/core/java/android/content/pm/ProviderInfoList.java @@ -0,0 +1,87 @@ +/* + * Copyright (C) 2020 The Android Open Source Project + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ +package android.content.pm; + +import android.annotation.NonNull; +import android.annotation.TestApi; +import android.os.Parcel; +import android.os.Parcelable; + +import java.util.ArrayList; +import java.util.List; + +/** + * Equivalent to List<ProviderInfo>, but it "squashes" the ApplicationInfo in the elements. + * + * @hide + */ +@TestApi +public final class ProviderInfoList implements Parcelable { + private final List<ProviderInfo> mList; + + private ProviderInfoList(Parcel source) { + final ArrayList<ProviderInfo> list = new ArrayList<>(); + source.readTypedList(list, ProviderInfo.CREATOR); + mList = list; + } + + private ProviderInfoList(List<ProviderInfo> list) { + mList = list; + } + + @Override + public int describeContents() { + return 0; + } + + @Override + public void writeToParcel(@NonNull Parcel dest, int flags) { + // Allow ApplicationInfo to be squashed. + final boolean prevAllowSquashing = dest.allowSquashing(); + dest.writeTypedList(mList, flags); + dest.restoreAllowSquashing(prevAllowSquashing); + } + + public static final @android.annotation.NonNull Parcelable.Creator<ProviderInfoList> CREATOR + = new Parcelable.Creator<ProviderInfoList>() { + @Override + public ProviderInfoList createFromParcel(@NonNull Parcel source) { + return new ProviderInfoList(source); + } + + @Override + public ProviderInfoList[] newArray(int size) { + return new ProviderInfoList[size]; + } + }; + + /** + * Return the stored list. + */ + @NonNull + public List<ProviderInfo> getList() { + return mList; + } + + /** + * Create a new instance with a {@code list}. The passed list will be shared with the new + * instance, so the caller shouldn't modify it. + */ + @NonNull + public static ProviderInfoList fromList(@NonNull List<ProviderInfo> list) { + return new ProviderInfoList(list); + } +} diff --git a/core/java/android/os/Parcel.java b/core/java/android/os/Parcel.java index 1a4dac78855f..f0b7b5fa5a1a 100644 --- a/core/java/android/os/Parcel.java +++ b/core/java/android/os/Parcel.java @@ -28,6 +28,7 @@ import android.util.ExceptionUtils; import android.util.Log; import android.util.Size; import android.util.SizeF; +import android.util.Slog; import android.util.SparseArray; import android.util.SparseBooleanArray; import android.util.SparseIntArray; @@ -1827,6 +1828,179 @@ public final class Parcel { } /** + * A map used by {@link #maybeWriteSquashed} to keep track of what parcelables have + * been seen, and what positions they were written. The value is the absolute position of + * each parcelable. + */ + private ArrayMap<Parcelable, Integer> mWrittenSquashableParcelables; + + private void ensureWrittenSquashableParcelables() { + if (mWrittenSquashableParcelables != null) { + return; + } + mWrittenSquashableParcelables = new ArrayMap<>(); + } + + private boolean mAllowSquashing = false; + + /** + * Allow "squashing" writes in {@link #maybeWriteSquashed}. This allows subsequent calls to + * {@link #maybeWriteSquashed(Parcelable)} to "squash" the same instances into one in a Parcel. + * + * Typically, this method is called at the beginning of {@link Parcelable#writeToParcel}. The + * caller must retain the return value from this method and call {@link #restoreAllowSquashing} + * with it. + * + * See {@link #maybeWriteSquashed(Parcelable)} for the details. + * + * @see #restoreAllowSquashing(boolean) + * @see #maybeWriteSquashed(Parcelable) + * @see #readSquashed(SquashReadHelper) + * + * @hide + */ + @TestApi + public boolean allowSquashing() { + boolean previous = mAllowSquashing; + mAllowSquashing = true; + return previous; + } + + /** + * @see #allowSquashing() + * @hide + */ + @TestApi + public void restoreAllowSquashing(boolean previous) { + mAllowSquashing = previous; + if (!mAllowSquashing) { + mWrittenSquashableParcelables = null; + } + } + + private void resetSqaushingState() { + if (mAllowSquashing) { + Slog.wtf(TAG, "allowSquashing wasn't restored."); + } + mWrittenSquashableParcelables = null; + mReadSquashableParcelables = null; + mAllowSquashing = false; + } + + /** + * A map used by {@link #readSquashed} to cache parcelables. It's a map from + * an absolute position in a Parcel to the parcelable stored at the position. + */ + private ArrayMap<Integer, Parcelable> mReadSquashableParcelables; + + private void ensureReadSquashableParcelables() { + if (mReadSquashableParcelables != null) { + return; + } + mReadSquashableParcelables = new ArrayMap<>(); + } + + /** + * Write a parcelable with "squash" -- that is, when the same instance is written to the + * same Parcelable multiple times, instead of writing the entire instance multiple times, + * only write it once, and in subsequent writes we'll only write the offset to the original + * object. + * + * This approach does not work of the resulting Parcel is copied with {@link #appendFrom} with + * a non-zero offset, so we do not enable this behavior by default. Instead, we only enable + * it between {@link #allowSquashing} and {@link #restoreAllowSquashing}, in order to make sure + * we only do so within each "top level" Parcelable. + * + * Usage: Use this method in {@link Parcelable#writeToParcel}. + * If this method returns TRUE, it's a subsequent call, and the offset is already written, + * so the caller doesn't have to do anything. If this method returns FALSE, it's the first + * time for the instance to be written to this parcel. The caller has to proceed with its + * {@link Parcelable#writeToParcel}. + * + * (See {@code ApplicationInfo} for the example.) + * + * @param p the target Parcelable to write. + * + * @see #allowSquashing() + * @see #restoreAllowSquashing(boolean) + * @see #readSquashed(SquashReadHelper) + * + * @hide + */ + public boolean maybeWriteSquashed(@NonNull Parcelable p) { + if (!mAllowSquashing) { + // Don't squash, and don't put it in the map either. + writeInt(0); + return false; + } + ensureWrittenSquashableParcelables(); + final Integer firstPos = mWrittenSquashableParcelables.get(p); + if (firstPos != null) { + // Already written. + // Write the relative offset from the current position to the first position. + final int pos = dataPosition(); + + // We want the offset from the next byte of this integer, so we need to +4. + writeInt(pos - firstPos + 4); + return true; + } + // First time seen, write a marker. + writeInt(0); + + // Remember the position. + final int pos = dataPosition(); + mWrittenSquashableParcelables.put(p, pos); + + // Return false and let the caller actually write the content. + return false; + } + + /** + * Helper function that's used by {@link #readSquashed(SquashReadHelper)} + * @hide + */ + public interface SquashReadHelper<T> { + /** Read and instantiate {@code T} from a Parcel. */ + @NonNull + T readRawParceled(@NonNull Parcel p); + } + + /** + * Read a {@link Parcelable} that's written with {@link #maybeWriteSquashed}. + * + * @param reader a callback function that instantiates an instance from a parcel. + * Typicallly, a lambda to the instructor that takes a {@link Parcel} is passed. + * + * @see #maybeWriteSquashed(Parcelable) + * + * @hide + */ + @SuppressWarnings("unchecked") + @Nullable + public <T extends Parcelable> T readSquashed(SquashReadHelper<T> reader) { + final int offset = readInt(); + final int pos = dataPosition(); + + if (offset == 0) { + // First time read. Unparcel, and remember it. + final T p = reader.readRawParceled(this); + ensureReadSquashableParcelables(); + mReadSquashableParcelables.put(pos, p); + return p; + } + // Subsequent read. + final int firstAbsolutePos = pos - offset; + + final Parcelable p = mReadSquashableParcelables.get(firstAbsolutePos); + if (p == null) { + Slog.wtfStack(TAG, "Map doesn't contain offset " + + firstAbsolutePos + + " : contains=" + new ArrayList<>(mReadSquashableParcelables.keySet())); + } + return (T) p; + } + + /** * Write a generic serializable object in to a Parcel. It is strongly * recommended that this method be avoided, since the serialization * overhead is extremely large, and this approach will be much slower than @@ -3247,6 +3421,7 @@ public final class Parcel { } private void freeBuffer() { + resetSqaushingState(); if (mOwnsNativeParcelObject) { updateNativeSize(nativeFreeBuffer(mNativePtr)); } @@ -3254,6 +3429,7 @@ public final class Parcel { } private void destroy() { + resetSqaushingState(); if (mNativePtr != 0) { if (mOwnsNativeParcelObject) { nativeDestroy(mNativePtr); @@ -3261,7 +3437,6 @@ public final class Parcel { } mNativePtr = 0; } - mReadWriteHelper = null; } @Override diff --git a/core/java/android/os/image/DynamicSystemClient.java b/core/java/android/os/image/DynamicSystemClient.java index 5cb33615fe22..50d8d8079ed5 100644 --- a/core/java/android/os/image/DynamicSystemClient.java +++ b/core/java/android/os/image/DynamicSystemClient.java @@ -96,9 +96,6 @@ public class DynamicSystemClient { private static final String TAG = "DynSystemClient"; - private static final long DEFAULT_USERDATA_SIZE = (10L << 30); - - /** Listener for installation status updates. */ public interface OnStatusChangedListener { /** @@ -386,7 +383,7 @@ public class DynamicSystemClient { @SystemApi @TestApi public void start(@NonNull Uri systemUrl, @BytesLong long systemSize) { - start(systemUrl, systemSize, DEFAULT_USERDATA_SIZE); + start(systemUrl, systemSize, 0 /* Use the default userdata size */); } /** diff --git a/core/java/android/service/autofill/IInlineSuggestionRenderService.aidl b/core/java/android/service/autofill/IInlineSuggestionRenderService.aidl index c389b1a1a5ff..dd434b440af4 100644 --- a/core/java/android/service/autofill/IInlineSuggestionRenderService.aidl +++ b/core/java/android/service/autofill/IInlineSuggestionRenderService.aidl @@ -28,5 +28,5 @@ import android.service.autofill.InlinePresentation; oneway interface IInlineSuggestionRenderService { void renderSuggestion(in IInlineSuggestionUiCallback callback, in InlinePresentation presentation, int width, int height, - in IBinder hostInputToken); + in IBinder hostInputToken, int displayId); } diff --git a/core/java/android/service/autofill/InlineSuggestionRenderService.java b/core/java/android/service/autofill/InlineSuggestionRenderService.java index 29069e7035f9..17e0456df156 100644 --- a/core/java/android/service/autofill/InlineSuggestionRenderService.java +++ b/core/java/android/service/autofill/InlineSuggestionRenderService.java @@ -23,14 +23,18 @@ import android.annotation.SystemApi; import android.annotation.TestApi; import android.app.Service; import android.app.slice.Slice; +import android.content.Context; import android.content.Intent; import android.graphics.PixelFormat; +import android.hardware.display.DisplayManager; import android.os.Bundle; import android.os.Handler; import android.os.IBinder; import android.os.Looper; import android.os.RemoteException; +import android.util.DisplayMetrics; import android.util.Log; +import android.view.Display; import android.view.SurfaceControl; import android.view.SurfaceControlViewHost; import android.view.View; @@ -61,7 +65,8 @@ public abstract class InlineSuggestionRenderService extends Service { private final Handler mHandler = new Handler(Looper.getMainLooper(), null, true); private void handleRenderSuggestion(IInlineSuggestionUiCallback callback, - InlinePresentation presentation, int width, int height, IBinder hostInputToken) { + InlinePresentation presentation, int width, int height, IBinder hostInputToken, + int displayId) { if (hostInputToken == null) { try { callback.onError(); @@ -70,8 +75,17 @@ public abstract class InlineSuggestionRenderService extends Service { } return; } - final SurfaceControlViewHost host = new SurfaceControlViewHost(this, this.getDisplay(), - hostInputToken); + + final DisplayManager displayManager = getSystemService(DisplayManager.class); + final Display targetDisplay = displayManager.getDisplay(displayId); + if (targetDisplay == null) { + sendResult(callback, /*surface*/ null); + return; + } + final Context displayContext = createDisplayContext(targetDisplay); + + final SurfaceControlViewHost host = new SurfaceControlViewHost(displayContext, + displayContext.getDisplay(), hostInputToken); final SurfaceControl surface = host.getSurfacePackage().getSurfaceControl(); final View suggestionView = onRenderSuggestion(presentation, width, height); @@ -90,6 +104,11 @@ public abstract class InlineSuggestionRenderService extends Service { new WindowManager.LayoutParams(width, height, WindowManager.LayoutParams.TYPE_APPLICATION, 0, PixelFormat.TRANSPARENT); host.addView(suggestionRoot, lp); + sendResult(callback, surface); + } + + private void sendResult(@NonNull IInlineSuggestionUiCallback callback, + @Nullable SurfaceControl surface) { try { callback.onContent(surface); } catch (RemoteException e) { @@ -105,11 +124,11 @@ public abstract class InlineSuggestionRenderService extends Service { @Override public void renderSuggestion(@NonNull IInlineSuggestionUiCallback callback, @NonNull InlinePresentation presentation, int width, int height, - @Nullable IBinder hostInputToken) { + @Nullable IBinder hostInputToken, int displayId) { mHandler.sendMessage(obtainMessage( InlineSuggestionRenderService::handleRenderSuggestion, InlineSuggestionRenderService.this, callback, presentation, - width, height, hostInputToken)); + width, height, hostInputToken, displayId)); } }.asBinder(); } diff --git a/core/java/android/service/dreams/DreamActivity.java b/core/java/android/service/dreams/DreamActivity.java new file mode 100644 index 000000000000..8cdd24e0884d --- /dev/null +++ b/core/java/android/service/dreams/DreamActivity.java @@ -0,0 +1,59 @@ +/* + * Copyright (C) 2020 The Android Open Source Project + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +package android.service.dreams; + +import android.annotation.Nullable; +import android.app.Activity; +import android.os.Bundle; + +/** + * The Activity used by the {@link DreamService} to draw screensaver content + * on the screen. This activity runs in dream application's process, but is started by a + * specialized method: {@link com.android.server.wm.ActivityTaskManagerService#startDreamActivity}. + * Hence, it does not have to be declared in the dream application's manifest. + * + * We use an activity as the dream canvas, because it interacts easier with other activities on + * the screen (compared to a hover window). However, the DreamService is in charge of the dream and + * it receives all Window.Callbacks from its main window. Since a window can have only one callback + * receiver, the activity will not receive any window callbacks. + * + * Prior to the DreamActivity, the DreamService used to work with a hovering window and give the + * screensaver application control over that window. The DreamActivity is a replacement to that + * hover window. Using an activity allows for better-defined interactions with the rest of the + * activities on screen. The switch to DreamActivity should be transparent to the screensaver + * application, i.e. the application will still use DreamService APIs and not notice that the + * system is using an activity behind the scenes. + * + * @hide + */ +public class DreamActivity extends Activity { + static final String EXTRA_CALLBACK = "binder"; + + public DreamActivity() {} + + @Override + public void onCreate(@Nullable Bundle bundle) { + super.onCreate(bundle); + + DreamService.DreamServiceWrapper callback = + (DreamService.DreamServiceWrapper) getIntent().getIBinderExtra(EXTRA_CALLBACK); + + if (callback != null) { + callback.onActivityCreated(this); + } + } +} diff --git a/core/java/android/service/dreams/DreamManagerInternal.java b/core/java/android/service/dreams/DreamManagerInternal.java index ff7cef9d945a..41fdd0bfe477 100644 --- a/core/java/android/service/dreams/DreamManagerInternal.java +++ b/core/java/android/service/dreams/DreamManagerInternal.java @@ -16,6 +16,8 @@ package android.service.dreams; +import android.content.ComponentName; + /** * Dream manager local system service interface. * @@ -42,4 +44,13 @@ public abstract class DreamManagerInternal { * Called by the power manager to determine whether a dream is running. */ public abstract boolean isDreaming(); + + /** + * Called by the ActivityTaskManagerService to verify that the startDreamActivity + * request comes from the current active dream component. + * + * @param doze If true returns the current active doze component. Otherwise, returns the + * active dream component. + */ + public abstract ComponentName getActiveDreamComponent(boolean doze); } diff --git a/core/java/android/service/dreams/DreamService.java b/core/java/android/service/dreams/DreamService.java index de4a5511fcdb..28f492982a41 100644 --- a/core/java/android/service/dreams/DreamService.java +++ b/core/java/android/service/dreams/DreamService.java @@ -15,25 +15,29 @@ */ package android.service.dreams; +import static android.view.WindowManager.LayoutParams.TYPE_DREAM; + import android.annotation.IdRes; import android.annotation.LayoutRes; import android.annotation.NonNull; import android.annotation.Nullable; import android.annotation.SdkConstant; import android.annotation.SdkConstant.SdkConstantType; +import android.app.Activity; +import android.app.ActivityTaskManager; import android.app.AlarmManager; import android.app.Service; import android.compat.annotation.UnsupportedAppUsage; import android.content.Intent; -import android.graphics.PixelFormat; -import android.graphics.drawable.ColorDrawable; import android.os.Build; import android.os.Handler; import android.os.IBinder; import android.os.IRemoteCallback; +import android.os.Looper; import android.os.PowerManager; import android.os.RemoteException; import android.os.ServiceManager; +import android.util.Log; import android.util.MathUtils; import android.util.Slog; import android.view.ActionMode; @@ -48,10 +52,8 @@ import android.view.ViewGroup; import android.view.Window; import android.view.WindowManager; import android.view.WindowManager.LayoutParams; -import android.view.WindowManagerGlobal; import android.view.accessibility.AccessibilityEvent; -import com.android.internal.policy.PhoneWindow; import com.android.internal.util.DumpUtils; import com.android.internal.util.DumpUtils.Dump; @@ -176,10 +178,11 @@ public class DreamService extends Service implements Window.Callback { */ public static final String DREAM_META_DATA = "android.service.dream"; - private final IDreamManager mSandman; - private final Handler mHandler = new Handler(); - private IBinder mWindowToken; + private final IDreamManager mDreamManager; + private final Handler mHandler = new Handler(Looper.getMainLooper()); + private IBinder mDreamToken; private Window mWindow; + private Activity mActivity; private boolean mInteractive; private boolean mLowProfile = true; private boolean mFullscreen; @@ -195,8 +198,11 @@ public class DreamService extends Service implements Window.Callback { private boolean mDebug = false; + private DreamServiceWrapper mDreamServiceWrapper; + private Runnable mDispatchAfterOnAttachedToWindow; + public DreamService() { - mSandman = IDreamManager.Stub.asInterface(ServiceManager.getService(DREAM_SERVICE)); + mDreamManager = IDreamManager.Stub.asInterface(ServiceManager.getService(DREAM_SERVICE)); } /** @@ -602,6 +608,8 @@ public class DreamService extends Service implements Window.Callback { * Marks this dream as windowless. Only available to doze dreams. * * @hide + * + * TODO: Remove @UnsupportedAppUsage. */ @UnsupportedAppUsage public void setWindowless(boolean windowless) { @@ -670,14 +678,14 @@ public class DreamService extends Service implements Window.Callback { } private void updateDoze() { - if (mWindowToken == null) { - Slog.w(TAG, "Updating doze without a window token."); + if (mDreamToken == null) { + Slog.w(TAG, "Updating doze without a dream token."); return; } if (mDozing) { try { - mSandman.startDozing(mWindowToken, mDozeScreenState, mDozeScreenBrightness); + mDreamManager.startDozing(mDreamToken, mDozeScreenState, mDozeScreenBrightness); } catch (RemoteException ex) { // system server died } @@ -700,7 +708,7 @@ public class DreamService extends Service implements Window.Callback { if (mDozing) { mDozing = false; try { - mSandman.stopDozing(mWindowToken); + mDreamManager.stopDozing(mDreamToken); } catch (RemoteException ex) { // system server died } @@ -875,14 +883,15 @@ public class DreamService extends Service implements Window.Callback { * </p> */ public void onWakeUp() { - finish(); + mActivity.finishAndRemoveTask(); } /** {@inheritDoc} */ @Override public final IBinder onBind(Intent intent) { if (mDebug) Slog.v(TAG, "onBind() intent = " + intent); - return new DreamServiceWrapper(); + mDreamServiceWrapper = new DreamServiceWrapper(); + return mDreamServiceWrapper; } /** @@ -895,20 +904,25 @@ public class DreamService extends Service implements Window.Callback { public final void finish() { if (mDebug) Slog.v(TAG, "finish(): mFinished=" + mFinished); + if (mActivity == null) { + Slog.w(TAG, "Finish was called before the dream was attached."); + } else if (!mActivity.isFinishing()) { + // In case the activity is not finished yet, do it now. This can happen if someone calls + // finish() directly, without going through wakeUp(). + mActivity.finishAndRemoveTask(); + return; + } + if (!mFinished) { mFinished = true; - if (mWindowToken == null) { - Slog.w(TAG, "Finish was called before the dream was attached."); - } else { - try { - mSandman.finishSelf(mWindowToken, true /*immediate*/); - } catch (RemoteException ex) { - // system server died - } + try { + // finishSelf will unbind the dream controller from the dream service. This will + // trigger DreamService.this.onDestroy and DreamService.this will die. + mDreamManager.finishSelf(mDreamToken, true /*immediate*/); + } catch (RemoteException ex) { + // system server died } - - stopSelf(); // if launched via any other means } } @@ -938,11 +952,11 @@ public class DreamService extends Service implements Window.Callback { // Now tell the system we are waking gently, unless we already told // it we were finishing immediately. if (!fromSystem && !mFinished) { - if (mWindowToken == null) { + if (mActivity == null) { Slog.w(TAG, "WakeUp was called before the dream was attached."); } else { try { - mSandman.finishSelf(mWindowToken, false /*immediate*/); + mDreamManager.finishSelf(mDreamToken, false /*immediate*/); } catch (RemoteException ex) { // system server died } @@ -977,20 +991,14 @@ public class DreamService extends Service implements Window.Callback { onDreamingStopped(); } - if (mWindow != null) { - // force our window to be removed synchronously - if (mDebug) Slog.v(TAG, "detach(): Removing window from window manager"); - mWindow.getWindowManager().removeViewImmediate(mWindow.getDecorView()); - mWindow = null; + if (mActivity != null && !mActivity.isFinishing()) { + mActivity.finishAndRemoveTask(); + } else { + finish(); } - if (mWindowToken != null) { - // the following will print a log message if it finds any other leaked windows - WindowManagerGlobal.getInstance().closeAll(mWindowToken, - this.getClass().getName(), "Dream"); - mWindowToken = null; - mCanDoze = false; - } + mDreamToken = null; + mCanDoze = false; } /** @@ -998,95 +1006,107 @@ public class DreamService extends Service implements Window.Callback { * * Must run on mHandler. * - * @param windowToken A window token that will allow a window to be created in the correct layer. + * @param dreamToken Token for this dream service. * @param started A callback that will be invoked once onDreamingStarted has completed. */ - private final void attach(IBinder windowToken, boolean canDoze, IRemoteCallback started) { - if (mWindowToken != null) { - Slog.e(TAG, "attach() called when already attached with token=" + mWindowToken); + private void attach(IBinder dreamToken, boolean canDoze, IRemoteCallback started) { + if (mActivity != null) { + Slog.e(TAG, "attach() called when dream with token=" + mDreamToken + + " already attached"); return; } if (mFinished || mWaking) { Slog.w(TAG, "attach() called after dream already finished"); try { - mSandman.finishSelf(windowToken, true /*immediate*/); + mDreamManager.finishSelf(dreamToken, true /*immediate*/); } catch (RemoteException ex) { // system server died } return; } - mWindowToken = windowToken; + mDreamToken = dreamToken; mCanDoze = canDoze; if (mWindowless && !mCanDoze) { throw new IllegalStateException("Only doze dreams can be windowless"); } - if (!mWindowless) { - mWindow = new PhoneWindow(this); - mWindow.setCallback(this); - mWindow.requestFeature(Window.FEATURE_NO_TITLE); - mWindow.setBackgroundDrawable(new ColorDrawable(0xFF000000)); - mWindow.setFormat(PixelFormat.OPAQUE); - if (mDebug) Slog.v(TAG, String.format("Attaching window token: %s to window of type %s", - windowToken, WindowManager.LayoutParams.TYPE_DREAM)); - - WindowManager.LayoutParams lp = mWindow.getAttributes(); - lp.type = WindowManager.LayoutParams.TYPE_DREAM; - lp.token = windowToken; - lp.windowAnimations = com.android.internal.R.style.Animation_Dream; - lp.flags |= ( WindowManager.LayoutParams.FLAG_LAYOUT_IN_SCREEN - | WindowManager.LayoutParams.FLAG_LAYOUT_INSET_DECOR - | WindowManager.LayoutParams.FLAG_SHOW_WHEN_LOCKED - | WindowManager.LayoutParams.FLAG_DISMISS_KEYGUARD - | WindowManager.LayoutParams.FLAG_ALLOW_LOCK_WHILE_SCREEN_ON - | (mFullscreen ? WindowManager.LayoutParams.FLAG_FULLSCREEN : 0) - | (mScreenBright ? WindowManager.LayoutParams.FLAG_KEEP_SCREEN_ON : 0) - ); - mWindow.setAttributes(lp); - // Workaround: Currently low-profile and in-window system bar backgrounds don't go - // along well. Dreams usually don't need such bars anyways, so disable them by default. - mWindow.clearFlags(WindowManager.LayoutParams.FLAG_DRAWS_SYSTEM_BAR_BACKGROUNDS); - mWindow.setWindowManager(null, windowToken, "dream", true); + mDispatchAfterOnAttachedToWindow = () -> { + if (mWindow != null || mWindowless) { + mStarted = true; + try { + onDreamingStarted(); + } finally { + try { + started.sendResult(null); + } catch (RemoteException e) { + throw e.rethrowFromSystemServer(); + } + } + } + }; - applySystemUiVisibilityFlags( - (mLowProfile ? View.SYSTEM_UI_FLAG_LOW_PROFILE : 0), - View.SYSTEM_UI_FLAG_LOW_PROFILE); + // We need to defer calling onDreamingStarted until after the activity is created. + // If the dream is windowless, we can call it immediately. Otherwise, we wait + // for the DreamActivity to report onActivityCreated via + // DreamServiceWrapper.onActivityCreated. + if (!mWindowless) { + Intent i = new Intent(this, DreamActivity.class); + i.setFlags(Intent.FLAG_ACTIVITY_NEW_TASK); + i.putExtra(DreamActivity.EXTRA_CALLBACK, mDreamServiceWrapper); try { - getWindowManager().addView(mWindow.getDecorView(), mWindow.getAttributes()); - } catch (WindowManager.BadTokenException ex) { - // This can happen because the dream manager service will remove the token - // immediately without necessarily waiting for the dream to start. - // We should receive a finish message soon. - Slog.i(TAG, "attach() called after window token already removed, dream will " - + "finish soon"); - mWindow = null; - return; + if (!ActivityTaskManager.getService().startDreamActivity(i)) { + detach(); + return; + } + } catch (RemoteException e) { + Log.w(TAG, "Could not connect to activity task manager to start dream activity"); + e.rethrowFromSystemServer(); } + } else { + mDispatchAfterOnAttachedToWindow.run(); } - // We need to defer calling onDreamingStarted until after onWindowAttached, - // which is posted to the handler by addView, so we post onDreamingStarted - // to the handler also. Need to watch out here in case detach occurs before - // this callback is invoked. - mHandler.post(new Runnable() { - @Override - public void run() { - if (mWindow != null || mWindowless) { - if (mDebug) Slog.v(TAG, "Calling onDreamingStarted()"); - mStarted = true; - try { - onDreamingStarted(); - } finally { - try { - started.sendResult(null); - } catch (RemoteException e) { - throw e.rethrowFromSystemServer(); - } + } + + private void onWindowCreated(Window w) { + mWindow = w; + mWindow.setCallback(this); + mWindow.setType(TYPE_DREAM); + mWindow.requestFeature(Window.FEATURE_NO_TITLE); + + WindowManager.LayoutParams lp = mWindow.getAttributes(); + lp.windowAnimations = com.android.internal.R.style.Animation_Dream; + lp.flags |= (WindowManager.LayoutParams.FLAG_LAYOUT_IN_SCREEN + | WindowManager.LayoutParams.FLAG_LAYOUT_INSET_DECOR + | WindowManager.LayoutParams.FLAG_SHOW_WHEN_LOCKED + | WindowManager.LayoutParams.FLAG_DISMISS_KEYGUARD + | WindowManager.LayoutParams.FLAG_ALLOW_LOCK_WHILE_SCREEN_ON + | WindowManager.LayoutParams.FLAG_HARDWARE_ACCELERATED + | (mFullscreen ? WindowManager.LayoutParams.FLAG_FULLSCREEN : 0) + | (mScreenBright ? WindowManager.LayoutParams.FLAG_KEEP_SCREEN_ON : 0) + ); + mWindow.setAttributes(lp); + // Workaround: Currently low-profile and in-window system bar backgrounds don't go + // along well. Dreams usually don't need such bars anyways, so disable them by default. + mWindow.clearFlags(WindowManager.LayoutParams.FLAG_DRAWS_SYSTEM_BAR_BACKGROUNDS); + + applySystemUiVisibilityFlags( + (mLowProfile ? View.SYSTEM_UI_FLAG_LOW_PROFILE : 0), + View.SYSTEM_UI_FLAG_LOW_PROFILE); + + mWindow.getDecorView().addOnAttachStateChangeListener( + new View.OnAttachStateChangeListener() { + @Override + public void onViewAttachedToWindow(View v) { + mDispatchAfterOnAttachedToWindow.run(); } - } - } - }); + + @Override + public void onViewDetachedFromWindow(View v) { + finish(); + } + }); } private boolean getWindowFlagValue(int flag, boolean defaultValue) { @@ -1131,10 +1151,10 @@ public class DreamService extends Service implements Window.Callback { /** @hide */ protected void dumpOnHandler(FileDescriptor fd, PrintWriter pw, String[] args) { pw.print(TAG + ": "); - if (mWindowToken == null) { + if (mFinished) { pw.println("stopped"); } else { - pw.println("running (token=" + mWindowToken + ")"); + pw.println("running (dreamToken=" + mDreamToken + ")"); } pw.println(" window: " + mWindow); pw.print(" flags:"); @@ -1156,36 +1176,32 @@ public class DreamService extends Service implements Window.Callback { return MathUtils.constrain(value, PowerManager.BRIGHTNESS_OFF, PowerManager.BRIGHTNESS_ON); } - private final class DreamServiceWrapper extends IDreamService.Stub { + /** + * The DreamServiceWrapper is used as a gateway to the system_server, where DreamController + * uses it to control the DreamService. It is also used to receive callbacks from the + * DreamActivity. + */ + final class DreamServiceWrapper extends IDreamService.Stub { @Override - public void attach(final IBinder windowToken, final boolean canDoze, + public void attach(final IBinder dreamToken, final boolean canDoze, IRemoteCallback started) { - mHandler.post(new Runnable() { - @Override - public void run() { - DreamService.this.attach(windowToken, canDoze, started); - } - }); + mHandler.post(() -> DreamService.this.attach(dreamToken, canDoze, started)); } @Override public void detach() { - mHandler.post(new Runnable() { - @Override - public void run() { - DreamService.this.detach(); - } - }); + mHandler.post(DreamService.this::detach); } @Override public void wakeUp() { - mHandler.post(new Runnable() { - @Override - public void run() { - DreamService.this.wakeUp(true /*fromSystem*/); - } - }); + mHandler.post(() -> DreamService.this.wakeUp(true /*fromSystem*/)); + } + + /** @hide */ + void onActivityCreated(DreamActivity a) { + mActivity = a; + onWindowCreated(a.getWindow()); } } } diff --git a/core/java/android/service/dreams/IDreamManager.aidl b/core/java/android/service/dreams/IDreamManager.aidl index d254ffdb6986..6496de3e15a0 100644 --- a/core/java/android/service/dreams/IDreamManager.aidl +++ b/core/java/android/service/dreams/IDreamManager.aidl @@ -31,12 +31,14 @@ interface IDreamManager { void setDreamComponents(in ComponentName[] componentNames); @UnsupportedAppUsage ComponentName[] getDreamComponents(); - ComponentName getDefaultDreamComponent(); - void testDream(in ComponentName componentName); + ComponentName getDefaultDreamComponentForUser(int userId); + void testDream(int userId, in ComponentName componentName); @UnsupportedAppUsage boolean isDreaming(); void finishSelf(in IBinder token, boolean immediate); void startDozing(in IBinder token, int screenState, int screenBrightness); void stopDozing(in IBinder token); void forceAmbientDisplayEnabled(boolean enabled); + ComponentName[] getDreamComponentsForUser(int userId); + void setDreamComponentsForUser(int userId, in ComponentName[] componentNames); } diff --git a/core/java/android/service/notification/NotificationAssistantService.java b/core/java/android/service/notification/NotificationAssistantService.java index e976e18602c1..975e75ccaeeb 100644 --- a/core/java/android/service/notification/NotificationAssistantService.java +++ b/core/java/android/service/notification/NotificationAssistantService.java @@ -184,7 +184,7 @@ public abstract class NotificationAssistantService extends NotificationListenerS /** * Implement this to know when the notification panel is revealed * - * @param items Number of items on the panel at time of opening + * @param items Number of notifications on the panel at time of opening */ public void onPanelRevealed(int items) { diff --git a/core/java/android/service/notification/OWNERS b/core/java/android/service/notification/OWNERS new file mode 100644 index 000000000000..2e94be5bf329 --- /dev/null +++ b/core/java/android/service/notification/OWNERS @@ -0,0 +1,4 @@ +juliacr@google.com +beverlyt@google.com +dsandler@android.com +pixel@google.com
\ No newline at end of file diff --git a/core/java/android/view/DisplayCutout.java b/core/java/android/view/DisplayCutout.java index 31fc16188814..0ab856ec5b4d 100644 --- a/core/java/android/view/DisplayCutout.java +++ b/core/java/android/view/DisplayCutout.java @@ -554,26 +554,12 @@ public final class DisplayCutout { */ public DisplayCutout inset(int insetLeft, int insetTop, int insetRight, int insetBottom) { if (insetLeft == 0 && insetTop == 0 && insetRight == 0 && insetBottom == 0 - || isBoundsEmpty()) { + || (isBoundsEmpty() && mWaterfallInsets.equals(Insets.NONE))) { return this; } - Rect safeInsets = new Rect(mSafeInsets); - - // Note: it's not really well defined what happens when the inset is negative, because we - // don't know if the safe inset needs to expand in general. - if (insetTop > 0 || safeInsets.top > 0) { - safeInsets.top = atLeastZero(safeInsets.top - insetTop); - } - if (insetBottom > 0 || safeInsets.bottom > 0) { - safeInsets.bottom = atLeastZero(safeInsets.bottom - insetBottom); - } - if (insetLeft > 0 || safeInsets.left > 0) { - safeInsets.left = atLeastZero(safeInsets.left - insetLeft); - } - if (insetRight > 0 || safeInsets.right > 0) { - safeInsets.right = atLeastZero(safeInsets.right - insetRight); - } + Rect safeInsets = insetInsets(insetLeft, insetTop, insetRight, insetBottom, + new Rect(mSafeInsets)); // If we are not cutting off part of the cutout by insetting it on bottom/right, and we also // don't move it around, we can avoid the allocation and copy of the instance. @@ -581,6 +567,9 @@ public final class DisplayCutout { return this; } + Rect waterfallInsets = insetInsets(insetLeft, insetTop, insetRight, insetBottom, + mWaterfallInsets.toRect()); + Rect[] bounds = mBounds.getRects(); for (int i = 0; i < bounds.length; ++i) { if (!bounds[i].equals(ZERO_RECT)) { @@ -588,7 +577,27 @@ public final class DisplayCutout { } } - return new DisplayCutout(safeInsets, mWaterfallInsets, bounds, false /* copyArguments */); + return new DisplayCutout(safeInsets, Insets.of(waterfallInsets), bounds, + false /* copyArguments */); + } + + private Rect insetInsets(int insetLeft, int insetTop, int insetRight, int insetBottom, + Rect insets) { + // Note: it's not really well defined what happens when the inset is negative, because we + // don't know if the safe inset needs to expand in general. + if (insetTop > 0 || insets.top > 0) { + insets.top = atLeastZero(insets.top - insetTop); + } + if (insetBottom > 0 || insets.bottom > 0) { + insets.bottom = atLeastZero(insets.bottom - insetBottom); + } + if (insetLeft > 0 || insets.left > 0) { + insets.left = atLeastZero(insets.left - insetLeft); + } + if (insetRight > 0 || insets.right > 0) { + insets.right = atLeastZero(insets.right - insetRight); + } + return insets; } /** diff --git a/core/java/android/view/IPinnedStackController.aidl b/core/java/android/view/IPinnedStackController.aidl index cb82f168d991..1cf83a3f1e79 100644 --- a/core/java/android/view/IPinnedStackController.aidl +++ b/core/java/android/view/IPinnedStackController.aidl @@ -25,32 +25,8 @@ import android.graphics.Rect; * @hide */ interface IPinnedStackController { - - /** - * Notifies the controller that the PiP is currently minimized. - */ - oneway void setIsMinimized(boolean isMinimized); - /** * @return what WM considers to be the current device rotation. */ int getDisplayRotation(); - - /** - * Notifies the controller to actually start the PiP animation. - * The bounds would be calculated based on the last save reentry fraction internally. - * {@param destinationBounds} is the stack bounds of the final PiP window - * and {@param sourceRectHint} is the source bounds hint used when entering picture-in-picture, - * expect the same bound passed via IPinnedStackListener#onPrepareAnimation. - * {@param animationDuration} suggests the animation duration transitioning to PiP window. - */ - void startAnimation(in Rect destinationBounds, in Rect sourceRectHint, int animationDuration); - - /** - * Notifies the controller to reset on bounds animation, if there is any. - * This could happen when screen rotation is happening and we need to notify the WM to reset - * any running bounds animation on the pinned stack. - * {@param bounds} here is the final destination bounds. - */ - void resetBoundsAnimation(in Rect bounds); } diff --git a/core/java/android/view/IPinnedStackListener.aidl b/core/java/android/view/IPinnedStackListener.aidl index d01c9330dcb2..596d55aebc6d 100644 --- a/core/java/android/view/IPinnedStackListener.aidl +++ b/core/java/android/view/IPinnedStackListener.aidl @@ -43,8 +43,7 @@ oneway interface IPinnedStackListener { * pinned stack (the final bounds if animating, the current bounds if not), * which may be helpful in calculating dependent animation bounds. */ - void onMovementBoundsChanged(in Rect animatingBounds, boolean fromImeAdjustment, - boolean fromShelfAdjustment); + void onMovementBoundsChanged(in Rect animatingBounds, boolean fromImeAdjustment); /** * Called when window manager decides to adjust the pinned stack bounds because of the IME, or @@ -55,12 +54,6 @@ oneway interface IPinnedStackListener { void onImeVisibilityChanged(boolean imeVisible, int imeHeight); /** - * Called when window manager decides to adjust the minimized state, or when the listener - * is first registered to allow the listener to synchronized its state with the controller. - */ - void onMinimizedStateChanged(boolean isMinimized); - - /** * Called when the set of actions for the current PiP activity changes, or when the listener * is first registered to allow the listener to synchronized its state with the controller. */ @@ -99,13 +92,4 @@ oneway interface IPinnedStackListener { * Called by the window manager when the aspect ratio is reset. */ void onAspectRatioChanged(float aspectRatio); - - /** - * Called by the window manager to notify the listener to prepare for PiP animation. - * Internally, the target bounds would be calculated from the given {@param aspectRatio} - * and {@param bounds}, the saved reentry snap fraction also contributes. - * Caller would wait for a IPinnedStackController#startAnimation callback to actually - * start the animation, see details in IPinnedStackController. - */ - void onPrepareAnimation(in Rect sourceRectHint, float aspectRatio, in Rect bounds); } diff --git a/core/java/android/view/IRecentsAnimationController.aidl b/core/java/android/view/IRecentsAnimationController.aidl index 762366eb6295..530dffbf0620 100644 --- a/core/java/android/view/IRecentsAnimationController.aidl +++ b/core/java/android/view/IRecentsAnimationController.aidl @@ -77,13 +77,6 @@ interface IRecentsAnimationController { void hideCurrentInputMethod(); /** - * This call is deprecated, use #setDeferCancelUntilNextTransition() instead - * TODO(138144750): Remove this method once there are no callers - * @deprecated - */ - void setCancelWithDeferredScreenshot(boolean screenshot); - - /** * Clean up the screenshot of previous task which was created during recents animation that * was cancelled by a stack order change. * diff --git a/core/java/android/view/KeyEvent.java b/core/java/android/view/KeyEvent.java index 9e344e8d2f75..e249c777caf6 100644 --- a/core/java/android/view/KeyEvent.java +++ b/core/java/android/view/KeyEvent.java @@ -1974,7 +1974,6 @@ public class KeyEvent extends InputEvent implements Parcelable { /** @hide */ public static final boolean isWakeKey(int keyCode) { switch (keyCode) { - case KeyEvent.KEYCODE_BACK: case KeyEvent.KEYCODE_CAMERA: case KeyEvent.KEYCODE_MENU: case KeyEvent.KEYCODE_PAIRING: diff --git a/core/java/android/view/inputmethod/InlineSuggestionsRequest.java b/core/java/android/view/inputmethod/InlineSuggestionsRequest.java index 5700dda3a2e3..e50da40cdc16 100644 --- a/core/java/android/view/inputmethod/InlineSuggestionsRequest.java +++ b/core/java/android/view/inputmethod/InlineSuggestionsRequest.java @@ -22,7 +22,9 @@ import android.app.ActivityThread; import android.os.Bundle; import android.os.IBinder; import android.os.LocaleList; +import android.os.Parcel; import android.os.Parcelable; +import android.view.Display; import android.view.inline.InlinePresentationSpec; import com.android.internal.util.DataClass; @@ -67,7 +69,11 @@ public final class InlineSuggestionsRequest implements Parcelable { */ private @NonNull LocaleList mSupportedLocales; - // TODO(b/149609075): the generated code needs to be manually fixed due to the bug. + /** + * The extras state propagated from the IME to pass extra data. + */ + private @Nullable Bundle mExtras; + /** * The host input token of the IME that made the request. This will be set by the system for * safety reasons. @@ -77,9 +83,12 @@ public final class InlineSuggestionsRequest implements Parcelable { private @Nullable IBinder mHostInputToken; /** - * The extras state propagated from the IME to pass extra data. + * The host display id of the IME that made the request. This will be set by the system for + * safety reasons. + * + * @hide */ - private @Nullable Bundle mExtras; + private int mHostDisplayId; /** * @hide @@ -89,6 +98,24 @@ public final class InlineSuggestionsRequest implements Parcelable { mHostInputToken = hostInputToken; } + // TODO(b/149609075): remove once IBinder parcelling is natively supported + private void parcelHostInputToken(@NonNull Parcel parcel, int flags) { + parcel.writeStrongBinder(mHostInputToken); + } + + // TODO(b/149609075): remove once IBinder parcelling is natively supported + private @Nullable IBinder unparcelHostInputToken(Parcel parcel) { + return parcel.readStrongBinder(); + } + + /** + * @hide + * @see {@link #mHostDisplayId}. + */ + public void setHostDisplayId(int hostDisplayId) { + mHostDisplayId = hostDisplayId; + } + private void onConstructed() { Preconditions.checkState(mMaxSuggestionCount >= mPresentationSpecs.size()); } @@ -111,10 +138,17 @@ public final class InlineSuggestionsRequest implements Parcelable { } @Nullable + private static int defaultHostDisplayId() { + return Display.INVALID_DISPLAY; + } + + @Nullable private static Bundle defaultExtras() { return null; } + + /** @hide */ abstract static class BaseBuilder { abstract Builder setPresentationSpecs(@NonNull List<InlinePresentationSpec> value); @@ -122,6 +156,8 @@ public final class InlineSuggestionsRequest implements Parcelable { abstract Builder setHostPackageName(@Nullable String value); abstract Builder setHostInputToken(IBinder hostInputToken); + + abstract Builder setHostDisplayId(int value); } @@ -145,8 +181,9 @@ public final class InlineSuggestionsRequest implements Parcelable { @NonNull List<InlinePresentationSpec> presentationSpecs, @NonNull String hostPackageName, @NonNull LocaleList supportedLocales, + @Nullable Bundle extras, @Nullable IBinder hostInputToken, - @Nullable Bundle extras) { + int hostDisplayId) { this.mMaxSuggestionCount = maxSuggestionCount; this.mPresentationSpecs = presentationSpecs; com.android.internal.util.AnnotationValidations.validate( @@ -157,8 +194,9 @@ public final class InlineSuggestionsRequest implements Parcelable { this.mSupportedLocales = supportedLocales; com.android.internal.util.AnnotationValidations.validate( NonNull.class, null, mSupportedLocales); - this.mHostInputToken = hostInputToken; this.mExtras = extras; + this.mHostInputToken = hostInputToken; + this.mHostDisplayId = hostDisplayId; onConstructed(); } @@ -202,6 +240,14 @@ public final class InlineSuggestionsRequest implements Parcelable { } /** + * The extras state propagated from the IME to pass extra data. + */ + @DataClass.Generated.Member + public @Nullable Bundle getExtras() { + return mExtras; + } + + /** * The host input token of the IME that made the request. This will be set by the system for * safety reasons. * @@ -213,11 +259,14 @@ public final class InlineSuggestionsRequest implements Parcelable { } /** - * The extras state propagated from the IME to pass extra data. + * The host display id of the IME that made the request. This will be set by the system for + * safety reasons. + * + * @hide */ @DataClass.Generated.Member - public @Nullable Bundle getExtras() { - return mExtras; + public int getHostDisplayId() { + return mHostDisplayId; } @Override @@ -231,8 +280,9 @@ public final class InlineSuggestionsRequest implements Parcelable { "presentationSpecs = " + mPresentationSpecs + ", " + "hostPackageName = " + mHostPackageName + ", " + "supportedLocales = " + mSupportedLocales + ", " + + "extras = " + mExtras + ", " + "hostInputToken = " + mHostInputToken + ", " + - "extras = " + mExtras + + "hostDisplayId = " + mHostDisplayId + " }"; } @@ -253,8 +303,9 @@ public final class InlineSuggestionsRequest implements Parcelable { && java.util.Objects.equals(mPresentationSpecs, that.mPresentationSpecs) && java.util.Objects.equals(mHostPackageName, that.mHostPackageName) && java.util.Objects.equals(mSupportedLocales, that.mSupportedLocales) + && java.util.Objects.equals(mExtras, that.mExtras) && java.util.Objects.equals(mHostInputToken, that.mHostInputToken) - && java.util.Objects.equals(mExtras, that.mExtras); + && mHostDisplayId == that.mHostDisplayId; } @Override @@ -268,27 +319,29 @@ public final class InlineSuggestionsRequest implements Parcelable { _hash = 31 * _hash + java.util.Objects.hashCode(mPresentationSpecs); _hash = 31 * _hash + java.util.Objects.hashCode(mHostPackageName); _hash = 31 * _hash + java.util.Objects.hashCode(mSupportedLocales); - _hash = 31 * _hash + java.util.Objects.hashCode(mHostInputToken); _hash = 31 * _hash + java.util.Objects.hashCode(mExtras); + _hash = 31 * _hash + java.util.Objects.hashCode(mHostInputToken); + _hash = 31 * _hash + mHostDisplayId; return _hash; } @Override @DataClass.Generated.Member - public void writeToParcel(@NonNull android.os.Parcel dest, int flags) { + public void writeToParcel(@NonNull Parcel dest, int flags) { // You can override field parcelling by defining methods like: // void parcelFieldName(Parcel dest, int flags) { ... } byte flg = 0; - if (mHostInputToken != null) flg |= 0x10; - if (mExtras != null) flg |= 0x20; + if (mExtras != null) flg |= 0x10; + if (mHostInputToken != null) flg |= 0x20; dest.writeByte(flg); dest.writeInt(mMaxSuggestionCount); dest.writeParcelableList(mPresentationSpecs, flags); dest.writeString(mHostPackageName); dest.writeTypedObject(mSupportedLocales, flags); - if (mHostInputToken != null) dest.writeStrongBinder(mHostInputToken); if (mExtras != null) dest.writeBundle(mExtras); + parcelHostInputToken(dest, flags); + dest.writeInt(mHostDisplayId); } @Override @@ -298,7 +351,7 @@ public final class InlineSuggestionsRequest implements Parcelable { /** @hide */ @SuppressWarnings({"unchecked", "RedundantCast"}) @DataClass.Generated.Member - /* package-private */ InlineSuggestionsRequest(@NonNull android.os.Parcel in) { + /* package-private */ InlineSuggestionsRequest(@NonNull Parcel in) { // You can override field unparcelling by defining methods like: // static FieldType unparcelFieldName(Parcel in) { ... } @@ -308,8 +361,9 @@ public final class InlineSuggestionsRequest implements Parcelable { in.readParcelableList(presentationSpecs, InlinePresentationSpec.class.getClassLoader()); String hostPackageName = in.readString(); LocaleList supportedLocales = (LocaleList) in.readTypedObject(LocaleList.CREATOR); - IBinder hostInputToken = (flg & 0x10) == 0 ? null : in.readStrongBinder(); - Bundle extras = (flg & 0x20) == 0 ? null : in.readBundle(); + Bundle extras = (flg & 0x10) == 0 ? null : in.readBundle(); + IBinder hostInputToken = unparcelHostInputToken(in); + int hostDisplayId = in.readInt(); this.mMaxSuggestionCount = maxSuggestionCount; this.mPresentationSpecs = presentationSpecs; @@ -321,8 +375,9 @@ public final class InlineSuggestionsRequest implements Parcelable { this.mSupportedLocales = supportedLocales; com.android.internal.util.AnnotationValidations.validate( NonNull.class, null, mSupportedLocales); - this.mHostInputToken = hostInputToken; this.mExtras = extras; + this.mHostInputToken = hostInputToken; + this.mHostDisplayId = hostDisplayId; onConstructed(); } @@ -336,7 +391,7 @@ public final class InlineSuggestionsRequest implements Parcelable { } @Override - public InlineSuggestionsRequest createFromParcel(@NonNull android.os.Parcel in) { + public InlineSuggestionsRequest createFromParcel(@NonNull Parcel in) { return new InlineSuggestionsRequest(in); } }; @@ -352,8 +407,9 @@ public final class InlineSuggestionsRequest implements Parcelable { private @NonNull List<InlinePresentationSpec> mPresentationSpecs; private @NonNull String mHostPackageName; private @NonNull LocaleList mSupportedLocales; - private @Nullable IBinder mHostInputToken; private @Nullable Bundle mExtras; + private @Nullable IBinder mHostInputToken; + private int mHostDisplayId; private long mBuilderFieldsSet = 0L; @@ -436,6 +492,17 @@ public final class InlineSuggestionsRequest implements Parcelable { } /** + * The extras state propagated from the IME to pass extra data. + */ + @DataClass.Generated.Member + public @NonNull Builder setExtras(@Nullable Bundle value) { + checkNotUsed(); + mBuilderFieldsSet |= 0x10; + mExtras = value; + return this; + } + + /** * The host input token of the IME that made the request. This will be set by the system for * safety reasons. * @@ -445,26 +512,30 @@ public final class InlineSuggestionsRequest implements Parcelable { @Override @NonNull Builder setHostInputToken(@Nullable IBinder value) { checkNotUsed(); - mBuilderFieldsSet |= 0x10; + mBuilderFieldsSet |= 0x20; mHostInputToken = value; return this; } /** - * The extras state propagated from the IME to pass extra data. + * The host display id of the IME that made the request. This will be set by the system for + * safety reasons. + * + * @hide */ @DataClass.Generated.Member - public @NonNull Builder setExtras(@Nullable Bundle value) { + @Override + @NonNull Builder setHostDisplayId(int value) { checkNotUsed(); - mBuilderFieldsSet |= 0x20; - mExtras = value; + mBuilderFieldsSet |= 0x40; + mHostDisplayId = value; return this; } /** Builds the instance. This builder should not be touched after calling this! */ public @NonNull InlineSuggestionsRequest build() { checkNotUsed(); - mBuilderFieldsSet |= 0x40; // Mark builder used + mBuilderFieldsSet |= 0x80; // Mark builder used if ((mBuilderFieldsSet & 0x1) == 0) { mMaxSuggestionCount = defaultMaxSuggestionCount(); @@ -476,23 +547,27 @@ public final class InlineSuggestionsRequest implements Parcelable { mSupportedLocales = defaultSupportedLocales(); } if ((mBuilderFieldsSet & 0x10) == 0) { - mHostInputToken = defaultHostInputToken(); + mExtras = defaultExtras(); } if ((mBuilderFieldsSet & 0x20) == 0) { - mExtras = defaultExtras(); + mHostInputToken = defaultHostInputToken(); + } + if ((mBuilderFieldsSet & 0x40) == 0) { + mHostDisplayId = defaultHostDisplayId(); } InlineSuggestionsRequest o = new InlineSuggestionsRequest( mMaxSuggestionCount, mPresentationSpecs, mHostPackageName, mSupportedLocales, + mExtras, mHostInputToken, - mExtras); + mHostDisplayId); return o; } private void checkNotUsed() { - if ((mBuilderFieldsSet & 0x40) != 0) { + if ((mBuilderFieldsSet & 0x80) != 0) { throw new IllegalStateException( "This Builder should not be reused. Use a new Builder instance instead"); } @@ -500,10 +575,10 @@ public final class InlineSuggestionsRequest implements Parcelable { } @DataClass.Generated( - time = 1581747892762L, + time = 1582339908980L, codegenVersion = "1.0.14", sourceFile = "frameworks/base/core/java/android/view/inputmethod/InlineSuggestionsRequest.java", - inputSignatures = "public static final int SUGGESTION_COUNT_UNLIMITED\nprivate final int mMaxSuggestionCount\nprivate final @android.annotation.NonNull java.util.List<android.view.inline.InlinePresentationSpec> mPresentationSpecs\nprivate @android.annotation.NonNull java.lang.String mHostPackageName\nprivate @android.annotation.NonNull android.os.LocaleList mSupportedLocales\nprivate @android.annotation.Nullable android.os.IBinder mHostInputToken\nprivate @android.annotation.Nullable android.os.Bundle mExtras\npublic void setHostInputToken(android.os.IBinder)\nprivate void onConstructed()\nprivate static int defaultMaxSuggestionCount()\nprivate static java.lang.String defaultHostPackageName()\nprivate static android.os.LocaleList defaultSupportedLocales()\nprivate static @android.annotation.Nullable android.os.IBinder defaultHostInputToken()\nprivate static @android.annotation.Nullable android.os.Bundle defaultExtras()\nclass InlineSuggestionsRequest extends java.lang.Object implements [android.os.Parcelable]\n@com.android.internal.util.DataClass(genEqualsHashCode=true, genToString=true, genBuilder=true)\nabstract android.view.inputmethod.InlineSuggestionsRequest.Builder setPresentationSpecs(java.util.List<android.view.inline.InlinePresentationSpec>)\nabstract android.view.inputmethod.InlineSuggestionsRequest.Builder setHostPackageName(java.lang.String)\nabstract android.view.inputmethod.InlineSuggestionsRequest.Builder setHostInputToken(android.os.IBinder)\nclass BaseBuilder extends java.lang.Object implements []") + inputSignatures = "public static final int SUGGESTION_COUNT_UNLIMITED\nprivate final int mMaxSuggestionCount\nprivate final @android.annotation.NonNull java.util.List<android.view.inline.InlinePresentationSpec> mPresentationSpecs\nprivate @android.annotation.NonNull java.lang.String mHostPackageName\nprivate @android.annotation.NonNull android.os.LocaleList mSupportedLocales\nprivate @android.annotation.Nullable android.os.Bundle mExtras\nprivate @android.annotation.Nullable android.os.IBinder mHostInputToken\nprivate int mHostDisplayId\npublic void setHostInputToken(android.os.IBinder)\nprivate void parcelHostInputToken(android.os.Parcel,int)\nprivate @android.annotation.Nullable android.os.IBinder unparcelHostInputToken(android.os.Parcel)\npublic void setHostDisplayId(int)\nprivate void onConstructed()\nprivate static int defaultMaxSuggestionCount()\nprivate static java.lang.String defaultHostPackageName()\nprivate static android.os.LocaleList defaultSupportedLocales()\nprivate static @android.annotation.Nullable android.os.IBinder defaultHostInputToken()\nprivate static @android.annotation.Nullable int defaultHostDisplayId()\nprivate static @android.annotation.Nullable android.os.Bundle defaultExtras()\nclass InlineSuggestionsRequest extends java.lang.Object implements [android.os.Parcelable]\n@com.android.internal.util.DataClass(genEqualsHashCode=true, genToString=true, genBuilder=true)\nabstract android.view.inputmethod.InlineSuggestionsRequest.Builder setPresentationSpecs(java.util.List<android.view.inline.InlinePresentationSpec>)\nabstract android.view.inputmethod.InlineSuggestionsRequest.Builder setHostPackageName(java.lang.String)\nabstract android.view.inputmethod.InlineSuggestionsRequest.Builder setHostInputToken(android.os.IBinder)\nabstract android.view.inputmethod.InlineSuggestionsRequest.Builder setHostDisplayId(int)\nclass BaseBuilder extends java.lang.Object implements []") @Deprecated private void __metadata() {} diff --git a/core/java/com/android/internal/accessibility/AccessibilityShortcutController.java b/core/java/com/android/internal/accessibility/AccessibilityShortcutController.java index bb404654b741..5cdcab029877 100644 --- a/core/java/com/android/internal/accessibility/AccessibilityShortcutController.java +++ b/core/java/com/android/internal/accessibility/AccessibilityShortcutController.java @@ -412,8 +412,13 @@ public class AccessibilityShortcutController { * Class to wrap TextToSpeech for shortcut dialog spoken feedback. */ private class TtsPrompt implements TextToSpeech.OnInitListener { + private static final int RETRY_MILLIS = 1000; + private final CharSequence mText; + + private int mRetryCount = 3; private boolean mDismiss; + private boolean mLanguageReady = false; private TextToSpeech mTts; TtsPrompt(String serviceName) { @@ -437,17 +442,15 @@ public class AccessibilityShortcutController { playNotificationTone(); return; } - mHandler.sendMessage(PooledLambda.obtainMessage(TtsPrompt::play, this)); + mHandler.sendMessage(PooledLambda.obtainMessage( + TtsPrompt::waitForTtsReady, this)); } private void play() { if (mDismiss) { return; } - int status = TextToSpeech.ERROR; - if (setLanguage(Locale.getDefault())) { - status = mTts.speak(mText, TextToSpeech.QUEUE_FLUSH, null, null); - } + final int status = mTts.speak(mText, TextToSpeech.QUEUE_FLUSH, null, null); if (status != TextToSpeech.SUCCESS) { Slog.d(TAG, "Tts play fail"); playNotificationTone(); @@ -455,21 +458,42 @@ public class AccessibilityShortcutController { } /** - * @return false if tts language is not available + * Waiting for tts is ready to speak. Trying again if tts language pack is not available + * or tts voice data is not installed yet. */ - private boolean setLanguage(final Locale locale) { - int status = mTts.isLanguageAvailable(locale); - if (status == TextToSpeech.LANG_MISSING_DATA - || status == TextToSpeech.LANG_NOT_SUPPORTED) { - return false; + private void waitForTtsReady() { + if (mDismiss) { + return; + } + if (!mLanguageReady) { + final int status = mTts.setLanguage(Locale.getDefault()); + // True if language is available and TTS#loadVoice has called once + // that trigger TTS service to start initialization. + mLanguageReady = status != TextToSpeech.LANG_MISSING_DATA + && status != TextToSpeech.LANG_NOT_SUPPORTED; } - mTts.setLanguage(locale); - Voice voice = mTts.getVoice(); - if (voice == null || (voice.getFeatures() != null && voice.getFeatures() - .contains(TextToSpeech.Engine.KEY_FEATURE_NOT_INSTALLED))) { - return false; + if (mLanguageReady) { + final Voice voice = mTts.getVoice(); + final boolean voiceDataInstalled = voice != null + && voice.getFeatures() != null + && !voice.getFeatures().contains( + TextToSpeech.Engine.KEY_FEATURE_NOT_INSTALLED); + if (voiceDataInstalled) { + mHandler.sendMessage(PooledLambda.obtainMessage( + TtsPrompt::play, this)); + return; + } + } + + if (mRetryCount == 0) { + Slog.d(TAG, "Tts not ready to speak."); + playNotificationTone(); + return; } - return true; + // Retry if TTS service not ready yet. + mRetryCount -= 1; + mHandler.sendMessageDelayed(PooledLambda.obtainMessage( + TtsPrompt::waitForTtsReady, this), RETRY_MILLIS); } } diff --git a/core/java/com/android/internal/inputmethod/InputMethodDebug.java b/core/java/com/android/internal/inputmethod/InputMethodDebug.java index 382a254b67a6..3876976575ae 100644 --- a/core/java/com/android/internal/inputmethod/InputMethodDebug.java +++ b/core/java/com/android/internal/inputmethod/InputMethodDebug.java @@ -16,6 +16,8 @@ package com.android.internal.inputmethod; +import android.annotation.AnyThread; +import android.annotation.NonNull; import android.view.WindowManager; import android.view.WindowManager.LayoutParams.SoftInputModeFlags; @@ -25,6 +27,7 @@ import java.util.StringJoiner; * Provides useful methods for debugging. */ public final class InputMethodDebug { + /** * Not intended to be instantiated. */ @@ -174,4 +177,71 @@ public final class InputMethodDebug { return joiner.setEmptyValue("(none)").toString(); } + + + /** + * Converts {@link SoftInputShowHideReason} to {@link String} for history dump. + */ + public static String softInputDisplayReasonToString(@SoftInputShowHideReason int reason) { + switch (reason) { + case SoftInputShowHideReason.SHOW_SOFT_INPUT: + return "SHOW_SOFT_INPUT"; + case SoftInputShowHideReason.ATTACH_NEW_INPUT: + return "ATTACH_NEW_INPUT"; + case SoftInputShowHideReason.SHOW_MY_SOFT_INPUT: + return "SHOW_MY_SOFT_INPUT"; + case SoftInputShowHideReason.HIDE_SOFT_INPUT: + return "HIDE_SOFT_INPUT"; + case SoftInputShowHideReason.HIDE_MY_SOFT_INPUT: + return "HIDE_MY_SOFT_INPUT"; + case SoftInputShowHideReason.SHOW_AUTO_EDITOR_FORWARD_NAV: + return "SHOW_AUTO_EDITOR_FORWARD_NAV"; + case SoftInputShowHideReason.SHOW_STATE_VISIBLE_FORWARD_NAV: + return "SHOW_STATE_VISIBLE_FORWARD_NAV"; + case SoftInputShowHideReason.SHOW_STATE_ALWAYS_VISIBLE: + return "SHOW_STATE_ALWAYS_VISIBLE"; + case SoftInputShowHideReason.SHOW_SETTINGS_ON_CHANGE: + return "SHOW_SETTINGS_ON_CHANGE"; + case SoftInputShowHideReason.HIDE_SWITCH_USER: + return "HIDE_SWITCH_USER"; + case SoftInputShowHideReason.HIDE_INVALID_USER: + return "HIDE_INVALID_USER"; + case SoftInputShowHideReason.HIDE_UNSPECIFIED_WINDOW: + return "HIDE_UNSPECIFIED_WINDOW"; + case SoftInputShowHideReason.HIDE_STATE_HIDDEN_FORWARD_NAV: + return "HIDE_STATE_HIDDEN_FORWARD_NAV"; + case SoftInputShowHideReason.HIDE_ALWAYS_HIDDEN_STATE: + return "HIDE_ALWAYS_HIDDEN_STATE"; + case SoftInputShowHideReason.HIDE_RESET_SHELL_COMMAND: + return "HIDE_RESET_SHELL_COMMAND"; + case SoftInputShowHideReason.HIDE_SETTINGS_ON_CHANGE: + return "HIDE_SETTINGS_ON_CHANGE"; + case SoftInputShowHideReason.HIDE_POWER_BUTTON_GO_HOME: + return "HIDE_POWER_BUTTON_GO_HOME"; + case SoftInputShowHideReason.HIDE_DOCKED_STACK_ATTACHED: + return "HIDE_DOCKED_STACK_ATTACHED"; + case SoftInputShowHideReason.HIDE_RECENTS_ANIMATION: + return "HIDE_RECENTS_ANIMATION"; + default: + return "Unknown=" + reason; + } + } + + /** + * Return a fixed size string of the object. + * TODO(b/141738570): Take & return with StringBuilder to make more memory efficient. + */ + @NonNull + @AnyThread + public static String objToString(Object obj) { + if (obj == null) { + return "null"; + } + StringBuilder sb = new StringBuilder(64); + sb.setLength(0); + sb.append(obj.getClass().getName()); + sb.append("@"); + sb.append(Integer.toHexString(obj.hashCode())); + return sb.toString(); + } } diff --git a/core/java/com/android/internal/inputmethod/SoftInputShowHideReason.java b/core/java/com/android/internal/inputmethod/SoftInputShowHideReason.java new file mode 100644 index 000000000000..79397b81ace7 --- /dev/null +++ b/core/java/com/android/internal/inputmethod/SoftInputShowHideReason.java @@ -0,0 +1,143 @@ +/* + * Copyright (C) 2020 The Android Open Source Project + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +package com.android.internal.inputmethod; + +import static java.lang.annotation.RetentionPolicy.SOURCE; + +import android.annotation.IntDef; +import android.view.WindowManager.LayoutParams; + +import java.lang.annotation.Retention; + +/** + * Describes the reason why Soft input window visible / hidden. + */ +@Retention(SOURCE) +@IntDef(value = { + SoftInputShowHideReason.SHOW_SOFT_INPUT, + SoftInputShowHideReason.ATTACH_NEW_INPUT, + SoftInputShowHideReason.SHOW_MY_SOFT_INPUT, + SoftInputShowHideReason.HIDE_SOFT_INPUT, + SoftInputShowHideReason.HIDE_MY_SOFT_INPUT, + SoftInputShowHideReason.SHOW_AUTO_EDITOR_FORWARD_NAV, + SoftInputShowHideReason.SHOW_STATE_VISIBLE_FORWARD_NAV, + SoftInputShowHideReason.SHOW_STATE_ALWAYS_VISIBLE, + SoftInputShowHideReason.SHOW_SETTINGS_ON_CHANGE, + SoftInputShowHideReason.HIDE_SWITCH_USER, + SoftInputShowHideReason.HIDE_INVALID_USER, + SoftInputShowHideReason.HIDE_UNSPECIFIED_WINDOW, + SoftInputShowHideReason.HIDE_STATE_HIDDEN_FORWARD_NAV, + SoftInputShowHideReason.HIDE_ALWAYS_HIDDEN_STATE, + SoftInputShowHideReason.HIDE_RESET_SHELL_COMMAND, + SoftInputShowHideReason.HIDE_SETTINGS_ON_CHANGE, + SoftInputShowHideReason.HIDE_POWER_BUTTON_GO_HOME, + SoftInputShowHideReason.HIDE_DOCKED_STACK_ATTACHED, + SoftInputShowHideReason.HIDE_RECENTS_ANIMATION}) +public @interface SoftInputShowHideReason { + /** Show soft input by {@link android.view.inputmethod.InputMethodManager#showSoftInput}. */ + int SHOW_SOFT_INPUT = 0; + + /** Show soft input when {@code InputMethodManagerService#attachNewInputLocked} called. */ + int ATTACH_NEW_INPUT = 1; + + /** Show soft input by {@code InputMethodManagerService#showMySoftInput}. */ + int SHOW_MY_SOFT_INPUT = 2; + + /** + * Hide soft input by + * {@link android.view.inputmethod.InputMethodManager#hideSoftInputFromWindow}. + */ + int HIDE_SOFT_INPUT = 3; + + /** Hide soft input by {@code InputMethodManagerService#hideMySoftInput}. */ + int HIDE_MY_SOFT_INPUT = 4; + + /** + * Show soft input when navigated forward to the window (with + * {@link LayoutParams#SOFT_INPUT_IS_FORWARD_NAVIGATION}} which the focused view is text + * editor and system will auto-show the IME when the window can resize or running on a large + * screen. + */ + int SHOW_AUTO_EDITOR_FORWARD_NAV = 5; + + /** + * Show soft input when navigated forward to the window with + * {@link LayoutParams#SOFT_INPUT_IS_FORWARD_NAVIGATION} and + * {@link LayoutParams#SOFT_INPUT_STATE_VISIBLE}. + */ + int SHOW_STATE_VISIBLE_FORWARD_NAV = 6; + + /** + * Show soft input when the window with {@link LayoutParams#SOFT_INPUT_STATE_ALWAYS_VISIBLE}. + */ + int SHOW_STATE_ALWAYS_VISIBLE = 7; + + /** + * Show soft input during {@code InputMethodManagerService} receive changes from + * {@code SettingsProvider}. + */ + int SHOW_SETTINGS_ON_CHANGE = 8; + + /** Hide soft input during switching user. */ + int HIDE_SWITCH_USER = 9; + + /** Hide soft input when the user is invalid. */ + int HIDE_INVALID_USER = 10; + + /** + * Hide soft input when the window with {@link LayoutParams#SOFT_INPUT_STATE_UNSPECIFIED} which + * the focused view is not text editor. + */ + int HIDE_UNSPECIFIED_WINDOW = 11; + + /** + * Hide soft input when navigated forward to the window with + * {@link LayoutParams#SOFT_INPUT_IS_FORWARD_NAVIGATION} and + * {@link LayoutParams#SOFT_INPUT_STATE_HIDDEN}. + */ + int HIDE_STATE_HIDDEN_FORWARD_NAV = 12; + + /** + * Hide soft input when the window with {@link LayoutParams#SOFT_INPUT_STATE_ALWAYS_HIDDEN}. + */ + int HIDE_ALWAYS_HIDDEN_STATE = 13; + + /** Hide soft input when "adb shell ime <command>" called. */ + int HIDE_RESET_SHELL_COMMAND = 14; + + /** + * Hide soft input during {@code InputMethodManagerService} receive changes from + * {@code SettingsProvider}. + */ + int HIDE_SETTINGS_ON_CHANGE = 15; + + /** + * Hide soft input from {@link com.android.server.policy.PhoneWindowManager} when setting + * {@link com.android.internal.R.integer#config_shortPressOnPowerBehavior} in config.xml as + * dismiss IME. + */ + int HIDE_POWER_BUTTON_GO_HOME = 16; + + /** Hide soft input when attaching docked stack. */ + int HIDE_DOCKED_STACK_ATTACHED = 17; + + /** + * Hide soft input when {@link com.android.server.wm.RecentsAnimationController} starts + * intercept touch from app window. + */ + int HIDE_RECENTS_ANIMATION = 18; +} diff --git a/core/res/AndroidManifest.xml b/core/res/AndroidManifest.xml index 61d229807d0d..60621022ad26 100644 --- a/core/res/AndroidManifest.xml +++ b/core/res/AndroidManifest.xml @@ -1817,6 +1817,9 @@ android:protectionLevel="normal" /> <!-- @SystemApi Allows an internal user to use privileged SecureElement APIs. + Applications holding this permission can access OMAPI reset system API + and bypass OMAPI AccessControlEnforcer. + <p>Not for use by third-party applications. @hide --> <permission android:name="android.permission.SECURE_ELEMENT_PRIVILEGED" android:protectionLevel="signature|privileged" /> diff --git a/core/res/res/values/config.xml b/core/res/res/values/config.xml index 4ac51c69b05a..fb9158fb2e98 100644 --- a/core/res/res/values/config.xml +++ b/core/res/res/values/config.xml @@ -2752,7 +2752,9 @@ <!-- The amount to scale reduced scale snapshots for Overview and snapshot starting windows. Reduced scale snapshots are loaded before full screen snapshots to improve load times and - minimize the chance the user will see an empty task card. --> + minimize the chance the user will see an empty task card. If set to 0, reduced scale + snapshots are disabled, and snapshots will only be stored at config_highResTaskSnapshotScale + --> <item name="config_lowResTaskSnapshotScale" format="float" type="dimen">0.5</item> <!-- Feature flag to store TaskSnapshot in 16 bit pixel format to save memory. --> @@ -4404,6 +4406,7 @@ Determines whether the specified key groups can be used to wake up the device. --> <bool name="config_wakeOnDpadKeyPress">true</bool> <bool name="config_wakeOnAssistKeyPress">true</bool> + <bool name="config_wakeOnBackKeyPress">true</bool> <!-- Whether to default to an expanded list of users on the lock screen user switcher. --> <bool name="config_expandLockScreenUserSwitcher">false</bool> diff --git a/core/res/res/values/dimens.xml b/core/res/res/values/dimens.xml index 4f221d0d85fd..e1d94f50f260 100644 --- a/core/res/res/values/dimens.xml +++ b/core/res/res/values/dimens.xml @@ -685,7 +685,7 @@ <!-- The size of the right icon image when on low ram --> <dimen name="notification_right_icon_size_low_ram">@dimen/notification_right_icon_size</dimen> - <dimen name="messaging_avatar_size">@dimen/notification_right_icon_size</dimen> + <dimen name="messaging_avatar_size">52dp</dimen> <dimen name="messaging_group_sending_progress_size">24dp</dimen> diff --git a/core/res/res/values/styles_device_defaults.xml b/core/res/res/values/styles_device_defaults.xml index 64768cf4c730..966f495c96e5 100644 --- a/core/res/res/values/styles_device_defaults.xml +++ b/core/res/res/values/styles_device_defaults.xml @@ -146,7 +146,7 @@ easier. <item name="textAppearance">@style/TextAppearance.DeviceDefault.Notification</item> </style> <style name="Widget.DeviceDefault.Notification.MessagingName" parent="Widget.Material.Notification.MessagingName"> - <item name="textAppearance">@style/TextAppearance.DeviceDefault.Notification.Title</item> + <item name="textAppearance">@style/TextAppearance.DeviceDefault.Notification.MessagingName</item> </style> <style name="Widget.DeviceDefault.PreferenceFrameLayout" parent="Widget.Material.PreferenceFrameLayout"/> <style name="Widget.DeviceDefault.ProgressBar.Inverse" parent="Widget.Material.ProgressBar.Inverse"/> @@ -290,6 +290,9 @@ easier. <style name="TextAppearance.DeviceDefault.Notification.Title" parent="TextAppearance.Material.Notification.Title"> <item name="fontFamily">@string/config_headlineFontFamilyMedium</item> </style> + <style name="TextAppearance.DeviceDefault.Notification.MessagingName" parent="TextAppearance.DeviceDefault.Notification.Title"> + <item name="textSize">16sp</item> + </style> <style name="TextAppearance.DeviceDefault.Notification.Reply" parent="TextAppearance.Material.Notification.Reply"> <item name="fontFamily">@string/config_bodyFontFamily</item> </style> diff --git a/core/res/res/values/symbols.xml b/core/res/res/values/symbols.xml index d5954596535d..7690b94906dd 100644 --- a/core/res/res/values/symbols.xml +++ b/core/res/res/values/symbols.xml @@ -1612,6 +1612,7 @@ <java-symbol type="style" name="TextAppearance.SlidingTabNormal" /> <java-symbol type="style" name="Theme.DeviceDefault.Dialog.NoFrame" /> <java-symbol type="style" name="Theme.IconMenu" /> + <java-symbol type="style" name="Theme.Dream" /> <java-symbol type="style" name="Theme.DeviceDefault.VoiceInteractionSession" /> <java-symbol type="style" name="Pointer" /> <java-symbol type="style" name="LargePointer" /> @@ -3051,6 +3052,7 @@ <!-- Override Wake Key Behavior When Screen is Off --> <java-symbol type="bool" name="config_wakeOnDpadKeyPress" /> <java-symbol type="bool" name="config_wakeOnAssistKeyPress" /> + <java-symbol type="bool" name="config_wakeOnBackKeyPress" /> <!-- Pinner Service --> <java-symbol type="array" name="config_defaultPinnerServiceFiles" /> diff --git a/core/res/res/values/themes.xml b/core/res/res/values/themes.xml index 5d9cb48aa5ed..2ef0c927cc61 100644 --- a/core/res/res/values/themes.xml +++ b/core/res/res/values/themes.xml @@ -701,6 +701,11 @@ please see themes_device_defaults.xml. <item name="windowNoDisplay">true</item> </style> + <style name="Theme.Dream"> + <item name="windowBackground">@null</item> + <item name="windowDisablePreview">true</item> + </style> + <!-- Default theme for dialog windows and activities (on API level 10 and lower), which is used by the {@link android.app.Dialog} class. This changes the window to be diff --git a/core/tests/coretests/src/android/app/servertransaction/TransactionParcelTests.java b/core/tests/coretests/src/android/app/servertransaction/TransactionParcelTests.java index 372b8c294702..f4fbefe9dde4 100644 --- a/core/tests/coretests/src/android/app/servertransaction/TransactionParcelTests.java +++ b/core/tests/coretests/src/android/app/servertransaction/TransactionParcelTests.java @@ -37,6 +37,7 @@ import android.content.pm.ActivityInfo; import android.content.pm.ApplicationInfo; import android.content.pm.ParceledListSlice; import android.content.pm.ProviderInfo; +import android.content.pm.ProviderInfoList; import android.content.pm.ServiceInfo; import android.content.res.CompatibilityInfo; import android.content.res.Configuration; @@ -397,7 +398,7 @@ public class TransactionParcelTests { @Override public void bindApplication(String s, ApplicationInfo applicationInfo, - List<ProviderInfo> list, ComponentName componentName, ProfilerInfo profilerInfo, + ProviderInfoList list, ComponentName componentName, ProfilerInfo profilerInfo, Bundle bundle, IInstrumentationWatcher iInstrumentationWatcher, IUiAutomationConnection iUiAutomationConnection, int i, boolean b, boolean b1, boolean b2, boolean b3, Configuration configuration, diff --git a/core/tests/coretests/src/android/view/DisplayCutoutTest.java b/core/tests/coretests/src/android/view/DisplayCutoutTest.java index 7c2b98f3e167..d02c6d588585 100644 --- a/core/tests/coretests/src/android/view/DisplayCutoutTest.java +++ b/core/tests/coretests/src/android/view/DisplayCutoutTest.java @@ -230,6 +230,16 @@ public class DisplayCutoutTest { } @Test + public void inset_insets_withWaterfallCutout() throws Exception { + DisplayCutout cutout = createCutoutWaterfallOnly(Insets.of(0, 10, 0, 10)).inset(1, 2, 3, 4); + + assertEquals(cutout.getSafeInsetLeft(), 0); + assertEquals(cutout.getSafeInsetTop(), 8); + assertEquals(cutout.getSafeInsetRight(), 0); + assertEquals(cutout.getSafeInsetBottom(), 6); + } + + @Test public void inset_insets_consumeInset() throws Exception { DisplayCutout cutout = mCutoutTop.inset(0, 1000, 0, 0); @@ -457,7 +467,8 @@ public class DisplayCutoutTest { private static DisplayCutout createCutoutWaterfallOnly(Insets waterfallInsets) { return new DisplayCutout( - Insets.of(20, 0, 20, 0), + Insets.of(waterfallInsets.left, waterfallInsets.top, waterfallInsets.right, + waterfallInsets.bottom), ZERO_RECT, ZERO_RECT, ZERO_RECT, diff --git a/core/tests/coretests/src/com/android/internal/accessibility/AccessibilityShortcutControllerTest.java b/core/tests/coretests/src/com/android/internal/accessibility/AccessibilityShortcutControllerTest.java index bbf3b12d9b7d..9af0ed0ab826 100644 --- a/core/tests/coretests/src/com/android/internal/accessibility/AccessibilityShortcutControllerTest.java +++ b/core/tests/coretests/src/com/android/internal/accessibility/AccessibilityShortcutControllerTest.java @@ -36,6 +36,7 @@ import static org.mockito.Matchers.anyObject; import static org.mockito.Matchers.eq; import static org.mockito.Mockito.atLeastOnce; import static org.mockito.Mockito.doAnswer; +import static org.mockito.Mockito.doReturn; import static org.mockito.Mockito.mock; import static org.mockito.Mockito.times; import static org.mockito.Mockito.verify; @@ -85,7 +86,9 @@ import org.mockito.invocation.InvocationOnMock; import java.lang.reflect.Field; import java.util.Collections; +import java.util.HashSet; import java.util.Map; +import java.util.Set; @RunWith(AndroidJUnit4.class) @@ -534,6 +537,36 @@ public class AccessibilityShortcutControllerTest { verify(mRingtone).play(); } + @Test + public void testOnAccessibilityShortcut_showsWarningDialog_ttsLongTimeInit_retrySpoken() + throws Exception { + configureShortcutEnabled(ENABLED_EXCEPT_LOCK_SCREEN); + configureValidShortcutService(); + configureTtsSpokenPromptEnabled(); + configureHandlerCallbackInvocation(); + AccessibilityShortcutController accessibilityShortcutController = getController(); + Settings.Secure.putInt(mContentResolver, ACCESSIBILITY_SHORTCUT_DIALOG_SHOWN, 0); + Set<String> features = new HashSet<>(); + features.add(TextToSpeech.Engine.KEY_FEATURE_NOT_INSTALLED); + doReturn(features, Collections.emptySet()).when(mVoice).getFeatures(); + doReturn(TextToSpeech.LANG_NOT_SUPPORTED, TextToSpeech.LANG_AVAILABLE) + .when(mTextToSpeech).setLanguage(any()); + accessibilityShortcutController.performAccessibilityShortcut(); + + verify(mAlertDialog).show(); + ArgumentCaptor<TextToSpeech.OnInitListener> onInitCap = ArgumentCaptor.forClass( + TextToSpeech.OnInitListener.class); + verify(mFrameworkObjectProvider).getTextToSpeech(any(), onInitCap.capture()); + onInitCap.getValue().onInit(TextToSpeech.SUCCESS); + verify(mTextToSpeech).speak(any(), eq(TextToSpeech.QUEUE_FLUSH), any(), any()); + ArgumentCaptor<DialogInterface.OnDismissListener> onDismissCap = ArgumentCaptor.forClass( + DialogInterface.OnDismissListener.class); + verify(mAlertDialog).setOnDismissListener(onDismissCap.capture()); + onDismissCap.getValue().onDismiss(mAlertDialog); + verify(mTextToSpeech).shutdown(); + verify(mRingtone, times(0)).play(); + } + private void configureNoShortcutService() throws Exception { when(mAccessibilityManagerService .getAccessibilityShortcutTargets(ACCESSIBILITY_SHORTCUT_KEY)) diff --git a/media/java/android/media/MediaCodec.java b/media/java/android/media/MediaCodec.java index f2b4db1afdac..f800f9ee91c9 100644 --- a/media/java/android/media/MediaCodec.java +++ b/media/java/android/media/MediaCodec.java @@ -2531,7 +2531,7 @@ final public class MediaCodec { int offset, int size, long presentationTimeUs, int flags) throws CryptoException { synchronized(mBufferLock) { - if (mBufferMode != BUFFER_MODE_LEGACY) { + if (mBufferMode == BUFFER_MODE_BLOCK) { throw new IncompatibleWithBlockModelException(); } invalidateByteBuffer(mCachedInputBuffers, index); @@ -2783,7 +2783,7 @@ final public class MediaCodec { long presentationTimeUs, int flags) throws CryptoException { synchronized(mBufferLock) { - if (mBufferMode != BUFFER_MODE_LEGACY) { + if (mBufferMode == BUFFER_MODE_BLOCK) { throw new IncompatibleWithBlockModelException(); } invalidateByteBuffer(mCachedInputBuffers, index); @@ -2818,7 +2818,7 @@ final public class MediaCodec { */ public final int dequeueInputBuffer(long timeoutUs) { synchronized (mBufferLock) { - if (mBufferMode != BUFFER_MODE_LEGACY) { + if (mBufferMode == BUFFER_MODE_BLOCK) { throw new IncompatibleWithBlockModelException(); } } @@ -3534,7 +3534,7 @@ final public class MediaCodec { public final int dequeueOutputBuffer( @NonNull BufferInfo info, long timeoutUs) { synchronized (mBufferLock) { - if (mBufferMode != BUFFER_MODE_LEGACY) { + if (mBufferMode == BUFFER_MODE_BLOCK) { throw new IncompatibleWithBlockModelException(); } } @@ -3916,7 +3916,7 @@ final public class MediaCodec { @NonNull public ByteBuffer[] getInputBuffers() { synchronized (mBufferLock) { - if (mBufferMode != BUFFER_MODE_LEGACY) { + if (mBufferMode == BUFFER_MODE_BLOCK) { throw new IncompatibleWithBlockModelException(); } if (mCachedInputBuffers == null) { @@ -3952,7 +3952,7 @@ final public class MediaCodec { @NonNull public ByteBuffer[] getOutputBuffers() { synchronized (mBufferLock) { - if (mBufferMode != BUFFER_MODE_LEGACY) { + if (mBufferMode == BUFFER_MODE_BLOCK) { throw new IncompatibleWithBlockModelException(); } if (mCachedOutputBuffers == null) { @@ -3984,7 +3984,7 @@ final public class MediaCodec { @Nullable public ByteBuffer getInputBuffer(int index) { synchronized (mBufferLock) { - if (mBufferMode != BUFFER_MODE_LEGACY) { + if (mBufferMode == BUFFER_MODE_BLOCK) { throw new IncompatibleWithBlockModelException(); } } @@ -4018,7 +4018,7 @@ final public class MediaCodec { @Nullable public Image getInputImage(int index) { synchronized (mBufferLock) { - if (mBufferMode != BUFFER_MODE_LEGACY) { + if (mBufferMode == BUFFER_MODE_BLOCK) { throw new IncompatibleWithBlockModelException(); } } @@ -4052,7 +4052,7 @@ final public class MediaCodec { @Nullable public ByteBuffer getOutputBuffer(int index) { synchronized (mBufferLock) { - if (mBufferMode != BUFFER_MODE_LEGACY) { + if (mBufferMode == BUFFER_MODE_BLOCK) { throw new IncompatibleWithBlockModelException(); } } @@ -4085,7 +4085,7 @@ final public class MediaCodec { @Nullable public Image getOutputImage(int index) { synchronized (mBufferLock) { - if (mBufferMode != BUFFER_MODE_LEGACY) { + if (mBufferMode == BUFFER_MODE_BLOCK) { throw new IncompatibleWithBlockModelException(); } } diff --git a/media/java/android/media/tv/TvInputService.java b/media/java/android/media/tv/TvInputService.java index b579144b45e8..95199cc8e045 100755 --- a/media/java/android/media/tv/TvInputService.java +++ b/media/java/android/media/tv/TvInputService.java @@ -240,7 +240,7 @@ public abstract class TvInputService extends Service { * @param inputId The ID of the TV input associated with the session. */ @Nullable - public abstract Session onCreateSession(String inputId); + public abstract Session onCreateSession(@NonNull String inputId); /** * Returns a concrete implementation of {@link RecordingSession}. @@ -251,7 +251,7 @@ public abstract class TvInputService extends Service { * @param inputId The ID of the TV input associated with the recording session. */ @Nullable - public RecordingSession onCreateRecordingSession(String inputId) { + public RecordingSession onCreateRecordingSession(@NonNull String inputId) { return null; } diff --git a/media/java/android/media/tv/tunerresourcemanager/ResourceClientProfile.java b/media/java/android/media/tv/tunerresourcemanager/ResourceClientProfile.java index 68372444cb83..598ff8f3f075 100644 --- a/media/java/android/media/tv/tunerresourcemanager/ResourceClientProfile.java +++ b/media/java/android/media/tv/tunerresourcemanager/ResourceClientProfile.java @@ -78,7 +78,8 @@ public final class ResourceClientProfile implements Parcelable { * {@link android.media.tv.TvInputService.PRIORITY_HINT_USE_CASE_TYPE_LIVE} * {@link android.media.tv.TvInputService.PRIORITY_HINT_USE_CASE_TYPE_RECORD}. * New [use case : priority value] pair can be defined in the manifest by the - * OEM. Any undefined use case would cause IllegalArgumentException. + * OEM. The id of the useCaseVendor should be passed through this parameter. Any + * undefined use case would cause IllegalArgumentException. */ public ResourceClientProfile(@NonNull String tvInputSessionId, int useCase) { diff --git a/packages/DynamicSystemInstallationService/src/com/android/dynsystem/DynamicSystemInstallationService.java b/packages/DynamicSystemInstallationService/src/com/android/dynsystem/DynamicSystemInstallationService.java index 37a77be52983..f36f97ddf610 100644 --- a/packages/DynamicSystemInstallationService/src/com/android/dynsystem/DynamicSystemInstallationService.java +++ b/packages/DynamicSystemInstallationService/src/com/android/dynsystem/DynamicSystemInstallationService.java @@ -82,6 +82,9 @@ public class DynamicSystemInstallationService extends Service static final String DEFAULT_DSU_SLOT = "dsu"; static final String KEY_PUBKEY = "KEY_PUBKEY"; + // Default userdata partition size is 2GiB. + private static final long DEFAULT_USERDATA_SIZE = 2L << 30; + /* * Intent actions */ @@ -270,6 +273,10 @@ public class DynamicSystemInstallationService extends Service String dsuSlot = intent.getStringExtra(KEY_DSU_SLOT); String publicKey = intent.getStringExtra(KEY_PUBKEY); + if (userdataSize == 0) { + userdataSize = DEFAULT_USERDATA_SIZE; + } + if (TextUtils.isEmpty(dsuSlot)) { dsuSlot = DEFAULT_DSU_SLOT; } diff --git a/packages/SettingsLib/src/com/android/settingslib/dream/DreamBackend.java b/packages/SettingsLib/src/com/android/settingslib/dream/DreamBackend.java index 3c0f6fe8ccbb..57e680849fec 100644 --- a/packages/SettingsLib/src/com/android/settingslib/dream/DreamBackend.java +++ b/packages/SettingsLib/src/com/android/settingslib/dream/DreamBackend.java @@ -136,7 +136,7 @@ public class DreamBackend { if (mDreamManager == null) return null; try { - return mDreamManager.getDefaultDreamComponent(); + return mDreamManager.getDefaultDreamComponentForUser(mContext.getUserId()); } catch (RemoteException e) { Log.w(TAG, "Failed to get default dream", e); return null; @@ -269,7 +269,7 @@ public class DreamBackend { if (mDreamManager == null || dreamInfo == null || dreamInfo.componentName == null) return; try { - mDreamManager.testDream(dreamInfo.componentName); + mDreamManager.testDream(mContext.getUserId(), dreamInfo.componentName); } catch (RemoteException e) { Log.w(TAG, "Failed to preview " + dreamInfo, e); } diff --git a/packages/SystemUI/res/drawable/rounded_user_switcher_bg.xml b/packages/SystemUI/res/drawable/rounded_user_switcher_bg.xml new file mode 100644 index 000000000000..e3d010ee7674 --- /dev/null +++ b/packages/SystemUI/res/drawable/rounded_user_switcher_bg.xml @@ -0,0 +1,32 @@ +<?xml version="1.0" encoding="utf-8"?> +<!-- + ~ Copyright (C) 2020 The Android Open Source Project + ~ + ~ Licensed under the Apache License, Version 2.0 (the "License"); + ~ you may not use this file except in compliance with the License. + ~ You may obtain a copy of the License at + ~ + ~ http://www.apache.org/licenses/LICENSE-2.0 + ~ + ~ Unless required by applicable law or agreed to in writing, software + ~ distributed under the License is distributed on an "AS IS" BASIS, + ~ WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + ~ See the License for the specific language governing permissions and + ~ limitations under the License. + --> +<ripple xmlns:android="http://schemas.android.com/apk/res/android" + android:color="?android:attr/colorControlHighlight"> + <item> + <shape> + <solid android:color="@color/qs_user_detail_avatar_frame" /> + + <padding + android:left="1dp" + android:right="1dp" + android:bottom="1dp" + android:top="1dp" /> + + <corners android:radius="48dp" /> + </shape> + </item> +</ripple> diff --git a/packages/SystemUI/res/layout-sw600dp/keyguard_user_switcher_item.xml b/packages/SystemUI/res/layout-sw600dp/keyguard_user_switcher_item.xml new file mode 100644 index 000000000000..1f38b1ea1da5 --- /dev/null +++ b/packages/SystemUI/res/layout-sw600dp/keyguard_user_switcher_item.xml @@ -0,0 +1,44 @@ +<?xml version="1.0" encoding="utf-8"?> + +<!-- + ~ Copyright (C) 2020 The Android Open Source Project + ~ + ~ Licensed under the Apache License, Version 2.0 (the "License"); + ~ you may not use this file except in compliance with the License. + ~ You may obtain a copy of the License at + ~ + ~ http://www.apache.org/licenses/LICENSE-2.0 + ~ + ~ Unless required by applicable law or agreed to in writing, software + ~ distributed under the License is distributed on an "AS IS" BASIS, + ~ WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + ~ See the License for the specific language governing permissions and + ~ limitations under the License. + --> + +<!-- LinearLayout --> +<com.android.systemui.statusbar.policy.KeyguardUserDetailItemView + xmlns:android="http://schemas.android.com/apk/res/android" + xmlns:sysui="http://schemas.android.com/apk/res-auto" + android:layout_width="match_parent" + android:layout_height="wrap_content" + android:padding="8dp" + android:layout_marginEnd="8dp" + android:gravity="end|center_vertical" + android:clickable="true" + android:background="@drawable/rounded_user_switcher_bg" + sysui:regularTextAppearance="@style/TextAppearance.StatusBar.Expanded.UserSwitcher" + sysui:activatedTextAppearance="@style/TextAppearance.StatusBar.Expanded.UserSwitcher.Activated"> + <TextView android:id="@+id/user_name" + android:layout_width="wrap_content" + android:layout_height="wrap_content" + android:layout_marginEnd="13dp" + android:textAppearance="@style/TextAppearance.StatusBar.Expanded.UserSwitcher" + /> + <com.android.systemui.statusbar.phone.UserAvatarView android:id="@+id/user_picture" + android:layout_width="@dimen/framed_avatar_size" + android:layout_height="@dimen/framed_avatar_size" + android:contentDescription="@null" + sysui:badgeDiameter="18dp" + sysui:badgeMargin="1dp" /> +</com.android.systemui.statusbar.policy.KeyguardUserDetailItemView> diff --git a/packages/SystemUI/res/layout/keyguard_user_switcher_item.xml b/packages/SystemUI/res/layout/keyguard_user_switcher_item.xml index fb38e1c6dc6b..b1e51659817e 100644 --- a/packages/SystemUI/res/layout/keyguard_user_switcher_item.xml +++ b/packages/SystemUI/res/layout/keyguard_user_switcher_item.xml @@ -27,8 +27,6 @@ android:gravity="center_vertical" android:clickable="true" android:background="@drawable/ripple_drawable" - android:clipChildren="false" - android:clipToPadding="false" sysui:regularTextAppearance="@style/TextAppearance.StatusBar.Expanded.UserSwitcher" sysui:activatedTextAppearance="@style/TextAppearance.StatusBar.Expanded.UserSwitcher.Activated"> <TextView android:id="@+id/user_name" diff --git a/packages/SystemUI/res/values-sw600dp/styles.xml b/packages/SystemUI/res/values-sw600dp/styles.xml index 23b64e34a939..b375364fdab9 100644 --- a/packages/SystemUI/res/values-sw600dp/styles.xml +++ b/packages/SystemUI/res/values-sw600dp/styles.xml @@ -22,4 +22,28 @@ <style name="UserDetailView"> <item name="numColumns">4</item> </style> + + <style name="TextAppearance.StatusBar.Expanded.UserSwitcher"> + <item name="android:textSize">@dimen/kg_user_switcher_text_size</item> + <item name="android:textStyle">normal</item> + <item name="android:fontFamily">@*android:string/config_headlineFontFamily</item> + <item name="android:textColor">?attr/wallpaperTextColor</item> + </style> + + <style name="TextAppearance.StatusBar.Expanded.UserSwitcher.Activated"> + <item name="android:fontWeight">700</item> + <item name="android:textStyle">bold</item> + <item name="android:textColor">?android:attr/textColorPrimaryInverse</item> + </style> + + <style name="TextAppearance.QS.UserSwitcher"> + <item name="android:textSize">@dimen/qs_detail_item_primary_text_size</item> + <item name="android:textColor">?android:attr/textColorSecondary</item> + <item name="android:fontFamily">@*android:string/config_headlineFontFamily</item> + </style> + + <style name="TextAppearance.QS.UserSwitcher.Activated"> + <item name="android:fontWeight">700</item> + <item name="android:textStyle">bold</item> + </style> </resources> diff --git a/packages/SystemUI/res/values/dimens.xml b/packages/SystemUI/res/values/dimens.xml index 1c2404f1921e..9caaa9f8565d 100644 --- a/packages/SystemUI/res/values/dimens.xml +++ b/packages/SystemUI/res/values/dimens.xml @@ -124,6 +124,9 @@ <!-- Increased height of a collapsed media notification in the status bar --> <dimen name="notification_min_height_media">160dp</dimen> + <!-- Increased height of a collapsed messaging notification in the status bar --> + <dimen name="notification_min_height_messaging">118dp</dimen> + <!-- Height of a small notification in the status bar which was used before android N --> <dimen name="notification_min_height_legacy">64dp</dimen> diff --git a/packages/SystemUI/shared/src/com/android/systemui/shared/recents/IPinnedStackAnimationListener.aidl b/packages/SystemUI/shared/src/com/android/systemui/shared/recents/IPinnedStackAnimationListener.aidl new file mode 100644 index 000000000000..97aa512ea7df --- /dev/null +++ b/packages/SystemUI/shared/src/com/android/systemui/shared/recents/IPinnedStackAnimationListener.aidl @@ -0,0 +1,28 @@ +/* + * Copyright (C) 2020 The Android Open Source Project + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +package com.android.systemui.shared.recents; + +/** + * Listener interface that Launcher attaches to SystemUI to get + * pinned stack animation callbacks. + */ +oneway interface IPinnedStackAnimationListener { + /** + * Notifies the pinned stack animation is started. + */ + void onPinnedStackAnimationStarted(); +} diff --git a/packages/SystemUI/shared/src/com/android/systemui/shared/recents/ISystemUiProxy.aidl b/packages/SystemUI/shared/src/com/android/systemui/shared/recents/ISystemUiProxy.aidl index b1d39f59f789..80fd826f28c6 100644 --- a/packages/SystemUI/shared/src/com/android/systemui/shared/recents/ISystemUiProxy.aidl +++ b/packages/SystemUI/shared/src/com/android/systemui/shared/recents/ISystemUiProxy.aidl @@ -22,9 +22,11 @@ import android.graphics.Rect; import android.os.Bundle; import android.view.MotionEvent; +import com.android.systemui.shared.recents.IPinnedStackAnimationListener; + /** * Temporary callbacks into SystemUI. - * Next id = 22 + * Next id = 25 */ interface ISystemUiProxy { @@ -121,11 +123,21 @@ interface ISystemUiProxy { /** * Handle the provided image as if it was a screenshot. */ - void handleImageAsScreenshot(in Bitmap screenImage, in Rect locationInScreen, + void handleImageAsScreenshot(in Bitmap screenImage, in Rect locationInScreen, in Insets visibleInsets, int taskId) = 21; /** * Sets the split-screen divider minimized state */ void setSplitScreenMinimized(boolean minimized) = 22; + + /* + * Notifies that the swipe-to-home (recents animation) is finished. + */ + void notifySwipeToHomeFinished() = 23; + + /** + * Sets listener to get pinned stack animation callbacks. + */ + void setPinnedStackAnimationListener(IPinnedStackAnimationListener listener) = 24; } diff --git a/packages/SystemUI/shared/src/com/android/systemui/shared/recents/model/ThumbnailData.java b/packages/SystemUI/shared/src/com/android/systemui/shared/recents/model/ThumbnailData.java index 4474a49e55b8..eca6ebf7f8e5 100644 --- a/packages/SystemUI/shared/src/com/android/systemui/shared/recents/model/ThumbnailData.java +++ b/packages/SystemUI/shared/src/com/android/systemui/shared/recents/model/ThumbnailData.java @@ -62,7 +62,9 @@ public class ThumbnailData { orientation = snapshot.getOrientation(); rotation = snapshot.getRotation(); reducedResolution = snapshot.isLowResolution(); - scale = snapshot.getScale(); + // TODO(b/149579527): Pass task size instead of computing scale. + // Assume width and height were scaled the same; compute scale only for width + scale = (float) thumbnail.getWidth() / snapshot.getTaskSize().x; isRealSnapshot = snapshot.isRealSnapshot(); isTranslucent = snapshot.isTranslucent(); windowingMode = snapshot.getWindowingMode(); diff --git a/packages/SystemUI/shared/src/com/android/systemui/shared/system/PinnedStackListenerForwarder.java b/packages/SystemUI/shared/src/com/android/systemui/shared/system/PinnedStackListenerForwarder.java index 8c0ffb82f0ed..360244c9af52 100644 --- a/packages/SystemUI/shared/src/com/android/systemui/shared/system/PinnedStackListenerForwarder.java +++ b/packages/SystemUI/shared/src/com/android/systemui/shared/system/PinnedStackListenerForwarder.java @@ -53,11 +53,9 @@ public class PinnedStackListenerForwarder extends IPinnedStackListener.Stub { } @Override - public void onMovementBoundsChanged(Rect animatingBounds, boolean fromImeAdjustment, - boolean fromShelfAdjustment) { + public void onMovementBoundsChanged(Rect animatingBounds, boolean fromImeAdjustment) { for (PinnedStackListener listener : mListeners) { - listener.onMovementBoundsChanged(animatingBounds, fromImeAdjustment, - fromShelfAdjustment); + listener.onMovementBoundsChanged(animatingBounds, fromImeAdjustment); } } @@ -69,13 +67,6 @@ public class PinnedStackListenerForwarder extends IPinnedStackListener.Stub { } @Override - public void onMinimizedStateChanged(boolean isMinimized) { - for (PinnedStackListener listener : mListeners) { - listener.onMinimizedStateChanged(isMinimized); - } - } - - @Override public void onActionsChanged(ParceledListSlice actions) { for (PinnedStackListener listener : mListeners) { listener.onActionsChanged(actions); @@ -117,13 +108,6 @@ public class PinnedStackListenerForwarder extends IPinnedStackListener.Stub { } } - @Override - public void onPrepareAnimation(Rect sourceRectHint, float aspectRatio, Rect bounds) { - for (PinnedStackListener listener : mListeners) { - listener.onPrepareAnimation(sourceRectHint, aspectRatio, bounds); - } - } - /** * A counterpart of {@link IPinnedStackListener} with empty implementations. * Subclasses can ignore those methods they do not intend to take action upon. @@ -131,13 +115,10 @@ public class PinnedStackListenerForwarder extends IPinnedStackListener.Stub { public static class PinnedStackListener { public void onListenerRegistered(IPinnedStackController controller) {} - public void onMovementBoundsChanged(Rect animatingBounds, boolean fromImeAdjustment, - boolean fromShelfAdjustment) {} + public void onMovementBoundsChanged(Rect animatingBounds, boolean fromImeAdjustment) {} public void onImeVisibilityChanged(boolean imeVisible, int imeHeight) {} - public void onMinimizedStateChanged(boolean isMinimized) {} - public void onActionsChanged(ParceledListSlice actions) {} public void onSaveReentryBounds(ComponentName componentName, Rect bounds) {} @@ -149,7 +130,5 @@ public class PinnedStackListenerForwarder extends IPinnedStackListener.Stub { public void onConfigurationChanged() {} public void onAspectRatioChanged(float aspectRatio) {} - - public void onPrepareAnimation(Rect sourceRectHint, float aspectRatio, Rect bounds) {} } } diff --git a/packages/SystemUI/shared/src/com/android/systemui/shared/system/RecentsAnimationControllerCompat.java b/packages/SystemUI/shared/src/com/android/systemui/shared/system/RecentsAnimationControllerCompat.java index 748f356c25b9..6cd6118ede6b 100644 --- a/packages/SystemUI/shared/src/com/android/systemui/shared/system/RecentsAnimationControllerCompat.java +++ b/packages/SystemUI/shared/src/com/android/systemui/shared/system/RecentsAnimationControllerCompat.java @@ -91,15 +91,6 @@ public class RecentsAnimationControllerCompat { } } - @Deprecated - public void setCancelWithDeferredScreenshot(boolean screenshot) { - try { - mAnimationController.setCancelWithDeferredScreenshot(screenshot); - } catch (RemoteException e) { - Log.e(TAG, "Failed to set cancel with deferred screenshot", e); - } - } - public void setDeferCancelUntilNextTransition(boolean defer, boolean screenshot) { try { mAnimationController.setDeferCancelUntilNextTransition(defer, screenshot); diff --git a/packages/SystemUI/shared/src/com/android/systemui/shared/system/TaskStackChangeListener.java b/packages/SystemUI/shared/src/com/android/systemui/shared/system/TaskStackChangeListener.java index 5f92b2811807..1c6223b847de 100644 --- a/packages/SystemUI/shared/src/com/android/systemui/shared/system/TaskStackChangeListener.java +++ b/packages/SystemUI/shared/src/com/android/systemui/shared/system/TaskStackChangeListener.java @@ -20,8 +20,6 @@ import android.app.ActivityManager.RunningTaskInfo; import android.app.ITaskStackListener; import android.content.ComponentName; import android.os.IBinder; -import android.os.UserHandle; -import android.util.Log; import com.android.systemui.shared.recents.model.ThumbnailData; @@ -40,8 +38,6 @@ public abstract class TaskStackChangeListener { public void onActivityPinned(String packageName, int userId, int taskId, int stackId) { } public void onActivityUnpinned() { } public void onPinnedActivityRestartAttempt(boolean clearedTask) { } - public void onPinnedStackAnimationStarted() { } - public void onPinnedStackAnimationEnded() { } public void onActivityForcedResizable(String packageName, int taskId, int reason) { } public void onActivityDismissingDockedStack() { } public void onActivityLaunchOnSecondaryDisplayFailed() { } @@ -117,22 +113,4 @@ public abstract class TaskStackChangeListener { /** @see ITaskStackListener#onRecentTaskListFrozenChanged(boolean) */ public void onRecentTaskListFrozenChanged(boolean frozen) { } - - /** - * Checks that the current user matches the process. Since - * {@link android.app.ITaskStackListener} is not multi-user aware, handlers of - * {@link TaskStackChangeListener} should make this call to verify that we don't act on events - * originating from another user's interactions. - */ - protected final boolean checkCurrentUserId(int currentUserId, boolean debug) { - int processUserId = UserHandle.myUserId(); - if (processUserId != currentUserId) { - if (debug) { - Log.d("TaskStackChangeListener", "UID mismatch. Process is uid=" + processUserId - + " and the current user is uid=" + currentUserId); - } - return false; - } - return true; - } } diff --git a/packages/SystemUI/shared/src/com/android/systemui/shared/system/TaskStackChangeListeners.java b/packages/SystemUI/shared/src/com/android/systemui/shared/system/TaskStackChangeListeners.java index acce41cc942e..cbdd3f8191f4 100644 --- a/packages/SystemUI/shared/src/com/android/systemui/shared/system/TaskStackChangeListeners.java +++ b/packages/SystemUI/shared/src/com/android/systemui/shared/system/TaskStackChangeListeners.java @@ -128,18 +128,6 @@ public class TaskStackChangeListeners extends TaskStackListener { } @Override - public void onPinnedStackAnimationStarted() throws RemoteException { - mHandler.removeMessages(H.ON_PINNED_STACK_ANIMATION_STARTED); - mHandler.sendEmptyMessage(H.ON_PINNED_STACK_ANIMATION_STARTED); - } - - @Override - public void onPinnedStackAnimationEnded() throws RemoteException { - mHandler.removeMessages(H.ON_PINNED_STACK_ANIMATION_ENDED); - mHandler.sendEmptyMessage(H.ON_PINNED_STACK_ANIMATION_ENDED); - } - - @Override public void onActivityForcedResizable(String packageName, int taskId, int reason) throws RemoteException { mHandler.obtainMessage(H.ON_ACTIVITY_FORCED_RESIZABLE, taskId, reason, packageName) @@ -249,11 +237,9 @@ public class TaskStackChangeListeners extends TaskStackListener { private static final int ON_TASK_SNAPSHOT_CHANGED = 2; private static final int ON_ACTIVITY_PINNED = 3; private static final int ON_PINNED_ACTIVITY_RESTART_ATTEMPT = 4; - private static final int ON_PINNED_STACK_ANIMATION_ENDED = 5; private static final int ON_ACTIVITY_FORCED_RESIZABLE = 6; private static final int ON_ACTIVITY_DISMISSING_DOCKED_STACK = 7; private static final int ON_TASK_PROFILE_LOCKED = 8; - private static final int ON_PINNED_STACK_ANIMATION_STARTED = 9; private static final int ON_ACTIVITY_UNPINNED = 10; private static final int ON_ACTIVITY_LAUNCH_ON_SECONDARY_DISPLAY_FAILED = 11; private static final int ON_TASK_CREATED = 12; @@ -317,18 +303,6 @@ public class TaskStackChangeListeners extends TaskStackListener { } break; } - case ON_PINNED_STACK_ANIMATION_STARTED: { - for (int i = mTaskStackListeners.size() - 1; i >= 0; i--) { - mTaskStackListeners.get(i).onPinnedStackAnimationStarted(); - } - break; - } - case ON_PINNED_STACK_ANIMATION_ENDED: { - for (int i = mTaskStackListeners.size() - 1; i >= 0; i--) { - mTaskStackListeners.get(i).onPinnedStackAnimationEnded(); - } - break; - } case ON_ACTIVITY_FORCED_RESIZABLE: { for (int i = mTaskStackListeners.size() - 1; i >= 0; i--) { mTaskStackListeners.get(i).onActivityForcedResizable( diff --git a/packages/SystemUI/src/com/android/systemui/controls/controller/ControlsControllerImpl.kt b/packages/SystemUI/src/com/android/systemui/controls/controller/ControlsControllerImpl.kt index a2a08502a58f..bb665fe3113b 100644 --- a/packages/SystemUI/src/com/android/systemui/controls/controller/ControlsControllerImpl.kt +++ b/packages/SystemUI/src/com/android/systemui/controls/controller/ControlsControllerImpl.kt @@ -67,6 +67,7 @@ class ControlsControllerImpl @Inject constructor ( internal const val CONTROLS_AVAILABLE = "systemui.controls_available" internal val URI = Settings.Secure.getUriFor(CONTROLS_AVAILABLE) private const val USER_CHANGE_RETRY_DELAY = 500L // ms + private const val DEFAULT_ENABLED = 1 } // Map of map: ComponentName -> (String -> ControlInfo). @@ -79,7 +80,8 @@ class ControlsControllerImpl @Inject constructor ( private val contentResolver: ContentResolver get() = context.contentResolver - override var available = Settings.Secure.getInt(contentResolver, CONTROLS_AVAILABLE, 0) != 0 + override var available = Settings.Secure.getInt( + contentResolver, CONTROLS_AVAILABLE, DEFAULT_ENABLED) != 0 private set private var currentUser = context.user @@ -104,7 +106,7 @@ class ControlsControllerImpl @Inject constructor ( userContext.filesDir, ControlsFavoritePersistenceWrapper.FILE_NAME) persistenceWrapper.changeFile(fileName) available = Settings.Secure.getIntForUser(contentResolver, CONTROLS_AVAILABLE, - /* default */ 0, newUser.identifier) != 0 + /* default */ DEFAULT_ENABLED, newUser.identifier) != 0 synchronized(currentFavorites) { currentFavorites.clear() } @@ -140,7 +142,7 @@ class ControlsControllerImpl @Inject constructor ( return } available = Settings.Secure.getIntForUser(contentResolver, CONTROLS_AVAILABLE, - /* default */ 0, currentUserId) != 0 + /* default */ DEFAULT_ENABLED, currentUserId) != 0 synchronized(currentFavorites) { currentFavorites.clear() } diff --git a/packages/SystemUI/src/com/android/systemui/globalactions/GlobalActionsDialog.java b/packages/SystemUI/src/com/android/systemui/globalactions/GlobalActionsDialog.java index 9203e6df61e3..5da02dc57242 100644 --- a/packages/SystemUI/src/com/android/systemui/globalactions/GlobalActionsDialog.java +++ b/packages/SystemUI/src/com/android/systemui/globalactions/GlobalActionsDialog.java @@ -25,7 +25,6 @@ import android.annotation.Nullable; import android.app.ActivityManager; import android.app.Dialog; import android.app.IActivityManager; -import android.app.KeyguardManager; import android.app.PendingIntent; import android.app.StatusBarManager; import android.app.WallpaperManager; @@ -93,6 +92,7 @@ import com.android.systemui.MultiListLayout; import com.android.systemui.MultiListLayout.MultiListAdapter; import com.android.systemui.broadcast.BroadcastDispatcher; import com.android.systemui.colorextraction.SysuiColorExtractor; +import com.android.systemui.controls.management.ControlsListingController; import com.android.systemui.controls.ui.ControlsUiController; import com.android.systemui.dagger.qualifiers.Background; import com.android.systemui.dagger.qualifiers.Main; @@ -152,7 +152,7 @@ public class GlobalActionsDialog implements DialogInterface.OnDismissListener, private final IDreamManager mDreamManager; private final DevicePolicyManager mDevicePolicyManager; private final LockPatternUtils mLockPatternUtils; - private final KeyguardManager mKeyguardManager; + private final KeyguardStateController mKeyguardStateController; private final BroadcastDispatcher mBroadcastDispatcher; private final ContentResolver mContentResolver; private final Resources mResources; @@ -190,6 +190,8 @@ public class GlobalActionsDialog implements DialogInterface.OnDismissListener, private ControlsUiController mControlsUiController; private final IWindowManager mIWindowManager; private final Executor mBackgroundExecutor; + private final ControlsListingController mControlsListingController; + private boolean mAnyControlsProviders = false; /** * @param context everything needs a context :( @@ -198,7 +200,7 @@ public class GlobalActionsDialog implements DialogInterface.OnDismissListener, public GlobalActionsDialog(Context context, GlobalActionsManager windowManagerFuncs, AudioManager audioManager, IDreamManager iDreamManager, DevicePolicyManager devicePolicyManager, LockPatternUtils lockPatternUtils, - KeyguardManager keyguardManager, BroadcastDispatcher broadcastDispatcher, + BroadcastDispatcher broadcastDispatcher, ConnectivityManager connectivityManager, TelephonyManager telephonyManager, ContentResolver contentResolver, @Nullable Vibrator vibrator, @Main Resources resources, ConfigurationController configurationController, ActivityStarter activityStarter, @@ -209,14 +211,15 @@ public class GlobalActionsDialog implements DialogInterface.OnDismissListener, IStatusBarService statusBarService, NotificationShadeWindowController notificationShadeWindowController, ControlsUiController controlsUiController, IWindowManager iWindowManager, - @Background Executor backgroundExecutor) { + @Background Executor backgroundExecutor, + ControlsListingController controlsListingController) { mContext = new ContextThemeWrapper(context, com.android.systemui.R.style.qs_theme); mWindowManagerFuncs = windowManagerFuncs; mAudioManager = audioManager; mDreamManager = iDreamManager; mDevicePolicyManager = devicePolicyManager; mLockPatternUtils = lockPatternUtils; - mKeyguardManager = keyguardManager; + mKeyguardStateController = keyguardStateController; mBroadcastDispatcher = broadcastDispatcher; mContentResolver = contentResolver; mResources = resources; @@ -233,6 +236,7 @@ public class GlobalActionsDialog implements DialogInterface.OnDismissListener, mControlsUiController = controlsUiController; mIWindowManager = iWindowManager; mBackgroundExecutor = backgroundExecutor; + mControlsListingController = controlsListingController; // receive broadcasts IntentFilter filter = new IntentFilter(); @@ -269,6 +273,8 @@ public class GlobalActionsDialog implements DialogInterface.OnDismissListener, } } }); + + mControlsListingController.addCallback(list -> mAnyControlsProviders = !list.isEmpty()); } /** @@ -427,7 +433,7 @@ public class GlobalActionsDialog implements DialogInterface.OnDismissListener, .startPendingIntentDismissingKeyguard(intent); } }, - mKeyguardManager.isDeviceLocked()) + !mKeyguardStateController.isUnlocked()) : null; ActionsDialog dialog = new ActionsDialog(mContext, mAdapter, panelViewController, @@ -444,12 +450,12 @@ public class GlobalActionsDialog implements DialogInterface.OnDismissListener, } private boolean shouldDisplayLockdown() { - int userId = getCurrentUser().id; // Lockdown is meaningless without a place to go. - if (!mKeyguardManager.isDeviceSecure(userId)) { + if (!mKeyguardStateController.isMethodSecure()) { return false; } + int userId = getCurrentUser().id; // Only show the lockdown button if the device isn't locked down (for whatever reason). int state = mLockPatternUtils.getStrongAuthForUser(userId); return (state == STRONG_AUTH_NOT_REQUIRED @@ -1914,7 +1920,8 @@ public class GlobalActionsDialog implements DialogInterface.OnDismissListener, } private boolean shouldShowControls() { - return !mKeyguardManager.isDeviceLocked() - && mControlsUiController.getAvailable(); + return mKeyguardStateController.isUnlocked() + && mControlsUiController.getAvailable() + && mAnyControlsProviders; } } diff --git a/packages/SystemUI/src/com/android/systemui/pip/BasePipManager.java b/packages/SystemUI/src/com/android/systemui/pip/BasePipManager.java index adee7f23e709..38744fe1b670 100644 --- a/packages/SystemUI/src/com/android/systemui/pip/BasePipManager.java +++ b/packages/SystemUI/src/com/android/systemui/pip/BasePipManager.java @@ -18,6 +18,8 @@ package com.android.systemui.pip; import android.content.res.Configuration; +import com.android.systemui.shared.recents.IPinnedStackAnimationListener; + import java.io.PrintWriter; @@ -27,5 +29,7 @@ public interface BasePipManager { default void hidePipMenu(Runnable onStartCallback, Runnable onEndCallback) {} void onConfigurationChanged(Configuration newConfig); default void setShelfHeight(boolean visible, int height) {} + default void setPinnedStackAnimationType(int animationType) {} + default void setPinnedStackAnimationListener(IPinnedStackAnimationListener listener) {} default void dump(PrintWriter pw) {} } diff --git a/packages/SystemUI/src/com/android/systemui/pip/PipAnimationController.java b/packages/SystemUI/src/com/android/systemui/pip/PipAnimationController.java new file mode 100644 index 000000000000..b5fd406de368 --- /dev/null +++ b/packages/SystemUI/src/com/android/systemui/pip/PipAnimationController.java @@ -0,0 +1,317 @@ +/* + * Copyright (C) 2020 The Android Open Source Project + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +package com.android.systemui.pip; + +import android.animation.Animator; +import android.animation.ValueAnimator; +import android.annotation.IntDef; +import android.annotation.MainThread; +import android.content.Context; +import android.graphics.Rect; +import android.os.RemoteException; +import android.view.IWindowContainer; +import android.view.SurfaceControl; +import android.view.animation.AnimationUtils; +import android.view.animation.Interpolator; + +import com.android.internal.annotations.VisibleForTesting; + +import java.lang.annotation.Retention; +import java.lang.annotation.RetentionPolicy; + +/** + * Controller class of PiP animations (both from and to PiP mode). + */ +public class PipAnimationController { + private static final float FRACTION_START = 0f; + private static final float FRACTION_END = 1f; + + public static final int DURATION_NONE = 0; + public static final int DURATION_DEFAULT_MS = 425; + public static final int ANIM_TYPE_BOUNDS = 0; + public static final int ANIM_TYPE_ALPHA = 1; + + @IntDef(prefix = { "ANIM_TYPE_" }, value = { + ANIM_TYPE_BOUNDS, + ANIM_TYPE_ALPHA + }) + @Retention(RetentionPolicy.SOURCE) + public @interface AnimationType {} + + private final Interpolator mFastOutSlowInInterpolator; + + private PipTransitionAnimator mCurrentAnimator; + + PipAnimationController(Context context) { + mFastOutSlowInInterpolator = AnimationUtils.loadInterpolator(context, + com.android.internal.R.interpolator.fast_out_slow_in); + } + + @MainThread + PipTransitionAnimator getAnimator(IWindowContainer wc, boolean scheduleFinishPip, + Rect destinationBounds, float alphaStart, float alphaEnd) { + if (mCurrentAnimator == null) { + mCurrentAnimator = setupPipTransitionAnimator( + PipTransitionAnimator.ofAlpha(wc, scheduleFinishPip, + destinationBounds, alphaStart, alphaEnd)); + } else if (mCurrentAnimator.getAnimationType() == ANIM_TYPE_ALPHA + && mCurrentAnimator.isRunning()) { + mCurrentAnimator.updateEndValue(alphaEnd); + } else { + mCurrentAnimator.cancel(); + mCurrentAnimator = setupPipTransitionAnimator( + PipTransitionAnimator.ofAlpha(wc, scheduleFinishPip, + destinationBounds, alphaStart, alphaEnd)); + } + return mCurrentAnimator; + } + + @MainThread + PipTransitionAnimator getAnimator(IWindowContainer wc, boolean scheduleFinishPip, + Rect startBounds, Rect endBounds) { + if (mCurrentAnimator == null) { + mCurrentAnimator = setupPipTransitionAnimator( + PipTransitionAnimator.ofBounds(wc, scheduleFinishPip, startBounds, endBounds)); + } else if (mCurrentAnimator.getAnimationType() == ANIM_TYPE_BOUNDS + && mCurrentAnimator.isRunning()) { + mCurrentAnimator.setDestinationBounds(endBounds); + // construct new Rect instances in case they are recycled + mCurrentAnimator.updateEndValue(new Rect(endBounds)); + } else { + mCurrentAnimator.cancel(); + mCurrentAnimator = setupPipTransitionAnimator( + PipTransitionAnimator.ofBounds(wc, scheduleFinishPip, startBounds, endBounds)); + } + return mCurrentAnimator; + } + + private PipTransitionAnimator setupPipTransitionAnimator(PipTransitionAnimator animator) { + animator.setInterpolator(mFastOutSlowInInterpolator); + animator.setFloatValues(FRACTION_START, FRACTION_END); + return animator; + } + + /** + * Additional callback interface for PiP animation + */ + public static class PipAnimationCallback { + /** + * Called when PiP animation is started. + */ + public void onPipAnimationStart(IWindowContainer wc, PipTransitionAnimator animator) {} + + /** + * Called when PiP animation is ended. + */ + public void onPipAnimationEnd(IWindowContainer wc, SurfaceControl.Transaction tx, + PipTransitionAnimator animator) {} + + /** + * Called when PiP animation is cancelled. + */ + public void onPipAnimationCancel(IWindowContainer wc, PipTransitionAnimator animator) {} + } + + /** + * Animator for PiP transition animation which supports both alpha and bounds animation. + * @param <T> Type of property to animate, either alpha (float) or bounds (Rect) + */ + public abstract static class PipTransitionAnimator<T> extends ValueAnimator implements + ValueAnimator.AnimatorUpdateListener, + ValueAnimator.AnimatorListener { + private final IWindowContainer mWindowContainer; + private final boolean mScheduleFinishPip; + private final SurfaceControl mLeash; + private final @AnimationType int mAnimationType; + private final Rect mDestinationBounds = new Rect(); + + private T mStartValue; + private T mEndValue; + private T mCurrentValue; + private PipAnimationCallback mPipAnimationCallback; + private SurfaceControlTransactionFactory mSurfaceControlTransactionFactory; + + private PipTransitionAnimator(IWindowContainer wc, boolean scheduleFinishPip, + @AnimationType int animationType, Rect destinationBounds, + T startValue, T endValue) { + mWindowContainer = wc; + mScheduleFinishPip = scheduleFinishPip; + try { + mLeash = wc.getLeash(); + mAnimationType = animationType; + mDestinationBounds.set(destinationBounds); + mStartValue = startValue; + mEndValue = endValue; + addListener(this); + addUpdateListener(this); + mSurfaceControlTransactionFactory = SurfaceControl.Transaction::new; + } catch (RemoteException e) { + throw new RuntimeException(e); + } + } + + @Override + public void onAnimationStart(Animator animation) { + mCurrentValue = mStartValue; + applySurfaceControlTransaction(mLeash, newSurfaceControlTransaction(), FRACTION_START); + if (mPipAnimationCallback != null) { + mPipAnimationCallback.onPipAnimationStart(mWindowContainer, this); + } + } + + @Override + public void onAnimationUpdate(ValueAnimator animation) { + applySurfaceControlTransaction(mLeash, newSurfaceControlTransaction(), + animation.getAnimatedFraction()); + } + + @Override + public void onAnimationEnd(Animator animation) { + mCurrentValue = mEndValue; + final SurfaceControl.Transaction tx = newSurfaceControlTransaction(); + applySurfaceControlTransaction(mLeash, tx, FRACTION_END); + if (mPipAnimationCallback != null) { + mPipAnimationCallback.onPipAnimationEnd(mWindowContainer, tx, this); + } + } + + @Override + public void onAnimationCancel(Animator animation) { + if (mPipAnimationCallback != null) { + mPipAnimationCallback.onPipAnimationCancel(mWindowContainer, this); + } + } + + @Override public void onAnimationRepeat(Animator animation) {} + + @AnimationType int getAnimationType() { + return mAnimationType; + } + + PipTransitionAnimator<T> setPipAnimationCallback(PipAnimationCallback callback) { + mPipAnimationCallback = callback; + return this; + } + + boolean shouldScheduleFinishPip() { + return mScheduleFinishPip; + } + + T getStartValue() { + return mStartValue; + } + + T getEndValue() { + return mEndValue; + } + + Rect getDestinationBounds() { + return mDestinationBounds; + } + + void setDestinationBounds(Rect destinationBounds) { + mDestinationBounds.set(destinationBounds); + } + + void setCurrentValue(T value) { + mCurrentValue = value; + } + + /** + * Updates the {@link #mEndValue}. + * + * NOTE: Do not forget to call {@link #setDestinationBounds(Rect)} for bounds animation. + * This is typically used when we receive a shelf height adjustment during the bounds + * animation. In which case we can update the end bounds and keep the existing animation + * running instead of cancelling it. + */ + void updateEndValue(T endValue) { + mEndValue = endValue; + mStartValue = mCurrentValue; + } + + SurfaceControl.Transaction newSurfaceControlTransaction() { + return mSurfaceControlTransactionFactory.getTransaction(); + } + + @VisibleForTesting + void setSurfaceControlTransactionFactory(SurfaceControlTransactionFactory factory) { + mSurfaceControlTransactionFactory = factory; + } + + abstract void applySurfaceControlTransaction(SurfaceControl leash, + SurfaceControl.Transaction tx, float fraction); + + static PipTransitionAnimator<Float> ofAlpha(IWindowContainer wc, boolean scheduleFinishPip, + Rect destinationBounds, float startValue, float endValue) { + return new PipTransitionAnimator<Float>(wc, scheduleFinishPip, ANIM_TYPE_ALPHA, + destinationBounds, startValue, endValue) { + @Override + void applySurfaceControlTransaction(SurfaceControl leash, + SurfaceControl.Transaction tx, float fraction) { + final float alpha = getStartValue() * (1 - fraction) + getEndValue() * fraction; + setCurrentValue(alpha); + tx.setAlpha(leash, alpha); + if (Float.compare(fraction, FRACTION_START) == 0) { + // Ensure the start condition + final Rect bounds = getDestinationBounds(); + tx.setPosition(leash, bounds.left, bounds.top) + .setWindowCrop(leash, bounds.width(), bounds.height()); + } + tx.apply(); + } + }; + } + + static PipTransitionAnimator<Rect> ofBounds(IWindowContainer wc, boolean scheduleFinishPip, + Rect startValue, Rect endValue) { + // construct new Rect instances in case they are recycled + return new PipTransitionAnimator<Rect>(wc, scheduleFinishPip, ANIM_TYPE_BOUNDS, + endValue, new Rect(startValue), new Rect(endValue)) { + private final Rect mTmpRect = new Rect(); + + private int getCastedFractionValue(float start, float end, float fraction) { + return (int) (start * (1 - fraction) + end * fraction + .5f); + } + + @Override + void applySurfaceControlTransaction(SurfaceControl leash, + SurfaceControl.Transaction tx, float fraction) { + final Rect start = getStartValue(); + final Rect end = getEndValue(); + mTmpRect.set( + getCastedFractionValue(start.left, end.left, fraction), + getCastedFractionValue(start.top, end.top, fraction), + getCastedFractionValue(start.right, end.right, fraction), + getCastedFractionValue(start.bottom, end.bottom, fraction)); + setCurrentValue(mTmpRect); + tx.setPosition(leash, mTmpRect.left, mTmpRect.top) + .setWindowCrop(leash, mTmpRect.width(), mTmpRect.height()); + if (Float.compare(fraction, FRACTION_START) == 0) { + // Ensure the start condition + tx.setAlpha(leash, 1f); + } + tx.apply(); + } + }; + } + } + + interface SurfaceControlTransactionFactory { + SurfaceControl.Transaction getTransaction(); + } +} diff --git a/packages/SystemUI/src/com/android/systemui/pip/PipBoundsHandler.java b/packages/SystemUI/src/com/android/systemui/pip/PipBoundsHandler.java index 41b31306a931..8c3ccaab8249 100644 --- a/packages/SystemUI/src/com/android/systemui/pip/PipBoundsHandler.java +++ b/packages/SystemUI/src/com/android/systemui/pip/PipBoundsHandler.java @@ -36,7 +36,6 @@ import android.util.Size; import android.util.TypedValue; import android.view.DisplayInfo; import android.view.Gravity; -import android.view.IPinnedStackController; import android.view.IWindowManager; import android.view.WindowContainerTransaction; import android.view.WindowManagerGlobal; @@ -56,9 +55,7 @@ public class PipBoundsHandler { private final IWindowManager mWindowManager; private final PipSnapAlgorithm mSnapAlgorithm; private final DisplayInfo mDisplayInfo = new DisplayInfo(); - private final Rect mStableInsets = new Rect(); private final Rect mTmpInsets = new Rect(); - private final Point mTmpDisplaySize = new Point(); /** * Tracks the destination bounds, used for any following @@ -66,7 +63,6 @@ public class PipBoundsHandler { */ private final Rect mLastDestinationBounds = new Rect(); - private IPinnedStackController mPinnedStackController; private ComponentName mLastPipComponentName; private float mReentrySnapFraction = INVALID_SNAP_FRACTION; private Size mReentrySize = null; @@ -80,7 +76,6 @@ public class PipBoundsHandler { private Point mScreenEdgeInsets; private int mCurrentMinSize; - private boolean mIsMinimized; private boolean mIsImeShowing; private int mImeHeight; private boolean mIsShelfShowing; @@ -123,10 +118,6 @@ public class PipBoundsHandler { com.android.internal.R.dimen.config_pictureInPictureMaxAspectRatio); } - public void setPinnedStackController(IPinnedStackController controller) { - mPinnedStackController = controller; - } - public void setMinEdgeSize(int minEdgeSize) { mCurrentMinSize = minEdgeSize; } @@ -155,14 +146,6 @@ public class PipBoundsHandler { } /** - * Responds to IPinnedStackListener on minimized state change. - */ - public void onMinimizedStateChanged(boolean minimized) { - mIsMinimized = minimized; - mSnapAlgorithm.setMinimized(minimized); - } - - /** * Responds to IPinnedStackListener on movement bounds change. * Note that both inset and normal bounds will be calculated here rather than in the caller. */ @@ -238,9 +221,9 @@ public class PipBoundsHandler { } /** - * Responds to IPinnedStackListener on preparing the pinned stack animation. + * @return {@link Rect} of the destination PiP window bounds. */ - public void onPrepareAnimation(Rect sourceRectHint, float aspectRatio, Rect bounds) { + Rect getDestinationBounds(float aspectRatio, Rect bounds) { final Rect destinationBounds; final Rect defaultBounds = getDefaultBounds(mReentrySnapFraction, mReentrySize); if (bounds == null) { @@ -253,17 +236,16 @@ public class PipBoundsHandler { false /* useCurrentMinEdgeSize */); } if (destinationBounds.equals(bounds)) { - return; + return bounds; } mAspectRatio = aspectRatio; onResetReentryBoundsUnchecked(); - try { - mPinnedStackController.startAnimation(destinationBounds, sourceRectHint, - -1 /* animationDuration */); - mLastDestinationBounds.set(destinationBounds); - } catch (RemoteException e) { - Log.e(TAG, "Failed to start PiP animation from SysUI", e); - } + mLastDestinationBounds.set(destinationBounds); + return destinationBounds; + } + + float getDefaultAspectRatio() { + return mDefaultAspectRatio; } /** @@ -307,18 +289,10 @@ public class PipBoundsHandler { false /* adjustForIme */); mSnapAlgorithm.applySnapFraction(postChangeStackBounds, postChangeMovementBounds, snapFraction); - if (mIsMinimized) { - applyMinimizedOffset(postChangeStackBounds, postChangeMovementBounds); - } - try { - outBounds.set(postChangeStackBounds); - mLastDestinationBounds.set(outBounds); - mPinnedStackController.resetBoundsAnimation(outBounds); - t.setBounds(pinnedStackInfo.stackToken, outBounds); - } catch (RemoteException e) { - Log.e(TAG, "Failed to resize PiP on display rotation", e); - } + outBounds.set(postChangeStackBounds); + mLastDestinationBounds.set(outBounds); + t.setBounds(pinnedStackInfo.stackToken, outBounds); return true; } @@ -370,9 +344,6 @@ public class PipBoundsHandler { final int top = (int) (stackBounds.centerY() - size.getHeight() / 2f); stackBounds.set(left, top, left + size.getWidth(), top + size.getHeight()); mSnapAlgorithm.applySnapFraction(stackBounds, getMovementBounds(stackBounds), snapFraction); - if (mIsMinimized) { - applyMinimizedOffset(stackBounds, getMovementBounds(stackBounds)); - } } /** @@ -436,20 +407,6 @@ public class PipBoundsHandler { } /** - * Applies the minimized offsets to the given stack bounds. - */ - private void applyMinimizedOffset(Rect stackBounds, Rect movementBounds) { - mTmpDisplaySize.set(mDisplayInfo.logicalWidth, mDisplayInfo.logicalHeight); - try { - mWindowManager.getStableInsets(mContext.getDisplayId(), mStableInsets); - mSnapAlgorithm.applyMinimizedOffset(stackBounds, movementBounds, mTmpDisplaySize, - mStableInsets); - } catch (RemoteException e) { - Log.e(TAG, "Failed to get stable insets from WM", e); - } - } - - /** * @return the default snap fraction to apply instead of the default gravity when calculating * the default stack bounds when first entering PiP. */ @@ -486,7 +443,6 @@ public class PipBoundsHandler { pw.println(innerPrefix + "mMaxAspectRatio=" + mMaxAspectRatio); pw.println(innerPrefix + "mAspectRatio=" + mAspectRatio); pw.println(innerPrefix + "mDefaultStackGravity=" + mDefaultStackGravity); - pw.println(innerPrefix + "mIsMinimized=" + mIsMinimized); pw.println(innerPrefix + "mIsImeShowing=" + mIsImeShowing); pw.println(innerPrefix + "mImeHeight=" + mImeHeight); pw.println(innerPrefix + "mIsShelfShowing=" + mIsShelfShowing); diff --git a/packages/SystemUI/src/com/android/systemui/pip/PipSnapAlgorithm.java b/packages/SystemUI/src/com/android/systemui/pip/PipSnapAlgorithm.java index f3e707c6408c..6b89718acee8 100644 --- a/packages/SystemUI/src/com/android/systemui/pip/PipSnapAlgorithm.java +++ b/packages/SystemUI/src/com/android/systemui/pip/PipSnapAlgorithm.java @@ -63,14 +63,9 @@ public class PipSnapAlgorithm { private int mOrientation = Configuration.ORIENTATION_UNDEFINED; - private final int mMinimizedVisibleSize; - private boolean mIsMinimized; - public PipSnapAlgorithm(Context context) { Resources res = context.getResources(); mContext = context; - mMinimizedVisibleSize = res.getDimensionPixelSize( - com.android.internal.R.dimen.pip_minimized_visible_size); mDefaultSizePercent = res.getFloat( com.android.internal.R.dimen.config_pictureInPictureDefaultSizePercent); mMaxAspectRatioForMinSize = res.getFloat( @@ -92,13 +87,6 @@ public class PipSnapAlgorithm { } /** - * Sets the PIP's minimized state. - */ - public void setMinimized(boolean isMinimized) { - mIsMinimized = isMinimized; - } - - /** * @return the closest absolute snap stack bounds for the given {@param stackBounds} moving at * the given {@param velocityX} and {@param velocityY}. The {@param movementBounds} should be * those for the given {@param stackBounds}. @@ -235,20 +223,6 @@ public class PipSnapAlgorithm { } /** - * Applies the offset to the {@param stackBounds} to adjust it to a minimized state. - */ - public void applyMinimizedOffset(Rect stackBounds, Rect movementBounds, Point displaySize, - Rect stableInsets) { - if (stackBounds.left <= movementBounds.centerX()) { - stackBounds.offsetTo(stableInsets.left + mMinimizedVisibleSize - stackBounds.width(), - stackBounds.top); - } else { - stackBounds.offsetTo(displaySize.x - stableInsets.right - mMinimizedVisibleSize, - stackBounds.top); - } - } - - /** * @return returns a fraction that describes where along the {@param movementBounds} the * {@param stackBounds} are. If the {@param stackBounds} are not currently on the * {@param movementBounds} exactly, then they will be snapped to the movement bounds. @@ -402,16 +376,11 @@ public class PipSnapAlgorithm { * the new bounds out to {@param boundsOut}. */ private void snapRectToClosestEdge(Rect stackBounds, Rect movementBounds, Rect boundsOut) { - // If the stackBounds are minimized, then it should only be snapped back horizontally final int boundedLeft = Math.max(movementBounds.left, Math.min(movementBounds.right, stackBounds.left)); final int boundedTop = Math.max(movementBounds.top, Math.min(movementBounds.bottom, stackBounds.top)); boundsOut.set(stackBounds); - if (mIsMinimized) { - boundsOut.offsetTo(boundedLeft, boundedTop); - return; - } // Otherwise, just find the closest edge final int fromLeft = Math.abs(stackBounds.left - movementBounds.left); @@ -479,7 +448,5 @@ public class PipSnapAlgorithm { pw.println(prefix + PipSnapAlgorithm.class.getSimpleName()); pw.println(innerPrefix + "mSnapMode=" + mSnapMode); pw.println(innerPrefix + "mOrientation=" + mOrientation); - pw.println(innerPrefix + "mMinimizedVisibleSize=" + mMinimizedVisibleSize); - pw.println(innerPrefix + "mIsMinimized=" + mIsMinimized); } } diff --git a/packages/SystemUI/src/com/android/systemui/pip/PipTaskOrganizer.java b/packages/SystemUI/src/com/android/systemui/pip/PipTaskOrganizer.java new file mode 100644 index 000000000000..1555153e8d4f --- /dev/null +++ b/packages/SystemUI/src/com/android/systemui/pip/PipTaskOrganizer.java @@ -0,0 +1,291 @@ +/* + * Copyright (C) 2020 The Android Open Source Project + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +package com.android.systemui.pip; + +import static com.android.systemui.pip.PipAnimationController.ANIM_TYPE_ALPHA; +import static com.android.systemui.pip.PipAnimationController.ANIM_TYPE_BOUNDS; +import static com.android.systemui.pip.PipAnimationController.DURATION_NONE; + +import android.annotation.NonNull; +import android.annotation.Nullable; +import android.app.ActivityManager; +import android.app.ActivityTaskManager; +import android.app.ITaskOrganizerController; +import android.app.PictureInPictureParams; +import android.content.Context; +import android.graphics.Rect; +import android.os.Handler; +import android.os.Looper; +import android.os.RemoteException; +import android.util.Log; +import android.view.DisplayInfo; +import android.view.ITaskOrganizer; +import android.view.IWindowContainer; +import android.view.SurfaceControl; +import android.view.WindowContainerTransaction; + +import java.util.ArrayList; +import java.util.List; +import java.util.Objects; + +/** + * Manages PiP tasks such as resize and offset. + * + * This class listens on {@link ITaskOrganizer} callbacks for windowing mode change + * both to and from PiP and issues corresponding animation if applicable. + * Normally, we apply series of {@link SurfaceControl.Transaction} when the animator is running + * and files a final {@link WindowContainerTransaction} at the end of the transition. + * + * This class is also responsible for general resize/offset PiP operations within SysUI component, + * see also {@link com.android.systemui.pip.phone.PipMotionHelper}. + */ +public class PipTaskOrganizer extends ITaskOrganizer.Stub { + private static final String TAG = PipTaskOrganizer.class.getSimpleName(); + + private final Handler mMainHandler; + private final ITaskOrganizerController mTaskOrganizerController; + private final PipBoundsHandler mPipBoundsHandler; + private final PipAnimationController mPipAnimationController; + private final List<PipTransitionCallback> mPipTransitionCallbacks = new ArrayList<>(); + private final Rect mDisplayBounds = new Rect(); + private final Rect mLastReportedBounds = new Rect(); + + private final PipAnimationController.PipAnimationCallback mPipAnimationCallback = + new PipAnimationController.PipAnimationCallback() { + @Override + public void onPipAnimationStart(IWindowContainer wc, + PipAnimationController.PipTransitionAnimator animator) { + mMainHandler.post(() -> { + for (int i = mPipTransitionCallbacks.size() - 1; i >= 0; i--) { + final PipTransitionCallback callback = mPipTransitionCallbacks.get(i); + callback.onPipTransitionStarted(); + } + }); + } + + @Override + public void onPipAnimationEnd(IWindowContainer wc, SurfaceControl.Transaction tx, + PipAnimationController.PipTransitionAnimator animator) { + mMainHandler.post(() -> { + for (int i = mPipTransitionCallbacks.size() - 1; i >= 0; i--) { + final PipTransitionCallback callback = mPipTransitionCallbacks.get(i); + callback.onPipTransitionFinished(); + } + }); + final Rect destinationBounds = animator.getDestinationBounds(); + mLastReportedBounds.set(destinationBounds); + try { + final WindowContainerTransaction wct = new WindowContainerTransaction(); + if (animator.shouldScheduleFinishPip()) { + wct.scheduleFinishEnterPip(wc, destinationBounds); + } else { + wct.setBounds(wc, destinationBounds); + } + wct.setBoundsChangeTransaction(wc, tx); + mTaskOrganizerController.applyContainerTransaction(wct, null /* ITaskOrganizer */); + } catch (RemoteException e) { + Log.e(TAG, "Failed to apply container transaction", e); + } + } + + @Override + public void onPipAnimationCancel(IWindowContainer wc, + PipAnimationController.PipTransitionAnimator animator) { + mMainHandler.post(() -> { + for (int i = mPipTransitionCallbacks.size() - 1; i >= 0; i--) { + final PipTransitionCallback callback = mPipTransitionCallbacks.get(i); + callback.onPipTransitionCanceled(); + } + }); + } + }; + + private ActivityManager.RunningTaskInfo mTaskInfo; + private @PipAnimationController.AnimationType int mOneShotAnimationType = ANIM_TYPE_BOUNDS; + + public PipTaskOrganizer(Context context, @NonNull PipBoundsHandler boundsHandler) { + mMainHandler = new Handler(Looper.getMainLooper()); + mTaskOrganizerController = ActivityTaskManager.getTaskOrganizerController(); + mPipBoundsHandler = boundsHandler; + mPipAnimationController = new PipAnimationController(context); + } + + /** + * Resize the PiP window, animate if the given duration is not {@link #DURATION_NONE} + */ + public void resizePinnedStack(Rect destinationBounds, int durationMs) { + Objects.requireNonNull(mTaskInfo, "Requires valid IWindowContainer"); + resizePinnedStackInternal(mTaskInfo.token, false /* scheduleFinishPip */, + mLastReportedBounds, destinationBounds, durationMs); + } + + /** + * Offset the PiP window, animate if the given duration is not {@link #DURATION_NONE} + */ + public void offsetPinnedStack(Rect originalBounds, int xOffset, int yOffset, int durationMs) { + if (mTaskInfo == null) { + Log.w(TAG, "mTaskInfo is not set"); + return; + } + final Rect destinationBounds = new Rect(originalBounds); + destinationBounds.offset(xOffset, yOffset); + resizePinnedStackInternal(mTaskInfo.token, false /* scheduleFinishPip*/, + originalBounds, destinationBounds, durationMs); + } + + /** + * Registers {@link PipTransitionCallback} to receive transition callbacks. + */ + public void registerPipTransitionCallback(PipTransitionCallback callback) { + mPipTransitionCallbacks.add(callback); + } + + /** + * Sets the preferred animation type for one time. + * This is typically used to set the animation type to {@link #ANIM_TYPE_ALPHA}. + */ + public void setOneShotAnimationType(@PipAnimationController.AnimationType int animationType) { + mOneShotAnimationType = animationType; + } + + /** + * Updates the display dimension with given {@link DisplayInfo} + */ + public void onDisplayInfoChanged(DisplayInfo displayInfo) { + mDisplayBounds.set(0, 0, displayInfo.logicalWidth, displayInfo.logicalHeight); + } + + /** + * Callback to issue the final {@link WindowContainerTransaction} on end of movements. + * @param destinationBounds the final bounds. + */ + public void onMotionMovementEnd(Rect destinationBounds) { + try { + mLastReportedBounds.set(destinationBounds); + final WindowContainerTransaction wct = new WindowContainerTransaction(); + wct.setBounds(mTaskInfo.token, destinationBounds); + mTaskOrganizerController.applyContainerTransaction(wct, null /* ITaskOrganizer */); + } catch (RemoteException e) { + Log.w(TAG, "Failed to apply window container transaction", e); + } + } + + @Override + public void taskAppeared(ActivityManager.RunningTaskInfo info) { + Objects.requireNonNull(info, "Requires RunningTaskInfo"); + final Rect destinationBounds = mPipBoundsHandler.getDestinationBounds( + getAspectRatioOrDefault(info.pictureInPictureParams), null /* bounds */); + Objects.requireNonNull(destinationBounds, "Missing destination bounds"); + mTaskInfo = info; + if (mOneShotAnimationType == ANIM_TYPE_BOUNDS) { + final Rect currentBounds = mTaskInfo.configuration.windowConfiguration.getBounds(); + resizePinnedStackInternal(mTaskInfo.token, true /* scheduleFinishPip */, + currentBounds, destinationBounds, + PipAnimationController.DURATION_DEFAULT_MS); + } else if (mOneShotAnimationType == ANIM_TYPE_ALPHA) { + mMainHandler.post(() -> mPipAnimationController + .getAnimator(mTaskInfo.token, true /* scheduleFinishPip */, + destinationBounds, 0f, 1f) + .setPipAnimationCallback(mPipAnimationCallback) + .setDuration(PipAnimationController.DURATION_DEFAULT_MS) + .start()); + mOneShotAnimationType = ANIM_TYPE_BOUNDS; + } else { + throw new RuntimeException("Unrecognized animation type: " + mOneShotAnimationType); + } + } + + @Override + public void taskVanished(IWindowContainer token) { + Objects.requireNonNull(token, "Requires valid IWindowContainer"); + if (token.asBinder() != mTaskInfo.token.asBinder()) { + Log.wtf(TAG, "Unrecognized token: " + token); + return; + } + resizePinnedStackInternal(token, false /* scheduleFinishPip */, + mLastReportedBounds, mDisplayBounds, + PipAnimationController.DURATION_DEFAULT_MS); + } + + @Override + public void transactionReady(int id, SurfaceControl.Transaction t) { + } + + @Override + public void onTaskInfoChanged(ActivityManager.RunningTaskInfo info) { + final Rect destinationBounds = mPipBoundsHandler.getDestinationBounds( + getAspectRatioOrDefault(info.pictureInPictureParams), null /* bounds */); + Objects.requireNonNull(destinationBounds, "Missing destination bounds"); + resizePinnedStack(destinationBounds, PipAnimationController.DURATION_DEFAULT_MS); + } + + private void resizePinnedStackInternal(IWindowContainer wc, boolean scheduleFinishPip, + Rect currentBounds, Rect destinationBounds, int animationDurationMs) { + try { + // Could happen when dismissPip + if (wc == null || wc.getLeash() == null) { + Log.w(TAG, "Abort animation, invalid leash"); + return; + } + final SurfaceControl leash = wc.getLeash(); + if (animationDurationMs == DURATION_NONE) { + // Directly resize if no animation duration is set. When fling, wait for final + // callback to issue the proper WindowContainerTransaction with destination bounds. + new SurfaceControl.Transaction() + .setPosition(leash, destinationBounds.left, destinationBounds.top) + .setWindowCrop(leash, destinationBounds.width(), destinationBounds.height()) + .apply(); + } else { + mMainHandler.post(() -> mPipAnimationController + .getAnimator(wc, scheduleFinishPip, currentBounds, destinationBounds) + .setPipAnimationCallback(mPipAnimationCallback) + .setDuration(animationDurationMs) + .start()); + } + } catch (RemoteException e) { + Log.w(TAG, "Abort animation, invalid window container", e); + } catch (Exception e) { + Log.e(TAG, "Should not reach here, terrible thing happened", e); + } + } + + private float getAspectRatioOrDefault(@Nullable PictureInPictureParams params) { + return params == null + ? mPipBoundsHandler.getDefaultAspectRatio() + : params.getAspectRatio(); + } + + /** + * Callback interface for PiP transitions (both from and to PiP mode) + */ + public interface PipTransitionCallback { + /** + * Callback when the pip transition is started. + */ + void onPipTransitionStarted(); + + /** + * Callback when the pip transition is finished. + */ + void onPipTransitionFinished(); + + /** + * Callback when the pip transition is cancelled. + */ + void onPipTransitionCanceled(); + } +} diff --git a/packages/SystemUI/src/com/android/systemui/pip/PipUI.java b/packages/SystemUI/src/com/android/systemui/pip/PipUI.java index 599c845ba39a..4fb675e31f0e 100644 --- a/packages/SystemUI/src/com/android/systemui/pip/PipUI.java +++ b/packages/SystemUI/src/com/android/systemui/pip/PipUI.java @@ -25,6 +25,7 @@ import android.os.UserHandle; import android.os.UserManager; import com.android.systemui.SystemUI; +import com.android.systemui.shared.recents.IPinnedStackAnimationListener; import com.android.systemui.statusbar.CommandQueue; import java.io.FileDescriptor; @@ -98,6 +99,18 @@ public class PipUI extends SystemUI implements CommandQueue.Callbacks { mPipManager.setShelfHeight(visible, height); } + public void setPinnedStackAnimationType(int animationType) { + if (mPipManager != null) { + mPipManager.setPinnedStackAnimationType(animationType); + } + } + + public void setPinnedStackAnimationListener(IPinnedStackAnimationListener listener) { + if (mPipManager != null) { + mPipManager.setPinnedStackAnimationListener(listener); + } + } + @Override public void dump(FileDescriptor fd, PrintWriter pw, String[] args) { if (mPipManager == null) { diff --git a/packages/SystemUI/src/com/android/systemui/pip/phone/PipManager.java b/packages/SystemUI/src/com/android/systemui/pip/phone/PipManager.java index cb94e28e3467..e98dec0835bf 100644 --- a/packages/SystemUI/src/com/android/systemui/pip/phone/PipManager.java +++ b/packages/SystemUI/src/com/android/systemui/pip/phone/PipManager.java @@ -41,6 +41,8 @@ import com.android.systemui.UiOffloadThread; import com.android.systemui.broadcast.BroadcastDispatcher; import com.android.systemui.pip.BasePipManager; import com.android.systemui.pip.PipBoundsHandler; +import com.android.systemui.pip.PipTaskOrganizer; +import com.android.systemui.shared.recents.IPinnedStackAnimationListener; import com.android.systemui.shared.system.ActivityManagerWrapper; import com.android.systemui.shared.system.InputConsumerController; import com.android.systemui.shared.system.PinnedStackListenerForwarder.PinnedStackListener; @@ -59,12 +61,11 @@ import javax.inject.Singleton; * Manages the picture-in-picture (PIP) UI and states for Phones. */ @Singleton -public class PipManager implements BasePipManager { +public class PipManager implements BasePipManager, PipTaskOrganizer.PipTransitionCallback { private static final String TAG = "PipManager"; private Context mContext; private IActivityManager mActivityManager; - private IActivityTaskManager mActivityTaskManager; private Handler mHandler = new Handler(); private final PinnedStackListener mPinnedStackListener = new PipManagerPinnedStackListener(); @@ -78,7 +79,9 @@ public class PipManager implements BasePipManager { private PipMenuActivityController mMenuController; private PipMediaController mMediaController; private PipTouchHandler mTouchHandler; + private PipTaskOrganizer mPipTaskOrganizer; private PipAppOpsListener mAppOpsListener; + private IPinnedStackAnimationListener mPinnedStackAnimationRecentsListener; /** * Handler for display rotation changes. @@ -124,20 +127,6 @@ public class PipManager implements BasePipManager { } @Override - public void onPinnedStackAnimationStarted() { - // Disable touches while the animation is running - mTouchHandler.setTouchEnabled(false); - } - - @Override - public void onPinnedStackAnimationEnded() { - // Re-enable touches after the animation completes - mTouchHandler.setTouchEnabled(true); - mTouchHandler.onPinnedStackAnimationEnded(); - mMenuController.onPinnedStackAnimationEnded(); - } - - @Override public void onPinnedActivityRestartAttempt(boolean clearedTask) { mTouchHandler.getMotionHelper().expandPip(clearedTask /* skipAnimation */); } @@ -149,10 +138,7 @@ public class PipManager implements BasePipManager { private class PipManagerPinnedStackListener extends PinnedStackListener { @Override public void onListenerRegistered(IPinnedStackController controller) { - mHandler.post(() -> { - mPipBoundsHandler.setPinnedStackController(controller); - mTouchHandler.setPinnedStackController(controller); - }); + mHandler.post(() -> mTouchHandler.setPinnedStackController(controller)); } @Override @@ -164,18 +150,9 @@ public class PipManager implements BasePipManager { } @Override - public void onMinimizedStateChanged(boolean isMinimized) { - mHandler.post(() -> { - mPipBoundsHandler.onMinimizedStateChanged(isMinimized); - mTouchHandler.setMinimizedState(isMinimized, true /* fromController */); - }); - } - - @Override - public void onMovementBoundsChanged(Rect animatingBounds, boolean fromImeAdjustment, - boolean fromShelfAdjustment) { + public void onMovementBoundsChanged(Rect animatingBounds, boolean fromImeAdjustment) { mHandler.post(() -> updateMovementBounds(animatingBounds, fromImeAdjustment, - fromShelfAdjustment)); + false /* fromShelfAdjustment */)); } @Override @@ -207,7 +184,10 @@ public class PipManager implements BasePipManager { @Override public void onDisplayInfoChanged(DisplayInfo displayInfo) { - mHandler.post(() -> mPipBoundsHandler.onDisplayInfoChanged(displayInfo)); + mHandler.post(() -> { + mPipBoundsHandler.onDisplayInfoChanged(displayInfo); + mPipTaskOrganizer.onDisplayInfoChanged(displayInfo); + }); } @Override @@ -219,13 +199,6 @@ public class PipManager implements BasePipManager { public void onAspectRatioChanged(float aspectRatio) { mHandler.post(() -> mPipBoundsHandler.onAspectRatioChanged(aspectRatio)); } - - @Override - public void onPrepareAnimation(Rect sourceRectHint, float aspectRatio, Rect bounds) { - mHandler.post(() -> { - mPipBoundsHandler.onPrepareAnimation(sourceRectHint, aspectRatio, bounds); - }); - } } @Inject @@ -234,7 +207,6 @@ public class PipManager implements BasePipManager { FloatingContentCoordinator floatingContentCoordinator) { mContext = context; mActivityManager = ActivityManager.getService(); - mActivityTaskManager = ActivityTaskManager.getService(); try { WindowManagerWrapper.getInstance().addPinnedStackListener(mPinnedStackListener); @@ -243,24 +215,29 @@ public class PipManager implements BasePipManager { } ActivityManagerWrapper.getInstance().registerTaskStackListener(mTaskStackListener); + final IActivityTaskManager activityTaskManager = ActivityTaskManager.getService(); mPipBoundsHandler = new PipBoundsHandler(context); + mPipTaskOrganizer = new PipTaskOrganizer(mContext, mPipBoundsHandler); + mPipTaskOrganizer.registerPipTransitionCallback(this); mInputConsumerController = InputConsumerController.getPipInputConsumer(); mMediaController = new PipMediaController(context, mActivityManager, broadcastDispatcher); - mMenuController = new PipMenuActivityController(context, mActivityManager, mMediaController, + mMenuController = new PipMenuActivityController(context, mMediaController, mInputConsumerController); - mTouchHandler = new PipTouchHandler(context, mActivityManager, mActivityTaskManager, - mMenuController, mInputConsumerController, mPipBoundsHandler, + mTouchHandler = new PipTouchHandler(context, mActivityManager, activityTaskManager, + mMenuController, mInputConsumerController, mPipBoundsHandler, mPipTaskOrganizer, floatingContentCoordinator); mAppOpsListener = new PipAppOpsListener(context, mActivityManager, mTouchHandler.getMotionHelper()); displayController.addDisplayChangingController(mRotationController); - // If SystemUI restart, and it already existed a pinned stack, - // register the pip input consumer to ensure touch can send to it. try { - ActivityManager.StackInfo stackInfo = mActivityTaskManager.getStackInfo( + ActivityTaskManager.getTaskOrganizerController().registerTaskOrganizer( + mPipTaskOrganizer, WINDOWING_MODE_PINNED); + ActivityManager.StackInfo stackInfo = activityTaskManager.getStackInfo( WINDOWING_MODE_PINNED, ACTIVITY_TYPE_UNDEFINED); if (stackInfo != null) { + // If SystemUI restart, and it already existed a pinned stack, + // register the pip input consumer to ensure touch can send to it. mInputConsumerController.registerInputConsumer(); } } catch (RemoteException e) { @@ -320,6 +297,46 @@ public class PipManager implements BasePipManager { }); } + @Override + public void setPinnedStackAnimationType(int animationType) { + mHandler.post(() -> mPipTaskOrganizer.setOneShotAnimationType(animationType)); + } + + @Override + public void setPinnedStackAnimationListener(IPinnedStackAnimationListener listener) { + mHandler.post(() -> mPinnedStackAnimationRecentsListener = listener); + } + + @Override + public void onPipTransitionStarted() { + // Disable touches while the animation is running + mTouchHandler.setTouchEnabled(false); + if (mPinnedStackAnimationRecentsListener != null) { + try { + mPinnedStackAnimationRecentsListener.onPinnedStackAnimationStarted(); + } catch (RemoteException e) { + Log.e(TAG, "Failed to callback recents", e); + } + } + } + + @Override + public void onPipTransitionFinished() { + onPipTransitionFinishedOrCanceled(); + } + + @Override + public void onPipTransitionCanceled() { + onPipTransitionFinishedOrCanceled(); + } + + private void onPipTransitionFinishedOrCanceled() { + // Re-enable touches after the animation completes + mTouchHandler.setTouchEnabled(true); + mTouchHandler.onPinnedStackAnimationEnded(); + mMenuController.onPinnedStackAnimationEnded(); + } + private void updateMovementBounds(Rect animatingBounds, boolean fromImeAdjustment, boolean fromShelfAdjustment) { // Populate inset / normal bounds and DisplayInfo from mPipBoundsHandler first. diff --git a/packages/SystemUI/src/com/android/systemui/pip/phone/PipMenuActivityController.java b/packages/SystemUI/src/com/android/systemui/pip/phone/PipMenuActivityController.java index c7bfc06829b3..d660b670446b 100644 --- a/packages/SystemUI/src/com/android/systemui/pip/phone/PipMenuActivityController.java +++ b/packages/SystemUI/src/com/android/systemui/pip/phone/PipMenuActivityController.java @@ -22,7 +22,6 @@ import static android.app.WindowConfiguration.WINDOWING_MODE_PINNED; import android.app.ActivityManager.StackInfo; import android.app.ActivityOptions; import android.app.ActivityTaskManager; -import android.app.IActivityManager; import android.app.RemoteAction; import android.content.Context; import android.content.Intent; @@ -68,11 +67,8 @@ public class PipMenuActivityController { public static final int MESSAGE_MENU_STATE_CHANGED = 100; public static final int MESSAGE_EXPAND_PIP = 101; - public static final int MESSAGE_MINIMIZE_PIP = 102; public static final int MESSAGE_DISMISS_PIP = 103; public static final int MESSAGE_UPDATE_ACTIVITY_CALLBACK = 104; - public static final int MESSAGE_REGISTER_INPUT_CONSUMER = 105; - public static final int MESSAGE_UNREGISTER_INPUT_CONSUMER = 106; public static final int MESSAGE_SHOW_MENU = 107; public static final int MENU_STATE_NONE = 0; @@ -100,11 +96,6 @@ public class PipMenuActivityController { void onPipExpand(); /** - * Called when the PIP requested to be minimized. - */ - void onPipMinimize(); - - /** * Called when the PIP requested to be dismissed. */ void onPipDismiss(); @@ -116,7 +107,6 @@ public class PipMenuActivityController { } private Context mContext; - private IActivityManager mActivityManager; private PipMediaController mMediaController; private InputConsumerController mInputConsumerController; @@ -146,10 +136,6 @@ public class PipMenuActivityController { mListeners.forEach(l -> l.onPipExpand()); break; } - case MESSAGE_MINIMIZE_PIP: { - mListeners.forEach(l -> l.onPipMinimize()); - break; - } case MESSAGE_DISMISS_PIP: { mListeners.forEach(l -> l.onPipDismiss()); break; @@ -194,10 +180,9 @@ public class PipMenuActivityController { } }; - public PipMenuActivityController(Context context, IActivityManager activityManager, + public PipMenuActivityController(Context context, PipMediaController mediaController, InputConsumerController inputConsumerController) { mContext = context; - mActivityManager = activityManager; mMediaController = mediaController; mInputConsumerController = inputConsumerController; } diff --git a/packages/SystemUI/src/com/android/systemui/pip/phone/PipMotionHelper.java b/packages/SystemUI/src/com/android/systemui/pip/phone/PipMotionHelper.java index c6e28522ccbd..91f539c3a13d 100644 --- a/packages/SystemUI/src/com/android/systemui/pip/phone/PipMotionHelper.java +++ b/packages/SystemUI/src/com/android/systemui/pip/phone/PipMotionHelper.java @@ -22,7 +22,6 @@ import static android.app.WindowConfiguration.WINDOWING_MODE_PINNED; import android.annotation.NonNull; import android.annotation.Nullable; import android.app.ActivityManager.StackInfo; -import android.app.IActivityManager; import android.app.IActivityTaskManager; import android.content.Context; import android.graphics.Point; @@ -39,7 +38,9 @@ import androidx.dynamicanimation.animation.SpringForce; import com.android.internal.graphics.SfVsyncFrameCallbackProvider; import com.android.internal.os.SomeArgs; +import com.android.systemui.pip.PipAnimationController; import com.android.systemui.pip.PipSnapAlgorithm; +import com.android.systemui.pip.PipTaskOrganizer; import com.android.systemui.shared.system.WindowManagerWrapper; import com.android.systemui.statusbar.FlingAnimationUtils; import com.android.systemui.util.FloatingContentCoordinator; @@ -65,8 +66,6 @@ public class PipMotionHelper implements Handler.Callback, PipAppOpsListener.Call /** Friction to use for PIP when it moves via physics fling animations. */ private static final float DEFAULT_FRICTION = 2f; - // The fraction of the stack width that the user has to drag offscreen to minimize the PiP - private static final float MINIMIZE_OFFSCREEN_FRACTION = 0.3f; // The fraction of the stack height that the user has to drag offscreen to dismiss the PiP private static final float DISMISS_OFFSCREEN_FRACTION = 0.3f; @@ -74,10 +73,10 @@ public class PipMotionHelper implements Handler.Callback, PipAppOpsListener.Call private static final int MSG_RESIZE_ANIMATE = 2; private static final int MSG_OFFSET_ANIMATE = 3; - private Context mContext; - private IActivityManager mActivityManager; - private IActivityTaskManager mActivityTaskManager; - private Handler mHandler; + private final Context mContext; + private final IActivityTaskManager mActivityTaskManager; + private final PipTaskOrganizer mPipTaskOrganizer; + private final Handler mHandler; private PipMenuActivityController mMenuController; private PipSnapAlgorithm mSnapAlgorithm; @@ -139,14 +138,14 @@ public class PipMotionHelper implements Handler.Callback, PipAppOpsListener.Call new PhysicsAnimator.SpringConfig( SpringForce.STIFFNESS_LOW, SpringForce.DAMPING_RATIO_LOW_BOUNCY); - public PipMotionHelper(Context context, IActivityManager activityManager, - IActivityTaskManager activityTaskManager, PipMenuActivityController menuController, + public PipMotionHelper(Context context, IActivityTaskManager activityTaskManager, + PipTaskOrganizer pipTaskOrganizer, PipMenuActivityController menuController, PipSnapAlgorithm snapAlgorithm, FlingAnimationUtils flingAnimationUtils, FloatingContentCoordinator floatingContentCoordinator) { mContext = context; mHandler = new Handler(ForegroundThread.get().getLooper(), this); - mActivityManager = activityManager; mActivityTaskManager = activityTaskManager; + mPipTaskOrganizer = pipTaskOrganizer; mMenuController = menuController; mSnapAlgorithm = snapAlgorithm; mFlingAnimationUtils = flingAnimationUtils; @@ -285,35 +284,6 @@ public class PipMotionHelper implements Handler.Callback, PipAppOpsListener.Call } /** - * @return the closest minimized PiP bounds. - */ - Rect getClosestMinimizedBounds(Rect stackBounds) { - Point displaySize = new Point(); - mContext.getDisplay().getRealSize(displaySize); - Rect toBounds = mSnapAlgorithm.findClosestSnapBounds(mMovementBounds, stackBounds); - mSnapAlgorithm.applyMinimizedOffset(toBounds, mMovementBounds, displaySize, mStableInsets); - return toBounds; - } - - /** - * @return whether the PiP at the current bounds should be minimized. - */ - boolean shouldMinimizePip() { - Point displaySize = new Point(); - mContext.getDisplay().getRealSize(displaySize); - if (mBounds.left < 0) { - float offscreenFraction = (float) -mBounds.left / mBounds.width(); - return offscreenFraction >= MINIMIZE_OFFSCREEN_FRACTION; - } else if (mBounds.right > displaySize.x) { - float offscreenFraction = (float) (mBounds.right - displaySize.x) / - mBounds.width(); - return offscreenFraction >= MINIMIZE_OFFSCREEN_FRACTION; - } else { - return false; - } - } - - /** * @return whether the PiP at the current bounds should be dismissed. */ boolean shouldDismissPip() { @@ -328,25 +298,6 @@ public class PipMotionHelper implements Handler.Callback, PipAppOpsListener.Call } /** - * Animates the PiP to the minimized state, slightly offscreen. - */ - void animateToClosestMinimizedState(@Nullable Runnable updateAction) { - final Rect toBounds = getClosestMinimizedBounds(mBounds); - - mAnimatedBounds.set(mBounds); - mAnimatedBoundsPhysicsAnimator - .spring(FloatProperties.RECT_X, toBounds.left, mSpringConfig) - .spring(FloatProperties.RECT_Y, toBounds.top, mSpringConfig); - - if (updateAction != null) { - mAnimatedBoundsPhysicsAnimator.addUpdateListener( - (target, values) -> updateAction.run()); - } - - startBoundsAnimation(); - } - - /** * Flings the PiP to the closest snap target. */ void flingToSnapTarget( @@ -436,8 +387,7 @@ public class PipMotionHelper implements Handler.Callback, PipAppOpsListener.Call * Animates the PiP from the expanded state to the normal state after the menu is hidden. */ void animateToUnexpandedState(Rect normalBounds, float savedSnapFraction, - Rect normalMovementBounds, Rect currentMovementBounds, boolean minimized, - boolean immediate) { + Rect normalMovementBounds, Rect currentMovementBounds, boolean immediate) { if (savedSnapFraction < 0f) { // If there are no saved snap fractions, then just use the current bounds savedSnapFraction = mSnapAlgorithm.getSnapFraction(new Rect(mBounds), @@ -445,10 +395,6 @@ public class PipMotionHelper implements Handler.Callback, PipAppOpsListener.Call } mSnapAlgorithm.applySnapFraction(normalBounds, normalMovementBounds, savedSnapFraction); - if (minimized) { - normalBounds = getClosestMinimizedBounds(normalBounds); - } - if (immediate) { movePip(normalBounds); } else { @@ -503,6 +449,7 @@ public class PipMotionHelper implements Handler.Callback, PipAppOpsListener.Call cancelAnimations(); mAnimatedBoundsPhysicsAnimator + .withEndActions(() -> mPipTaskOrganizer.onMotionMovementEnd(mAnimatedBounds)) .addUpdateListener(mResizePipVsyncUpdateListener) .start(); } @@ -594,13 +541,6 @@ public class PipMotionHelper implements Handler.Callback, PipAppOpsListener.Call } /** - * @return the distance between points {@param p1} and {@param p2}. - */ - private float distanceBetweenRectOffsets(Rect r1, Rect r2) { - return PointF.length(r1.left - r2.left, r1.top - r2.top); - } - - /** * Handles messages to be processed on the background thread. */ public boolean handleMessage(Message msg) { @@ -608,13 +548,8 @@ public class PipMotionHelper implements Handler.Callback, PipAppOpsListener.Call case MSG_RESIZE_IMMEDIATE: { SomeArgs args = (SomeArgs) msg.obj; Rect toBounds = (Rect) args.arg1; - try { - mActivityTaskManager.resizePinnedStack( - toBounds, null /* tempPinnedTaskBounds */); - mBounds.set(toBounds); - } catch (RemoteException e) { - Log.e(TAG, "Could not resize pinned stack to bounds: " + toBounds, e); - } + mPipTaskOrganizer.resizePinnedStack(toBounds, PipAnimationController.DURATION_NONE); + mBounds.set(toBounds); return true; } @@ -631,8 +566,7 @@ public class PipMotionHelper implements Handler.Callback, PipAppOpsListener.Call return true; } - mActivityTaskManager.animateResizePinnedStack(stackInfo.stackId, toBounds, - duration); + mPipTaskOrganizer.resizePinnedStack(toBounds, duration); mBounds.set(toBounds); } catch (RemoteException e) { Log.e(TAG, "Could not animate resize pinned stack to bounds: " + toBounds, e); @@ -654,8 +588,8 @@ public class PipMotionHelper implements Handler.Callback, PipAppOpsListener.Call return true; } - mActivityTaskManager.offsetPinnedStackBounds(stackInfo.stackId, originalBounds, - 0/* xOffset */, offset, duration); + mPipTaskOrganizer.offsetPinnedStack(originalBounds, + 0 /* xOffset */, offset, duration); Rect toBounds = new Rect(originalBounds); toBounds.offset(0, offset); mBounds.set(toBounds); diff --git a/packages/SystemUI/src/com/android/systemui/pip/phone/PipTouchHandler.java b/packages/SystemUI/src/com/android/systemui/pip/phone/PipTouchHandler.java index 8e588e67861c..79a25b2269f6 100644 --- a/packages/SystemUI/src/com/android/systemui/pip/phone/PipTouchHandler.java +++ b/packages/SystemUI/src/com/android/systemui/pip/phone/PipTouchHandler.java @@ -45,6 +45,7 @@ import com.android.internal.os.logging.MetricsLoggerWrapper; import com.android.systemui.R; import com.android.systemui.pip.PipBoundsHandler; import com.android.systemui.pip.PipSnapAlgorithm; +import com.android.systemui.pip.PipTaskOrganizer; import com.android.systemui.shared.system.InputConsumerController; import com.android.systemui.statusbar.FlingAnimationUtils; import com.android.systemui.util.FloatingContentCoordinator; @@ -58,8 +59,6 @@ import java.io.PrintWriter; public class PipTouchHandler { private static final String TAG = "PipTouchHandler"; - // Allow the PIP to be dragged to the edge of the screen to be minimized. - private static final boolean ENABLE_MINIMIZE = false; // Allow the PIP to be flung from anywhere on the screen to the bottom to be dismissed. private static final boolean ENABLE_FLING_DISMISS = false; @@ -67,12 +66,9 @@ public class PipTouchHandler { private static final int BOTTOM_OFFSET_BUFFER_DP = 1; // Allow dragging the PIP to a location to close it - private final boolean mEnableDimissDragToEdge; + private final boolean mEnableDismissDragToEdge; private final Context mContext; private final IActivityManager mActivityManager; - private final IActivityTaskManager mActivityTaskManager; - private final ViewConfiguration mViewConfig; - private final PipMenuListener mMenuListener = new PipMenuListener(); private final PipBoundsHandler mPipBoundsHandler; private final PipResizeGestureHandler mPipResizeGestureHandler; private IPinnedStackController mPinnedStackController; @@ -104,7 +100,7 @@ public class PipTouchHandler { private Runnable mShowDismissAffordance = new Runnable() { @Override public void run() { - if (mEnableDimissDragToEdge) { + if (mEnableDismissDragToEdge) { mDismissViewController.showDismissTarget(); } } @@ -112,7 +108,6 @@ public class PipTouchHandler { // Behaviour states private int mMenuState = MENU_STATE_NONE; - private boolean mIsMinimized; private boolean mIsImeShowing; private int mImeHeight; private int mImeOffset; @@ -121,7 +116,6 @@ public class PipTouchHandler { private int mMovementBoundsExtraOffsets; private float mSavedSnapFraction = -1f; private boolean mSendingHoverAccessibilityEvents; - private boolean mMovementWithinMinimize; private boolean mMovementWithinDismiss; private PipAccessibilityInteractionConnection mConnection; @@ -146,15 +140,7 @@ public class PipTouchHandler { @Override public void onPipExpand() { - if (!mIsMinimized) { - mMotionHelper.expandPip(); - } - } - - @Override - public void onPipMinimize() { - setMinimizedStateInternal(true); - mMotionHelper.animateToClosestMinimizedState(null /* updateAction */); + mMotionHelper.expandPip(); } @Override @@ -175,26 +161,24 @@ public class PipTouchHandler { IActivityTaskManager activityTaskManager, PipMenuActivityController menuController, InputConsumerController inputConsumerController, PipBoundsHandler pipBoundsHandler, + PipTaskOrganizer pipTaskOrganizer, FloatingContentCoordinator floatingContentCoordinator) { - // Initialize the Pip input consumer mContext = context; mActivityManager = activityManager; - mActivityTaskManager = activityTaskManager; mAccessibilityManager = context.getSystemService(AccessibilityManager.class); - mViewConfig = ViewConfiguration.get(context); mMenuController = menuController; - mMenuController.addListener(mMenuListener); + mMenuController.addListener(new PipMenuListener()); mDismissViewController = new PipDismissViewController(context); mSnapAlgorithm = new PipSnapAlgorithm(mContext); mFlingAnimationUtils = new FlingAnimationUtils(context.getResources().getDisplayMetrics(), 2.5f); mGesture = new DefaultPipTouchGesture(); - mMotionHelper = new PipMotionHelper(mContext, mActivityManager, mActivityTaskManager, + mMotionHelper = new PipMotionHelper(mContext, activityTaskManager, pipTaskOrganizer, mMenuController, mSnapAlgorithm, mFlingAnimationUtils, floatingContentCoordinator); mPipResizeGestureHandler = new PipResizeGestureHandler(context, pipBoundsHandler, this, mMotionHelper); - mTouchState = new PipTouchState(mViewConfig, mHandler, + mTouchState = new PipTouchState(ViewConfiguration.get(context), mHandler, () -> mMenuController.showMenu(MENU_STATE_FULL, mMotionHelper.getBounds(), mMovementBounds, true /* allowMenuTimeout */, willResizeMenu())); @@ -203,7 +187,7 @@ public class PipTouchHandler { R.dimen.pip_expanded_shortest_edge_size); mImeOffset = res.getDimensionPixelSize(R.dimen.pip_ime_offset); - mEnableDimissDragToEdge = res.getBoolean(R.bool.config_pipEnableDismissDragToEdge); + mEnableDismissDragToEdge = res.getBoolean(R.bool.config_pipEnableDismissDragToEdge); // Register the listener for input consumer touch events inputConsumerController.setInputListener(this::handleTouchEvent); @@ -339,8 +323,7 @@ public class PipTouchHandler { // If we have a deferred resize, apply it now if (mDeferResizeToNormalBoundsUntilRotation == displayRotation) { mMotionHelper.animateToUnexpandedState(normalBounds, mSavedSnapFraction, - mNormalMovementBounds, mMovementBounds, mIsMinimized, - true /* immediate */); + mNormalMovementBounds, mMovementBounds, true /* immediate */); mSavedSnapFraction = -1f; mDeferResizeToNormalBoundsUntilRotation = -1; } @@ -482,44 +465,6 @@ public class PipTouchHandler { } /** - * Sets the minimized state. - */ - private void setMinimizedStateInternal(boolean isMinimized) { - if (!ENABLE_MINIMIZE) { - return; - } - setMinimizedState(isMinimized, false /* fromController */); - } - - /** - * Sets the minimized state. - */ - void setMinimizedState(boolean isMinimized, boolean fromController) { - if (!ENABLE_MINIMIZE) { - return; - } - if (mIsMinimized != isMinimized) { - MetricsLoggerWrapper.logPictureInPictureMinimize(mContext, - isMinimized, PipUtils.getTopPinnedActivity(mContext, mActivityManager)); - } - mIsMinimized = isMinimized; - mSnapAlgorithm.setMinimized(isMinimized); - - if (fromController) { - if (isMinimized) { - // Move the PiP to the new bounds immediately if minimized - mMotionHelper.movePip(mMotionHelper.getClosestMinimizedBounds(mNormalBounds)); - } - } else if (mPinnedStackController != null) { - try { - mPinnedStackController.setIsMinimized(isMinimized); - } catch (RemoteException e) { - Log.e(TAG, "Could not set minimized state", e); - } - } - } - - /** * Sets the menu visibility. */ private void setMenuState(int menuState, boolean resize) { @@ -562,8 +507,7 @@ public class PipTouchHandler { if (mDeferResizeToNormalBoundsUntilRotation == -1) { Rect normalBounds = new Rect(mNormalBounds); mMotionHelper.animateToUnexpandedState(normalBounds, mSavedSnapFraction, - mNormalMovementBounds, mMovementBounds, mIsMinimized, - false /* immediate */); + mNormalMovementBounds, mMovementBounds, false /* immediate */); mSavedSnapFraction = -1f; } } else { @@ -601,8 +545,6 @@ public class PipTouchHandler { * Gesture controlling normal movement of the PIP. */ private class DefaultPipTouchGesture extends PipTouchGesture { - // Whether the PiP was on the left side of the screen at the start of the gesture - private boolean mStartedOnLeft; private final Point mStartPosition = new Point(); private final PointF mDelta = new PointF(); @@ -615,17 +557,15 @@ public class PipTouchHandler { Rect bounds = mMotionHelper.getBounds(); mDelta.set(0f, 0f); mStartPosition.set(bounds.left, bounds.top); - mStartedOnLeft = bounds.left < mMovementBounds.centerX(); - mMovementWithinMinimize = true; mMovementWithinDismiss = touchState.getDownTouchPosition().y >= mMovementBounds.bottom; - // If the menu is still visible, and we aren't minimized, then just poke the menu + // If the menu is still visible then just poke the menu // so that it will timeout after the user stops touching it - if (mMenuState != MENU_STATE_NONE && !mIsMinimized) { + if (mMenuState != MENU_STATE_NONE) { mMenuController.pokeMenu(); } - if (mEnableDimissDragToEdge) { + if (mEnableDismissDragToEdge) { mDismissViewController.createDismissTarget(); mHandler.postDelayed(mShowDismissAffordance, SHOW_DISMISS_AFFORDANCE_DELAY); } @@ -640,7 +580,7 @@ public class PipTouchHandler { if (touchState.startedDragging()) { mSavedSnapFraction = -1f; - if (mEnableDimissDragToEdge) { + if (mEnableDismissDragToEdge) { mHandler.removeCallbacks(mShowDismissAffordance); mDismissViewController.showDismissTarget(); } @@ -662,17 +602,11 @@ public class PipTouchHandler { mTmpBounds.offsetTo((int) left, (int) top); mMotionHelper.movePip(mTmpBounds, true /* isDragging */); - if (mEnableDimissDragToEdge) { + if (mEnableDismissDragToEdge) { updateDismissFraction(); } final PointF curPos = touchState.getLastTouchPosition(); - if (mMovementWithinMinimize) { - // Track if movement remains near starting edge to identify swipes to minimize - mMovementWithinMinimize = mStartedOnLeft - ? curPos.x <= mMovementBounds.left + mTmpBounds.width() - : curPos.x >= mMovementBounds.right; - } if (mMovementWithinDismiss) { // Track if movement remains near the bottom edge to identify swipe to dismiss mMovementWithinDismiss = curPos.y >= mMovementBounds.bottom; @@ -684,7 +618,7 @@ public class PipTouchHandler { @Override public boolean onUp(PipTouchState touchState) { - if (mEnableDimissDragToEdge) { + if (mEnableDismissDragToEdge) { // Clean up the dismiss target regardless of the touch state in case the touch // enabled state changes while the user is interacting cleanUpDismissTarget(); @@ -704,7 +638,7 @@ public class PipTouchHandler { vel.y, isFling); final boolean isFlingToBot = isFling && vel.y > 0 && !isHorizontal && (mMovementWithinDismiss || isUpWithinDimiss); - if (mEnableDimissDragToEdge) { + if (mEnableDismissDragToEdge) { // Check if the user dragged or flung the PiP offscreen to dismiss it if (mMotionHelper.shouldDismissPip() || isFlingToBot) { MetricsLoggerWrapper.logPictureInPictureDismissByDrag(mContext, @@ -717,33 +651,10 @@ public class PipTouchHandler { } if (touchState.isDragging()) { - final boolean isFlingToEdge = isFling && isHorizontal && mMovementWithinMinimize - && (mStartedOnLeft ? vel.x < 0 : vel.x > 0); - if (ENABLE_MINIMIZE && - !mIsMinimized && (mMotionHelper.shouldMinimizePip() || isFlingToEdge)) { - // Pip should be minimized - setMinimizedStateInternal(true); - if (mMenuState == MENU_STATE_FULL) { - // If the user dragged the expanded PiP to the edge, then hiding the menu - // will trigger the PiP to be scaled back to the normal size with the - // minimize offset adjusted - mMenuController.hideMenu(); - } else { - mMotionHelper.animateToClosestMinimizedState( - PipTouchHandler.this::updateDismissFraction /* updateAction */); - } - return true; - } - if (mIsMinimized) { - // If we're dragging and it wasn't a minimize gesture then we shouldn't be - // minimized. - setMinimizedStateInternal(false); - } - Runnable endAction = null; if (mMenuState != MENU_STATE_NONE) { - // If the menu is still visible, and we aren't minimized, then just poke the - // menu so that it will timeout after the user stops touching it + // If the menu is still visible, then just poke the menu so that + // it will timeout after the user stops touching it mMenuController.showMenu(mMenuState, mMotionHelper.getBounds(), mMovementBounds, true /* allowMenuTimeout */, willResizeMenu()); } else { @@ -759,10 +670,6 @@ public class PipTouchHandler { } else { mMotionHelper.animateToClosestSnapTarget(); } - } else if (mIsMinimized) { - // This was a tap, so no longer minimized - mMotionHelper.animateToClosestSnapTarget(); - setMinimizedStateInternal(false); } else if (mTouchState.isDoubleTap()) { // Expand to fullscreen if this is a double tap mMotionHelper.expandPip(); @@ -821,14 +728,12 @@ public class PipTouchHandler { pw.println(innerPrefix + "mExpandedBounds=" + mExpandedBounds); pw.println(innerPrefix + "mExpandedMovementBounds=" + mExpandedMovementBounds); pw.println(innerPrefix + "mMenuState=" + mMenuState); - pw.println(innerPrefix + "mIsMinimized=" + mIsMinimized); pw.println(innerPrefix + "mIsImeShowing=" + mIsImeShowing); pw.println(innerPrefix + "mImeHeight=" + mImeHeight); pw.println(innerPrefix + "mIsShelfShowing=" + mIsShelfShowing); pw.println(innerPrefix + "mShelfHeight=" + mShelfHeight); pw.println(innerPrefix + "mSavedSnapFraction=" + mSavedSnapFraction); - pw.println(innerPrefix + "mEnableDragToEdgeDismiss=" + mEnableDimissDragToEdge); - pw.println(innerPrefix + "mEnableMinimize=" + ENABLE_MINIMIZE); + pw.println(innerPrefix + "mEnableDragToEdgeDismiss=" + mEnableDismissDragToEdge); mSnapAlgorithm.dump(pw, innerPrefix); mTouchState.dump(pw, innerPrefix); mMotionHelper.dump(pw, innerPrefix); diff --git a/packages/SystemUI/src/com/android/systemui/pip/tv/PipManager.java b/packages/SystemUI/src/com/android/systemui/pip/tv/PipManager.java index 487c2533b0bb..cb1a218af954 100644 --- a/packages/SystemUI/src/com/android/systemui/pip/tv/PipManager.java +++ b/packages/SystemUI/src/com/android/systemui/pip/tv/PipManager.java @@ -50,7 +50,9 @@ import com.android.systemui.R; import com.android.systemui.UiOffloadThread; import com.android.systemui.broadcast.BroadcastDispatcher; import com.android.systemui.pip.BasePipManager; +import com.android.systemui.pip.PipAnimationController; import com.android.systemui.pip.PipBoundsHandler; +import com.android.systemui.pip.PipTaskOrganizer; import com.android.systemui.shared.system.ActivityManagerWrapper; import com.android.systemui.shared.system.PinnedStackListenerForwarder.PinnedStackListener; import com.android.systemui.shared.system.TaskStackChangeListener; @@ -66,7 +68,7 @@ import javax.inject.Singleton; * Manages the picture-in-picture (PIP) UI and states. */ @Singleton -public class PipManager implements BasePipManager { +public class PipManager implements BasePipManager, PipTaskOrganizer.PipTransitionCallback { private static final String TAG = "PipManager"; static final boolean DEBUG = Log.isLoggable(TAG, Log.DEBUG); @@ -91,7 +93,6 @@ public class PipManager implements BasePipManager { private static final int INVALID_RESOURCE_TYPE = -1; public static final int SUSPEND_PIP_RESIZE_REASON_WAITING_FOR_MENU_ACTIVITY_FINISH = 0x1; - public static final int SUSPEND_PIP_RESIZE_REASON_WAITING_FOR_OVERLAY_ACTIVITY_FINISH = 0x2; /** * PIPed activity is playing a media and it can be paused. @@ -112,6 +113,7 @@ public class PipManager implements BasePipManager { private Context mContext; private PipBoundsHandler mPipBoundsHandler; + private PipTaskOrganizer mPipTaskOrganizer; private IActivityTaskManager mActivityTaskManager; private MediaSessionManager mMediaSessionManager; private int mState = STATE_NO_PIP; @@ -205,8 +207,7 @@ public class PipManager implements BasePipManager { } @Override - public void onMovementBoundsChanged(Rect animatingBounds, boolean fromImeAdjustment, - boolean fromShelfAdjustment) { + public void onMovementBoundsChanged(Rect animatingBounds, boolean fromImeAdjustment) { mHandler.post(() -> { // Populate the inset / normal bounds and DisplayInfo from mPipBoundsHandler first. mPipBoundsHandler.onMovementBoundsChanged(mTmpInsetBounds, mTmpNormalBounds, @@ -234,6 +235,8 @@ public class PipManager implements BasePipManager { mInitialized = true; mContext = context; mPipBoundsHandler = new PipBoundsHandler(context); + mPipTaskOrganizer = new PipTaskOrganizer(mContext, mPipBoundsHandler); + mPipTaskOrganizer.registerPipTransitionCallback(this); mActivityTaskManager = ActivityTaskManager.getService(); ActivityManagerWrapper.getInstance().registerTaskStackListener(mTaskStackListener); IntentFilter intentFilter = new IntentFilter(); @@ -279,9 +282,12 @@ public class PipManager implements BasePipManager { mMediaSessionManager = (MediaSessionManager) mContext.getSystemService(Context.MEDIA_SESSION_SERVICE); + mPipTaskOrganizer.registerPipTransitionCallback(this); try { WindowManagerWrapper.getInstance().addPinnedStackListener(mPinnedStackListener); + ActivityTaskManager.getTaskOrganizerController().registerTaskOrganizer( + mPipTaskOrganizer, WINDOWING_MODE_PINNED); } catch (RemoteException e) { Log.e(TAG, "Failed to register pinned stack listener", e); } @@ -422,20 +428,13 @@ public class PipManager implements BasePipManager { case STATE_PIP_MENU: mCurrentPipBounds = mMenuModePipBounds; break; - case STATE_PIP: - mCurrentPipBounds = mPipBounds; - break; + case STATE_PIP: // fallthrough default: mCurrentPipBounds = mPipBounds; break; } - try { - int animationDurationMs = -1; - mActivityTaskManager.animateResizePinnedStack(mPinnedStackId, mCurrentPipBounds, - animationDurationMs); - } catch (RemoteException e) { - Log.e(TAG, "resizeStack failed", e); - } + mPipTaskOrganizer.resizePinnedStack( + mCurrentPipBounds, PipAnimationController.DURATION_DEFAULT_MS); } /** @@ -449,13 +448,6 @@ public class PipManager implements BasePipManager { } /** - * Returns the default PIP bound. - */ - public Rect getPipBounds() { - return mPipBounds; - } - - /** * Shows PIP menu UI by launching {@link PipMenuActivity}. It also locates the pinned * stack to the centered PIP bound {@link R.config_centeredPictureInPictureBounds}. */ @@ -692,18 +684,27 @@ public class PipManager implements BasePipManager { // If PIPed activity is launched again by Launcher or intent, make it fullscreen. movePipToFullscreen(); } + }; - @Override - public void onPinnedStackAnimationEnded() { - if (DEBUG) Log.d(TAG, "onPinnedStackAnimationEnded()"); + @Override + public void onPipTransitionStarted() { } - switch (getState()) { - case STATE_PIP_MENU: - showPipMenu(); - break; - } + @Override + public void onPipTransitionFinished() { + onPipTransitionFinishedOrCanceled(); + } + + @Override + public void onPipTransitionCanceled() { + onPipTransitionFinishedOrCanceled(); + } + + private void onPipTransitionFinishedOrCanceled() { + if (DEBUG) Log.d(TAG, "onPipTransitionFinishedOrCanceled()"); + if (getState() == STATE_PIP_MENU) { + showPipMenu(); } - }; + } /** * A listener interface to receive notification on changes in PIP. diff --git a/packages/SystemUI/src/com/android/systemui/qs/customize/QSCustomizer.java b/packages/SystemUI/src/com/android/systemui/qs/customize/QSCustomizer.java index fb89ed264628..bfac85bd4c10 100644 --- a/packages/SystemUI/src/com/android/systemui/qs/customize/QSCustomizer.java +++ b/packages/SystemUI/src/com/android/systemui/qs/customize/QSCustomizer.java @@ -37,8 +37,8 @@ import androidx.recyclerview.widget.DefaultItemAnimator; import androidx.recyclerview.widget.GridLayoutManager; import androidx.recyclerview.widget.RecyclerView; -import com.android.internal.logging.MetricsLogger; -import com.android.internal.logging.nano.MetricsProto; +import com.android.internal.logging.UiEventLogger; +import com.android.internal.logging.UiEventLoggerImpl; import com.android.systemui.R; import com.android.systemui.keyguard.ScreenLifecycle; import com.android.systemui.plugins.qs.QS; @@ -86,6 +86,7 @@ public class QSCustomizer extends LinearLayout implements OnMenuItemClickListene private int mY; private boolean mOpening; private boolean mIsShowingNavBackdrop; + private UiEventLogger mUiEventLogger = new UiEventLoggerImpl(); @Inject public QSCustomizer(Context context, AttributeSet attrs, @@ -187,7 +188,7 @@ public class QSCustomizer extends LinearLayout implements OnMenuItemClickListene int containerLocation[] = findViewById(R.id.customize_container).getLocationOnScreen(); mX = x - containerLocation[0]; mY = y - containerLocation[1]; - MetricsLogger.visible(getContext(), MetricsProto.MetricsEvent.QS_EDIT); + mUiEventLogger.log(QSEditEvent.QS_EDIT_OPEN); isShown = true; mOpening = true; setTileSpecs(); @@ -224,7 +225,7 @@ public class QSCustomizer extends LinearLayout implements OnMenuItemClickListene public void hide() { final boolean animate = mScreenLifecycle.getScreenState() != ScreenLifecycle.SCREEN_OFF; if (isShown) { - MetricsLogger.hidden(getContext(), MetricsProto.MetricsEvent.QS_EDIT); + mUiEventLogger.log(QSEditEvent.QS_EDIT_CLOSED); isShown = false; mToolbar.dismissPopupMenus(); setCustomizing(false); @@ -258,7 +259,7 @@ public class QSCustomizer extends LinearLayout implements OnMenuItemClickListene public boolean onMenuItemClick(MenuItem item) { switch (item.getItemId()) { case MENU_RESET: - MetricsLogger.action(getContext(), MetricsProto.MetricsEvent.ACTION_QS_EDIT_RESET); + mUiEventLogger.log(QSEditEvent.QS_EDIT_RESET); reset(); break; } diff --git a/packages/SystemUI/src/com/android/systemui/qs/customize/QSEditEvent.kt b/packages/SystemUI/src/com/android/systemui/qs/customize/QSEditEvent.kt new file mode 100644 index 000000000000..ff8ddec8397a --- /dev/null +++ b/packages/SystemUI/src/com/android/systemui/qs/customize/QSEditEvent.kt @@ -0,0 +1,38 @@ +/* + * Copyright (C) 2020 The Android Open Source Project + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +package com.android.systemui.qs.customize + +import com.android.internal.logging.UiEvent +import com.android.internal.logging.UiEventLogger + +enum class QSEditEvent(private val _id: Int) : UiEventLogger.UiEventEnum { + + @UiEvent(doc = "Tile removed from current tiles") + QS_EDIT_REMOVE(210), + @UiEvent(doc = "Tile added to current tiles") + QS_EDIT_ADD(211), + @UiEvent(doc = "Tile moved") + QS_EDIT_MOVE(212), + @UiEvent(doc = "QS customizer open") + QS_EDIT_OPEN(213), + @UiEvent(doc = "QS customizer closed") + QS_EDIT_CLOSED(214), + @UiEvent(doc = "QS tiles reset") + QS_EDIT_RESET(215); + + override fun getId() = _id +}
\ No newline at end of file diff --git a/packages/SystemUI/src/com/android/systemui/qs/customize/TileAdapter.java b/packages/SystemUI/src/com/android/systemui/qs/customize/TileAdapter.java index 3afc46045a77..58de95d7ed6d 100644 --- a/packages/SystemUI/src/com/android/systemui/qs/customize/TileAdapter.java +++ b/packages/SystemUI/src/com/android/systemui/qs/customize/TileAdapter.java @@ -40,8 +40,8 @@ import androidx.recyclerview.widget.RecyclerView.ItemDecoration; import androidx.recyclerview.widget.RecyclerView.State; import androidx.recyclerview.widget.RecyclerView.ViewHolder; -import com.android.internal.logging.MetricsLogger; -import com.android.internal.logging.nano.MetricsProto; +import com.android.internal.logging.UiEventLogger; +import com.android.internal.logging.UiEventLoggerImpl; import com.android.systemui.R; import com.android.systemui.qs.QSTileHost; import com.android.systemui.qs.customize.TileAdapter.Holder; @@ -92,6 +92,7 @@ public class TileAdapter extends RecyclerView.Adapter<Holder> implements TileSta private int mAccessibilityFromIndex; private CharSequence mAccessibilityFromLabel; private QSTileHost mHost; + private UiEventLogger mUiEventLogger = new UiEventLoggerImpl(); public TileAdapter(Context context) { mContext = context; @@ -436,20 +437,11 @@ public class TileAdapter extends RecyclerView.Adapter<Holder> implements TileSta move(from, to, mTiles); updateDividerLocations(); if (to >= mEditIndex) { - MetricsLogger.action(mContext, MetricsProto.MetricsEvent.ACTION_QS_EDIT_REMOVE_SPEC, - strip(mTiles.get(to))); - MetricsLogger.action(mContext, MetricsProto.MetricsEvent.ACTION_QS_EDIT_REMOVE, - from); + mUiEventLogger.log(QSEditEvent.QS_EDIT_REMOVE, 0, strip(mTiles.get(to))); } else if (from >= mEditIndex) { - MetricsLogger.action(mContext, MetricsProto.MetricsEvent.ACTION_QS_EDIT_ADD_SPEC, - strip(mTiles.get(to))); - MetricsLogger.action(mContext, MetricsProto.MetricsEvent.ACTION_QS_EDIT_ADD, - to); + mUiEventLogger.log(QSEditEvent.QS_EDIT_ADD, 0, strip(mTiles.get(to))); } else { - MetricsLogger.action(mContext, MetricsProto.MetricsEvent.ACTION_QS_EDIT_MOVE_SPEC, - strip(mTiles.get(to))); - MetricsLogger.action(mContext, MetricsProto.MetricsEvent.ACTION_QS_EDIT_MOVE, - to); + mUiEventLogger.log(QSEditEvent.QS_EDIT_MOVE, 0, strip(mTiles.get(to))); } saveSpecs(mHost); return true; diff --git a/packages/SystemUI/src/com/android/systemui/recents/OverviewProxyService.java b/packages/SystemUI/src/com/android/systemui/recents/OverviewProxyService.java index 34cad51e1c9f..1cd63881a700 100644 --- a/packages/SystemUI/src/com/android/systemui/recents/OverviewProxyService.java +++ b/packages/SystemUI/src/com/android/systemui/recents/OverviewProxyService.java @@ -61,9 +61,11 @@ import com.android.internal.policy.ScreenDecorationsUtils; import com.android.internal.util.ScreenshotHelper; import com.android.systemui.Dumpable; import com.android.systemui.model.SysUiState; +import com.android.systemui.pip.PipAnimationController; import com.android.systemui.pip.PipUI; import com.android.systemui.recents.OverviewProxyService.OverviewProxyListener; import com.android.systemui.shared.recents.IOverviewProxy; +import com.android.systemui.shared.recents.IPinnedStackAnimationListener; import com.android.systemui.shared.recents.ISystemUiProxy; import com.android.systemui.shared.system.ActivityManagerWrapper; import com.android.systemui.shared.system.QuickStepContract; @@ -388,6 +390,32 @@ public class OverviewProxyService implements CallbackController<OverviewProxyLis } } + @Override + public void notifySwipeToHomeFinished() { + if (!verifyCaller("notifySwipeToHomeFinished")) { + return; + } + long token = Binder.clearCallingIdentity(); + try { + mPipUI.setPinnedStackAnimationType(PipAnimationController.ANIM_TYPE_ALPHA); + } finally { + Binder.restoreCallingIdentity(token); + } + } + + @Override + public void setPinnedStackAnimationListener(IPinnedStackAnimationListener listener) { + if (!verifyCaller("setPinnedStackAnimationListener")) { + return; + } + long token = Binder.clearCallingIdentity(); + try { + mPipUI.setPinnedStackAnimationListener(listener); + } finally { + Binder.restoreCallingIdentity(token); + } + } + private boolean verifyCaller(String reason) { final int callerId = Binder.getCallingUserHandle().getIdentifier(); if (callerId != mCurrentBoundedUserId) { diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/notification/row/ExpandableNotificationRow.java b/packages/SystemUI/src/com/android/systemui/statusbar/notification/row/ExpandableNotificationRow.java index 500813318484..97755fcb8ea9 100644 --- a/packages/SystemUI/src/com/android/systemui/statusbar/notification/row/ExpandableNotificationRow.java +++ b/packages/SystemUI/src/com/android/systemui/statusbar/notification/row/ExpandableNotificationRow.java @@ -28,6 +28,7 @@ import android.animation.ObjectAnimator; import android.animation.ValueAnimator.AnimatorUpdateListener; import android.annotation.NonNull; import android.annotation.Nullable; +import android.app.Notification; import android.app.NotificationChannel; import android.content.Context; import android.content.pm.PackageInfo; @@ -148,6 +149,7 @@ public class ExpandableNotificationRow extends ActivatableNotificationView private int mNotificationMinHeight; private int mNotificationMinHeightLarge; private int mNotificationMinHeightMedia; + private int mNotificationMinHeightMessaging; private int mNotificationMaxHeight; private int mIncreasedPaddingBetweenElements; private int mNotificationLaunchHeight; @@ -630,10 +632,16 @@ public class ExpandableNotificationRow extends ActivatableNotificationView && expandedView.findViewById(com.android.internal.R.id.media_actions) != null; boolean showCompactMediaSeekbar = mMediaManager.getShowCompactMediaSeekbar(); + Class<? extends Notification.Style> style = + mEntry.getSbn().getNotification().getNotificationStyle(); + boolean isMessagingLayout = Notification.MessagingStyle.class.equals(style); + if (customView && beforeP && !mIsSummaryWithChildren) { minHeight = beforeN ? mNotificationMinHeightBeforeN : mNotificationMinHeightBeforeP; } else if (isMediaLayout && showCompactMediaSeekbar) { minHeight = mNotificationMinHeightMedia; + } else if (isMessagingLayout) { + minHeight = mNotificationMinHeightMessaging; } else if (mUseIncreasedCollapsedHeight && layout == mPrivateLayout) { minHeight = mNotificationMinHeightLarge; } else { @@ -1635,6 +1643,8 @@ public class ExpandableNotificationRow extends ActivatableNotificationView R.dimen.notification_min_height_increased); mNotificationMinHeightMedia = NotificationUtils.getFontScaledHeight(mContext, R.dimen.notification_min_height_media); + mNotificationMinHeightMessaging = NotificationUtils.getFontScaledHeight(mContext, + R.dimen.notification_min_height_messaging); mNotificationMaxHeight = NotificationUtils.getFontScaledHeight(mContext, R.dimen.notification_max_height); mMaxHeadsUpHeightBeforeN = NotificationUtils.getFontScaledHeight(mContext, diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/policy/HotspotControllerImpl.java b/packages/SystemUI/src/com/android/systemui/statusbar/policy/HotspotControllerImpl.java index d0904049d85a..7d532a88caac 100644 --- a/packages/SystemUI/src/com/android/systemui/statusbar/policy/HotspotControllerImpl.java +++ b/packages/SystemUI/src/com/android/systemui/statusbar/policy/HotspotControllerImpl.java @@ -56,8 +56,9 @@ public class HotspotControllerImpl implements HotspotController, WifiManager.Sof private int mHotspotState; private volatile int mNumConnectedDevices; - private volatile boolean mIsTetheringSupported; - private volatile boolean mHasTetherableWifiRegexs; + // Assume tethering is available until told otherwise + private volatile boolean mIsTetheringSupported = true; + private volatile boolean mHasTetherableWifiRegexs = true; private boolean mWaitingForTerminalState; private TetheringManager.TetheringEventCallback mTetheringCallback = @@ -97,6 +98,15 @@ public class HotspotControllerImpl implements HotspotController, WifiManager.Sof new HandlerExecutor(backgroundHandler), mTetheringCallback); } + /** + * Whether hotspot is currently supported. + * + * This will return {@code true} immediately on creation of the controller, but may be updated + * later. Callbacks from this controllers will notify if the state changes. + * + * @return {@code true} if hotspot is supported (or we haven't been told it's not) + * @see #addCallback + */ @Override public boolean isHotspotSupported() { return mIsTetheringSupported && mHasTetherableWifiRegexs diff --git a/packages/SystemUI/tests/src/com/android/systemui/pip/PipAnimationControllerTest.java b/packages/SystemUI/tests/src/com/android/systemui/pip/PipAnimationControllerTest.java new file mode 100644 index 000000000000..6c09a46833a2 --- /dev/null +++ b/packages/SystemUI/tests/src/com/android/systemui/pip/PipAnimationControllerTest.java @@ -0,0 +1,181 @@ +/* + * Copyright (C) 2020 The Android Open Source Project + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +package com.android.systemui.pip; + +import static org.junit.Assert.assertEquals; +import static org.junit.Assert.assertFalse; +import static org.junit.Assert.assertTrue; +import static org.mockito.ArgumentMatchers.any; +import static org.mockito.ArgumentMatchers.eq; +import static org.mockito.Mockito.verify; + +import android.graphics.Rect; +import android.testing.AndroidTestingRunner; +import android.testing.TestableLooper; +import android.view.IWindowContainer; +import android.view.SurfaceControl; + +import androidx.test.filters.SmallTest; + +import com.android.systemui.SysuiTestCase; + +import org.junit.Before; +import org.junit.Test; +import org.junit.runner.RunWith; +import org.mockito.Mock; +import org.mockito.MockitoAnnotations; + +/** + * Unit tests against {@link PipAnimationController} to ensure that it sends the right callbacks + * depending on the various interactions. + */ +@RunWith(AndroidTestingRunner.class) +@SmallTest +@TestableLooper.RunWithLooper(setAsMainLooper = true) +public class PipAnimationControllerTest extends SysuiTestCase { + + private PipAnimationController mPipAnimationController; + + @Mock + private IWindowContainer mWindowContainer; + + @Mock + private PipAnimationController.PipAnimationCallback mPipAnimationCallback; + + @Before + public void setUp() throws Exception { + mPipAnimationController = new PipAnimationController(mContext); + MockitoAnnotations.initMocks(this); + } + + @Test + public void getAnimator_withAlpha_returnFloatAnimator() { + final PipAnimationController.PipTransitionAnimator animator = mPipAnimationController + .getAnimator(mWindowContainer, true /* scheduleFinishPip */, + new Rect(), 0f, 1f); + + assertEquals("Expect ANIM_TYPE_ALPHA animation", + animator.getAnimationType(), PipAnimationController.ANIM_TYPE_ALPHA); + } + + @Test + public void getAnimator_withBounds_returnBoundsAnimator() { + final PipAnimationController.PipTransitionAnimator animator = mPipAnimationController + .getAnimator(mWindowContainer, true /* scheduleFinishPip */, + new Rect(), new Rect()); + + assertEquals("Expect ANIM_TYPE_BOUNDS animation", + animator.getAnimationType(), PipAnimationController.ANIM_TYPE_BOUNDS); + } + + @Test + public void getAnimator_whenSameTypeRunning_updateExistingAnimator() { + final Rect startValue = new Rect(0, 0, 100, 100); + final Rect endValue1 = new Rect(100, 100, 200, 200); + final Rect endValue2 = new Rect(200, 200, 300, 300); + final PipAnimationController.PipTransitionAnimator oldAnimator = mPipAnimationController + .getAnimator(mWindowContainer, true /* scheduleFinishPip */, + startValue, endValue1); + oldAnimator.setSurfaceControlTransactionFactory(DummySurfaceControlTx::new); + oldAnimator.start(); + + final PipAnimationController.PipTransitionAnimator newAnimator = mPipAnimationController + .getAnimator(mWindowContainer, true /* scheduleFinishPip */, + startValue, endValue2); + + assertEquals("getAnimator with same type returns same animator", + oldAnimator, newAnimator); + assertEquals("getAnimator with same type updates end value", + endValue2, newAnimator.getEndValue()); + } + + @Test + public void getAnimator_scheduleFinishPip() { + PipAnimationController.PipTransitionAnimator animator = mPipAnimationController + .getAnimator(mWindowContainer, true /* scheduleFinishPip */, + new Rect(), 0f, 1f); + assertTrue("scheduleFinishPip is true", animator.shouldScheduleFinishPip()); + + animator = mPipAnimationController + .getAnimator(mWindowContainer, false /* scheduleFinishPip */, + new Rect(), 0f, 1f); + assertFalse("scheduleFinishPip is false", animator.shouldScheduleFinishPip()); + } + + @Test + public void pipTransitionAnimator_updateEndValue() { + final Rect startValue = new Rect(0, 0, 100, 100); + final Rect endValue1 = new Rect(100, 100, 200, 200); + final Rect endValue2 = new Rect(200, 200, 300, 300); + final PipAnimationController.PipTransitionAnimator animator = mPipAnimationController + .getAnimator(mWindowContainer, true /* scheduleFinishPip */, + startValue, endValue1); + + animator.updateEndValue(endValue2); + + assertEquals("updateEndValue updates end value", animator.getEndValue(), endValue2); + } + + @Test + public void pipTransitionAnimator_setPipAnimationCallback() { + final Rect startValue = new Rect(0, 0, 100, 100); + final Rect endValue = new Rect(100, 100, 200, 200); + final PipAnimationController.PipTransitionAnimator animator = mPipAnimationController + .getAnimator(mWindowContainer, true /* scheduleFinishPip */, + startValue, endValue); + animator.setSurfaceControlTransactionFactory(DummySurfaceControlTx::new); + + animator.setPipAnimationCallback(mPipAnimationCallback); + + // onAnimationStart triggers onPipAnimationStart + animator.onAnimationStart(animator); + verify(mPipAnimationCallback).onPipAnimationStart(mWindowContainer, animator); + + // onAnimationCancel triggers onPipAnimationCancel + animator.onAnimationCancel(animator); + verify(mPipAnimationCallback).onPipAnimationCancel(mWindowContainer, animator); + + // onAnimationEnd triggers onPipAnimationEnd + animator.onAnimationEnd(animator); + verify(mPipAnimationCallback).onPipAnimationEnd(eq(mWindowContainer), + any(SurfaceControl.Transaction.class), eq(animator)); + } + + /** + * A dummy {@link SurfaceControl.Transaction} class. + * This is created as {@link Mock} does not support method chaining. + */ + private static class DummySurfaceControlTx extends SurfaceControl.Transaction { + @Override + public SurfaceControl.Transaction setAlpha(SurfaceControl leash, float alpha) { + return this; + } + + @Override + public SurfaceControl.Transaction setPosition(SurfaceControl leash, float x, float y) { + return this; + } + + @Override + public SurfaceControl.Transaction setWindowCrop(SurfaceControl leash, int w, int h) { + return this; + } + + @Override + public void apply() {} + } +} diff --git a/packages/SystemUI/tests/src/com/android/systemui/pip/PipBoundsHandlerTest.java b/packages/SystemUI/tests/src/com/android/systemui/pip/PipBoundsHandlerTest.java index bc3ce8baddee..1dbcf10d08d6 100644 --- a/packages/SystemUI/tests/src/com/android/systemui/pip/PipBoundsHandlerTest.java +++ b/packages/SystemUI/tests/src/com/android/systemui/pip/PipBoundsHandlerTest.java @@ -16,13 +16,7 @@ package com.android.systemui.pip; -import static org.junit.Assert.assertFalse; import static org.junit.Assert.assertTrue; -import static org.mockito.ArgumentMatchers.anyInt; -import static org.mockito.ArgumentMatchers.eq; -import static org.mockito.ArgumentMatchers.isNull; -import static org.mockito.Mockito.reset; -import static org.mockito.Mockito.verify; import android.content.ComponentName; import android.graphics.Rect; @@ -31,7 +25,6 @@ import android.testing.TestableLooper; import android.testing.TestableResources; import android.view.DisplayInfo; import android.view.Gravity; -import android.view.IPinnedStackController; import androidx.test.filters.SmallTest; @@ -40,9 +33,6 @@ import com.android.systemui.SysuiTestCase; import org.junit.Before; import org.junit.Test; import org.junit.runner.RunWith; -import org.mockito.ArgumentCaptor; -import org.mockito.Mock; -import org.mockito.MockitoAnnotations; /** * Unit tests against {@link PipBoundsHandler}, including but not limited to: @@ -55,22 +45,18 @@ import org.mockito.MockitoAnnotations; @TestableLooper.RunWithLooper(setAsMainLooper = true) public class PipBoundsHandlerTest extends SysuiTestCase { private static final int ROUNDING_ERROR_MARGIN = 10; + private static final float DEFAULT_ASPECT_RATIO = 1f; + private static final Rect EMPTY_CURRENT_BOUNDS = null; private PipBoundsHandler mPipBoundsHandler; private DisplayInfo mDefaultDisplayInfo; - private Rect mDefaultDisplayRect; - - @Mock - private IPinnedStackController mPinnedStackController; @Before public void setUp() throws Exception { mPipBoundsHandler = new PipBoundsHandler(mContext); - MockitoAnnotations.initMocks(this); initializeMockResources(); mPipBoundsHandler.onDisplayInfoChanged(mDefaultDisplayInfo); - mPipBoundsHandler.setPinnedStackController(mPinnedStackController); } private void initializeMockResources() { @@ -94,142 +80,80 @@ public class PipBoundsHandlerTest extends SysuiTestCase { mDefaultDisplayInfo.displayId = 1; mDefaultDisplayInfo.logicalWidth = 1000; mDefaultDisplayInfo.logicalHeight = 1500; - mDefaultDisplayRect = new Rect(0, 0, - mDefaultDisplayInfo.logicalWidth, mDefaultDisplayInfo.logicalHeight); } @Test - public void setShelfHeight_offsetBounds() throws Exception { - final ArgumentCaptor<Rect> destinationBounds = ArgumentCaptor.forClass(Rect.class); + public void setShelfHeight_offsetBounds() { final int shelfHeight = 100; - - mPipBoundsHandler.onPrepareAnimation(null, 1f, null); - - verify(mPinnedStackController).startAnimation( - destinationBounds.capture(), isNull(), anyInt()); - final Rect lastPosition = destinationBounds.getValue(); - // Reset the pinned stack controller since we will do another verify later on - reset(mPinnedStackController); + final Rect oldPosition = mPipBoundsHandler.getDestinationBounds( + DEFAULT_ASPECT_RATIO, EMPTY_CURRENT_BOUNDS); mPipBoundsHandler.setShelfHeight(true, shelfHeight); - mPipBoundsHandler.onPrepareAnimation(null, 1f, null); + final Rect newPosition = mPipBoundsHandler.getDestinationBounds( + DEFAULT_ASPECT_RATIO, EMPTY_CURRENT_BOUNDS); - verify(mPinnedStackController).startAnimation( - destinationBounds.capture(), isNull(), anyInt()); - lastPosition.offset(0, -shelfHeight); - assertBoundsWithMargin("PiP bounds offset by shelf height", - lastPosition, destinationBounds.getValue()); + oldPosition.offset(0, -shelfHeight); + assertBoundsWithMargin("PiP bounds offset by shelf height", oldPosition, newPosition); } @Test - public void onImeVisibilityChanged_offsetBounds() throws Exception { - final ArgumentCaptor<Rect> destinationBounds = ArgumentCaptor.forClass(Rect.class); + public void onImeVisibilityChanged_offsetBounds() { final int imeHeight = 100; - - mPipBoundsHandler.onPrepareAnimation(null, 1f, null); - - verify(mPinnedStackController).startAnimation( - destinationBounds.capture(), isNull(), anyInt()); - final Rect lastPosition = destinationBounds.getValue(); - // Reset the pinned stack controller since we will do another verify later on - reset(mPinnedStackController); + final Rect oldPosition = mPipBoundsHandler.getDestinationBounds( + DEFAULT_ASPECT_RATIO, EMPTY_CURRENT_BOUNDS); mPipBoundsHandler.onImeVisibilityChanged(true, imeHeight); - mPipBoundsHandler.onPrepareAnimation(null, 1f, null); + final Rect newPosition = mPipBoundsHandler.getDestinationBounds( + DEFAULT_ASPECT_RATIO, EMPTY_CURRENT_BOUNDS); - verify(mPinnedStackController).startAnimation( - destinationBounds.capture(), isNull(), anyInt()); - lastPosition.offset(0, -imeHeight); - assertBoundsWithMargin("PiP bounds offset by IME height", - lastPosition, destinationBounds.getValue()); + oldPosition.offset(0, -imeHeight); + assertBoundsWithMargin("PiP bounds offset by IME height", oldPosition, newPosition); } @Test - public void onPrepareAnimation_startAnimation() throws Exception { - final Rect sourceRectHint = new Rect(100, 100, 200, 200); - final ArgumentCaptor<Rect> destinationBounds = ArgumentCaptor.forClass(Rect.class); - - mPipBoundsHandler.onPrepareAnimation(sourceRectHint, 1f, null); - - verify(mPinnedStackController).startAnimation( - destinationBounds.capture(), eq(sourceRectHint), anyInt()); - final Rect capturedDestinationBounds = destinationBounds.getValue(); - assertFalse("Destination bounds is not empty", - capturedDestinationBounds.isEmpty()); - assertBoundsWithMargin("Destination bounds within Display", - mDefaultDisplayRect, capturedDestinationBounds); - } - - @Test - public void onSaveReentryBounds_restoreLastPosition() throws Exception { + public void onSaveReentryBounds_restoreLastPosition() { final ComponentName componentName = new ComponentName(mContext, "component1"); - final ArgumentCaptor<Rect> destinationBounds = ArgumentCaptor.forClass(Rect.class); - - mPipBoundsHandler.onPrepareAnimation(null, 1f, null); + final Rect oldPosition = mPipBoundsHandler.getDestinationBounds( + DEFAULT_ASPECT_RATIO, EMPTY_CURRENT_BOUNDS); - verify(mPinnedStackController).startAnimation( - destinationBounds.capture(), isNull(), anyInt()); - final Rect lastPosition = destinationBounds.getValue(); - lastPosition.offset(0, -100); - mPipBoundsHandler.onSaveReentryBounds(componentName, lastPosition); - // Reset the pinned stack controller since we will do another verify later on - reset(mPinnedStackController); + oldPosition.offset(0, -100); + mPipBoundsHandler.onSaveReentryBounds(componentName, oldPosition); - mPipBoundsHandler.onPrepareAnimation(null, 1f, null); + final Rect newPosition = mPipBoundsHandler.getDestinationBounds( + DEFAULT_ASPECT_RATIO, EMPTY_CURRENT_BOUNDS); - verify(mPinnedStackController).startAnimation( - destinationBounds.capture(), isNull(), anyInt()); - assertBoundsWithMargin("Last position is restored", - lastPosition, destinationBounds.getValue()); + assertBoundsWithMargin("Last position is restored", oldPosition, newPosition); } @Test - public void onResetReentryBounds_componentMatch_useDefaultBounds() throws Exception { + public void onResetReentryBounds_componentMatch_useDefaultBounds() { final ComponentName componentName = new ComponentName(mContext, "component1"); - final ArgumentCaptor<Rect> destinationBounds = ArgumentCaptor.forClass(Rect.class); - - mPipBoundsHandler.onPrepareAnimation(null, 1f, null); - - verify(mPinnedStackController).startAnimation( - destinationBounds.capture(), isNull(), anyInt()); - final Rect defaultBounds = new Rect(destinationBounds.getValue()); + final Rect defaultBounds = mPipBoundsHandler.getDestinationBounds( + DEFAULT_ASPECT_RATIO, EMPTY_CURRENT_BOUNDS); final Rect newBounds = new Rect(defaultBounds); newBounds.offset(0, -100); mPipBoundsHandler.onSaveReentryBounds(componentName, newBounds); - // Reset the pinned stack controller since we will do another verify later on - reset(mPinnedStackController); mPipBoundsHandler.onResetReentryBounds(componentName); - mPipBoundsHandler.onPrepareAnimation(null, 1f, null); + final Rect actualBounds = mPipBoundsHandler.getDestinationBounds( + DEFAULT_ASPECT_RATIO, EMPTY_CURRENT_BOUNDS); - verify(mPinnedStackController).startAnimation( - destinationBounds.capture(), isNull(), anyInt()); - final Rect actualBounds = destinationBounds.getValue(); assertBoundsWithMargin("Use default bounds", defaultBounds, actualBounds); } @Test - public void onResetReentryBounds_componentMismatch_restoreLastPosition() throws Exception { + public void onResetReentryBounds_componentMismatch_restoreLastPosition() { final ComponentName componentName = new ComponentName(mContext, "component1"); - final ArgumentCaptor<Rect> destinationBounds = ArgumentCaptor.forClass(Rect.class); - - mPipBoundsHandler.onPrepareAnimation(null, 1f, null); - - verify(mPinnedStackController).startAnimation( - destinationBounds.capture(), isNull(), anyInt()); - final Rect defaultBounds = new Rect(destinationBounds.getValue()); + final Rect defaultBounds = mPipBoundsHandler.getDestinationBounds( + DEFAULT_ASPECT_RATIO, EMPTY_CURRENT_BOUNDS); final Rect newBounds = new Rect(defaultBounds); newBounds.offset(0, -100); mPipBoundsHandler.onSaveReentryBounds(componentName, newBounds); - // Reset the pinned stack controller since we will do another verify later on - reset(mPinnedStackController); mPipBoundsHandler.onResetReentryBounds(new ComponentName(mContext, "component2")); - mPipBoundsHandler.onPrepareAnimation(null, 1f, null); + final Rect actualBounds = mPipBoundsHandler.getDestinationBounds( + DEFAULT_ASPECT_RATIO, EMPTY_CURRENT_BOUNDS); - verify(mPinnedStackController).startAnimation( - destinationBounds.capture(), isNull(), anyInt()); - final Rect actualBounds = destinationBounds.getValue(); assertBoundsWithMargin("Last position is restored", newBounds, actualBounds); } diff --git a/packages/SystemUI/tests/src/com/android/systemui/statusbar/notification/collection/NotificationRankingManagerTest.kt b/packages/SystemUI/tests/src/com/android/systemui/statusbar/notification/collection/NotificationRankingManagerTest.kt index f11c42bda6cc..f37836c7a506 100644 --- a/packages/SystemUI/tests/src/com/android/systemui/statusbar/notification/collection/NotificationRankingManagerTest.kt +++ b/packages/SystemUI/tests/src/com/android/systemui/statusbar/notification/collection/NotificationRankingManagerTest.kt @@ -208,7 +208,7 @@ class NotificationRankingManagerTest : SysuiTestCase() { .setTag("tag") .setNotification(aN) .setChannel(NotificationChannel("test", "", IMPORTANCE_DEFAULT)) - .setUser(mContext.getUser()) + .setUser(mContext.user) .setOverrideGroupKey("") .build() whenever(personNotificationIdentifier.isImportantPeopleNotification(a.sbn, a.ranking)) @@ -226,12 +226,12 @@ class NotificationRankingManagerTest : SysuiTestCase() { .setTag("tag") .setNotification(bN) .setChannel(NotificationChannel("test", "", IMPORTANCE_DEFAULT)) - .setUser(mContext.getUser()) + .setUser(mContext.user) .setOverrideGroupKey("") .build() - whenever(personNotificationIdentifier.isImportantPeopleNotification(a.sbn, a.ranking)) - .thenReturn(false) - whenever(personNotificationIdentifier.isPeopleNotification(a.sbn, a.ranking)) + whenever(personNotificationIdentifier.isImportantPeopleNotification(b.sbn, b.ranking)) + .thenReturn(true) + whenever(personNotificationIdentifier.isPeopleNotification(b.sbn, b.ranking)) .thenReturn(true) assertEquals( diff --git a/packages/SystemUI/tests/src/com/android/systemui/statusbar/policy/HotspotControllerImplTest.java b/packages/SystemUI/tests/src/com/android/systemui/statusbar/policy/HotspotControllerImplTest.java index cd91f22bb753..57714722aea4 100644 --- a/packages/SystemUI/tests/src/com/android/systemui/statusbar/policy/HotspotControllerImplTest.java +++ b/packages/SystemUI/tests/src/com/android/systemui/statusbar/policy/HotspotControllerImplTest.java @@ -16,7 +16,6 @@ package com.android.systemui.statusbar.policy; -import static org.junit.Assert.assertFalse; import static org.junit.Assert.assertTrue; import static org.mockito.ArgumentMatchers.anyBoolean; import static org.mockito.ArgumentMatchers.anyInt; @@ -140,13 +139,16 @@ public class HotspotControllerImplTest extends SysuiTestCase { } @Test - public void testDefault_hotspotNotSupported() { - assertFalse(mController.isHotspotSupported()); + public void testHotspotSupported_default() { + assertTrue(mController.isHotspotSupported()); } @Test public void testHotspotSupported_rightConditions() { mTetheringCallbackCaptor.getValue().onTetheringSupported(true); + + assertTrue(mController.isHotspotSupported()); + mTetheringCallbackCaptor.getValue() .onTetherableInterfaceRegexpsChanged(mTetheringInterfaceRegexps); @@ -154,13 +156,21 @@ public class HotspotControllerImplTest extends SysuiTestCase { } @Test - public void testHotspotSupported_callbackCalledOnChange() { + public void testHotspotSupported_callbackCalledOnChange_tetheringSupported() { + mController.addCallback(mCallback1); + mTetheringCallbackCaptor.getValue().onTetheringSupported(false); + + verify(mCallback1).onHotspotAvailabilityChanged(false); + } + + @Test + public void testHotspotSupported_callbackCalledOnChange_tetherableInterfaces() { + when(mTetheringInterfaceRegexps.getTetherableWifiRegexs()) + .thenReturn(Collections.emptyList()); mController.addCallback(mCallback1); - mTetheringCallbackCaptor.getValue().onTetheringSupported(true); mTetheringCallbackCaptor.getValue() .onTetherableInterfaceRegexpsChanged(mTetheringInterfaceRegexps); - verify(mCallback1).onHotspotAvailabilityChanged(true); + verify(mCallback1).onHotspotAvailabilityChanged(false); } - } diff --git a/packages/SystemUI/tests/src/com/android/systemui/statusbar/policy/SecurityControllerTest.java b/packages/SystemUI/tests/src/com/android/systemui/statusbar/policy/SecurityControllerTest.java index 0c9130d08d6d..e6b04405a4ce 100644 --- a/packages/SystemUI/tests/src/com/android/systemui/statusbar/policy/SecurityControllerTest.java +++ b/packages/SystemUI/tests/src/com/android/systemui/statusbar/policy/SecurityControllerTest.java @@ -86,6 +86,7 @@ public class SecurityControllerTest extends SysuiTestCase implements SecurityCon mContext.addMockService(comp, mKeyChainService); when(mUserManager.getUserInfo(anyInt())).thenReturn(new UserInfo()); + when(mUserManager.isUserUnlocked(any())).thenReturn(true); when(mKeyChainService.getUserCaAliases()) .thenReturn(new StringParceledListSlice(new ArrayList<String>())); diff --git a/packages/Tethering/common/TetheringLib/Android.bp b/packages/Tethering/common/TetheringLib/Android.bp index 1a3d5b659ee3..5b73dd53a285 100644 --- a/packages/Tethering/common/TetheringLib/Android.bp +++ b/packages/Tethering/common/TetheringLib/Android.bp @@ -41,8 +41,7 @@ aidl_interface { java_library { name: "framework-tethering", - // TODO (b/146757305): change to module_app_current once available - sdk_version: "core_platform", + sdk_version: "module_current", srcs: [ "src/android/net/TetheredClient.java", "src/android/net/TetheringManager.java", @@ -56,7 +55,6 @@ java_library { libs: [ "framework-annotations-lib", - "android_system_stubs_current", ], hostdex: true, // for hiddenapi check diff --git a/proto/src/task_snapshot.proto b/proto/src/task_snapshot.proto index 789019ce8b75..2006fb3d7bf1 100644 --- a/proto/src/task_snapshot.proto +++ b/proto/src/task_snapshot.proto @@ -32,7 +32,12 @@ int32 system_ui_visibility = 8; bool is_translucent = 9; string top_activity_component = 10; - float scale = 11; + // deprecated because original width and height are stored now instead of the scale. + float legacy_scale = 11 [deprecated=true]; int64 id = 12; int32 rotation = 13; + // The task width when the snapshot was taken + int32 task_width = 14; + // The task height when the snapshot was taken + int32 task_height = 15; } diff --git a/services/autofill/java/com/android/server/autofill/RemoteInlineSuggestionRenderService.java b/services/autofill/java/com/android/server/autofill/RemoteInlineSuggestionRenderService.java index 5d5af535920b..7ad5632dd70e 100644 --- a/services/autofill/java/com/android/server/autofill/RemoteInlineSuggestionRenderService.java +++ b/services/autofill/java/com/android/server/autofill/RemoteInlineSuggestionRenderService.java @@ -86,9 +86,9 @@ public final class RemoteInlineSuggestionRenderService extends */ public void renderSuggestion(@NonNull IInlineSuggestionUiCallback callback, @NonNull InlinePresentation presentation, int width, int height, - @Nullable IBinder hostInputToken) { - scheduleAsyncRequest( - (s) -> s.renderSuggestion(callback, presentation, width, height, hostInputToken)); + @Nullable IBinder hostInputToken, int displayId) { + scheduleAsyncRequest((s) -> s.renderSuggestion(callback, presentation, width, height, + hostInputToken, displayId)); } @Nullable diff --git a/services/autofill/java/com/android/server/autofill/ui/InlineSuggestionFactory.java b/services/autofill/java/com/android/server/autofill/ui/InlineSuggestionFactory.java index 0d8c89b4124d..fef49d44bed0 100644 --- a/services/autofill/java/com/android/server/autofill/ui/InlineSuggestionFactory.java +++ b/services/autofill/java/com/android/server/autofill/ui/InlineSuggestionFactory.java @@ -134,7 +134,7 @@ public final class InlineSuggestionFactory { if (inlineAuthentication != null) { InlineSuggestion inlineAuthSuggestion = createInlineAuthSuggestion(inlineAuthentication, remoteRenderService, onClickFactory, onErrorCallback, - request.getHostInputToken()); + request.getHostInputToken(), request.getHostDisplayId()); inlineSuggestions.add(inlineAuthSuggestion); return new InlineSuggestionsResponse(inlineSuggestions); @@ -162,9 +162,10 @@ public final class InlineSuggestionFactory { continue; } InlineSuggestion inlineSuggestion = createInlineSuggestion(isAugmented, dataset, - fieldIndex, mergedInlinePresentation(request, datasetIndex, inlinePresentation), + datasetIndex, + mergedInlinePresentation(request, datasetIndex, inlinePresentation), onClickFactory, remoteRenderService, onErrorCallback, - request.getHostInputToken()); + request.getHostInputToken(), request.getHostDisplayId()); inlineSuggestions.add(inlineSuggestion); } @@ -172,7 +173,8 @@ public final class InlineSuggestionFactory { for (InlinePresentation inlinePresentation : inlineActions) { final InlineSuggestion inlineAction = createInlineAction(isAugmented, context, mergedInlinePresentation(request, 0, inlinePresentation), - remoteRenderService, onErrorCallback, request.getHostInputToken()); + remoteRenderService, onErrorCallback, request.getHostInputToken(), + request.getHostDisplayId()); inlineSuggestions.add(inlineAction); } } @@ -215,7 +217,8 @@ public final class InlineSuggestionFactory { @NonNull Context context, @NonNull InlinePresentation inlinePresentation, @Nullable RemoteInlineSuggestionRenderService remoteRenderService, - @NonNull Runnable onErrorCallback, @Nullable IBinder hostInputToken) { + @NonNull Runnable onErrorCallback, @Nullable IBinder hostInputToken, + int displayId) { // TODO(b/146453195): fill in the autofill hint properly. final InlineSuggestionInfo inlineSuggestionInfo = new InlineSuggestionInfo( inlinePresentation.getInlinePresentationSpec(), @@ -227,7 +230,7 @@ public final class InlineSuggestionFactory { }; return new InlineSuggestion(inlineSuggestionInfo, createInlineContentProvider(inlinePresentation, onClickAction, onErrorCallback, - remoteRenderService, hostInputToken)); + remoteRenderService, hostInputToken, displayId)); } private static InlineSuggestion createInlineSuggestion(boolean isAugmented, @@ -235,7 +238,8 @@ public final class InlineSuggestionFactory { @NonNull InlinePresentation inlinePresentation, @NonNull BiConsumer<Dataset, Integer> onClickFactory, @NonNull RemoteInlineSuggestionRenderService remoteRenderService, - @NonNull Runnable onErrorCallback, @Nullable IBinder hostInputToken) { + @NonNull Runnable onErrorCallback, @Nullable IBinder hostInputToken, + int displayId) { // TODO(b/146453195): fill in the autofill hint properly. final InlineSuggestionInfo inlineSuggestionInfo = new InlineSuggestionInfo( inlinePresentation.getInlinePresentationSpec(), @@ -246,7 +250,7 @@ public final class InlineSuggestionFactory { final InlineSuggestion inlineSuggestion = new InlineSuggestion(inlineSuggestionInfo, createInlineContentProvider(inlinePresentation, () -> onClickFactory.accept(dataset, datasetIndex), onErrorCallback, - remoteRenderService, hostInputToken)); + remoteRenderService, hostInputToken, displayId)); return inlineSuggestion; } @@ -255,7 +259,7 @@ public final class InlineSuggestionFactory { @NonNull InlinePresentation inlinePresentation, @NonNull RemoteInlineSuggestionRenderService remoteRenderService, @NonNull BiConsumer<Dataset, Integer> onClickFactory, @NonNull Runnable onErrorCallback, - @Nullable IBinder hostInputToken) { + @Nullable IBinder hostInputToken, int displayId) { final InlineSuggestionInfo inlineSuggestionInfo = new InlineSuggestionInfo( inlinePresentation.getInlinePresentationSpec(), InlineSuggestionInfo.SOURCE_AUTOFILL, null, InlineSuggestionInfo.TYPE_SUGGESTION); @@ -264,7 +268,7 @@ public final class InlineSuggestionFactory { createInlineContentProvider(inlinePresentation, () -> onClickFactory.accept(null, AutofillManager.AUTHENTICATION_ID_DATASET_ID_UNDEFINED), - onErrorCallback, remoteRenderService, hostInputToken)); + onErrorCallback, remoteRenderService, hostInputToken, displayId)); } /** @@ -291,7 +295,8 @@ public final class InlineSuggestionFactory { @NonNull InlinePresentation inlinePresentation, @Nullable Runnable onClickAction, @NonNull Runnable onErrorCallback, @Nullable RemoteInlineSuggestionRenderService remoteRenderService, - @Nullable IBinder hostInputToken) { + @Nullable IBinder hostInputToken, + int displayId) { return new IInlineContentProvider.Stub() { @Override public void provideContent(int width, int height, IInlineContentCallback callback) { @@ -305,7 +310,7 @@ public final class InlineSuggestionFactory { } remoteRenderService.renderSuggestion(uiCallback, inlinePresentation, - width, height, hostInputToken); + width, height, hostInputToken, displayId); }); } }; diff --git a/services/core/java/android/content/pm/PackageManagerInternal.java b/services/core/java/android/content/pm/PackageManagerInternal.java index 1230bd79ff97..1b1e06a9a92f 100644 --- a/services/core/java/android/content/pm/PackageManagerInternal.java +++ b/services/core/java/android/content/pm/PackageManagerInternal.java @@ -458,8 +458,8 @@ public abstract class PackageManagerInternal { Bundle verificationBundle, int userId); /** - * Grants implicit access based on an interaction between two apps. This grants the target app - * access to the calling application's package metadata. + * Grants implicit access based on an interaction between two apps. This grants access to the + * from one application to the other's package metadata. * <p> * When an application explicitly tries to interact with another application [via an * activity, service or provider that is either declared in the caller's @@ -468,14 +468,22 @@ public abstract class PackageManagerInternal { * metadata about the calling app. If the calling application uses an implicit intent [ie * action VIEW, category BROWSABLE], it remains hidden from the launched app. * <p> + * If an interaction is not explicit, the {@code direct} argument should be set to false as + * visibility should not be granted in some cases. This method handles that logic. + * <p> * @param userId the user * @param intent the intent that triggered the grant - * @param callingUid The uid of the calling application - * @param targetAppId The app ID of the target application + * @param recipientAppId The app ID of the application that is being given access to {@code + * visibleUid} + * @param visibleUid The uid of the application that is becoming accessible to {@code + * recipientAppId} + * @param direct true if the access is being made due to direct interaction between visibleUid + * and recipientAppId. */ public abstract void grantImplicitAccess( - @UserIdInt int userId, Intent intent, int callingUid, - @AppIdInt int targetAppId); + @UserIdInt int userId, Intent intent, + @AppIdInt int recipientAppId, int visibleUid, + boolean direct); public abstract boolean isInstantAppInstallerComponent(ComponentName component); /** diff --git a/services/core/java/com/android/server/PinnerService.java b/services/core/java/com/android/server/PinnerService.java index 93859b35fbe0..3148a6205871 100644 --- a/services/core/java/com/android/server/PinnerService.java +++ b/services/core/java/com/android/server/PinnerService.java @@ -345,7 +345,7 @@ public final class PinnerService extends SystemService { @Override public void onUidCachedChanged(int uid, boolean cached) throws RemoteException { } - }, UID_OBSERVER_GONE | UID_OBSERVER_ACTIVE, 0, "system"); + }, UID_OBSERVER_GONE | UID_OBSERVER_ACTIVE, 0, null); } catch (RemoteException e) { Slog.e(TAG, "Failed to register uid observer", e); } diff --git a/services/core/java/com/android/server/StorageManagerService.java b/services/core/java/com/android/server/StorageManagerService.java index b1cb1380e4da..95bfbd72dc56 100644 --- a/services/core/java/com/android/server/StorageManagerService.java +++ b/services/core/java/com/android/server/StorageManagerService.java @@ -4458,8 +4458,11 @@ class StorageManagerService extends IStorageManager.Stub mVold.fixupAppDir(packageObbDir.getCanonicalPath() + "/", uid); } catch (IOException e) { Log.e(TAG, "Failed to get canonical path for " + packageName); - } catch (RemoteException e) { - Log.e(TAG, "Failed to fixup app dir for " + packageName); + } catch (RemoteException | ServiceSpecificException e) { + // TODO(b/149975102) there is a known case where this fails, when a new + // user is setup and we try to fixup app dirs for some existing apps. + // For now catch the exception and don't crash. + Log.e(TAG, "Failed to fixup app dir for " + packageName, e); } } } diff --git a/services/core/java/com/android/server/adb/AdbDebuggingManager.java b/services/core/java/com/android/server/adb/AdbDebuggingManager.java index 6c68c312bbde..e49357bee896 100644 --- a/services/core/java/com/android/server/adb/AdbDebuggingManager.java +++ b/services/core/java/com/android/server/adb/AdbDebuggingManager.java @@ -1853,6 +1853,7 @@ public class AdbDebuggingManager { public void removeKey(String key) { if (mKeyMap.containsKey(key)) { mKeyMap.remove(key); + writeKeys(mKeyMap.keySet()); sendPersistKeyStoreMessage(); } } diff --git a/services/core/java/com/android/server/am/ActivityManagerService.java b/services/core/java/com/android/server/am/ActivityManagerService.java index ca0b03dff259..cea3bb83deb0 100644 --- a/services/core/java/com/android/server/am/ActivityManagerService.java +++ b/services/core/java/com/android/server/am/ActivityManagerService.java @@ -218,6 +218,7 @@ import android.content.pm.PathPermission; import android.content.pm.PermissionInfo; import android.content.pm.ProcessInfo; import android.content.pm.ProviderInfo; +import android.content.pm.ProviderInfoList; import android.content.pm.ResolveInfo; import android.content.pm.SELinuxUtil; import android.content.pm.ServiceInfo; @@ -5151,12 +5152,13 @@ public class ActivityManagerService extends IActivityManager.Stub if (mPlatformCompat != null) { mPlatformCompat.resetReporting(app.info); } + final ProviderInfoList providerList = ProviderInfoList.fromList(providers); if (app.isolatedEntryPoint != null) { // This is an isolated process which should just call an entry point instead of // being bound to an application. thread.runIsolatedEntryPoint(app.isolatedEntryPoint, app.isolatedEntryPointArgs); } else if (instr2 != null) { - thread.bindApplication(processName, appInfo, providers, + thread.bindApplication(processName, appInfo, providerList, instr2.mClass, profilerInfo, instr2.mArguments, instr2.mWatcher, @@ -5169,7 +5171,7 @@ public class ActivityManagerService extends IActivityManager.Stub buildSerial, autofillOptions, contentCaptureOptions, app.mDisabledCompatChanges); } else { - thread.bindApplication(processName, appInfo, providers, null, profilerInfo, + thread.bindApplication(processName, appInfo, providerList, null, profilerInfo, null, null, null, testMode, mBinderTransactionTrackingEnabled, enableTrackAllocation, isRestrictedBackupMode || !normalMode, app.isPersistent(), @@ -6285,9 +6287,9 @@ public class ActivityManagerService extends IActivityManager.Stub } @VisibleForTesting - public void grantImplicitAccess(int userId, Intent intent, int callingUid, int targetAppId) { + public void grantImplicitAccess(int userId, Intent intent, int visibleUid, int recipientAppId) { getPackageManagerInternalLocked(). - grantImplicitAccess(userId, intent, callingUid, targetAppId); + grantImplicitAccess(userId, intent, recipientAppId, visibleUid, true /*direct*/); } /** diff --git a/services/core/java/com/android/server/am/ActivityManagerShellCommand.java b/services/core/java/com/android/server/am/ActivityManagerShellCommand.java index c7f5f630ca1a..bf797291380a 100644 --- a/services/core/java/com/android/server/am/ActivityManagerShellCommand.java +++ b/services/core/java/com/android/server/am/ActivityManagerShellCommand.java @@ -2570,8 +2570,6 @@ final class ActivityManagerShellCommand extends ShellCommand { switch (op) { case "move-task": return runStackMoveTask(pw); - case "resize-animated": - return runStackResizeAnimated(pw); case "resize-docked-stack": return runStackResizeDocked(pw); case "positiontask": @@ -2648,23 +2646,6 @@ final class ActivityManagerShellCommand extends ShellCommand { return 0; } - int runStackResizeAnimated(PrintWriter pw) throws RemoteException { - String stackIdStr = getNextArgRequired(); - int stackId = Integer.parseInt(stackIdStr); - final Rect bounds; - if ("null".equals(peekNextArg())) { - bounds = null; - } else { - bounds = getBounds(); - if (bounds == null) { - getErrPrintWriter().println("Error: invalid input bounds"); - return -1; - } - } - mTaskInterface.animateResizePinnedStack(stackId, bounds, -1); - return 0; - } - int runStackResizeDocked(PrintWriter pw) throws RemoteException { final Rect bounds = getBounds(); final Rect taskBounds = getBounds(); @@ -3285,8 +3266,6 @@ final class ActivityManagerShellCommand extends ShellCommand { pw.println(" move-task <TASK_ID> <STACK_ID> [true|false]"); pw.println(" Move <TASK_ID> from its current stack to the top (true) or"); pw.println(" bottom (false) of <STACK_ID>."); - pw.println(" resize-animated <STACK_ID> <LEFT,TOP,RIGHT,BOTTOM>"); - pw.println(" Same as resize, but allow animation."); pw.println(" resize-docked-stack <LEFT,TOP,RIGHT,BOTTOM> [<TASK_LEFT,TASK_TOP,TASK_RIGHT,TASK_BOTTOM>]"); pw.println(" Change docked stack to <LEFT,TOP,RIGHT,BOTTOM>"); pw.println(" and supplying temporary different task bounds indicated by"); diff --git a/services/core/java/com/android/server/dreams/DreamController.java b/services/core/java/com/android/server/dreams/DreamController.java index fbad8dede87d..532045320988 100644 --- a/services/core/java/com/android/server/dreams/DreamController.java +++ b/services/core/java/com/android/server/dreams/DreamController.java @@ -16,9 +16,6 @@ package com.android.server.dreams; -import com.android.internal.logging.MetricsLogger; -import com.android.internal.logging.nano.MetricsProto.MetricsEvent; - import android.content.ComponentName; import android.content.Context; import android.content.Intent; @@ -27,10 +24,10 @@ import android.os.Binder; import android.os.Bundle; import android.os.Handler; import android.os.IBinder; +import android.os.IBinder.DeathRecipient; import android.os.IRemoteCallback; import android.os.PowerManager; import android.os.RemoteException; -import android.os.IBinder.DeathRecipient; import android.os.SystemClock; import android.os.Trace; import android.os.UserHandle; @@ -38,15 +35,14 @@ import android.service.dreams.DreamService; import android.service.dreams.IDreamService; import android.util.Slog; import android.view.IWindowManager; -import android.view.WindowManager; import android.view.WindowManagerGlobal; +import com.android.internal.logging.MetricsLogger; +import com.android.internal.logging.nano.MetricsProto.MetricsEvent; + import java.io.PrintWriter; import java.util.NoSuchElementException; -import static android.view.Display.DEFAULT_DISPLAY; -import static android.view.WindowManager.LayoutParams.TYPE_DREAM; - /** * Internal controller for starting and stopping the current dream and managing related state. * @@ -86,12 +82,9 @@ final class DreamController { } }; - private final Runnable mStopStubbornDreamRunnable = new Runnable() { - @Override - public void run() { - Slog.w(TAG, "Stubborn dream did not finish itself in the time allotted"); - stopDream(true /*immediate*/); - } + private final Runnable mStopStubbornDreamRunnable = () -> { + Slog.w(TAG, "Stubborn dream did not finish itself in the time allotted"); + stopDream(true /*immediate*/); }; public DreamController(Context context, Handler handler, Listener listener) { @@ -140,14 +133,6 @@ final class DreamController { MetricsLogger.visible(mContext, mCurrentDream.mCanDoze ? MetricsEvent.DOZING : MetricsEvent.DREAMING); - try { - mIWindowManager.addWindowToken(token, TYPE_DREAM, DEFAULT_DISPLAY); - } catch (RemoteException ex) { - Slog.e(TAG, "Unable to add window token for dream.", ex); - stopDream(true /*immediate*/); - return; - } - Intent intent = new Intent(DreamService.SERVICE_INTERFACE); intent.setComponent(name); intent.addFlags(Intent.FLAG_ACTIVITY_EXCLUDE_FROM_RECENTS); @@ -216,9 +201,6 @@ final class DreamController { } if (oldDream.mService != null) { - // Tell the dream that it's being stopped so that - // it can shut down nicely before we yank its window token out from - // under it. try { oldDream.mService.detach(); } catch (RemoteException ex) { @@ -238,18 +220,7 @@ final class DreamController { } oldDream.releaseWakeLockIfNeeded(); - try { - mIWindowManager.removeWindowToken(oldDream.mToken, DEFAULT_DISPLAY); - } catch (RemoteException ex) { - Slog.w(TAG, "Error removing window token for dream.", ex); - } - - mHandler.post(new Runnable() { - @Override - public void run() { - mListener.onDreamStopped(oldDream.mToken); - } - }); + mHandler.post(() -> mListener.onDreamStopped(oldDream.mToken)); } finally { Trace.traceEnd(Trace.TRACE_TAG_POWER); } @@ -313,13 +284,10 @@ final class DreamController { // May be called on any thread. @Override public void binderDied() { - mHandler.post(new Runnable() { - @Override - public void run() { - mService = null; - if (mCurrentDream == DreamRecord.this) { - stopDream(true /*immediate*/); - } + mHandler.post(() -> { + mService = null; + if (mCurrentDream == DreamRecord.this) { + stopDream(true /*immediate*/); } }); } @@ -327,16 +295,13 @@ final class DreamController { // May be called on any thread. @Override public void onServiceConnected(ComponentName name, final IBinder service) { - mHandler.post(new Runnable() { - @Override - public void run() { - mConnected = true; - if (mCurrentDream == DreamRecord.this && mService == null) { - attach(IDreamService.Stub.asInterface(service)); - // Wake lock will be released once dreaming starts. - } else { - releaseWakeLockIfNeeded(); - } + mHandler.post(() -> { + mConnected = true; + if (mCurrentDream == DreamRecord.this && mService == null) { + attach(IDreamService.Stub.asInterface(service)); + // Wake lock will be released once dreaming starts. + } else { + releaseWakeLockIfNeeded(); } }); } @@ -344,13 +309,10 @@ final class DreamController { // May be called on any thread. @Override public void onServiceDisconnected(ComponentName name) { - mHandler.post(new Runnable() { - @Override - public void run() { - mService = null; - if (mCurrentDream == DreamRecord.this) { - stopDream(true /*immediate*/); - } + mHandler.post(() -> { + mService = null; + if (mCurrentDream == DreamRecord.this) { + stopDream(true /*immediate*/); } }); } @@ -373,4 +335,4 @@ final class DreamController { } }; } -}
\ No newline at end of file +} diff --git a/services/core/java/com/android/server/dreams/DreamManagerService.java b/services/core/java/com/android/server/dreams/DreamManagerService.java index 3052e3cdcd21..eb0257e95b6c 100644 --- a/services/core/java/com/android/server/dreams/DreamManagerService.java +++ b/services/core/java/com/android/server/dreams/DreamManagerService.java @@ -268,6 +268,10 @@ public final class DreamManagerService extends SystemService { } } + private ComponentName getActiveDreamComponentInternal(boolean doze) { + return chooseDreamForUser(doze, ActivityManager.getCurrentUser()); + } + private ComponentName chooseDreamForUser(boolean doze, int userId) { if (doze) { ComponentName dozeComponent = getDozeComponent(userId); @@ -501,12 +505,18 @@ public final class DreamManagerService extends SystemService { @Override // Binder call public ComponentName[] getDreamComponents() { + return getDreamComponentsForUser(UserHandle.getCallingUserId()); + } + + @Override // Binder call + public ComponentName[] getDreamComponentsForUser(int userId) { checkPermission(android.Manifest.permission.READ_DREAM_STATE); + userId = ActivityManager.handleIncomingUser(Binder.getCallingPid(), + Binder.getCallingUid(), userId, false, true, "getDreamComponents", null); - final int userId = UserHandle.getCallingUserId(); final long ident = Binder.clearCallingIdentity(); try { - return getDreamComponentsForUser(userId); + return DreamManagerService.this.getDreamComponentsForUser(userId); } finally { Binder.restoreCallingIdentity(ident); } @@ -526,13 +536,28 @@ public final class DreamManagerService extends SystemService { } @Override // Binder call - public ComponentName getDefaultDreamComponent() { + public void setDreamComponentsForUser(int userId, ComponentName[] componentNames) { + checkPermission(android.Manifest.permission.WRITE_DREAM_STATE); + userId = ActivityManager.handleIncomingUser(Binder.getCallingPid(), + Binder.getCallingUid(), userId, false, true, "setDreamComponents", null); + + final long ident = Binder.clearCallingIdentity(); + try { + DreamManagerService.this.setDreamComponentsForUser(userId, componentNames); + } finally { + Binder.restoreCallingIdentity(ident); + } + } + + @Override // Binder call + public ComponentName getDefaultDreamComponentForUser(int userId) { checkPermission(android.Manifest.permission.READ_DREAM_STATE); + userId = ActivityManager.handleIncomingUser(Binder.getCallingPid(), + Binder.getCallingUid(), userId, false, true, "getDefaultDreamComponent", null); - final int userId = UserHandle.getCallingUserId(); final long ident = Binder.clearCallingIdentity(); try { - return getDefaultDreamComponentForUser(userId); + return DreamManagerService.this.getDefaultDreamComponentForUser(userId); } finally { Binder.restoreCallingIdentity(ident); } @@ -563,24 +588,25 @@ public final class DreamManagerService extends SystemService { } @Override // Binder call - public void testDream(ComponentName dream) { + public void testDream(int userId, ComponentName dream) { if (dream == null) { throw new IllegalArgumentException("dream must not be null"); } checkPermission(android.Manifest.permission.WRITE_DREAM_STATE); + userId = ActivityManager.handleIncomingUser(Binder.getCallingPid(), + Binder.getCallingUid(), userId, false, true, "testDream", null); - final int callingUserId = UserHandle.getCallingUserId(); final int currentUserId = ActivityManager.getCurrentUser(); - if (callingUserId != currentUserId) { + if (userId != currentUserId) { // This check is inherently prone to races but at least it's something. Slog.w(TAG, "Aborted attempt to start a test dream while a different " - + " user is active: callingUserId=" + callingUserId + + " user is active: userId=" + userId + ", currentUserId=" + currentUserId); return; } final long ident = Binder.clearCallingIdentity(); try { - testDreamInternal(dream, callingUserId); + testDreamInternal(dream, userId); } finally { Binder.restoreCallingIdentity(ident); } @@ -671,6 +697,11 @@ public final class DreamManagerService extends SystemService { public boolean isDreaming() { return isDreamingInternal(); } + + @Override + public ComponentName getActiveDreamComponent(boolean doze) { + return getActiveDreamComponentInternal(doze); + } } private final Runnable mSystemPropertiesChanged = new Runnable() { diff --git a/services/core/java/com/android/server/inputmethod/InputMethodManagerInternal.java b/services/core/java/com/android/server/inputmethod/InputMethodManagerInternal.java index eac2d24c3dab..f24699a6ae64 100644 --- a/services/core/java/com/android/server/inputmethod/InputMethodManagerInternal.java +++ b/services/core/java/com/android/server/inputmethod/InputMethodManagerInternal.java @@ -21,6 +21,7 @@ import android.annotation.UserIdInt; import android.view.inputmethod.InlineSuggestionsRequest; import android.view.inputmethod.InputMethodInfo; +import com.android.internal.inputmethod.SoftInputShowHideReason; import com.android.internal.view.IInlineSuggestionsRequestCallback; import com.android.internal.view.InlineSuggestionsRequestInfo; import com.android.server.LocalServices; @@ -51,7 +52,7 @@ public abstract class InputMethodManagerInternal { /** * Hides the current input method, if visible. */ - public abstract void hideCurrentInputMethod(); + public abstract void hideCurrentInputMethod(@SoftInputShowHideReason int reason); /** * Returns the list of installed input methods for the specified user. @@ -106,7 +107,7 @@ public abstract class InputMethodManagerInternal { } @Override - public void hideCurrentInputMethod() { + public void hideCurrentInputMethod(@SoftInputShowHideReason int reason) { } @Override diff --git a/services/core/java/com/android/server/inputmethod/InputMethodManagerService.java b/services/core/java/com/android/server/inputmethod/InputMethodManagerService.java index 87262a844b18..e3c545c3cf28 100644 --- a/services/core/java/com/android/server/inputmethod/InputMethodManagerService.java +++ b/services/core/java/com/android/server/inputmethod/InputMethodManagerService.java @@ -131,6 +131,7 @@ import com.android.internal.content.PackageMonitor; import com.android.internal.inputmethod.IInputContentUriToken; import com.android.internal.inputmethod.IInputMethodPrivilegedOperations; import com.android.internal.inputmethod.InputMethodDebug; +import com.android.internal.inputmethod.SoftInputShowHideReason; import com.android.internal.inputmethod.StartInputFlags; import com.android.internal.inputmethod.StartInputReason; import com.android.internal.inputmethod.UnbindReason; @@ -767,6 +768,75 @@ public class InputMethodManagerService extends IInputMethodManager.Stub @GuardedBy("mMethodMap") private final WeakHashMap<IBinder, IBinder> mImeTargetWindowMap = new WeakHashMap<>(); + private static final class SoftInputShowHideHistory { + private Entry[] mEntries = new Entry[16]; + private int mNextIndex = 0; + private static final AtomicInteger sSequenceNumber = new AtomicInteger(0); + + // TODO(b/141738570): add requestWindowToken to track who request show / hide softInput. + private static final class Entry { + ClientState mClientState; + String mFocusedWindowString; + @SoftInputModeFlags + int mFocusedWindowSoftInputMode; + @SoftInputShowHideReason + int mReason; + boolean mRequestShowKeyboard; + // The timing of handling MSG_SHOW_SOFT_INPUT or MSG_HIDE_SOFT_INPUT. + long mTimestamp; + long mWallTime; + int mTargetDisplayId; + + Entry(ClientState client, String focusedWindow, @SoftInputModeFlags int softInputMode, + @SoftInputShowHideReason int reason, boolean show) { + mClientState = client; + mFocusedWindowString = focusedWindow; + mFocusedWindowSoftInputMode = softInputMode; + mReason = reason; + mRequestShowKeyboard = show; + mTimestamp = SystemClock.uptimeMillis(); + mWallTime = System.currentTimeMillis(); + } + } + + void addEntry(@NonNull Entry entry) { + final int index = mNextIndex; + mEntries[index] = entry; + mNextIndex = (mNextIndex + 1) % mEntries.length; + } + + void dump(@NonNull PrintWriter pw, @NonNull String prefix) { + final SimpleDateFormat dataFormat = + new SimpleDateFormat("yyyy-MM-dd HH:mm:ss.SSS", Locale.US); + + for (int i = 0; i < mEntries.length; ++i) { + final Entry entry = mEntries[(i + mNextIndex) % mEntries.length]; + if (entry == null) { + continue; + } + pw.print(prefix); + pw.println("SoftInputShowHideHistory #" + sSequenceNumber.getAndIncrement() + ":"); + + pw.print(prefix); + pw.println(" time=" + dataFormat.format(new Date(entry.mWallTime)) + + " (timestamp=" + entry.mTimestamp + ")"); + + pw.print(prefix); + pw.print(" requestShowKeyboard=" + entry.mRequestShowKeyboard); + pw.print(" targetDisplayId=" + entry.mTargetDisplayId); + pw.println(" reason=" + entry.mReason); + + pw.print(prefix); + pw.print(" requestClient=" + entry.mClientState); + pw.println(" focusedWindow=" + entry.mFocusedWindowString); + + pw.print(prefix); + pw.println(" focusedWindowSoftInputMode=" + InputMethodDebug.softInputModeToString( + entry.mFocusedWindowSoftInputMode)); + } + } + } + /** * Map of generated token to windowToken that is requesting * {@link InputMethodManager#showSoftInput(View, int)}. @@ -933,6 +1003,11 @@ public class InputMethodManagerService extends IInputMethodManager.Stub @NonNull private final StartInputHistory mStartInputHistory = new StartInputHistory(); + @GuardedBy("mMethodMap") + @NonNull + private final SoftInputShowHideHistory mSoftInputShowHideHistory = + new SoftInputShowHideHistory(); + class SettingsObserver extends ContentObserver { int mUserId; boolean mRegistered = false; @@ -989,11 +1064,13 @@ public class InputMethodManagerService extends IInputMethodManager.Stub == AccessibilityService.SHOW_MODE_HIDDEN; if (mAccessibilityRequestingNoSoftKeyboard) { final boolean showRequested = mShowRequested; - hideCurrentInputLocked(0, null); + hideCurrentInputLocked(0, null, + SoftInputShowHideReason.HIDE_SETTINGS_ON_CHANGE); mShowRequested = showRequested; } else if (mShowRequested) { - showCurrentInputLocked( - mCurFocusedWindow, InputMethodManager.SHOW_IMPLICIT, null); + showCurrentInputLocked(mCurFocusedWindow, + InputMethodManager.SHOW_IMPLICIT, null, + SoftInputShowHideReason.SHOW_SETTINGS_ON_CHANGE); } } else { boolean enabledChanged = false; @@ -1618,7 +1695,7 @@ public class InputMethodManagerService extends IInputMethodManager.Stub // TODO: Is it really possible that switchUserLocked() happens before system ready? if (mSystemReady) { - hideCurrentInputLocked(0, null); + hideCurrentInputLocked(0, null, SoftInputShowHideReason.HIDE_SWITCH_USER); resetCurrentMethodAndClient(UnbindReason.SWITCH_USER); buildInputMethodListLocked(initialUserSwitch); if (TextUtils.isEmpty(mSettings.getSelectedInputMethod())) { @@ -1882,7 +1959,7 @@ public class InputMethodManagerService extends IInputMethodManager.Stub executeOrSendMessage(mCurMethod, mCaller.obtainMessageOOO(MSG_INLINE_SUGGESTIONS_REQUEST, mCurMethod, requestInfo, new InlineSuggestionsRequestCallbackDecorator(callback, - imi.getPackageName()))); + imi.getPackageName(), mCurTokenDisplayId))); } else { callback.onInlineSuggestionsUnsupported(); } @@ -1902,11 +1979,14 @@ public class InputMethodManagerService extends IInputMethodManager.Stub @NonNull private final String mImePackageName; + private final int mImeDisplayId; + InlineSuggestionsRequestCallbackDecorator( @NonNull IInlineSuggestionsRequestCallback callback, - @NonNull String imePackageName) { + @NonNull String imePackageName, int displayId) { mCallback = callback; mImePackageName = imePackageName; + mImeDisplayId = displayId; } @Override @@ -1923,6 +2003,7 @@ public class InputMethodManagerService extends IInputMethodManager.Stub + "] doesn't match the IME package name=[" + mImePackageName + "]."); } + request.setHostDisplayId(mImeDisplayId); mCallback.onInlineSuggestionsRequest(request, callback); } } @@ -2154,7 +2235,8 @@ public class InputMethodManagerService extends IInputMethodManager.Stub startInputToken, session, mCurInputContext, mCurAttribute)); if (mShowRequested) { if (DEBUG) Slog.v(TAG, "Attach new input asks to show input"); - showCurrentInputLocked(mCurFocusedWindow, getAppShowFlags(), null); + showCurrentInputLocked(mCurFocusedWindow, getAppShowFlags(), null, + SoftInputShowHideReason.ATTACH_NEW_INPUT); } return new InputBindResult(InputBindResult.ResultCode.SUCCESS_WITH_IME_SESSION, session.session, (session.channel != null ? session.channel.dup() : null), @@ -2893,7 +2975,8 @@ public class InputMethodManagerService extends IInputMethodManager.Stub } } if (DEBUG) Slog.v(TAG, "Client requesting input be shown"); - return showCurrentInputLocked(windowToken, flags, resultReceiver); + return showCurrentInputLocked(windowToken, flags, resultReceiver, + SoftInputShowHideReason.SHOW_SOFT_INPUT); } finally { Binder.restoreCallingIdentity(ident); } @@ -2901,7 +2984,8 @@ public class InputMethodManagerService extends IInputMethodManager.Stub } @GuardedBy("mMethodMap") - boolean showCurrentInputLocked(IBinder windowToken, int flags, ResultReceiver resultReceiver) { + boolean showCurrentInputLocked(IBinder windowToken, int flags, ResultReceiver resultReceiver, + @SoftInputShowHideReason int reason) { mShowRequested = true; if (mAccessibilityRequestingNoSoftKeyboard) { return false; @@ -2924,9 +3008,9 @@ public class InputMethodManagerService extends IInputMethodManager.Stub // create a dummy token for IMS so that IMS cannot inject windows into client app. Binder showInputToken = new Binder(); mShowRequestWindowMap.put(showInputToken, windowToken); - executeOrSendMessage(mCurMethod, mCaller.obtainMessageIOOO( - MSG_SHOW_SOFT_INPUT, getImeShowFlags(), mCurMethod, - resultReceiver, showInputToken)); + executeOrSendMessage(mCurMethod, mCaller.obtainMessageIIOOO( + MSG_SHOW_SOFT_INPUT, getImeShowFlags(), reason, mCurMethod, resultReceiver, + showInputToken)); mInputShown = true; if (mHaveConnection && !mVisibleBound) { bindCurrentInputMethodServiceLocked( @@ -2984,14 +3068,16 @@ public class InputMethodManagerService extends IInputMethodManager.Stub } if (DEBUG) Slog.v(TAG, "Client requesting input be hidden"); - return hideCurrentInputLocked(flags, resultReceiver); + return hideCurrentInputLocked(flags, resultReceiver, + SoftInputShowHideReason.HIDE_SOFT_INPUT); } finally { Binder.restoreCallingIdentity(ident); } } } - boolean hideCurrentInputLocked(int flags, ResultReceiver resultReceiver) { + boolean hideCurrentInputLocked(int flags, ResultReceiver resultReceiver, + @SoftInputShowHideReason int reason) { if ((flags&InputMethodManager.HIDE_IMPLICIT_ONLY) != 0 && (mShowExplicitlyRequested || mShowForced)) { if (DEBUG) Slog.v(TAG, "Not hiding: explicit show not cancelled by non-explicit hide"); @@ -3018,8 +3104,8 @@ public class InputMethodManagerService extends IInputMethodManager.Stub // delivered to the IME process as an IPC. Hence the inconsistency between // IMMS#mInputShown and IMMS#mImeWindowVis should be resolved spontaneously in // the final state. - executeOrSendMessage(mCurMethod, mCaller.obtainMessageOO( - MSG_HIDE_SOFT_INPUT, mCurMethod, resultReceiver)); + executeOrSendMessage(mCurMethod, mCaller.obtainMessageIOO(MSG_HIDE_SOFT_INPUT, + reason, mCurMethod, resultReceiver)); res = true; } else { res = false; @@ -3156,7 +3242,7 @@ public class InputMethodManagerService extends IInputMethodManager.Stub Slog.w(TAG, "If you need to impersonate a foreground user/profile from" + " a background user, use EditorInfo.targetInputMethodUser with" + " INTERACT_ACROSS_USERS_FULL permission."); - hideCurrentInputLocked(0, null); + hideCurrentInputLocked(0, null, SoftInputShowHideReason.HIDE_INVALID_USER); return InputBindResult.INVALID_USER; } @@ -3219,7 +3305,8 @@ public class InputMethodManagerService extends IInputMethodManager.Stub // be behind any soft input window, so hide the // soft input window if it is shown. if (DEBUG) Slog.v(TAG, "Unspecified window will hide input"); - hideCurrentInputLocked(InputMethodManager.HIDE_NOT_ALWAYS, null); + hideCurrentInputLocked(InputMethodManager.HIDE_NOT_ALWAYS, null, + SoftInputShowHideReason.HIDE_UNSPECIFIED_WINDOW); // If focused display changed, we should unbind current method // to make app window in previous display relayout after Ime @@ -3245,7 +3332,8 @@ public class InputMethodManagerService extends IInputMethodManager.Stub attribute, startInputFlags, startInputReason); didStart = true; } - showCurrentInputLocked(windowToken, InputMethodManager.SHOW_IMPLICIT, null); + showCurrentInputLocked(windowToken, InputMethodManager.SHOW_IMPLICIT, null, + SoftInputShowHideReason.SHOW_AUTO_EDITOR_FORWARD_NAV); } break; case LayoutParams.SOFT_INPUT_STATE_UNCHANGED: @@ -3254,12 +3342,14 @@ public class InputMethodManagerService extends IInputMethodManager.Stub case LayoutParams.SOFT_INPUT_STATE_HIDDEN: if ((softInputMode & LayoutParams.SOFT_INPUT_IS_FORWARD_NAVIGATION) != 0) { if (DEBUG) Slog.v(TAG, "Window asks to hide input going forward"); - hideCurrentInputLocked(0, null); + hideCurrentInputLocked(0, null, + SoftInputShowHideReason.HIDE_STATE_HIDDEN_FORWARD_NAV); } break; case LayoutParams.SOFT_INPUT_STATE_ALWAYS_HIDDEN: if (DEBUG) Slog.v(TAG, "Window asks to hide input"); - hideCurrentInputLocked(0, null); + hideCurrentInputLocked(0, null, + SoftInputShowHideReason.HIDE_ALWAYS_HIDDEN_STATE); break; case LayoutParams.SOFT_INPUT_STATE_VISIBLE: if ((softInputMode & LayoutParams.SOFT_INPUT_IS_FORWARD_NAVIGATION) != 0) { @@ -3271,7 +3361,8 @@ public class InputMethodManagerService extends IInputMethodManager.Stub attribute, startInputFlags, startInputReason); didStart = true; } - showCurrentInputLocked(windowToken, InputMethodManager.SHOW_IMPLICIT, null); + showCurrentInputLocked(windowToken, InputMethodManager.SHOW_IMPLICIT, null, + SoftInputShowHideReason.SHOW_STATE_VISIBLE_FORWARD_NAV); } else { Slog.e(TAG, "SOFT_INPUT_STATE_VISIBLE is ignored because" + " there is no focused view that also returns true from" @@ -3288,7 +3379,8 @@ public class InputMethodManagerService extends IInputMethodManager.Stub attribute, startInputFlags, startInputReason); didStart = true; } - showCurrentInputLocked(windowToken, InputMethodManager.SHOW_IMPLICIT, null); + showCurrentInputLocked(windowToken, InputMethodManager.SHOW_IMPLICIT, null, + SoftInputShowHideReason.SHOW_STATE_ALWAYS_VISIBLE); } else { Slog.e(TAG, "SOFT_INPUT_STATE_ALWAYS_VISIBLE is ignored because" + " there is no focused view that also returns true from" @@ -3780,7 +3872,7 @@ public class InputMethodManagerService extends IInputMethodManager.Stub } long ident = Binder.clearCallingIdentity(); try { - hideCurrentInputLocked(flags, null); + hideCurrentInputLocked(flags, null, SoftInputShowHideReason.HIDE_MY_SOFT_INPUT); } finally { Binder.restoreCallingIdentity(ident); } @@ -3795,7 +3887,8 @@ public class InputMethodManagerService extends IInputMethodManager.Stub } long ident = Binder.clearCallingIdentity(); try { - showCurrentInputLocked(mLastImeTargetWindow, flags, null); + showCurrentInputLocked(mLastImeTargetWindow, flags, null, + SoftInputShowHideReason.SHOW_MY_SOFT_INPUT); } finally { Binder.restoreCallingIdentity(ident); } @@ -3878,10 +3971,16 @@ public class InputMethodManagerService extends IInputMethodManager.Stub case MSG_SHOW_SOFT_INPUT: args = (SomeArgs)msg.obj; try { + final @SoftInputShowHideReason int reason = msg.arg2; if (DEBUG) Slog.v(TAG, "Calling " + args.arg1 + ".showSoftInput(" - + msg.arg1 + ", " + args.arg2 + ")"); + + msg.arg1 + ", " + args.arg2 + ") for reason: " + + InputMethodDebug.softInputDisplayReasonToString(reason)); ((IInputMethod) args.arg1).showSoftInput( (IBinder) args.arg3, msg.arg1, (ResultReceiver) args.arg2); + mSoftInputShowHideHistory.addEntry( + new SoftInputShowHideHistory.Entry(mCurClient, + InputMethodDebug.objToString(mCurFocusedWindow), + mCurFocusedWindowSoftInputMode, reason, true /* show */)); } catch (RemoteException e) { } args.recycle(); @@ -3889,16 +3988,23 @@ public class InputMethodManagerService extends IInputMethodManager.Stub case MSG_HIDE_SOFT_INPUT: args = (SomeArgs)msg.obj; try { + final @SoftInputShowHideReason int reason = msg.arg1; if (DEBUG) Slog.v(TAG, "Calling " + args.arg1 + ".hideSoftInput(0, " - + args.arg2 + ")"); + + args.arg2 + ") for reason: " + + InputMethodDebug.softInputDisplayReasonToString(reason)); ((IInputMethod)args.arg1).hideSoftInput(0, (ResultReceiver)args.arg2); + mSoftInputShowHideHistory.addEntry( + new SoftInputShowHideHistory.Entry(mCurClient, + InputMethodDebug.objToString(mCurFocusedWindow), + mCurFocusedWindowSoftInputMode, reason, false /* show */)); } catch (RemoteException e) { } args.recycle(); return true; case MSG_HIDE_CURRENT_INPUT_METHOD: synchronized (mMethodMap) { - hideCurrentInputLocked(0, null); + final @SoftInputShowHideReason int reason = (int) msg.obj; + hideCurrentInputLocked(0, null, reason); } return true; case MSG_INITIALIZE_IME: @@ -4682,9 +4788,9 @@ public class InputMethodManagerService extends IInputMethodManager.Stub } @Override - public void hideCurrentInputMethod() { + public void hideCurrentInputMethod(@SoftInputShowHideReason int reason) { mService.mHandler.removeMessages(MSG_HIDE_CURRENT_INPUT_METHOD); - mService.mHandler.sendEmptyMessage(MSG_HIDE_CURRENT_INPUT_METHOD); + mService.mHandler.obtainMessage(MSG_HIDE_CURRENT_INPUT_METHOD, reason).sendToTarget(); } @Override @@ -4841,6 +4947,9 @@ public class InputMethodManagerService extends IInputMethodManager.Stub p.println(" mStartInputHistory:"); mStartInputHistory.dump(pw, " "); + + p.println(" mSoftInputShowHideHistory:"); + mSoftInputShowHideHistory.dump(pw, " "); } p.println(" "); @@ -5300,7 +5409,8 @@ public class InputMethodManagerService extends IInputMethodManager.Stub final String nextIme; final List<InputMethodInfo> nextEnabledImes; if (userId == mSettings.getCurrentUserId()) { - hideCurrentInputLocked(0, null); + hideCurrentInputLocked(0, null, + SoftInputShowHideReason.HIDE_RESET_SHELL_COMMAND); unbindCurrentMethodLocked(); // Reset the current IME resetSelectedInputMethodAndSubtypeLocked(null); diff --git a/services/core/java/com/android/server/inputmethod/MultiClientInputMethodManagerService.java b/services/core/java/com/android/server/inputmethod/MultiClientInputMethodManagerService.java index 4904061ec1af..1aff23b09c0f 100644 --- a/services/core/java/com/android/server/inputmethod/MultiClientInputMethodManagerService.java +++ b/services/core/java/com/android/server/inputmethod/MultiClientInputMethodManagerService.java @@ -73,6 +73,7 @@ import com.android.internal.annotations.GuardedBy; import com.android.internal.inputmethod.IMultiClientInputMethod; import com.android.internal.inputmethod.IMultiClientInputMethodPrivilegedOperations; import com.android.internal.inputmethod.IMultiClientInputMethodSession; +import com.android.internal.inputmethod.SoftInputShowHideReason; import com.android.internal.inputmethod.StartInputFlags; import com.android.internal.inputmethod.StartInputReason; import com.android.internal.inputmethod.UnbindReason; @@ -174,7 +175,7 @@ public final class MultiClientInputMethodManagerService { } @Override - public void hideCurrentInputMethod() { + public void hideCurrentInputMethod(@SoftInputShowHideReason int reason) { reportNotSupported(); } diff --git a/services/core/java/com/android/server/notification/NotificationManagerService.java b/services/core/java/com/android/server/notification/NotificationManagerService.java index 475f229562dd..c9c6d51c40e1 100755 --- a/services/core/java/com/android/server/notification/NotificationManagerService.java +++ b/services/core/java/com/android/server/notification/NotificationManagerService.java @@ -380,7 +380,7 @@ public class NotificationManagerService extends SystemService { private static final int EVENTLOG_ENQUEUE_STATUS_IGNORED = 2; private static final long MIN_PACKAGE_OVERRATE_LOG_INTERVAL = 5000; // milliseconds - private static final long DELAY_FOR_ASSISTANT_TIME = 100; + private static final long DELAY_FOR_ASSISTANT_TIME = 200; private static final String ACTION_NOTIFICATION_TIMEOUT = NotificationManagerService.class.getSimpleName() + ".TIMEOUT"; diff --git a/services/core/java/com/android/server/pm/AppsFilter.java b/services/core/java/com/android/server/pm/AppsFilter.java index ae6e058ee931..0ad0b2373a79 100644 --- a/services/core/java/com/android/server/pm/AppsFilter.java +++ b/services/core/java/com/android/server/pm/AppsFilter.java @@ -354,14 +354,13 @@ public class AppsFilter { * Grants access based on an interaction between a calling and target package, granting * visibility of the caller from the target. * - * @param callingUid the uid initiating the interaction - * @param targetUid the uid being interacted with and thus gaining visibility of the - * initiating uid. + * @param recipientUid the uid gaining visibility of the {@code visibleUid}. + * @param visibleUid the uid becoming visible to the {@recipientUid} */ - public void grantImplicitAccess(int callingUid, int targetUid) { - if (targetUid != callingUid - && mImplicitlyQueryable.add(targetUid, callingUid) && DEBUG_LOGGING) { - Slog.wtf(TAG, "implicit access granted: " + targetUid + " -> " + callingUid); + public void grantImplicitAccess(int recipientUid, int visibleUid) { + if (recipientUid != visibleUid + && mImplicitlyQueryable.add(recipientUid, visibleUid) && DEBUG_LOGGING) { + Slog.wtf(TAG, "implicit access granted: " + recipientUid + " -> " + visibleUid); } } diff --git a/services/core/java/com/android/server/pm/ComponentResolver.java b/services/core/java/com/android/server/pm/ComponentResolver.java index 85810e3a9954..e86a42c284b6 100644 --- a/services/core/java/com/android/server/pm/ComponentResolver.java +++ b/services/core/java/com/android/server/pm/ComponentResolver.java @@ -28,6 +28,7 @@ import android.content.ComponentName; import android.content.Intent; import android.content.IntentFilter; import android.content.pm.ActivityInfo; +import android.content.pm.ApplicationInfo; import android.content.pm.AuxiliaryResolveInfo; import android.content.pm.InstantAppResolveInfo; import android.content.pm.PackageManager; @@ -272,6 +273,9 @@ public class ComponentResolver { return null; } List<ProviderInfo> providerList = null; + + // Map from a package name to the corresponding app info. + ArrayMap<String, ApplicationInfo> appInfos = null; synchronized (mLock) { for (int i = mProviders.mProviders.size() - 1; i >= 0; --i) { final ParsedProvider p = mProviders.mProviders.valueAt(i); @@ -300,8 +304,29 @@ public class ComponentResolver { && (p.getMetaData() == null || !p.getMetaData().containsKey(metaDataKey))) { continue; } + + // Make sure we have AppInfo for this provider. + final PackageUserState state = ps.readUserState(userId); + ApplicationInfo appInfo = + (appInfos == null) ? null : appInfos.get(pkg.getPackageName()); + if (appInfo == null) { + appInfo = PackageInfoUtils.generateApplicationInfo( + pkg, flags, state, userId, ps); + if (appInfo == null) { + // In this case, we should avoid calling generateApplicationInfo() for + // the same package in subsequent iterations, but appInfo shouldn't be null + // here, so we don't bother. + continue; + } + if (appInfos == null) { + appInfos = new ArrayMap<>(4); + } + appInfos.put(pkg.getPackageName(), appInfo); + } + // At this point, appInfo != null. + final ProviderInfo info = PackageInfoUtils.generateProviderInfo( - pkg, p, flags, ps.readUserState(userId), userId, ps); + pkg, p, flags, state, appInfo, userId, ps); if (info == null) { continue; } diff --git a/services/core/java/com/android/server/pm/InstantAppRegistry.java b/services/core/java/com/android/server/pm/InstantAppRegistry.java index cf85b0f3da7a..0eaac4140c14 100644 --- a/services/core/java/com/android/server/pm/InstantAppRegistry.java +++ b/services/core/java/com/android/server/pm/InstantAppRegistry.java @@ -403,7 +403,7 @@ class InstantAppRegistry { @GuardedBy("mService.mLock") public void grantInstantAccessLPw(@UserIdInt int userId, @Nullable Intent intent, - int instantAppId, int targetAppId) { + int recipientUid, int instantAppId) { if (mInstalledInstantAppUids == null) { return; // no instant apps installed; no need to grant } @@ -411,7 +411,7 @@ class InstantAppRegistry { if (instantAppList == null || !instantAppList.get(instantAppId)) { return; // instant app id isn't installed; no need to grant } - if (instantAppList.get(targetAppId)) { + if (instantAppList.get(recipientUid)) { return; // target app id is an instant app; no need to grant } if (intent != null && Intent.ACTION_VIEW.equals(intent.getAction())) { @@ -428,10 +428,10 @@ class InstantAppRegistry { targetAppList = new SparseArray<>(); mInstantGrants.put(userId, targetAppList); } - SparseBooleanArray instantGrantList = targetAppList.get(targetAppId); + SparseBooleanArray instantGrantList = targetAppList.get(recipientUid); if (instantGrantList == null) { instantGrantList = new SparseBooleanArray(); - targetAppList.put(targetAppId, instantGrantList); + targetAppList.put(recipientUid, instantGrantList); } instantGrantList.put(instantAppId, true /*granted*/); } diff --git a/services/core/java/com/android/server/pm/PackageManagerService.java b/services/core/java/com/android/server/pm/PackageManagerService.java index 2d8808052864..4eac79cff8b5 100644 --- a/services/core/java/com/android/server/pm/PackageManagerService.java +++ b/services/core/java/com/android/server/pm/PackageManagerService.java @@ -16345,7 +16345,27 @@ public class PackageManagerService extends IPackageManager.Stub REASON_INSTALL, DexoptOptions.DEXOPT_BOOT_COMPLETE | DexoptOptions.DEXOPT_INSTALL_WITH_DEX_METADATA_FILE); - mPackageDexOptimizer.performDexOpt(pkg, reconciledPkg.pkgSetting, + ScanResult result = reconciledPkg.scanResult; + + // This mirrors logic from commitReconciledScanResultLocked, where the library files + // needed for dexopt are assigned. + // TODO: Fix this to have 1 mutable PackageSetting for scan/install. If the previous + // setting needs to be passed to have a comparison, hide it behind an immutable + // interface. There's no good reason to have 3 different ways to access the real + // PackageSetting object, only one of which is actually correct. + PackageSetting realPkgSetting = result.existingSettingCopied + ? result.request.pkgSetting : result.pkgSetting; + if (realPkgSetting == null) { + realPkgSetting = reconciledPkg.pkgSetting; + } + + // Unfortunately, the updated system app flag is only tracked on this PackageSetting + boolean isUpdatedSystemApp = reconciledPkg.pkgSetting.getPkgState() + .isUpdatedSystemApp(); + + realPkgSetting.getPkgState().setUpdatedSystemApp(isUpdatedSystemApp); + + mPackageDexOptimizer.performDexOpt(pkg, realPkgSetting, null /* instructionSets */, getOrCreateCompilerPackageStats(pkg), mDexManager.getPackageUseInfoOrDefault(packageName), @@ -23569,22 +23589,27 @@ public class PackageManagerService extends IPackageManager.Stub @Override public void grantImplicitAccess(int userId, Intent intent, - int callingUid, int targetAppId) { + int recipientAppId, int visibleUid, boolean direct) { synchronized (mLock) { - final AndroidPackage callingPackage = getPackage(callingUid); - final int targetUid = UserHandle.getUid(userId, targetAppId); - final AndroidPackage targetPackage = getPackage(targetUid); - if (callingPackage == null || targetPackage == null) { + final AndroidPackage visiblePackage = getPackage(visibleUid); + final int recipientUid = UserHandle.getUid(userId, recipientAppId); + if (visiblePackage == null || getPackage(recipientUid) == null) { return; } - final boolean instantApp = isInstantAppInternal(callingPackage.getPackageName(), - userId, callingUid); + final boolean instantApp = + isInstantAppInternal(visiblePackage.getPackageName(), userId, visibleUid); if (instantApp) { + if (!direct) { + // if the interaction that lead to this granting access to an instant app + // was indirect (i.e.: URI permission grant), do not actually execute the + // grant. + return; + } mInstantAppRegistry.grantInstantAccessLPw(userId, intent, - UserHandle.getAppId(callingUid), targetAppId); + recipientAppId, UserHandle.getAppId(visibleUid) /*instantAppId*/); } else { - mAppsFilter.grantImplicitAccess(callingUid, targetUid); + mAppsFilter.grantImplicitAccess(recipientUid, visibleUid); } } } diff --git a/services/core/java/com/android/server/pm/StagingManager.java b/services/core/java/com/android/server/pm/StagingManager.java index fe992290a3c9..7dd2e5506bb7 100644 --- a/services/core/java/com/android/server/pm/StagingManager.java +++ b/services/core/java/com/android/server/pm/StagingManager.java @@ -1246,7 +1246,7 @@ public class StagingManager { try { IStorageManager storageManager = PackageHelper.getStorageManager(); if (storageManager.supportsCheckpoint()) { - storageManager.startCheckpoint(1); + storageManager.startCheckpoint(2); } } catch (Exception e) { // Failed to get hold of StorageManager diff --git a/services/core/java/com/android/server/pm/parsing/PackageInfoUtils.java b/services/core/java/com/android/server/pm/parsing/PackageInfoUtils.java index 23bdf5f101f3..f5ce0804236d 100644 --- a/services/core/java/com/android/server/pm/parsing/PackageInfoUtils.java +++ b/services/core/java/com/android/server/pm/parsing/PackageInfoUtils.java @@ -312,8 +312,14 @@ public class PackageInfoUtils { /** * @param pkgSetting See {@link PackageInfoUtils} for description of pkgSetting usage. + * + * @deprecated use {@link #generateProviderInfo( + * AndroidPackage, ParsedProvider, int, PackageUserState, ApplicationInfo, int, PackageSetting)} + * instead and pass {@link ApplicationInfo} explicitly to avoid generating duplicate instances + * of it. */ @Nullable + @Deprecated public static ProviderInfo generateProviderInfo(AndroidPackage pkg, ParsedProvider p, @PackageManager.ComponentInfoFlags int flags, PackageUserState state, int userId, @Nullable PackageSetting pkgSetting) { @@ -324,7 +330,7 @@ public class PackageInfoUtils { * @param pkgSetting See {@link PackageInfoUtils} for description of pkgSetting usage. */ @Nullable - private static ProviderInfo generateProviderInfo(AndroidPackage pkg, ParsedProvider p, + public static ProviderInfo generateProviderInfo(AndroidPackage pkg, ParsedProvider p, @PackageManager.ComponentInfoFlags int flags, PackageUserState state, @Nullable ApplicationInfo applicationInfo, int userId, @Nullable PackageSetting pkgSetting) { diff --git a/services/core/java/com/android/server/policy/PhoneWindowManager.java b/services/core/java/com/android/server/policy/PhoneWindowManager.java index 96bd3afa1d13..1b5cc6a248e3 100644 --- a/services/core/java/com/android/server/policy/PhoneWindowManager.java +++ b/services/core/java/com/android/server/policy/PhoneWindowManager.java @@ -186,6 +186,7 @@ import android.view.autofill.AutofillManagerInternal; import com.android.internal.R; import com.android.internal.accessibility.AccessibilityShortcutController; +import com.android.internal.inputmethod.SoftInputShowHideReason; import com.android.internal.logging.MetricsLogger; import com.android.internal.logging.nano.MetricsProto; import com.android.internal.os.RoSystemProperties; @@ -482,6 +483,7 @@ public class PhoneWindowManager implements WindowManagerPolicy { MetricsLogger mLogger; boolean mWakeOnDpadKeyPress; boolean mWakeOnAssistKeyPress; + boolean mWakeOnBackKeyPress; private boolean mHandleVolumeKeysInWM; @@ -1105,7 +1107,8 @@ public class PhoneWindowManager implements WindowManagerPolicy { LocalServices.getService(InputMethodManagerInternal.class); } if (mInputMethodManagerInternal != null) { - mInputMethodManagerInternal.hideCurrentInputMethod(); + mInputMethodManagerInternal.hideCurrentInputMethod( + SoftInputShowHideReason.HIDE_POWER_BUTTON_GO_HOME); } } else { shortPressPowerGoHome(); @@ -1736,6 +1739,8 @@ public class PhoneWindowManager implements WindowManagerPolicy { res.getBoolean(com.android.internal.R.bool.config_wakeOnDpadKeyPress); mWakeOnAssistKeyPress = res.getBoolean(com.android.internal.R.bool.config_wakeOnAssistKeyPress); + mWakeOnBackKeyPress = + res.getBoolean(com.android.internal.R.bool.config_wakeOnBackKeyPress); // Init display burn-in protection boolean burnInProtectionEnabled = context.getResources().getBoolean( @@ -4107,6 +4112,9 @@ public class PhoneWindowManager implements WindowManagerPolicy { case KeyEvent.KEYCODE_ASSIST: return mWakeOnAssistKeyPress; + + case KeyEvent.KEYCODE_BACK: + return mWakeOnBackKeyPress; } return true; diff --git a/services/core/java/com/android/server/stats/pull/StatsPullAtomService.java b/services/core/java/com/android/server/stats/pull/StatsPullAtomService.java index 3c8ef6c832a3..3f8f6bfed9ca 100644 --- a/services/core/java/com/android/server/stats/pull/StatsPullAtomService.java +++ b/services/core/java/com/android/server/stats/pull/StatsPullAtomService.java @@ -188,6 +188,7 @@ public class StatsPullAtomService extends SystemService { private static final int MAX_BATTERY_STATS_HELPER_FREQUENCY_MS = 1000; private static final int CPU_TIME_PER_THREAD_FREQ_MAX_NUM_FREQUENCIES = 8; private static final int OP_FLAGS_PULLED = OP_FLAG_SELF | OP_FLAG_TRUSTED_PROXIED; + private static final String COMMON_PERMISSION_PREFIX = "android.permission."; private final Object mNetworkStatsLock = new Object(); @GuardedBy("mNetworkStatsLock") @@ -2627,6 +2628,10 @@ public class StatsPullAtomService extends SystemService { continue; } + if (permName.startsWith(COMMON_PERMISSION_PREFIX)) { + permName = permName.substring(COMMON_PERMISSION_PREFIX.length()); + } + StatsEvent.Builder e = StatsEvent.newBuilder(); e.setAtomId(atomTag); e.writeString(permName); diff --git a/services/core/java/com/android/server/tv/tunerresourcemanager/ClientProfile.java b/services/core/java/com/android/server/tv/tunerresourcemanager/ClientProfile.java index bad2b78dab48..4eff954f1ec8 100644 --- a/services/core/java/com/android/server/tv/tunerresourcemanager/ClientProfile.java +++ b/services/core/java/com/android/server/tv/tunerresourcemanager/ClientProfile.java @@ -13,9 +13,11 @@ * See the License for the specific language governing permissions and * limitations under the License. */ - package com.android.server.tv.tunerresourcemanager; +import java.util.ArrayList; +import java.util.List; + /** * A client profile object used by the Tuner Resource Manager to record the registered clients' * information. @@ -23,12 +25,14 @@ package com.android.server.tv.tunerresourcemanager; * @hide */ public final class ClientProfile { + public static final int INVALID_GROUP_ID = -1; + /** * Client id sent to the client when registering with * {@link #registerClientProfile(ResourceClientProfile, TunerResourceManagerCallback, int[])} */ - private final int mClientId; + private final int mId; /** * see {@link ResourceClientProfile} @@ -41,7 +45,7 @@ public final class ClientProfile { private final int mUseCase; /** - * Process id queried from {@link TvInputManager#} + * Process id queried from {@link TvInputManager#getPid(String)}. */ private final int mProcessId; @@ -59,6 +63,11 @@ public final class ClientProfile { private int mNiceValue; /** + * List of the frontend ids that are used by the current client. + */ + private List<Integer> mUsingFrontendIds = new ArrayList<>(); + + /** * Optional arbitrary priority value given by the client. * * <p>This value can override the default priorotiy calculated from @@ -66,18 +75,15 @@ public final class ClientProfile { */ private int mPriority; - private ClientProfile(ClientProfileBuilder builder) { - this.mClientId = builder.mClientId; + private ClientProfile(Builder builder) { + this.mId = builder.mId; this.mTvInputSessionId = builder.mTvInputSessionId; this.mUseCase = builder.mUseCase; this.mProcessId = builder.mProcessId; - this.mGroupId = builder.mGroupId; - this.mNiceValue = builder.mNiceValue; - this.mPriority = builder.mPriority; } - public int getClientId() { - return mClientId; + public int getId() { + return mId; } public String getTvInputSessionId() { @@ -116,26 +122,47 @@ public final class ClientProfile { mNiceValue = niceValue; } + /** + * Set when the client starts to use a frontend. + * + * @param frontendId being used. + */ + public void useFrontend(int frontendId) { + mUsingFrontendIds.add(frontendId); + } + + public List<Integer> getInUseFrontendIds() { + return mUsingFrontendIds; + } + + /** + * Called when the client released a frontend. + * + * <p>This could happen when client resource reclaimed. + * + * @param frontendId being released. + */ + public void releaseFrontend(int frontendId) { + mUsingFrontendIds.remove(frontendId); + } + @Override public String toString() { - return "ClientProfile: " + this.mClientId + ", " + this.mTvInputSessionId + ", " - + this.mUseCase + ", " + this.mProcessId; + return "ClientProfile[id=" + this.mId + ", tvInputSessionId=" + this.mTvInputSessionId + + ", useCase=" + this.mUseCase + ", processId=" + this.mProcessId + "]"; } /** * Builder class for {@link ClientProfile}. */ - public static class ClientProfileBuilder { - private final int mClientId; + public static class Builder { + private final int mId; private String mTvInputSessionId; private int mUseCase; private int mProcessId; - private int mGroupId; - private int mNiceValue; - private int mPriority; - ClientProfileBuilder(int clientId) { - this.mClientId = clientId; + Builder(int id) { + this.mId = id; } /** @@ -143,7 +170,7 @@ public final class ClientProfile { * * @param useCase the useCase of the client. */ - public ClientProfileBuilder useCase(int useCase) { + public Builder useCase(int useCase) { this.mUseCase = useCase; return this; } @@ -153,7 +180,7 @@ public final class ClientProfile { * * @param tvInputSessionId the id of the tv input session. */ - public ClientProfileBuilder tvInputSessionId(String tvInputSessionId) { + public Builder tvInputSessionId(String tvInputSessionId) { this.mTvInputSessionId = tvInputSessionId; return this; } @@ -163,42 +190,11 @@ public final class ClientProfile { * * @param processId the id of process. */ - public ClientProfileBuilder processId(int processId) { + public Builder processId(int processId) { this.mProcessId = processId; return this; } - - /** - * Builder for {@link ClientProfile}. - * - * @param groupId the id of the group that shares the same resource. - */ - public ClientProfileBuilder groupId(int groupId) { - this.mGroupId = groupId; - return this; - } - - /** - * Builder for {@link ClientProfile}. - * - * @param niceValue the nice value of the client. - */ - public ClientProfileBuilder niceValue(int niceValue) { - this.mNiceValue = niceValue; - return this; - } - - /** - * Builder for {@link ClientProfile}. - * - * @param priority the priority value of the client. - */ - public ClientProfileBuilder priority(int priority) { - this.mPriority = priority; - return this; - } - /** * Build a {@link ClientProfile}. * diff --git a/services/core/java/com/android/server/tv/tunerresourcemanager/FrontendResource.java b/services/core/java/com/android/server/tv/tunerresourcemanager/FrontendResource.java new file mode 100644 index 000000000000..a109265e2f50 --- /dev/null +++ b/services/core/java/com/android/server/tv/tunerresourcemanager/FrontendResource.java @@ -0,0 +1,204 @@ +/* + * Copyright 2020 The Android Open Source Project + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ +package com.android.server.tv.tunerresourcemanager; + +import android.annotation.Nullable; +import android.media.tv.tuner.frontend.FrontendSettings.Type; + +import java.util.ArrayList; +import java.util.Arrays; +import java.util.List; + +/** + * A frontend resource object used by the Tuner Resource Manager to record the tuner frontend + * information. + * + * @hide + */ +public final class FrontendResource { + public static final int INVALID_OWNER_ID = -1; + + /** + * Id of the current frontend. Should not be changed and should be aligned with the driver level + * implementation. + */ + private final int mId; + + /** + * see {@link android.media.tv.tuner.frontend.FrontendSettings.Type} + */ + @Type private final int mType; + + /** + * The exclusive group id of the FE. FEs under the same id can't be used at the same time. + */ + private final int mExclusiveGroupId; + + /** + * An array to save all the FE ids under the same exclisive group. + */ + private List<Integer> mExclusiveGroupMemberFeIds = new ArrayList<>(); + + /** + * If the current resource is in use. Once resources under the same exclusive group id is in use + * all other resources in the same group would be considered in use. + */ + private boolean mIsInUse; + + /** + * The owner client's id if this resource is occupied. Owner of the resource under the same + * exclusive group id would be considered as the whole group's owner. + */ + private int mOwnerClientId = INVALID_OWNER_ID; + + private FrontendResource(Builder builder) { + this.mId = builder.mId; + this.mType = builder.mType; + this.mExclusiveGroupId = builder.mExclusiveGroupId; + } + + public int getId() { + return mId; + } + + public int getType() { + return mType; + } + + public int getExclusiveGroupId() { + return mExclusiveGroupId; + } + + public List<Integer> getExclusiveGroupMemberFeIds() { + return mExclusiveGroupMemberFeIds; + } + + /** + * Add one id into the exclusive group member id list. + * + * @param id the id to be added. + */ + public void addExclusiveGroupMemberFeId(int id) { + mExclusiveGroupMemberFeIds.add(id); + } + + /** + * Add one id list to the exclusive group member id list. + * + * @param ids the id list to be added. + */ + public void addExclusiveGroupMemberFeId(List<Integer> ids) { + mExclusiveGroupMemberFeIds.addAll(ids); + } + + /** + * Remove one id from the exclusive group member id list. + * + * @param id the id to be removed. + */ + public void removeExclusiveGroupMemberFeId(int id) { + mExclusiveGroupMemberFeIds.remove(new Integer(id)); + } + + public boolean isInUse() { + return mIsInUse; + } + + public int getOwnerClientId() { + return mOwnerClientId; + } + + /** + * Set an owner client on the resource. + * + * @param ownerClientId the id of the owner client. + */ + public void setOwner(int ownerClientId) { + mIsInUse = true; + mOwnerClientId = ownerClientId; + } + + /** + * Remove an owner client from the resource. + */ + public void removeOwner() { + mIsInUse = false; + mOwnerClientId = INVALID_OWNER_ID; + } + + @Override + public String toString() { + return "FrontendResource[id=" + this.mId + ", type=" + this.mType + + ", exclusiveGId=" + this.mExclusiveGroupId + ", exclusiveGMemeberIds=" + + Arrays.toString(this.mExclusiveGroupMemberFeIds.toArray()) + + ", isInUse=" + this.mIsInUse + ", ownerClientId=" + this.mOwnerClientId + "]"; + } + + @Override + public boolean equals(@Nullable Object o) { + if (o instanceof FrontendResource) { + FrontendResource fe = (FrontendResource) o; + return mId == fe.getId() && mType == fe.getType() + && mExclusiveGroupId == fe.getExclusiveGroupId() + && mExclusiveGroupMemberFeIds.equals(fe.getExclusiveGroupMemberFeIds()) + && mIsInUse == fe.isInUse() && mOwnerClientId == fe.getOwnerClientId(); + } + return false; + } + + /** + * Builder class for {@link FrontendResource}. + */ + public static class Builder { + private final int mId; + @Type private int mType; + private int mExclusiveGroupId; + + Builder(int id) { + this.mId = id; + } + + /** + * Builder for {@link FrontendResource}. + * + * @param type the type of the frontend. See {@link Type} + */ + public Builder type(@Type int type) { + this.mType = type; + return this; + } + + /** + * Builder for {@link FrontendResource}. + * + * @param exclusiveGroupId the id of exclusive group. + */ + public Builder exclusiveGroupId(int exclusiveGroupId) { + this.mExclusiveGroupId = exclusiveGroupId; + return this; + } + + /** + * Build a {@link FrontendResource}. + * + * @return {@link FrontendResource}. + */ + public FrontendResource build() { + FrontendResource frontendResource = new FrontendResource(this); + return frontendResource; + } + } +} diff --git a/services/core/java/com/android/server/tv/tunerresourcemanager/TunerResourceManagerService.java b/services/core/java/com/android/server/tv/tunerresourcemanager/TunerResourceManagerService.java index 49a7045bf57a..cb31a502ecfa 100644 --- a/services/core/java/com/android/server/tv/tunerresourcemanager/TunerResourceManagerService.java +++ b/services/core/java/com/android/server/tv/tunerresourcemanager/TunerResourceManagerService.java @@ -28,32 +28,48 @@ import android.media.tv.tunerresourcemanager.TunerFrontendInfo; import android.media.tv.tunerresourcemanager.TunerFrontendRequest; import android.media.tv.tunerresourcemanager.TunerLnbRequest; import android.media.tv.tunerresourcemanager.TunerResourceManager; +import android.os.Binder; import android.os.RemoteException; import android.util.Log; import android.util.Slog; import android.util.SparseArray; +import com.android.internal.annotations.VisibleForTesting; import com.android.server.SystemService; import java.util.ArrayList; import java.util.List; /** - * This class provides a system service that manages the TV tuner resources. - * - * @hide - */ + * This class provides a system service that manages the TV tuner resources. + * + * @hide + */ public class TunerResourceManagerService extends SystemService { private static final String TAG = "TunerResourceManagerService"; private static final boolean DEBUG = Log.isLoggable(TAG, Log.DEBUG); - private SparseArray<ClientProfile> mClientProfiles = new SparseArray<>(); - private SparseArray<IResourcesReclaimListener> mListeners = new SparseArray<>(); - private int mNextUnusedFrontendId = 0; - private List<Integer> mReleasedClientId = new ArrayList<Integer>(); + public static final int INVALID_CLIENT_ID = -1; + private static final int MAX_CLIENT_PRIORITY = 1000; + + // Array of the registered client profiles + @VisibleForTesting private SparseArray<ClientProfile> mClientProfiles = new SparseArray<>(); + private int mNextUnusedClientId = 0; + private List<Integer> mRegisteredClientIds = new ArrayList<Integer>(); + + // Array of the current available frontend resources + @VisibleForTesting + private SparseArray<FrontendResource> mFrontendResources = new SparseArray<>(); + // Array of the current available frontend ids private List<Integer> mAvailableFrontendIds = new ArrayList<Integer>(); + private SparseArray<IResourcesReclaimListener> mListeners = new SparseArray<>(); + private TvInputManager mManager; + private UseCasePriorityHints mPriorityCongfig = new UseCasePriorityHints(); + + // Used to synchronize the access to the service. + private final Object mLock = new Object(); public TunerResourceManagerService(@Nullable Context context) { super(context); @@ -61,97 +77,78 @@ public class TunerResourceManagerService extends SystemService { @Override public void onStart() { - publishBinderService(Context.TV_TUNER_RESOURCE_MGR_SERVICE, new BinderService()); - mManager = (TvInputManager) getContext() - .getSystemService(Context.TV_INPUT_SERVICE); + onStart(false /*isForTesting*/); + } + + @VisibleForTesting + protected void onStart(boolean isForTesting) { + if (!isForTesting) { + publishBinderService(Context.TV_TUNER_RESOURCE_MGR_SERVICE, new BinderService()); + } + mManager = (TvInputManager) getContext().getSystemService(Context.TV_INPUT_SERVICE); + mPriorityCongfig.parse(); } private final class BinderService extends ITunerResourceManager.Stub { @Override public void registerClientProfile(@NonNull ResourceClientProfile profile, - @NonNull IResourcesReclaimListener listener, - @NonNull int[] clientId) { - if (DEBUG) { - Slog.d(TAG, "registerClientProfile(clientProfile=" + profile + ")"); + @NonNull IResourcesReclaimListener listener, @NonNull int[] clientId) + throws RemoteException { + enforceAccessPermission(); + if (profile == null) { + throw new RemoteException("ResourceClientProfile can't be null"); } - // TODO tell if the client already exists - if (mReleasedClientId.isEmpty()) { - clientId[0] = mNextUnusedFrontendId++; - } else { - clientId[0] = mReleasedClientId.get(0); - mReleasedClientId.remove(0); + if (clientId == null) { + throw new RemoteException("clientId can't be null!"); } - if (mManager == null) { - Slog.e(TAG, "TvInputManager is null. Can't register client profile."); - return; + if (!mPriorityCongfig.isDefinedUseCase(profile.getUseCase())) { + throw new RemoteException("Use undefined client use case:" + profile.getUseCase()); } - int callingPid = mManager.getClientPid(profile.getTvInputSessionId()); - - ClientProfile clientProfile = new ClientProfile.ClientProfileBuilder( - clientId[0]) - .tvInputSessionId(profile.getTvInputSessionId()) - .useCase(profile.getUseCase()) - .processId(callingPid) - .build(); - mClientProfiles.append(clientId[0], clientProfile); - mListeners.append(clientId[0], listener); + synchronized (mLock) { + registerClientProfileInternal(profile, listener, clientId); + } } @Override - public void unregisterClientProfile(int clientId) { - if (DEBUG) { - Slog.d(TAG, "unregisterClientProfile(clientId=" + clientId + ")"); + public void unregisterClientProfile(int clientId) throws RemoteException { + enforceAccessPermission(); + synchronized (mLock) { + if (!checkClientExists(clientId)) { + Slog.e(TAG, "Unregistering non exists client:" + clientId); + return; + } + unregisterClientProfileInternal(clientId); } - - mClientProfiles.remove(clientId); - mListeners.remove(clientId); - mReleasedClientId.add(clientId); } @Override public boolean updateClientPriority(int clientId, int priority, int niceValue) { - if (DEBUG) { - Slog.d(TAG, "updateClientPriority(clientId=" + clientId - + ", priority=" + priority + ", niceValue=" + niceValue + ")"); - } - - ClientProfile profile = mClientProfiles.get(clientId); - if (profile == null) { - Slog.e(TAG, "Can not find client profile with id " + clientId - + " when trying to update the client priority."); - return false; + enforceAccessPermission(); + synchronized (mLock) { + return updateClientPriorityInternal(clientId, priority, niceValue); } - - profile.setPriority(priority); - profile.setNiceValue(niceValue); - - return true; } @Override - public void setFrontendInfoList(@NonNull TunerFrontendInfo[] infos) - throws RemoteException { - if (infos == null || infos.length == 0) { - Slog.d(TAG, "Can't update with empty frontend info"); - return; + public void setFrontendInfoList(@NonNull TunerFrontendInfo[] infos) throws RemoteException { + enforceAccessPermission(); + if (infos == null) { + throw new RemoteException("TunerFrontendInfo can't be null"); } - - if (DEBUG) { - Slog.d(TAG, "updateFrontendInfo:"); - for (int i = 0; i < infos.length; i++) { - Slog.d(TAG, infos[i].toString()); - } + synchronized (mLock) { + setFrontendInfoListInternal(infos); } } @Override public void updateCasInfo(int casSystemId, int maxSessionNum) { if (DEBUG) { - Slog.d(TAG, "updateCasInfo(casSystemId=" - + casSystemId + ", maxSessionNum=" + maxSessionNum + ")"); + Slog.d(TAG, + "updateCasInfo(casSystemId=" + casSystemId + + ", maxSessionNum=" + maxSessionNum + ")"); } } @@ -166,49 +163,30 @@ public class TunerResourceManagerService extends SystemService { @Override public boolean requestFrontend(@NonNull TunerFrontendRequest request, - @NonNull int[] frontendId) throws RemoteException { - if (DEBUG) { - Slog.d(TAG, "requestFrontend(request=" + request + ")"); - } - - frontendId[0] = TunerResourceManager.INVALID_FRONTEND_ID; - - if (getContext() == null) { - Slog.e(TAG, "Can not find context when requesting frontend"); - return false; + @NonNull int[] frontendId) throws RemoteException { + enforceAccessPermission(); + if (frontendId == null) { + throw new RemoteException("frontendId can't be null"); } - - if (mClientProfiles.get(request.getClientId()) == null) { - Slog.e(TAG, "Request from unregistered client. Id: " - + request.getClientId()); - return false; - } - - String sessionId = mClientProfiles.get(request.getClientId()) - .getTvInputSessionId(); - - if (DEBUG) { - Slog.d(TAG, "session Id:" + sessionId + ")"); - } - - if (DEBUG) { - Slog.d(TAG, "No available Frontend found."); + synchronized (mLock) { + try { + return requestFrontendInternal(request, frontendId); + } catch (RemoteException e) { + throw e.rethrowFromSystemServer(); + } } - - return false; } @Override public void shareFrontend(int selfClientId, int targetClientId) { if (DEBUG) { - Slog.d(TAG, "shareFrontend from " - + selfClientId + " with " + targetClientId); + Slog.d(TAG, "shareFrontend from " + selfClientId + " with " + targetClientId); } } @Override - public boolean requestCasSession(@NonNull CasSessionRequest request, - @NonNull int[] sessionResourceId) { + public boolean requestCasSession( + @NonNull CasSessionRequest request, @NonNull int[] sessionResourceId) { if (DEBUG) { Slog.d(TAG, "requestCasSession(request=" + request + ")"); } @@ -246,13 +224,284 @@ public class TunerResourceManagerService extends SystemService { } @Override - public boolean isHigherPriority(ResourceClientProfile challengerProfile, - ResourceClientProfile holderProfile) { + public boolean isHigherPriority( + ResourceClientProfile challengerProfile, ResourceClientProfile holderProfile) { if (DEBUG) { - Slog.d(TAG, "isHigherPriority(challengerProfile=" + challengerProfile - + ", holderProfile=" + challengerProfile + ")"); + Slog.d(TAG, + "isHigherPriority(challengerProfile=" + challengerProfile + + ", holderProfile=" + challengerProfile + ")"); } return true; } } + + @VisibleForTesting + protected void registerClientProfileInternal(ResourceClientProfile profile, + IResourcesReclaimListener listener, int[] clientId) { + if (DEBUG) { + Slog.d(TAG, "registerClientProfile(clientProfile=" + profile + ")"); + } + + clientId[0] = INVALID_CLIENT_ID; + if (mManager == null) { + Slog.e(TAG, "TvInputManager is null. Can't register client profile."); + return; + } + // TODO tell if the client already exists + clientId[0] = mNextUnusedClientId++; + + int pid = profile.getTvInputSessionId() == null + ? Binder.getCallingPid() /*callingPid*/ + : mManager.getClientPid(profile.getTvInputSessionId()); /*tvAppId*/ + + ClientProfile clientProfile = new ClientProfile.Builder(clientId[0]) + .tvInputSessionId(profile.getTvInputSessionId()) + .useCase(profile.getUseCase()) + .processId(pid) + .build(); + clientProfile.setPriority(getClientPriority(profile.getUseCase(), pid)); + + mClientProfiles.append(clientId[0], clientProfile); + mListeners.append(clientId[0], listener); + mRegisteredClientIds.add(clientId[0]); + } + + @VisibleForTesting + protected void unregisterClientProfileInternal(int clientId) { + if (DEBUG) { + Slog.d(TAG, "unregisterClientProfile(clientId=" + clientId + ")"); + } + for (int id : getClientProfile(clientId).getInUseFrontendIds()) { + getFrontendResource(id).removeOwner(); + for (int groupMemberId : getFrontendResource(id).getExclusiveGroupMemberFeIds()) { + getFrontendResource(groupMemberId).removeOwner(); + } + } + mClientProfiles.remove(clientId); + mListeners.remove(clientId); + mRegisteredClientIds.remove(clientId); + } + + @VisibleForTesting + protected boolean updateClientPriorityInternal(int clientId, int priority, int niceValue) { + if (DEBUG) { + Slog.d(TAG, + "updateClientPriority(clientId=" + clientId + ", priority=" + priority + + ", niceValue=" + niceValue + ")"); + } + + ClientProfile profile = getClientProfile(clientId); + if (profile == null) { + Slog.e(TAG, + "Can not find client profile with id " + clientId + + " when trying to update the client priority."); + return false; + } + + profile.setPriority(priority); + profile.setNiceValue(niceValue); + + return true; + } + + @VisibleForTesting + protected void setFrontendInfoListInternal(TunerFrontendInfo[] infos) { + if (DEBUG) { + Slog.d(TAG, "updateFrontendInfo:"); + for (int i = 0; i < infos.length; i++) { + Slog.d(TAG, infos[i].toString()); + } + } + + // An arrayList to record the frontends pending on updating. Ids will be removed + // from this list once its updating finished. Any frontend left in this list when all + // the updates are done will be removed from mAvailableFrontendIds and + // mFrontendResources. + List<Integer> updatingFrontendIds = new ArrayList<>(mAvailableFrontendIds); + + // Update frontendResources sparse array and other mappings accordingly + for (int i = 0; i < infos.length; i++) { + if (getFrontendResource(infos[i].getId()) != null) { + if (DEBUG) { + Slog.d(TAG, "Frontend id=" + infos[i].getId() + "exists."); + } + updatingFrontendIds.remove(new Integer(infos[i].getId())); + } else { + // Add a new fe resource + FrontendResource newFe = new FrontendResource.Builder(infos[i].getId()) + .type(infos[i].getFrontendType()) + .exclusiveGroupId(infos[i].getExclusiveGroupId()) + .build(); + // Update the exclusive group member list in all the existing Frontend resource + for (Integer feId : mAvailableFrontendIds) { + FrontendResource fe = getFrontendResource(feId.intValue()); + if (fe.getExclusiveGroupId() == newFe.getExclusiveGroupId()) { + newFe.addExclusiveGroupMemberFeId(fe.getId()); + newFe.addExclusiveGroupMemberFeId(fe.getExclusiveGroupMemberFeIds()); + for (Integer excGroupmemberFeId : fe.getExclusiveGroupMemberFeIds()) { + getFrontendResource(excGroupmemberFeId.intValue()) + .addExclusiveGroupMemberFeId(newFe.getId()); + } + fe.addExclusiveGroupMemberFeId(newFe.getId()); + break; + } + } + // Update resource list and available id list + mFrontendResources.append(newFe.getId(), newFe); + mAvailableFrontendIds.add(newFe.getId()); + } + } + + // TODO check if the removing resource is in use or not. Handle the conflict. + for (Integer removingId : updatingFrontendIds) { + // update the exclusive group id memver list + FrontendResource fe = getFrontendResource(removingId.intValue()); + fe.removeExclusiveGroupMemberFeId(new Integer(fe.getId())); + for (Integer excGroupmemberFeId : fe.getExclusiveGroupMemberFeIds()) { + getFrontendResource(excGroupmemberFeId.intValue()) + .removeExclusiveGroupMemberFeId(new Integer(fe.getId())); + } + mFrontendResources.remove(removingId.intValue()); + mAvailableFrontendIds.remove(removingId); + } + } + + @VisibleForTesting + protected boolean requestFrontendInternal(TunerFrontendRequest request, int[] frontendId) + throws RemoteException { + if (DEBUG) { + Slog.d(TAG, "requestFrontend(request=" + request + ")"); + } + + frontendId[0] = TunerResourceManager.INVALID_FRONTEND_ID; + if (!checkClientExists(request.getClientId())) { + Slog.e(TAG, "Request frontend from unregistered client:" + request.getClientId()); + return false; + } + ClientProfile requestClient = getClientProfile(request.getClientId()); + int grantingFrontendId = -1; + int inUseLowestPriorityFrId = -1; + // Priority max value is 1000 + int currentLowestPriority = MAX_CLIENT_PRIORITY + 1; + for (int id : mAvailableFrontendIds) { + FrontendResource fr = getFrontendResource(id); + if (fr.getType() == request.getFrontendType()) { + if (!fr.isInUse()) { + // Grant unused frontend with no exclusive group members first. + if (fr.getExclusiveGroupMemberFeIds().size() == 0) { + grantingFrontendId = id; + break; + } else if (grantingFrontendId < 0) { + // Grant the unused frontend with lower id first if all the unused + // frontends have exclusive group members. + grantingFrontendId = id; + } + } else if (grantingFrontendId < 0) { + // Record the frontend id with the lowest client priority among all the + // in use frontends when no available frontend has been found. + int priority = getOwnerClientPriority(id); + if (currentLowestPriority > priority) { + inUseLowestPriorityFrId = id; + currentLowestPriority = priority; + } + } + } + } + + // Grant frontend when there is unused resource. + if (grantingFrontendId > -1) { + frontendId[0] = grantingFrontendId; + updateFrontendClientMappingOnNewGrant(frontendId[0], request.getClientId()); + return true; + } + + // When all the resources are occupied, grant the lowest priority resource if the + // request client has higher priority. + if (inUseLowestPriorityFrId > -1 && (requestClient.getPriority() > currentLowestPriority)) { + frontendId[0] = inUseLowestPriorityFrId; + reclaimFrontendResource(getFrontendResource(frontendId[0]).getOwnerClientId()); + updateFrontendClientMappingOnNewGrant(frontendId[0], request.getClientId()); + return true; + } + + return false; + } + + @VisibleForTesting + protected int getClientPriority(int useCase, int pid) { + if (DEBUG) { + Slog.d(TAG, "getClientPriority useCase=" + useCase + + ", pid=" + pid + ")"); + } + + if (isForeground(pid)) { + return mPriorityCongfig.getForegroundPriority(useCase); + } + return mPriorityCongfig.getBackgroundPriority(useCase); + } + + @VisibleForTesting + protected boolean isForeground(int pid) { + // TODO: how to get fg/bg information from pid + return true; + } + + @VisibleForTesting + protected void reclaimFrontendResource(int reclaimingId) throws RemoteException { + if (mListeners.get(reclaimingId) != null) { + try { + mListeners.get(reclaimingId).onReclaimResources(); + } catch (RemoteException e) { + throw e.rethrowFromSystemServer(); + } + } + } + + private void updateFrontendClientMappingOnNewGrant(int grantingId, int ownerClientId) { + FrontendResource grantingFrontend = getFrontendResource(grantingId); + ClientProfile ownerProfile = getClientProfile(ownerClientId); + grantingFrontend.setOwner(ownerClientId); + ownerProfile.useFrontend(grantingId); + for (int exclusiveGroupMember : grantingFrontend.getExclusiveGroupMemberFeIds()) { + getFrontendResource(exclusiveGroupMember).setOwner(ownerClientId); + ownerProfile.useFrontend(exclusiveGroupMember); + } + } + + /** + * Get the owner client's priority from the frontend id. + * + * @param frontendId an in use frontend id. + * @return the priority of the owner client of the frontend. + */ + private int getOwnerClientPriority(int frontendId) { + return getClientProfile(getFrontendResource(frontendId).getOwnerClientId()).getPriority(); + } + + private ClientProfile getClientProfile(int clientId) { + return mClientProfiles.get(clientId); + } + + protected FrontendResource getFrontendResource(int frontendId) { + return mFrontendResources.get(frontendId); + } + + @VisibleForTesting + protected SparseArray<ClientProfile> getClientProfiles() { + return mClientProfiles; + } + + @VisibleForTesting + protected SparseArray<FrontendResource> getFrontendResources() { + return mFrontendResources; + } + + private boolean checkClientExists(int clientId) { + return mRegisteredClientIds.contains(clientId); + } + + private void enforceAccessPermission() { + getContext().enforceCallingOrSelfPermission( + "android.permission.TUNER_RESOURCE_ACCESS", TAG); + } } diff --git a/services/core/java/com/android/server/tv/tunerresourcemanager/UseCasePriorityHints.java b/services/core/java/com/android/server/tv/tunerresourcemanager/UseCasePriorityHints.java new file mode 100644 index 000000000000..8c2de475b99b --- /dev/null +++ b/services/core/java/com/android/server/tv/tunerresourcemanager/UseCasePriorityHints.java @@ -0,0 +1,236 @@ +/* + * Copyright 2020 The Android Open Source Project + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ +package com.android.server.tv.tunerresourcemanager; + +import android.media.tv.TvInputService; +import android.media.tv.TvInputService.PriorityHintUseCaseType; +import android.util.Log; +import android.util.Slog; +import android.util.SparseArray; +import android.util.Xml; + +import com.android.internal.annotations.VisibleForTesting; + +import org.xmlpull.v1.XmlPullParser; +import org.xmlpull.v1.XmlPullParserException; + +import java.io.File; +import java.io.FileInputStream; +import java.io.IOException; +import java.io.InputStream; +import java.util.ArrayList; +import java.util.List; + +/** + * This class provides the Tuner Resource Manager use case priority hints config info including a + * parser that can read the xml config from the vendors. + * + * @hide + */ +public class UseCasePriorityHints { + private static final String TAG = "UseCasePriorityHints"; + private static final boolean DEBUG = Log.isLoggable(TAG, Log.DEBUG); + + private static final String PATH_TO_VENDOR_CONFIG_XML = + "/vendor/etc/tunerResourceManagerUseCaseConfig.xml"; + private static final int INVALID_PRIORITY_VALUE = -1; + private static final int INVALID_USE_CASE = -1; + + /** + * Array of the configured use case priority hints. Key is the use case id. Value is a size 2 + * int array. The first element carries the priority of the use case on foreground. The second + * shows the background priority. + */ + SparseArray<int[]> mPriorityHints = new SparseArray<>(); + + List<Integer> mVendorDefinedUseCase = new ArrayList<>(); + + private int mDefaultForeground = 150; + private int mDefaultBackground = 50; + + int getForegroundPriority(int useCase) { + if (mPriorityHints.get(useCase) != null && mPriorityHints.get(useCase).length == 2) { + return mPriorityHints.get(useCase)[0]; + } + return mDefaultForeground; + } + + int getBackgroundPriority(int useCase) { + if (mPriorityHints.get(useCase) != null && mPriorityHints.get(useCase).length == 2) { + return mPriorityHints.get(useCase)[1]; + } + return mDefaultBackground; + } + + boolean isDefinedUseCase(int useCase) { + return (mVendorDefinedUseCase.contains(useCase) || isPredefinedUseCase(useCase)); + } + + /** + * To parse the vendor use case config. + */ + public void parse() { + // Override the default priority with vendor setting if available. + File file = new File(PATH_TO_VENDOR_CONFIG_XML); + if (file.exists()) { + try { + InputStream in = new FileInputStream(file); + parseInternal(in); + return; + } catch (IOException e) { + Slog.e(TAG, "Error reading vendor file: " + file, e); + } catch (XmlPullParserException e) { + Slog.e(TAG, "Unable to parse vendor file: " + file, e); + } + } else { + if (DEBUG) { + Slog.i(TAG, "no vendor priority configuration available. Using default priority"); + } + addNewUseCasePriority(TvInputService.PRIORITY_HINT_USE_CASE_TYPE_BACKGROUND, 180, 100); + addNewUseCasePriority(TvInputService.PRIORITY_HINT_USE_CASE_TYPE_SCAN, 450, 200); + addNewUseCasePriority(TvInputService.PRIORITY_HINT_USE_CASE_TYPE_PLAYBACK, 480, 300); + addNewUseCasePriority(TvInputService.PRIORITY_HINT_USE_CASE_TYPE_LIVE, 490, 400); + addNewUseCasePriority(TvInputService.PRIORITY_HINT_USE_CASE_TYPE_RECORD, 600, 500); + } + } + + // We don't use namespaces + private static final String NS = null; + + @VisibleForTesting + protected void parseInternal(InputStream in) + throws IOException, XmlPullParserException { + try { + XmlPullParser parser = Xml.newPullParser(); + parser.setFeature(XmlPullParser.FEATURE_PROCESS_NAMESPACES, false); + parser.setInput(in, null); + parser.nextTag(); + readUseCase(parser); + in.close(); + } catch (IOException | XmlPullParserException e) { + throw e; + } + for (int i = 0; i < mPriorityHints.size(); i++) { + int useCase = mPriorityHints.keyAt(i); + int[] priorities = mPriorityHints.get(useCase); + if (DEBUG) { + Slog.d(TAG, "{defaultFg=" + mDefaultForeground + + ", defaultBg=" + mDefaultBackground + "}"); + Slog.d(TAG, "{useCase=" + useCase + + ", fg=" + priorities[0] + + ", bg=" + priorities[1] + + "}"); + } + } + } + + private void readUseCase(XmlPullParser parser) + throws XmlPullParserException, IOException { + parser.require(XmlPullParser.START_TAG, NS, "config"); + while (parser.next() != XmlPullParser.END_TAG) { + if (parser.getEventType() != XmlPullParser.START_TAG) { + continue; + } + String name = parser.getName(); + int useCase; + if (name.equals("useCaseDefault")) { + mDefaultForeground = readAttributeToInt("fgPriority", parser); + mDefaultBackground = readAttributeToInt("bgPriority", parser); + parser.nextTag(); + parser.require(XmlPullParser.END_TAG, NS, name); + } else if (name.equals("useCasePreDefined")) { + useCase = formatTypeToNum("type", parser); + if (useCase == INVALID_USE_CASE) { + Slog.e(TAG, "Wrong predefined use case name given in the vendor config."); + continue; + } + addNewUseCasePriority(useCase, + readAttributeToInt("fgPriority", parser), + readAttributeToInt("bgPriority", parser)); + parser.nextTag(); + parser.require(XmlPullParser.END_TAG, NS, name); + } else if (name.equals("useCaseVendor")) { + useCase = readAttributeToInt("id", parser); + addNewUseCasePriority(useCase, + readAttributeToInt("fgPriority", parser), + readAttributeToInt("bgPriority", parser)); + mVendorDefinedUseCase.add(useCase); + parser.nextTag(); + parser.require(XmlPullParser.END_TAG, NS, name); + } else { + skip(parser); + } + } + } + + private void skip(XmlPullParser parser) throws XmlPullParserException, IOException { + if (parser.getEventType() != XmlPullParser.START_TAG) { + throw new IllegalStateException(); + } + int depth = 1; + while (depth != 0) { + switch (parser.next()) { + case XmlPullParser.END_TAG: + depth--; + break; + case XmlPullParser.START_TAG: + depth++; + break; + } + } + } + + private int readAttributeToInt(String attributeName, XmlPullParser parser) { + return Integer.valueOf(parser.getAttributeValue(null, attributeName)); + } + + private void addNewUseCasePriority(int useCase, int fgPriority, int bgPriority) { + int[] priorities = {fgPriority, bgPriority}; + mPriorityHints.append(useCase, priorities); + } + + @PriorityHintUseCaseType + private static int formatTypeToNum(String attributeName, XmlPullParser parser) { + String useCaseName = parser.getAttributeValue(null, attributeName); + switch (useCaseName) { + case "USE_CASE_BACKGROUND": + return TvInputService.PRIORITY_HINT_USE_CASE_TYPE_BACKGROUND; + case "USE_CASE_SCAN": + return TvInputService.PRIORITY_HINT_USE_CASE_TYPE_SCAN; + case "USE_CASE_PLAYBACK": + return TvInputService.PRIORITY_HINT_USE_CASE_TYPE_PLAYBACK; + case "USE_CASE_LIVE": + return TvInputService.PRIORITY_HINT_USE_CASE_TYPE_LIVE; + case "USE_CASE_RECORD": + return TvInputService.PRIORITY_HINT_USE_CASE_TYPE_RECORD; + default: + return INVALID_USE_CASE; + } + } + + private static boolean isPredefinedUseCase(int useCase) { + switch (useCase) { + case TvInputService.PRIORITY_HINT_USE_CASE_TYPE_BACKGROUND: + case TvInputService.PRIORITY_HINT_USE_CASE_TYPE_SCAN: + case TvInputService.PRIORITY_HINT_USE_CASE_TYPE_PLAYBACK: + case TvInputService.PRIORITY_HINT_USE_CASE_TYPE_LIVE: + case TvInputService.PRIORITY_HINT_USE_CASE_TYPE_RECORD: + return true; + default: + return false; + } + } +} diff --git a/services/core/java/com/android/server/uri/UriGrantsManagerService.java b/services/core/java/com/android/server/uri/UriGrantsManagerService.java index e3b7c0aae507..fe34e86a27ae 100644 --- a/services/core/java/com/android/server/uri/UriGrantsManagerService.java +++ b/services/core/java/com/android/server/uri/UriGrantsManagerService.java @@ -739,6 +739,8 @@ public class UriGrantsManagerService extends IUriGrantsManager.Stub { final UriPermission perm = findOrCreateUriPermission( pi.packageName, targetPkg, targetUid, grantUri); perm.grantModes(modeFlags, owner); + getPmInternal().grantImplicitAccess(UserHandle.getUserId(targetUid), null, + UserHandle.getAppId(targetUid), pi.applicationInfo.uid, false /*direct*/); } /** Like grantUriPermissionUnchecked, but takes an Intent. */ diff --git a/services/core/java/com/android/server/webkit/WebViewUpdateService.java b/services/core/java/com/android/server/webkit/WebViewUpdateService.java index 8130546e2699..9bbeb728ae33 100644 --- a/services/core/java/com/android/server/webkit/WebViewUpdateService.java +++ b/services/core/java/com/android/server/webkit/WebViewUpdateService.java @@ -211,8 +211,9 @@ public class WebViewUpdateService extends SystemService { PackageManagerInternal.class); final int webviewUid = pmInternal.getPackageUidInternal( webViewPackageName, 0, UserHandle.getUserId(callingUid)); - pmInternal.grantImplicitAccess(UserHandle.getUserId(callingUid), null, webviewUid, - UserHandle.getAppId(callingUid)); + pmInternal.grantImplicitAccess(UserHandle.getUserId(callingUid), null, + UserHandle.getAppId(callingUid), webviewUid, + true /*direct*/); } /** diff --git a/services/core/java/com/android/server/wm/ActivityRecord.java b/services/core/java/com/android/server/wm/ActivityRecord.java index 190af7a06c8d..d715ed408d51 100644 --- a/services/core/java/com/android/server/wm/ActivityRecord.java +++ b/services/core/java/com/android/server/wm/ActivityRecord.java @@ -3845,13 +3845,6 @@ final class ActivityRecord extends WindowToken implements WindowManagerService.A mDisplayContent.setLayoutNeeded(); } mWmService.mH.obtainMessage(H.NOTIFY_ACTIVITY_DRAWN, token).sendToTarget(); - - // Notify the pinned stack upon all windows drawn. If there was an animation in - // progress then this signal will resume that animation. - final ActivityStack pinnedStack = mDisplayContent.getRootPinnedTask(); - if (pinnedStack != null) { - pinnedStack.onAllWindowsDrawn(); - } } } } @@ -6755,18 +6748,8 @@ final class ActivityRecord extends WindowToken implements WindowManagerService.A // for the next re-entry into PiP (assuming the activity is not hidden or destroyed) final ActivityStack pinnedStack = mDisplayContent.getRootPinnedTask(); if (pinnedStack == null) return; - final Rect stackBounds; - if (pinnedStack.lastAnimatingBoundsWasToFullscreen()) { - // We are animating the bounds, use the pre-animation bounds to save the snap - // fraction - stackBounds = pinnedStack.mPreAnimationBounds; - } else { - // We skip the animation if the fullscreen configuration is not compatible, so - // use the current bounds to calculate the saved snap fraction instead - // (see PinnedActivityStack.skipResizeAnimation()) - stackBounds = mTmpRect; - pinnedStack.getBounds(stackBounds); - } + final Rect stackBounds = mTmpRect; + pinnedStack.getBounds(stackBounds); mDisplayContent.mPinnedStackControllerLocked.saveReentryBounds( mActivityComponent, stackBounds); } diff --git a/services/core/java/com/android/server/wm/ActivityStack.java b/services/core/java/com/android/server/wm/ActivityStack.java index 2f1cc0532ccc..ff890ff13ee3 100644 --- a/services/core/java/com/android/server/wm/ActivityStack.java +++ b/services/core/java/com/android/server/wm/ActivityStack.java @@ -98,10 +98,6 @@ import static com.android.server.wm.ActivityTaskManagerDebugConfig.TAG_WITH_CLAS import static com.android.server.wm.ActivityTaskManagerService.H.FIRST_ACTIVITY_STACK_MSG; import static com.android.server.wm.ActivityTaskManagerService.RELAUNCH_REASON_FREE_RESIZE; import static com.android.server.wm.ActivityTaskManagerService.RELAUNCH_REASON_WINDOWING_MODE_RESIZE; -import static com.android.server.wm.BoundsAnimationController.FADE_IN; -import static com.android.server.wm.BoundsAnimationController.NO_PIP_MODE_CHANGED_CALLBACKS; -import static com.android.server.wm.BoundsAnimationController.SCHEDULE_PIP_MODE_CHANGED_ON_END; -import static com.android.server.wm.BoundsAnimationController.SCHEDULE_PIP_MODE_CHANGED_ON_START; import static com.android.server.wm.DragResizeMode.DRAG_RESIZE_MODE_DOCKED_DIVIDER; import static com.android.server.wm.TaskProto.ACTIVITIES; import static com.android.server.wm.TaskProto.ACTIVITY_TYPE; @@ -202,7 +198,7 @@ import java.util.function.Consumer; /** * State and management of a single stack of activities. */ -class ActivityStack extends Task implements BoundsAnimationTarget { +class ActivityStack extends Task { private static final String TAG = TAG_WITH_CLASS_NAME ? "ActivityStack" : TAG_ATM; static final String TAG_ADD_REMOVE = TAG + POSTFIX_ADD_REMOVE; private static final String TAG_APP = TAG + POSTFIX_APP; @@ -327,11 +323,8 @@ class ActivityStack extends Task implements BoundsAnimationTarget { // Set when an animation has been requested but has not yet started from the UI thread. This is // cleared when the animation actually starts. private boolean mBoundsAnimatingRequested = false; - private boolean mBoundsAnimatingToFullscreen = false; - private boolean mCancelCurrentBoundsAnimation = false; private Rect mBoundsAnimationTarget = new Rect(); private Rect mBoundsAnimationSourceHintBounds = new Rect(); - private @BoundsAnimationController.AnimationType int mAnimationType; Rect mPreAnimationBounds = new Rect(); @@ -790,17 +783,6 @@ class ActivityStack extends Task implements BoundsAnimationTarget { setWindowingMode(windowingMode, false /* animate */, false /* showRecents */, false /* enteringSplitScreenMode */, false /* deferEnsuringVisibility */, false /* creating */); - if (windowingMode == WINDOWING_MODE_PINNED) { - // This stack will be visible before SystemUI requests PiP animation to start, and if - // the selected animation type is fade in, this stack will be close first. If the - // animation does not start early, user may see this full screen window appear again - // after the closing transition finish, which could cause screen to flicker. - // Check if animation should start here in advance. - final BoundsAnimationController controller = getDisplay().mBoundsAnimationController; - if (controller.isAnimationTypeFadeIn()) { - setPinnedStackAlpha(0f); - } - } } /** @@ -905,6 +887,9 @@ class ActivityStack extends Task implements BoundsAnimationTarget { likelyResolvedMode = parent != null ? parent.getWindowingMode() : WINDOWING_MODE_FULLSCREEN; } + if (currentMode == WINDOWING_MODE_PINNED) { + mAtmService.getTaskChangeNotificationController().notifyActivityUnpinned(); + } if (sendNonResizeableNotification && likelyResolvedMode != WINDOWING_MODE_FULLSCREEN && topActivity != null && !topActivity.noDisplay && topActivity.isNonResizableOrForcedResizable(likelyResolvedMode)) { @@ -3480,91 +3465,6 @@ class ActivityStack extends Task implements BoundsAnimationTarget { } } - void animateResizePinnedStack(Rect toBounds, Rect sourceHintBounds, int animationDuration, - boolean fromFullscreen) { - if (!inPinnedWindowingMode()) return; - - /** - * TODO(b/146594635): Remove all PIP animation code from WM once SysUI handles animation. - * If this PIP Task is controlled by a TaskOrganizer, the animation occurs entirely - * on the TaskOrganizer side, so we just hand over the leash without doing any animation. - * We have to be careful to not schedule the enter-pip callback as the TaskOrganizer - * needs to have flexibility to schedule that at an appropriate point in the animation. - */ - if (isControlledByTaskOrganizer()) return; - if (toBounds == null /* toFullscreen */) { - final Configuration parentConfig = getParent().getConfiguration(); - final ActivityRecord top = topRunningNonOverlayTaskActivity(); - if (top != null && !top.isConfigurationCompatible(parentConfig)) { - // The final orientation of this activity will change after moving to full screen. - // Start freezing screen here to prevent showing a temporary full screen window. - top.startFreezingScreenLocked(CONFIG_SCREEN_LAYOUT); - dismissPip(); - return; - } - } - - // Get the from-bounds - final Rect fromBounds = new Rect(); - getBounds(fromBounds); - - // Get non-null fullscreen to-bounds for animating if the bounds are null - @BoundsAnimationController.SchedulePipModeChangedState int schedulePipModeChangedState = - NO_PIP_MODE_CHANGED_CALLBACKS; - final boolean toFullscreen = toBounds == null; - if (toFullscreen) { - if (fromFullscreen) { - throw new IllegalArgumentException("Should not defer scheduling PiP mode" - + " change on animation to fullscreen."); - } - schedulePipModeChangedState = SCHEDULE_PIP_MODE_CHANGED_ON_START; - - mWmService.getStackBounds( - WINDOWING_MODE_FULLSCREEN, ACTIVITY_TYPE_STANDARD, mTmpToBounds); - if (!mTmpToBounds.isEmpty()) { - // If there is a fullscreen bounds, use that - toBounds = new Rect(mTmpToBounds); - } else { - // Otherwise, use the display bounds - toBounds = new Rect(); - getDisplayContent().getBounds(toBounds); - } - } else if (fromFullscreen) { - schedulePipModeChangedState = SCHEDULE_PIP_MODE_CHANGED_ON_END; - } - - setAnimationFinalBounds(sourceHintBounds, toBounds, toFullscreen); - - final Rect finalToBounds = toBounds; - final @BoundsAnimationController.SchedulePipModeChangedState int - finalSchedulePipModeChangedState = schedulePipModeChangedState; - final DisplayContent displayContent = getDisplayContent(); - @BoundsAnimationController.AnimationType int intendedAnimationType = - displayContent.mBoundsAnimationController.getAnimationType(); - if (intendedAnimationType == FADE_IN) { - if (fromFullscreen) { - setPinnedStackAlpha(0f); - } - if (toBounds.width() == fromBounds.width() - && toBounds.height() == fromBounds.height()) { - intendedAnimationType = BoundsAnimationController.BOUNDS; - } else if (!fromFullscreen && !toBounds.equals(fromBounds)) { - // intendedAnimationType may have been reset at the end of RecentsAnimation, - // force it to BOUNDS type if we know for certain we're animating to - // a different bounds, especially for expand and collapse of PiP window. - intendedAnimationType = BoundsAnimationController.BOUNDS; - } - } - - final @BoundsAnimationController.AnimationType int animationType = intendedAnimationType; - mCancelCurrentBoundsAnimation = false; - displayContent.mBoundsAnimationController.getHandler().post(() -> { - displayContent.mBoundsAnimationController.animateBounds(this, fromBounds, - finalToBounds, animationDuration, finalSchedulePipModeChangedState, - fromFullscreen, toFullscreen, animationType); - }); - } - void dismissPip() { if (!isActivityTypeStandardOrUndefined()) { throw new IllegalArgumentException( @@ -3593,21 +3493,6 @@ class ActivityStack extends Task implements BoundsAnimationTarget { }); } - private void updatePictureInPictureModeForPinnedStackAnimation(Rect targetStackBounds, - boolean forceUpdate) { - // It is guaranteed that the activities requiring the update will be in the pinned stack at - // this point (either reparented before the animation into PiP, or before reparenting after - // the animation out of PiP) - if (!isAttached()) { - return; - } - final PooledConsumer c = PooledLambda.obtainConsumer( - ActivityStackSupervisor::updatePictureInPictureMode, mStackSupervisor, - PooledLambda.__(Task.class), targetStackBounds, forceUpdate); - forAllLeafTasks(c, true /* traverseTopToBottom */); - c.recycle(); - } - void prepareFreezingTaskBounds() { forAllLeafTasks(Task::prepareFreezingBounds, true /* traverseTopToBottom */); } @@ -3704,34 +3589,6 @@ class ActivityStack extends Task implements BoundsAnimationTarget { } /** - * Sets the bounds animation target bounds ahead of an animation. This can't currently be done - * in onAnimationStart() since that is started on the UiThread. - */ - private void setAnimationFinalBounds(Rect sourceHintBounds, Rect destBounds, - boolean toFullscreen) { - if (mAnimationType == BoundsAnimationController.BOUNDS) { - mBoundsAnimatingRequested = true; - } - mBoundsAnimatingToFullscreen = toFullscreen; - if (destBounds != null) { - mBoundsAnimationTarget.set(destBounds); - } else { - mBoundsAnimationTarget.setEmpty(); - } - if (sourceHintBounds != null) { - mBoundsAnimationSourceHintBounds.set(sourceHintBounds); - } else if (!mBoundsAnimating) { - // If the bounds are already animating, we don't want to reset the source hint. This is - // because the source hint is sent when starting the animation from the client that - // requested to enter pip. Other requests can adjust the pip bounds during an animation, - // but could accidentally reset the source hint bounds. - mBoundsAnimationSourceHintBounds.setEmpty(); - } - - mPreAnimationBounds.set(getRawBounds()); - } - - /** * @return the final bounds for the bounds animation. */ void getFinalAnimationBounds(Rect outBounds) { @@ -3763,30 +3620,6 @@ class ActivityStack extends Task implements BoundsAnimationTarget { } /** - * Reset the current animation running on {@link #mBoundsAnimationTarget}. - * - * @param destinationBounds the final destination bounds - */ - void resetCurrentBoundsAnimation(Rect destinationBounds) { - boolean animating = (mBoundsAnimatingRequested || mBoundsAnimating) - && !mBoundsAnimationTarget.isEmpty(); - - // The final boundary is updated while there is an existing boundary animation. Let's - // cancel this animation to prevent the obsolete animation overwritten updated bounds. - if (animating && !destinationBounds.equals(mBoundsAnimationTarget)) { - final BoundsAnimationController controller = - getDisplayContent().mBoundsAnimationController; - controller.getHandler().post(() -> controller.cancel(this)); - } - // Once we've set the bounds based on the rotation of the old bounds in the new - // orientation, clear the animation target bounds since they are obsolete, and - // cancel any currently running animations - mBoundsAnimationTarget.setEmpty(); - mBoundsAnimationSourceHintBounds.setEmpty(); - mCancelCurrentBoundsAnimation = true; - } - - /** * Updates the passed-in {@code inOutBounds} based on the current state of the * docked controller. This gets run *after* the override configuration is updated, so it's * safe to rely on the controller's state in here (though eventually this dependence should @@ -4590,110 +4423,6 @@ class ActivityStack extends Task implements BoundsAnimationTarget { return task != null; } - public boolean setPinnedStackSize(Rect displayedBounds, Rect configBounds) { - // Hold the lock since this is called from the BoundsAnimator running on the UiThread - synchronized (mWmService.mGlobalLock) { - if (mCancelCurrentBoundsAnimation) { - return false; - } - mStackSupervisor.resizePinnedStack(displayedBounds, configBounds); - } - - return true; - } - - void onAllWindowsDrawn() { - if (!mBoundsAnimating && !mBoundsAnimatingRequested) { - return; - } - - getDisplayContent().mBoundsAnimationController.onAllWindowsDrawn(); - } - - @Override // AnimatesBounds - public boolean onAnimationStart(boolean schedulePipModeChangedCallback, boolean forceUpdate, - @BoundsAnimationController.AnimationType int animationType) { - // Hold the lock since this is called from the BoundsAnimator running on the UiThread - synchronized (mWmService.mGlobalLock) { - if (!isAttached()) { - // Don't run the animation if the stack is already detached - return false; - } - - if (animationType == BoundsAnimationController.BOUNDS) { - mBoundsAnimatingRequested = false; - mBoundsAnimating = true; - } - mAnimationType = animationType; - - // If we are changing UI mode, as in the PiP to fullscreen - // transition, then we need to wait for the window to draw. - if (schedulePipModeChangedCallback) { - forAllWindows((w) -> { - w.mWinAnimator.resetDrawState(); - }, false /* traverseTopToBottom */); - } - } - - if (inPinnedWindowingMode()) { - try { - mWmService.mActivityTaskManager.notifyPinnedStackAnimationStarted(); - } catch (RemoteException e) { - // I don't believe you... - } - - if ((schedulePipModeChangedCallback || animationType == FADE_IN)) { - // We need to schedule the PiP mode change before the animation up. It is possible - // in this case for the animation down to not have been completed, so always - // force-schedule and update to the client to ensure that it is notified that it - // is no longer in picture-in-picture mode - updatePictureInPictureModeForPinnedStackAnimation(null, forceUpdate); - } - } - return true; - } - - @Override // AnimatesBounds - public void onAnimationEnd(boolean schedulePipModeChangedCallback, Rect finalStackSize, - boolean moveToFullscreen) { - synchronized (mWmService.mGlobalLock) { - if (inPinnedWindowingMode()) { - // Update to the final bounds if requested. This is done here instead of in the - // bounds animator to allow us to coordinate this after we notify the PiP mode - // changed - - if (schedulePipModeChangedCallback) { - // We need to schedule the PiP mode change after the animation down, so use the - // final bounds - updatePictureInPictureModeForPinnedStackAnimation(mBoundsAnimationTarget, - false /* forceUpdate */); - } - - if (mAnimationType == BoundsAnimationController.FADE_IN) { - setPinnedStackAlpha(1f); - mWmService.mAtmService.notifyPinnedStackAnimationEnded(); - return; - } - - if (finalStackSize != null && !mCancelCurrentBoundsAnimation) { - setPinnedStackSize(finalStackSize, null); - } else { - // We have been canceled, so the final stack size is null, still run the - // animation-end logic - onPipAnimationEndResize(); - } - - mWmService.mAtmService.notifyPinnedStackAnimationEnded(); - if (moveToFullscreen) { - ((ActivityStack) this).dismissPip(); - } - } else { - // No PiP animation, just run the normal animation-end logic - onPipAnimationEndResize(); - } - } - } - /** * Sets the current picture-in-picture aspect ratio. */ @@ -4741,42 +4470,6 @@ class ActivityStack extends Task implements BoundsAnimationTarget { getDisplayContent().getPinnedStackController().setActions(actions); } - /** Called immediately prior to resizing the tasks at the end of the pinned stack animation. */ - void onPipAnimationEndResize() { - mBoundsAnimating = false; - forAllLeafTasks(Task::clearPreserveNonFloatingState, false /* traverseTopToBottom */); - mWmService.requestTraversal(); - } - - @Override - public boolean shouldDeferStartOnMoveToFullscreen() { - synchronized (mWmService.mGlobalLock) { - if (!isAttached()) { - // Unnecessary to pause the animation because the stack is detached. - return false; - } - - // Workaround for the recents animation -- normally we need to wait for the new activity - // to show before starting the PiP animation, but because we start and show the home - // activity early for the recents animation prior to the PiP animation starting, there - // is no subsequent all-drawn signal. In this case, we can skip the pause when the home - // stack is already visible and drawn. - final ActivityStack homeStack = mDisplayContent.getRootHomeTask(); - if (homeStack == null) { - return true; - } - final Task homeTask = homeStack.getTopMostTask(); - if (homeTask == null) { - return true; - } - final ActivityRecord homeApp = homeTask.getTopVisibleActivity(); - if (!homeTask.isVisible() || homeApp == null) { - return true; - } - return !homeApp.allDrawn; - } - } - /** * @return True if we are currently animating the pinned stack from fullscreen to non-fullscreen * bounds and we have a deferred PiP mode changed callback set with the animation. @@ -4798,25 +4491,6 @@ class ActivityStack extends Task implements BoundsAnimationTarget { return mBoundsAnimating; } - public boolean isAnimatingBounds() { - return mBoundsAnimating; - } - - public boolean lastAnimatingBoundsWasToFullscreen() { - return mBoundsAnimatingToFullscreen; - } - - public boolean isAnimatingBoundsToFullscreen() { - return isAnimatingBounds() && lastAnimatingBoundsWasToFullscreen(); - } - - public boolean pinnedStackResizeDisallowed() { - if (mBoundsAnimating && mCancelCurrentBoundsAnimation) { - return true; - } - return false; - } - /** Returns true if a removal action is still being deferred. */ boolean checkCompleteDeferredRemoval() { if (isAnimating(TRANSITION | CHILDREN)) { @@ -4843,21 +4517,6 @@ class ActivityStack extends Task implements BoundsAnimationTarget { || activityType == ACTIVITY_TYPE_ASSISTANT; } - @Override - public boolean setPinnedStackAlpha(float alpha) { - // Hold the lock since this is called from the BoundsAnimator running on the UiThread - synchronized (mWmService.mGlobalLock) { - final SurfaceControl sc = getSurfaceControl(); - if (sc == null || !sc.isValid()) { - // If the stack is already removed, don't bother updating any stack animation - return false; - } - getPendingTransaction().setAlpha(sc, mCancelCurrentBoundsAnimation ? 1 : alpha); - scheduleAnimation(); - return !mCancelCurrentBoundsAnimation; - } - } - public DisplayInfo getDisplayInfo() { return mDisplayContent.getDisplayInfo(); } diff --git a/services/core/java/com/android/server/wm/ActivityStackSupervisor.java b/services/core/java/com/android/server/wm/ActivityStackSupervisor.java index 97b638820a8a..b2d2f624e458 100644 --- a/services/core/java/com/android/server/wm/ActivityStackSupervisor.java +++ b/services/core/java/com/android/server/wm/ActivityStackSupervisor.java @@ -1696,40 +1696,6 @@ public class ActivityStackSupervisor implements RecentTasks.Callbacks { } } - void resizePinnedStack(Rect displayedBounds, Rect inConfigBounds) { - // TODO(multi-display): The display containing the stack should be passed in. - final ActivityStack stack = - mRootWindowContainer.getDefaultDisplay().getRootPinnedTask(); - if (stack == null) { - Slog.w(TAG, "resizePinnedStackLocked: pinned stack not found"); - return; - } - - Trace.traceBegin(TRACE_TAG_WINDOW_MANAGER, "resizePinnedStack"); - mService.deferWindowLayout(); - try { - Rect configBounds = null; - if (inConfigBounds != null) { - // Use 0,0 as the position for the inset rect because we are headed for fullscreen. - configBounds = tempRect; - configBounds.top = 0; - configBounds.left = 0; - configBounds.right = inConfigBounds.width(); - configBounds.bottom = inConfigBounds.height(); - } - if (displayedBounds != null && inConfigBounds == null) { - // We have finished the animation into PiP, and are resizing the tasks to match the - // stack bounds, while layouts are deferred, update any task state as a part of - // transitioning it from fullscreen into a floating state. - stack.onPipAnimationEndResize(); - } - stack.resize(displayedBounds, configBounds, !PRESERVE_WINDOWS, !DEFER_RESUME); - } finally { - mService.continueWindowLayout(); - Trace.traceEnd(TRACE_TAG_WINDOW_MANAGER); - } - } - private void removeStackInSurfaceTransaction(ActivityStack stack) { if (stack.getWindowingMode() == WINDOWING_MODE_PINNED) { /** diff --git a/services/core/java/com/android/server/wm/ActivityStarter.java b/services/core/java/com/android/server/wm/ActivityStarter.java index 600a125cdf43..f52c7f29ac8a 100644 --- a/services/core/java/com/android/server/wm/ActivityStarter.java +++ b/services/core/java/com/android/server/wm/ActivityStarter.java @@ -97,7 +97,6 @@ import android.content.pm.PackageManagerInternal; import android.content.pm.ResolveInfo; import android.content.pm.UserInfo; import android.content.res.Configuration; -import android.graphics.Rect; import android.os.Binder; import android.os.Bundle; import android.os.IBinder; @@ -336,6 +335,7 @@ class ActivityStarter { int filterCallingUid; PendingIntentRecord originatingPendingIntent; boolean allowBackgroundActivityStart; + boolean isDream; /** * If set to {@code true}, allows this activity start to look into @@ -387,6 +387,7 @@ class ActivityStarter { filterCallingUid = UserHandle.USER_NULL; originatingPendingIntent = null; allowBackgroundActivityStart = false; + isDream = false; } /** @@ -427,6 +428,7 @@ class ActivityStarter { filterCallingUid = request.filterCallingUid; originatingPendingIntent = request.originatingPendingIntent; allowBackgroundActivityStart = request.allowBackgroundActivityStart; + isDream = request.isDream; } /** @@ -970,7 +972,7 @@ class ActivityStarter { restrictedBgActivity = shouldAbortBackgroundActivityStart(callingUid, callingPid, callingPackage, realCallingUid, realCallingPid, callerApp, request.originatingPendingIntent, request.allowBackgroundActivityStart, - intent); + request.isDream, intent); } finally { Trace.traceEnd(Trace.TRACE_TAG_WINDOW_MANAGER); } @@ -1180,13 +1182,18 @@ class ActivityStarter { boolean shouldAbortBackgroundActivityStart(int callingUid, int callingPid, final String callingPackage, int realCallingUid, int realCallingPid, WindowProcessController callerApp, PendingIntentRecord originatingPendingIntent, - boolean allowBackgroundActivityStart, Intent intent) { + boolean allowBackgroundActivityStart, boolean isDream, Intent intent) { // don't abort for the most important UIDs final int callingAppId = UserHandle.getAppId(callingUid); if (callingUid == Process.ROOT_UID || callingAppId == Process.SYSTEM_UID || callingAppId == Process.NFC_UID) { return false; } + + // don't abort if this is the dream activity + if (isDream) { + return false; + } // don't abort if the callingUid has a visible window or is a persistent system process final int callingUidProcState = mService.getUidState(callingUid); final boolean callingUidHasAnyVisibleWindow = @@ -1558,9 +1565,8 @@ class ActivityStarter { mIntent, mStartActivity.getUriPermissionsLocked(), mStartActivity.mUserId); mService.getPackageManagerInternalLocked().grantImplicitAccess( mStartActivity.mUserId, mIntent, - mCallingUid, - UserHandle.getAppId(mStartActivity.info.applicationInfo.uid) - ); + UserHandle.getAppId(mStartActivity.info.applicationInfo.uid), mCallingUid, + true /*direct*/); if (newTask) { EventLogTags.writeWmCreateTask(mStartActivity.mUserId, mStartActivity.getTask().mTaskId); @@ -2401,7 +2407,6 @@ class ActivityStarter { mNewTaskIntent != null ? mNewTaskIntent : mIntent, mVoiceSession, mVoiceInteractor, toTop, mStartActivity, mSourceRecord, mOptions); addOrReparentStartingActivity(task, "setTaskFromReuseOrCreateNewTask - mReuseTask"); - updateBounds(mStartActivity.getTask(), mLaunchParams.mBounds); if (DEBUG_TASKS) { Slog.v(TAG_TASKS, "Starting new activity " + mStartActivity @@ -2424,21 +2429,6 @@ class ActivityStarter { mIntentDelivered = true; } - @VisibleForTesting - void updateBounds(Task task, Rect bounds) { - if (bounds.isEmpty()) { - return; - } - - final Task rootTask = task.getRootTask(); - if (rootTask != null && rootTask.inPinnedWindowingMode()) { - mService.animateResizePinnedStack(rootTask.mTaskId, bounds, -1); - } else { - // TODO: I don't believe it is possible to reach this else condition anymore... - task.setBounds(bounds); - } - } - private void addOrReparentStartingActivity(Task parent, String reason) { if (mStartActivity.getTask() == null || mStartActivity.getTask() == parent) { parent.addChild(mStartActivity); @@ -2686,6 +2676,11 @@ class ActivityStarter { return this; } + ActivityStarter setIsDream(boolean isDream) { + mRequest.isDream = isDream; + return this; + } + void dump(PrintWriter pw, String prefix) { prefix = prefix + " "; pw.print(prefix); diff --git a/services/core/java/com/android/server/wm/ActivityTaskManagerService.java b/services/core/java/com/android/server/wm/ActivityTaskManagerService.java index 5392257ae0b0..693a5e499552 100644 --- a/services/core/java/com/android/server/wm/ActivityTaskManagerService.java +++ b/services/core/java/com/android/server/wm/ActivityTaskManagerService.java @@ -212,6 +212,8 @@ import android.os.WorkSource; import android.os.storage.IStorageManager; import android.os.storage.StorageManager; import android.provider.Settings; +import android.service.dreams.DreamActivity; +import android.service.dreams.DreamManagerInternal; import android.service.voice.IVoiceInteractionSession; import android.service.voice.VoiceInteractionManagerInternal; import android.sysprop.DisplayProperties; @@ -1231,6 +1233,52 @@ public class ActivityTaskManagerService extends IActivityTaskManager.Stub { } @Override + public boolean startDreamActivity(Intent intent) { + final WindowProcessController process = mProcessMap.getProcess(Binder.getCallingPid()); + final long origId = Binder.clearCallingIdentity(); + + // The dream activity is only called for non-doze dreams. + final ComponentName currentDream = LocalServices.getService(DreamManagerInternal.class) + .getActiveDreamComponent(/* doze= */ false); + + if (currentDream == null || currentDream.getPackageName() == null + || !currentDream.getPackageName().equals(process.mInfo.packageName)) { + Slog.e(TAG, "Calling package is not the current dream package. " + + "Aborting startDreamActivity..."); + return false; + } + + final ActivityInfo a = new ActivityInfo(); + a.theme = com.android.internal.R.style.Theme_Dream; + a.exported = true; + a.name = DreamActivity.class.getName(); + + + a.packageName = process.mInfo.packageName; + a.applicationInfo = process.mInfo; + a.processName = process.mInfo.processName; + a.uiOptions = process.mInfo.uiOptions; + a.taskAffinity = "android:" + a.packageName + "/dream"; + a.enabled = true; + a.launchMode = ActivityInfo.LAUNCH_SINGLE_INSTANCE; + + a.persistableMode = ActivityInfo.PERSIST_NEVER; + a.screenOrientation = ActivityInfo.SCREEN_ORIENTATION_UNSPECIFIED; + a.colorMode = ActivityInfo.COLOR_MODE_DEFAULT; + a.flags |= ActivityInfo.FLAG_EXCLUDE_FROM_RECENTS; + + try { + getActivityStartController().obtainStarter(intent, "dream") + .setActivityInfo(a) + .setIsDream(true) + .execute(); + return true; + } finally { + Binder.restoreCallingIdentity(origId); + } + } + + @Override public final WaitResult startActivityAndWait(IApplicationThread caller, String callingPackage, String callingFeatureId, Intent intent, String resolvedType, IBinder resultTo, String resultWho, int requestCode, int startFlags, ProfilerInfo profilerInfo, @@ -2403,7 +2451,7 @@ public class ActivityTaskManagerService extends IActivityTaskManager.Stub { final ActivityStarter starter = getActivityStartController().obtainStarter( null /* intent */, "moveTaskToFront"); if (starter.shouldAbortBackgroundActivityStart(callingUid, callingPid, callingPackage, -1, - -1, callerApp, null, false, null)) { + -1, callerApp, null, false, false, null)) { if (!isBackgroundActivityStartsEnabled()) { return; } @@ -2645,64 +2693,6 @@ public class ActivityTaskManagerService extends IActivityTaskManager.Stub { } } - @Override - public void animateResizePinnedStack(int stackId, Rect destBounds, int animationDuration) { - enforceCallerIsRecentsOrHasPermission(MANAGE_ACTIVITY_STACKS, "animateResizePinnedStack()"); - - final long ident = Binder.clearCallingIdentity(); - try { - synchronized (mGlobalLock) { - final ActivityStack stack = mRootWindowContainer.getStack(stackId); - if (stack == null) { - Slog.w(TAG, "resizeStack: stackId " + stackId + " not found."); - return; - } - if (stack.getWindowingMode() != WINDOWING_MODE_PINNED) { - throw new IllegalArgumentException("Stack: " + stackId - + " doesn't support animated resize."); - } - stack.animateResizePinnedStack(destBounds, null /* sourceHintBounds */, - animationDuration, false /* fromFullscreen */); - } - } finally { - Binder.restoreCallingIdentity(ident); - } - } - - @Override - public void offsetPinnedStackBounds(int stackId, Rect compareBounds, int xOffset, int yOffset, - int animationDuration) { - enforceCallerIsRecentsOrHasPermission(MANAGE_ACTIVITY_STACKS, "offsetPinnedStackBounds()"); - - final long ident = Binder.clearCallingIdentity(); - try { - synchronized (mGlobalLock) { - if (xOffset == 0 && yOffset == 0) { - return; - } - final ActivityStack stack = mRootWindowContainer.getStack(stackId); - if (stack == null) { - Slog.w(TAG, "offsetPinnedStackBounds: stackId " + stackId + " not found."); - return; - } - if (stack.getWindowingMode() != WINDOWING_MODE_PINNED) { - throw new IllegalArgumentException("Stack: " + stackId - + " doesn't support animated resize."); - } - final Rect destBounds = new Rect(); - stack.getAnimationOrCurrentBounds(destBounds); - if (destBounds.isEmpty() || !destBounds.equals(compareBounds)) { - Slog.w(TAG, "The current stack bounds does not matched! It may be obsolete."); - return; - } - destBounds.offset(xOffset, yOffset); - stack.animateResizePinnedStack(destBounds, null /* sourceHintBounds */, - animationDuration, false /* fromFullscreen */); - } - } finally { - Binder.restoreCallingIdentity(ident); - } - } /** * Moves the specified task to the primary-split-screen stack. * @@ -4016,17 +4006,7 @@ public class ActivityTaskManagerService extends IActivityTaskManager.Stub { throw new IllegalArgumentException("Stack: " + stack + " doesn't support animated resize."); } - /** - * TODO(b/146594635): Remove all PIP animation code from WM - * once SysUI handles animation. Don't even try to animate TaskOrganized tasks. - */ - if (animate && !stack.isControlledByTaskOrganizer()) { - stack.animateResizePinnedStack(null /* destBounds */, - null /* sourceHintBounds */, animationDuration, - false /* fromFullscreen */); - } else { - stack.dismissPip(); - } + stack.dismissPip(); } } finally { Binder.restoreCallingIdentity(ident); @@ -4121,15 +4101,10 @@ public class ActivityTaskManagerService extends IActivityTaskManager.Stub { } private boolean isInPictureInPictureMode(ActivityRecord r) { - if (r == null || r.getRootTask() == null || !r.inPinnedWindowingMode() - || r.getRootTask().isInStackLocked(r) == null) { - return false; - } - - // If we are animating to fullscreen then we have already dispatched the PIP mode - // changed, so we should reflect that check here as well. - final ActivityStack taskStack = r.getRootTask(); - return !taskStack.isAnimatingBoundsToFullscreen(); + return r != null + && r.getRootTask() != null + && r.inPinnedWindowingMode() + && r.getRootTask().isInStackLocked(r) != null; } @Override @@ -4213,11 +4188,9 @@ public class ActivityTaskManagerService extends IActivityTaskManager.Stub { // if it is not already expanding to fullscreen. Otherwise, the arguments will // be used the next time the activity enters PiP final ActivityStack stack = r.getRootTask(); - if (!stack.isAnimatingBoundsToFullscreen()) { - stack.setPictureInPictureAspectRatio( - r.pictureInPictureArgs.getAspectRatio()); - stack.setPictureInPictureActions(r.pictureInPictureArgs.getActions()); - } + stack.setPictureInPictureAspectRatio( + r.pictureInPictureArgs.getAspectRatio()); + stack.setPictureInPictureActions(r.pictureInPictureArgs.getActions()); } logPictureInPictureArgs(params); } @@ -4421,31 +4394,6 @@ public class ActivityTaskManagerService extends IActivityTaskManager.Stub { .supportsLocalVoiceInteraction(); } - /** Notifies all listeners when the pinned stack animation starts. */ - @Override - public void notifyPinnedStackAnimationStarted() { - mTaskChangeNotificationController.notifyPinnedStackAnimationStarted(); - } - - /** Notifies all listeners when the pinned stack animation ends. */ - @Override - public void notifyPinnedStackAnimationEnded() { - mTaskChangeNotificationController.notifyPinnedStackAnimationEnded(); - } - - @Override - public void resizePinnedStack(Rect displayedBounds, Rect configBounds) { - enforceCallerIsRecentsOrHasPermission(MANAGE_ACTIVITY_STACKS, "resizePinnedStack()"); - final long ident = Binder.clearCallingIdentity(); - try { - synchronized (mGlobalLock) { - mStackSupervisor.resizePinnedStack(displayedBounds, configBounds); - } - } finally { - Binder.restoreCallingIdentity(ident); - } - } - @Override public boolean updateConfiguration(Configuration values) { mAmInternal.enforceCallingPermission(CHANGE_CONFIGURATION, "updateConfiguration()"); diff --git a/services/core/java/com/android/server/wm/AppTaskImpl.java b/services/core/java/com/android/server/wm/AppTaskImpl.java index 8fa811915fc2..4cce212cb779 100644 --- a/services/core/java/com/android/server/wm/AppTaskImpl.java +++ b/services/core/java/com/android/server/wm/AppTaskImpl.java @@ -111,7 +111,7 @@ class AppTaskImpl extends IAppTask.Stub { final ActivityStarter starter = mService.getActivityStartController().obtainStarter( null /* intent */, "moveToFront"); if (starter.shouldAbortBackgroundActivityStart(callingUid, callingPid, - callingPackage, -1, -1, callerApp, null, false, null)) { + callingPackage, -1, -1, callerApp, null, false, false, null)) { if (!mService.isBackgroundActivityStartsEnabled()) { return; } diff --git a/services/core/java/com/android/server/wm/BoundsAnimationController.java b/services/core/java/com/android/server/wm/BoundsAnimationController.java deleted file mode 100644 index 5385e2fce008..000000000000 --- a/services/core/java/com/android/server/wm/BoundsAnimationController.java +++ /dev/null @@ -1,584 +0,0 @@ -/* - * Copyright (C) 2016 The Android Open Source Project - * - * Licensed under the Apache License, Version 2.0 (the "License"); - * you may not use this file except in compliance with the License. - * You may obtain a copy of the License at - * - * http://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, - * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - * See the License for the specific language governing permissions and - * limitations under the License. - */ - -package com.android.server.wm; - -import static com.android.server.wm.WindowManagerDebugConfig.DEBUG_ANIM; -import static com.android.server.wm.WindowManagerDebugConfig.TAG_WITH_CLASS_NAME; -import static com.android.server.wm.WindowManagerDebugConfig.TAG_WM; - -import android.animation.AnimationHandler; -import android.animation.Animator; -import android.animation.ValueAnimator; -import android.annotation.IntDef; -import android.content.Context; -import android.graphics.Rect; -import android.os.Debug; -import android.os.Handler; -import android.os.IBinder; -import android.util.ArrayMap; -import android.util.Slog; -import android.view.Choreographer; -import android.view.animation.AnimationUtils; -import android.view.animation.Interpolator; - -import com.android.internal.annotations.VisibleForTesting; -import com.android.internal.graphics.SfVsyncFrameCallbackProvider; - -import java.lang.annotation.Retention; -import java.lang.annotation.RetentionPolicy; - -/** - * Enables animating bounds of objects. - * - * In multi-window world bounds of both stack and tasks can change. When we need these bounds to - * change smoothly and not require the app to relaunch (e.g. because it handles resizes and - * relaunching it would cause poorer experience), these class provides a way to directly animate - * the bounds of the resized object. - * - * The object that is resized needs to implement {@link BoundsAnimationTarget} interface. - * - * NOTE: All calls to methods in this class should be done on the Animation thread - */ -public class BoundsAnimationController { - private static final boolean DEBUG_LOCAL = false; - private static final boolean DEBUG = DEBUG_LOCAL || DEBUG_ANIM; - private static final String TAG = TAG_WITH_CLASS_NAME || DEBUG_LOCAL - ? "BoundsAnimationController" : TAG_WM; - private static final int DEBUG_ANIMATION_SLOW_DOWN_FACTOR = 1; - - private static final int DEFAULT_TRANSITION_DURATION = 425; - - @Retention(RetentionPolicy.SOURCE) - @IntDef({NO_PIP_MODE_CHANGED_CALLBACKS, SCHEDULE_PIP_MODE_CHANGED_ON_START, - SCHEDULE_PIP_MODE_CHANGED_ON_END}) - public @interface SchedulePipModeChangedState {} - /** Do not schedule any PiP mode changed callbacks as a part of this animation. */ - public static final int NO_PIP_MODE_CHANGED_CALLBACKS = 0; - /** Schedule a PiP mode changed callback when this animation starts. */ - public static final int SCHEDULE_PIP_MODE_CHANGED_ON_START = 1; - /** Schedule a PiP mode changed callback when this animation ends. */ - public static final int SCHEDULE_PIP_MODE_CHANGED_ON_END = 2; - - public static final int BOUNDS = 0; - public static final int FADE_IN = 1; - - @IntDef({BOUNDS, FADE_IN}) public @interface AnimationType {} - - private static final int FADE_IN_DURATION = 500; - - // Only accessed on UI thread. - private ArrayMap<BoundsAnimationTarget, BoundsAnimator> mRunningAnimations = new ArrayMap<>(); - - private final class AppTransitionNotifier - extends WindowManagerInternal.AppTransitionListener implements Runnable { - - public void onAppTransitionCancelledLocked() { - if (DEBUG) Slog.d(TAG, "onAppTransitionCancelledLocked:" - + " mFinishAnimationAfterTransition=" + mFinishAnimationAfterTransition); - animationFinished(); - } - public void onAppTransitionFinishedLocked(IBinder token) { - if (DEBUG) Slog.d(TAG, "onAppTransitionFinishedLocked:" - + " mFinishAnimationAfterTransition=" + mFinishAnimationAfterTransition); - animationFinished(); - } - private void animationFinished() { - if (mFinishAnimationAfterTransition) { - mHandler.removeCallbacks(this); - // This might end up calling into activity manager which will be bad since we have - // the window manager lock held at this point. Post a message to take care of the - // processing so we don't deadlock. - mHandler.post(this); - } - } - - @Override - public void run() { - for (int i = 0; i < mRunningAnimations.size(); i++) { - final BoundsAnimator b = mRunningAnimations.valueAt(i); - b.onAnimationEnd(null); - } - } - } - - private final Handler mHandler; - private final AppTransition mAppTransition; - private final AppTransitionNotifier mAppTransitionNotifier = new AppTransitionNotifier(); - private final Interpolator mFastOutSlowInInterpolator; - private boolean mFinishAnimationAfterTransition = false; - private final AnimationHandler mAnimationHandler; - private Choreographer mChoreographer; - private @AnimationType int mAnimationType; - - private static final int WAIT_FOR_DRAW_TIMEOUT_MS = 3000; - - BoundsAnimationController(Context context, AppTransition transition, Handler handler, - AnimationHandler animationHandler) { - mHandler = handler; - mAppTransition = transition; - mAppTransition.registerListenerLocked(mAppTransitionNotifier); - mFastOutSlowInInterpolator = AnimationUtils.loadInterpolator(context, - com.android.internal.R.interpolator.fast_out_slow_in); - mAnimationHandler = animationHandler; - if (animationHandler != null) { - // If an animation handler is provided, then ensure that it runs on the sf vsync tick - handler.post(() -> { - mChoreographer = Choreographer.getSfInstance(); - animationHandler.setProvider(new SfVsyncFrameCallbackProvider(mChoreographer)); - }); - } - } - - @VisibleForTesting - final class BoundsAnimator extends ValueAnimator - implements ValueAnimator.AnimatorUpdateListener, ValueAnimator.AnimatorListener { - - private final BoundsAnimationTarget mTarget; - private final @AnimationType int mAnimationType; - private final Rect mFrom = new Rect(); - private final Rect mTo = new Rect(); - private final Rect mTmpRect = new Rect(); - private final Rect mTmpTaskBounds = new Rect(); - - // True if this this animation was canceled and will be replaced the another animation from - // the same {@link #BoundsAnimationTarget} target. - private boolean mSkipFinalResize; - // True if this animation was canceled by the user, not as a part of a replacing animation - private boolean mSkipAnimationEnd; - - // True if the animation target is animating from the fullscreen. Only one of - // {@link mMoveToFullscreen} or {@link mMoveFromFullscreen} can be true at any time in the - // animation. - private boolean mMoveFromFullscreen; - // True if the animation target should be moved to the fullscreen stack at the end of this - // animation. Only one of {@link mMoveToFullscreen} or {@link mMoveFromFullscreen} can be - // true at any time in the animation. - private boolean mMoveToFullscreen; - - // Whether to schedule PiP mode changes on animation start/end - private @SchedulePipModeChangedState int mSchedulePipModeChangedState; - private @SchedulePipModeChangedState int mPrevSchedulePipModeChangedState; - - // Depending on whether we are animating from - // a smaller to a larger size - private int mFrozenTaskWidth; - private int mFrozenTaskHeight; - - // Timeout callback to ensure we continue the animation if waiting for resuming or app - // windows drawn fails - private final Runnable mResumeRunnable = () -> { - if (DEBUG) Slog.d(TAG, "pause: timed out waiting for windows drawn"); - resume(); - }; - - // If this animator is explicitly cancelled when it's in paused state, we should not - // attempt to resume the animation. Use this flag to avoid such behavior. - private boolean mIsCancelled; - - BoundsAnimator(BoundsAnimationTarget target, @AnimationType int animationType, Rect from, - Rect to, @SchedulePipModeChangedState int schedulePipModeChangedState, - @SchedulePipModeChangedState int prevShedulePipModeChangedState, - boolean moveFromFullscreen, boolean moveToFullscreen, Rect frozenTask) { - super(); - mTarget = target; - mAnimationType = animationType; - mFrom.set(from); - mTo.set(to); - mSchedulePipModeChangedState = schedulePipModeChangedState; - mPrevSchedulePipModeChangedState = prevShedulePipModeChangedState; - mMoveFromFullscreen = moveFromFullscreen; - mMoveToFullscreen = moveToFullscreen; - addUpdateListener(this); - addListener(this); - - // If we are animating from smaller to larger, we want to change the task bounds - // to their final size immediately so we can use scaling to make the window - // larger. Likewise if we are going from bigger to smaller, we want to wait until - // the end so we don't have to upscale from the smaller finished size. - if (mAnimationType == BOUNDS) { - if (animatingToLargerSize()) { - mFrozenTaskWidth = mTo.width(); - mFrozenTaskHeight = mTo.height(); - } else { - mFrozenTaskWidth = frozenTask.isEmpty() ? mFrom.width() : frozenTask.width(); - mFrozenTaskHeight = frozenTask.isEmpty() ? mFrom.height() : frozenTask.height(); - } - } - } - - @Override - public void onAnimationStart(Animator animation) { - if (DEBUG) Slog.d(TAG, "onAnimationStart: mTarget=" + mTarget - + " mPrevSchedulePipModeChangedState=" + mPrevSchedulePipModeChangedState - + " mSchedulePipModeChangedState=" + mSchedulePipModeChangedState); - mIsCancelled = false; - mFinishAnimationAfterTransition = false; - mTmpRect.set(mFrom.left, mFrom.top, mFrom.left + mFrozenTaskWidth, - mFrom.top + mFrozenTaskHeight); - - // Boost the thread priority of the animation thread while the bounds animation is - // running - updateBooster(); - - // Ensure that we have prepared the target for animation before we trigger any size - // changes, so it can swap surfaces in to appropriate modes, or do as it wishes - // otherwise. - boolean continueAnimation; - if (mPrevSchedulePipModeChangedState == NO_PIP_MODE_CHANGED_CALLBACKS) { - continueAnimation = mTarget.onAnimationStart( - mSchedulePipModeChangedState == SCHEDULE_PIP_MODE_CHANGED_ON_START, - false /* forceUpdate */, mAnimationType); - - // When starting an animation from fullscreen, pause here and wait for the - // windows-drawn signal before we start the rest of the transition down into PiP. - if (continueAnimation && mMoveFromFullscreen - && mTarget.shouldDeferStartOnMoveToFullscreen()) { - pause(); - } - } else if (mPrevSchedulePipModeChangedState == SCHEDULE_PIP_MODE_CHANGED_ON_END && - mSchedulePipModeChangedState == SCHEDULE_PIP_MODE_CHANGED_ON_START) { - // We are replacing a running animation into PiP, but since it hasn't completed, the - // client will not currently receive any picture-in-picture mode change callbacks. - // However, we still need to report to them that they are leaving PiP, so this will - // force an update via a mode changed callback. - continueAnimation = mTarget.onAnimationStart( - true /* schedulePipModeChangedCallback */, true /* forceUpdate */, - mAnimationType); - } else { - // The animation is already running, but we should check that the TaskStack is still - // valid before continuing with the animation - continueAnimation = mTarget.isAttached(); - } - - if (!continueAnimation) { - // No point of trying to animate something that isn't attached to the hierarchy - // anymore. - cancel(); - return; - } - - // Immediately update the task bounds if they have to become larger, but preserve - // the starting position so we don't jump at the beginning of the animation. - if (animatingToLargerSize()) { - mTarget.setPinnedStackSize(mFrom, mTmpRect); - - // We pause the animation until the app has drawn at the new size. - // The target will notify us via BoundsAnimationController#resume. - // We do this here and pause the animation, rather than just defer starting it - // so we can enter the animating state and have WindowStateAnimator apply the - // correct logic to make this resize seamless. - if (mMoveToFullscreen) { - pause(); - } - } - } - - @Override - public void pause() { - if (DEBUG) Slog.d(TAG, "pause: waiting for windows drawn"); - super.pause(); - mHandler.postDelayed(mResumeRunnable, WAIT_FOR_DRAW_TIMEOUT_MS); - } - - @Override - public void resume() { - if (DEBUG) Slog.d(TAG, "resume:"); - mHandler.removeCallbacks(mResumeRunnable); - if (!mIsCancelled) super.resume(); - } - - @Override - public void onAnimationUpdate(ValueAnimator animation) { - final float value = (Float) animation.getAnimatedValue(); - if (mAnimationType == FADE_IN) { - if (!mTarget.setPinnedStackAlpha(value)) { - cancelAndCallAnimationEnd(); - } - return; - } - - final float remains = 1 - value; - mTmpRect.left = (int) (mFrom.left * remains + mTo.left * value + 0.5f); - mTmpRect.top = (int) (mFrom.top * remains + mTo.top * value + 0.5f); - mTmpRect.right = (int) (mFrom.right * remains + mTo.right * value + 0.5f); - mTmpRect.bottom = (int) (mFrom.bottom * remains + mTo.bottom * value + 0.5f); - if (DEBUG) Slog.d(TAG, "animateUpdate: mTarget=" + mTarget + " mBounds=" - + mTmpRect + " from=" + mFrom + " mTo=" + mTo + " value=" + value - + " remains=" + remains); - - mTmpTaskBounds.set(mTmpRect.left, mTmpRect.top, - mTmpRect.left + mFrozenTaskWidth, mTmpRect.top + mFrozenTaskHeight); - - if (!mTarget.setPinnedStackSize(mTmpRect, mTmpTaskBounds)) { - // Whoops, the target doesn't feel like animating anymore. Let's immediately finish - // any further animation. - if (DEBUG) Slog.d(TAG, "animateUpdate: cancelled"); - - // If we have already scheduled a PiP mode changed at the start of the animation, - // then we need to clean up and schedule one at the end, since we have canceled the - // animation to the final state. - if (mSchedulePipModeChangedState == SCHEDULE_PIP_MODE_CHANGED_ON_START) { - mSchedulePipModeChangedState = SCHEDULE_PIP_MODE_CHANGED_ON_END; - } - - // Since we are cancelling immediately without a replacement animation, send the - // animation end to maintain callback parity, but also skip any further resizes - cancelAndCallAnimationEnd(); - } - } - - @Override - public void onAnimationEnd(Animator animation) { - if (DEBUG) Slog.d(TAG, "onAnimationEnd: mTarget=" + mTarget - + " mSkipFinalResize=" + mSkipFinalResize - + " mFinishAnimationAfterTransition=" + mFinishAnimationAfterTransition - + " mAppTransitionIsRunning=" + mAppTransition.isRunning() - + " callers=" + Debug.getCallers(2)); - - // There could be another animation running. For example in the - // move to fullscreen case, recents will also be closing while the - // previous task will be taking its place in the fullscreen stack. - // we have to ensure this is completed before we finish the animation - // and take our place in the fullscreen stack. - if (mAppTransition.isRunning() && !mFinishAnimationAfterTransition) { - mFinishAnimationAfterTransition = true; - return; - } - - if (!mSkipAnimationEnd) { - // If this animation has already scheduled the picture-in-picture mode on start, and - // we are not skipping the final resize due to being canceled, then move the PiP to - // fullscreen once the animation ends - if (DEBUG) Slog.d(TAG, "onAnimationEnd: mTarget=" + mTarget - + " moveToFullscreen=" + mMoveToFullscreen); - mTarget.onAnimationEnd(mSchedulePipModeChangedState == - SCHEDULE_PIP_MODE_CHANGED_ON_END, !mSkipFinalResize ? mTo : null, - mMoveToFullscreen); - } - - // Clean up this animation - removeListener(this); - removeUpdateListener(this); - mRunningAnimations.remove(mTarget); - - // Reset the thread priority of the animation thread after the bounds animation is done - updateBooster(); - } - - @Override - public void onAnimationCancel(Animator animation) { - mIsCancelled = true; - // Always skip the final resize when the animation is canceled - mSkipFinalResize = true; - mMoveToFullscreen = false; - } - - private void cancelAndCallAnimationEnd() { - if (DEBUG) Slog.d(TAG, "cancelAndCallAnimationEnd: mTarget=" + mTarget); - mSkipAnimationEnd = false; - super.cancel(); - } - - @Override - public void cancel() { - if (DEBUG) Slog.d(TAG, "cancel: mTarget=" + mTarget); - mSkipAnimationEnd = true; - super.cancel(); - - // Reset the thread priority of the animation thread if the bounds animation is canceled - updateBooster(); - } - - /** - * @return true if the animation target is the same as the input bounds. - */ - boolean isAnimatingTo(Rect bounds) { - return mTo.equals(bounds); - } - - /** - * @return true if we are animating to a larger surface size - */ - @VisibleForTesting - boolean animatingToLargerSize() { - // TODO: Fix this check for aspect ratio changes - return (mFrom.width() * mFrom.height() < mTo.width() * mTo.height()); - } - - @Override - public void onAnimationRepeat(Animator animation) { - // Do nothing - } - - @Override - public AnimationHandler getAnimationHandler() { - if (mAnimationHandler != null) { - return mAnimationHandler; - } - return super.getAnimationHandler(); - } - } - - public void animateBounds(final BoundsAnimationTarget target, Rect from, Rect to, - int animationDuration, @SchedulePipModeChangedState int schedulePipModeChangedState, - boolean moveFromFullscreen, boolean moveToFullscreen, - @AnimationType int animationType) { - animateBoundsImpl(target, from, to, animationDuration, schedulePipModeChangedState, - moveFromFullscreen, moveToFullscreen, animationType); - } - - /** - * Cancel existing animation if the destination was modified. - */ - void cancel(final BoundsAnimationTarget target) { - final BoundsAnimator existing = mRunningAnimations.get(target); - if (existing != null) { - // Cancel animation. Since its already started, send animation end to client. - if (DEBUG) Slog.d(TAG, "cancel: mTarget= " + target); - existing.cancelAndCallAnimationEnd(); - } - } - - @VisibleForTesting - BoundsAnimator animateBoundsImpl(final BoundsAnimationTarget target, Rect from, Rect to, - int animationDuration, @SchedulePipModeChangedState int schedulePipModeChangedState, - boolean moveFromFullscreen, boolean moveToFullscreen, - @AnimationType int animationType) { - final BoundsAnimator existing = mRunningAnimations.get(target); - - if (isRunningFadeInAnimation(target) && from.width() == to.width() - && from.height() == to.height()) { - animationType = FADE_IN; - } - final boolean replacing = existing != null; - @SchedulePipModeChangedState int prevSchedulePipModeChangedState = - NO_PIP_MODE_CHANGED_CALLBACKS; - - if (DEBUG) Slog.d(TAG, "animateBounds: target=" + target + " from=" + from + " to=" + to - + " schedulePipModeChangedState=" + schedulePipModeChangedState - + " replacing=" + replacing); - - Rect frozenTask = new Rect(); - if (replacing) { - if (existing.isAnimatingTo(to) && (!moveToFullscreen || existing.mMoveToFullscreen) - && (!moveFromFullscreen || existing.mMoveFromFullscreen)) { - // Just let the current animation complete if it has the same destination as the - // one we are trying to start, and, if moveTo/FromFullscreen was requested, already - // has that flag set. - if (DEBUG) Slog.d(TAG, "animateBounds: same destination and moveTo/From flags as " - + "existing=" + existing + ", ignoring..."); - return existing; - } - - // Save the previous state - prevSchedulePipModeChangedState = existing.mSchedulePipModeChangedState; - - // Update the PiP callback states if we are replacing the animation - if (existing.mSchedulePipModeChangedState == SCHEDULE_PIP_MODE_CHANGED_ON_START) { - if (schedulePipModeChangedState == SCHEDULE_PIP_MODE_CHANGED_ON_START) { - if (DEBUG) Slog.d(TAG, "animateBounds: still animating to fullscreen, keep" - + " existing deferred state"); - } else { - if (DEBUG) Slog.d(TAG, "animateBounds: fullscreen animation canceled, callback" - + " on start already processed, schedule deferred update on end"); - schedulePipModeChangedState = SCHEDULE_PIP_MODE_CHANGED_ON_END; - } - } else if (existing.mSchedulePipModeChangedState == SCHEDULE_PIP_MODE_CHANGED_ON_END) { - if (schedulePipModeChangedState == SCHEDULE_PIP_MODE_CHANGED_ON_START) { - if (DEBUG) Slog.d(TAG, "animateBounds: non-fullscreen animation canceled," - + " callback on start will be processed"); - } else { - if (DEBUG) Slog.d(TAG, "animateBounds: still animating from fullscreen, keep" - + " existing deferred state"); - schedulePipModeChangedState = SCHEDULE_PIP_MODE_CHANGED_ON_END; - } - } - - // We need to keep the previous moveTo/FromFullscreen flag, unless the new animation - // specifies a direction. - if (!moveFromFullscreen && !moveToFullscreen) { - moveToFullscreen = existing.mMoveToFullscreen; - moveFromFullscreen = existing.mMoveFromFullscreen; - } - - // We are in the middle of an existing animation, so that this new animation may - // start from an interpolated bounds. We should keep using the existing frozen task - // width/height for consistent configurations. - frozenTask.set(0, 0, existing.mFrozenTaskWidth, existing.mFrozenTaskHeight); - - // Since we are replacing, we skip both animation start and end callbacks - existing.cancel(); - } - if (animationType == FADE_IN) { - target.setPinnedStackSize(to, null); - } - - final BoundsAnimator animator = new BoundsAnimator(target, animationType, from, to, - schedulePipModeChangedState, prevSchedulePipModeChangedState, - moveFromFullscreen, moveToFullscreen, frozenTask); - mRunningAnimations.put(target, animator); - animator.setFloatValues(0f, 1f); - animator.setDuration(animationType == FADE_IN ? FADE_IN_DURATION - : (animationDuration != -1 ? animationDuration : DEFAULT_TRANSITION_DURATION) - * DEBUG_ANIMATION_SLOW_DOWN_FACTOR); - animator.setInterpolator(mFastOutSlowInInterpolator); - animator.start(); - return animator; - } - - public void setAnimationType(@AnimationType int animationType) { - mAnimationType = animationType; - } - - /** return the current animation type. */ - public @AnimationType int getAnimationType() { - @AnimationType int animationType = mAnimationType; - // Default to BOUNDS. - mAnimationType = BOUNDS; - return animationType; - } - - boolean isAnimationTypeFadeIn() { - return mAnimationType == FADE_IN; - } - - public Handler getHandler() { - return mHandler; - } - - public void onAllWindowsDrawn() { - if (DEBUG) Slog.d(TAG, "onAllWindowsDrawn:"); - mHandler.post(this::resume); - } - - private boolean isRunningFadeInAnimation(final BoundsAnimationTarget target) { - final BoundsAnimator existing = mRunningAnimations.get(target); - return existing != null && existing.mAnimationType == FADE_IN && existing.isStarted(); - } - - private void resume() { - for (int i = 0; i < mRunningAnimations.size(); i++) { - final BoundsAnimator b = mRunningAnimations.valueAt(i); - b.resume(); - } - } - - private void updateBooster() { - WindowManagerService.sThreadPriorityBooster.setBoundsAnimationRunning( - !mRunningAnimations.isEmpty()); - } -} diff --git a/services/core/java/com/android/server/wm/BoundsAnimationTarget.java b/services/core/java/com/android/server/wm/BoundsAnimationTarget.java deleted file mode 100644 index b1d535904fab..000000000000 --- a/services/core/java/com/android/server/wm/BoundsAnimationTarget.java +++ /dev/null @@ -1,78 +0,0 @@ -/* - * Copyright (C) 2017 The Android Open Source Project - * - * Licensed under the Apache License, Version 2.0 (the "License"); - * you may not use this file except in compliance with the License. - * You may obtain a copy of the License at - * - * http://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, - * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - * See the License for the specific language governing permissions and - * limitations under the License. - */ - -package com.android.server.wm; - -import android.graphics.Rect; - -/** - * The target for a BoundsAnimation. - * @see BoundsAnimationController - */ -interface BoundsAnimationTarget { - - /** - * Callback for the target to inform it that the animation has started, so it can do some - * necessary preparation. - * - * @param schedulePipModeChangedCallback whether or not to schedule the PiP mode changed - * callbacks - * @return whether to continue the animation - */ - boolean onAnimationStart(boolean schedulePipModeChangedCallback, boolean forceUpdate, - @BoundsAnimationController.AnimationType int animationType); - - /** - * @return Whether the animation should be paused waiting for the windows to draw before - * entering PiP. - */ - boolean shouldDeferStartOnMoveToFullscreen(); - - /** - * Sets the size of the target (without any intermediate steps, like scheduling animation) - * but freezes the bounds of any tasks in the target at taskBounds, to allow for more - * flexibility during resizing. Only works for the pinned stack at the moment. This will - * only be called between onAnimationStart() and onAnimationEnd(). - * - * @return Whether the target should continue to be animated and this call was successful. - * If false, the animation will be cancelled because the user has determined that the - * animation is now invalid and not required. In such a case, the cancel will trigger the - * animation end callback as well, but will not send any further size changes. - */ - boolean setPinnedStackSize(Rect displayedBounds, Rect configBounds); - - /** Sets the alpha of the animation target */ - boolean setPinnedStackAlpha(float alpha); - - /** - * Callback for the target to inform it that the animation has ended, so it can do some - * necessary cleanup. - * - * @param schedulePipModeChangedCallback whether or not to schedule the PiP mode changed - * callbacks - * @param finalStackSize the final stack bounds to set on the target (can be to indicate that - * the animation was cancelled and the target does not need to update to the final stack bounds) - * @param moveToFullscreen whether or the target should reparent itself to the fullscreen stack - * when the animation completes - */ - void onAnimationEnd(boolean schedulePipModeChangedCallback, Rect finalStackSize, - boolean moveToFullscreen); - - /** @return True if the target is attached to the window hierarchy. */ - default boolean isAttached() { - return true; - } -} diff --git a/services/core/java/com/android/server/wm/DisplayContent.java b/services/core/java/com/android/server/wm/DisplayContent.java index 0029dc8c3c25..e468810e0fdf 100644 --- a/services/core/java/com/android/server/wm/DisplayContent.java +++ b/services/core/java/com/android/server/wm/DisplayContent.java @@ -150,7 +150,6 @@ import static com.android.server.wm.WindowStateAnimator.READY_TO_SHOW; import static com.android.server.wm.utils.RegionUtils.forEachRectReverse; import static com.android.server.wm.utils.RegionUtils.rectListToRegion; -import android.animation.AnimationHandler; import android.annotation.IntDef; import android.annotation.NonNull; import android.annotation.Nullable; @@ -317,7 +316,6 @@ class DisplayContent extends WindowContainer<DisplayContent.DisplayChildWindowCo final ArraySet<ActivityRecord> mClosingApps = new ArraySet<>(); final ArraySet<ActivityRecord> mChangingApps = new ArraySet<>(); final UnknownAppVisibilityController mUnknownAppVisibilityController; - BoundsAnimationController mBoundsAnimationController; private MetricsLogger mMetricsLogger; @@ -977,10 +975,6 @@ class DisplayContent extends WindowContainer<DisplayContent.DisplayChildWindowCo mAppTransitionController = new AppTransitionController(mWmService, this); mUnknownAppVisibilityController = new UnknownAppVisibilityController(mWmService, this); - AnimationHandler animationHandler = new AnimationHandler(); - mBoundsAnimationController = new BoundsAnimationController(mWmService.mContext, - mAppTransition, mWmService.mAnimationHandler, animationHandler); - final InputChannel inputChannel = mWmService.mInputManager.monitorInput( "PointerEventDispatcher" + mDisplayId, mDisplayId); mPointerEventDispatcher = new PointerEventDispatcher(inputChannel); @@ -2718,6 +2712,9 @@ class DisplayContent extends WindowContainer<DisplayContent.DisplayChildWindowCo mRemovingDisplay = false; } + // Apply the pending transaction here since we may not be able to reach the DisplayContent + // on the next traversal if it's removed from RootWindowContainer child list. + getPendingTransaction().apply(); mWmService.mWindowPlacerLocked.requestTraversal(); } diff --git a/services/core/java/com/android/server/wm/DockedStackDividerController.java b/services/core/java/com/android/server/wm/DockedStackDividerController.java index 6431e117c4e6..958c8ae94e47 100644 --- a/services/core/java/com/android/server/wm/DockedStackDividerController.java +++ b/services/core/java/com/android/server/wm/DockedStackDividerController.java @@ -53,6 +53,7 @@ import android.view.animation.Interpolator; import android.view.animation.PathInterpolator; import com.android.internal.annotations.VisibleForTesting; +import com.android.internal.inputmethod.SoftInputShowHideReason; import com.android.internal.policy.DividerSnapAlgorithm; import com.android.internal.policy.DockedDividerUtils; import com.android.server.LocalServices; @@ -517,7 +518,8 @@ public class DockedStackDividerController { // Hide the current IME to avoid problems with animations from IME adjustment when // attaching the docked stack. - inputMethodManagerInternal.hideCurrentInputMethod(); + inputMethodManagerInternal.hideCurrentInputMethod( + SoftInputShowHideReason.HIDE_DOCKED_STACK_ATTACHED); mImeHideRequested = true; } diff --git a/services/core/java/com/android/server/wm/PinnedStackController.java b/services/core/java/com/android/server/wm/PinnedStackController.java index 668b6095738f..e14b8ae13bbc 100644 --- a/services/core/java/com/android/server/wm/PinnedStackController.java +++ b/services/core/java/com/android/server/wm/PinnedStackController.java @@ -16,8 +16,6 @@ package com.android.server.wm; -import static android.app.WindowConfiguration.WINDOWING_MODE_PINNED; - import static com.android.server.wm.WindowManagerDebugConfig.TAG_WITH_CLASS_NAME; import static com.android.server.wm.WindowManagerDebugConfig.TAG_WM; @@ -52,7 +50,7 @@ import java.util.List; * 1) When first entering PiP: the controller returns the valid bounds given, taking aspect ratio * and IME state into account. * 2) When rotating the device: the controller calculates the new bounds in the new orientation, - * taking the minimized and IME state into account. In this case, we currently ignore the + * taking the IME state into account. In this case, we currently ignore the * SystemUI adjustments (ie. expanded for menu, interaction, etc). * * Other changes in the system, including adjustment of IME, configuration change, and more are @@ -72,8 +70,6 @@ class PinnedStackController { private final PinnedStackControllerCallback mCallbacks = new PinnedStackControllerCallback(); - // States that affect how the PIP can be manipulated - private boolean mIsMinimized; private boolean mIsImeShowing; private int mImeHeight; @@ -84,9 +80,6 @@ class PinnedStackController { // Used to calculate stack bounds across rotations private final DisplayInfo mDisplayInfo = new DisplayInfo(); - // The size and position information that describes where the pinned stack will go by default. - private float mDefaultAspectRatio; - // The aspect ratio bounds of the PIP. private float mMinAspectRatio; private float mMaxAspectRatio; @@ -98,43 +91,12 @@ class PinnedStackController { * The callback object passed to listeners for them to notify the controller of state changes. */ private class PinnedStackControllerCallback extends IPinnedStackController.Stub { - - @Override - public void setIsMinimized(final boolean isMinimized) { - mHandler.post(() -> { - mIsMinimized = isMinimized; - }); - } - @Override public int getDisplayRotation() { synchronized (mService.mGlobalLock) { return mDisplayInfo.rotation; } } - - @Override - public void startAnimation(Rect destinationBounds, Rect sourceRectHint, - int animationDuration) { - synchronized (mService.mGlobalLock) { - final ActivityStack pinnedStack = mDisplayContent.getRootPinnedTask(); - pinnedStack.animateResizePinnedStack(destinationBounds, - sourceRectHint, animationDuration, true /* fromFullscreen */); - } - } - - @Override - public void resetBoundsAnimation(Rect bounds) { - synchronized (mService.mGlobalLock) { - if (mDisplayContent.hasPinnedTask()) { - final ActivityStack pinnedStack = mDisplayContent.getTopStackInWindowingMode( - WINDOWING_MODE_PINNED); - if (pinnedStack != null) { - pinnedStack.resetCurrentBoundsAnimation(bounds); - } - } - } - } } /** @@ -157,10 +119,6 @@ class PinnedStackController { mDisplayContent = displayContent; mDisplayInfo.copyFrom(mDisplayContent.getDisplayInfo()); reloadResources(); - // Initialize the aspect ratio to the default aspect ratio. Don't do this in reload - // resources as it would clobber mAspectRatio when entering PiP from fullscreen which - // triggers a configuration change and the resources to be reloaded. - mAspectRatio = mDefaultAspectRatio; } void onConfigurationChanged() { @@ -172,8 +130,6 @@ class PinnedStackController { */ private void reloadResources() { final Resources res = mService.mContext.getResources(); - mDefaultAspectRatio = res.getFloat( - com.android.internal.R.dimen.config_pictureInPictureDefaultAspectRatio); mDisplayContent.getDisplay().getRealMetrics(mTmpMetrics); mMinAspectRatio = res.getFloat( com.android.internal.R.dimen.config_pictureInPictureMinAspectRatio); @@ -191,12 +147,8 @@ class PinnedStackController { mPinnedStackListener = listener; notifyDisplayInfoChanged(mDisplayInfo); notifyImeVisibilityChanged(mIsImeShowing, mImeHeight); - // The movement bounds notification needs to be sent before the minimized state, since - // SystemUI may use the bounds to restore the minimized position - notifyMovementBoundsChanged(false /* fromImeAdjustment */, - false /* fromShelfAdjustment */); + notifyMovementBoundsChanged(false /* fromImeAdjustment */); notifyActionsChanged(mActions); - notifyMinimizeChanged(mIsMinimized); } catch (RemoteException e) { Log.e(TAG, "Failed to register pinned stack listener", e); } @@ -247,8 +199,7 @@ class PinnedStackController { void onDisplayInfoChanged(DisplayInfo displayInfo) { synchronized (mService.mGlobalLock) { setDisplayInfo(displayInfo); - notifyMovementBoundsChanged(false /* fromImeAdjustment */, - false /* fromShelfAdjustment */); + notifyMovementBoundsChanged(false /* fromImeAdjustment */); } } @@ -269,7 +220,7 @@ class PinnedStackController { mIsImeShowing = imeShowing; mImeHeight = imeHeight; notifyImeVisibilityChanged(imeShowing, imeHeight); - notifyMovementBoundsChanged(true /* fromImeAdjustment */, false /* fromShelfAdjustment */); + notifyMovementBoundsChanged(true /* fromImeAdjustment */); } /** @@ -279,10 +230,7 @@ class PinnedStackController { if (Float.compare(mAspectRatio, aspectRatio) != 0) { mAspectRatio = aspectRatio; notifyAspectRatioChanged(aspectRatio); - notifyMovementBoundsChanged(false /* fromImeAdjustment */, - false /* fromShelfAdjustment */); - notifyPrepareAnimation(null /* sourceHintRect */, aspectRatio, - null /* stackBounds */); + notifyMovementBoundsChanged(false /* fromImeAdjustment */); } } @@ -304,10 +252,6 @@ class PinnedStackController { notifyActionsChanged(mActions); } - void prepareAnimation(Rect sourceRectHint, float aspectRatio, Rect stackBounds) { - notifyPrepareAnimation(sourceRectHint, aspectRatio, stackBounds); - } - /** * Notifies listeners that the PIP needs to be adjusted for the IME. */ @@ -331,19 +275,6 @@ class PinnedStackController { } /** - * Notifies listeners that the PIP minimized state has changed. - */ - private void notifyMinimizeChanged(boolean isMinimized) { - if (mPinnedStackListener != null) { - try { - mPinnedStackListener.onMinimizedStateChanged(isMinimized); - } catch (RemoteException e) { - Slog.e(TAG_WM, "Error delivering minimize changed event.", e); - } - } - } - - /** * Notifies listeners that the PIP actions have changed. */ private void notifyActionsChanged(List<RemoteAction> actions) { @@ -359,8 +290,7 @@ class PinnedStackController { /** * Notifies listeners that the PIP movement bounds have changed. */ - private void notifyMovementBoundsChanged(boolean fromImeAdjustment, - boolean fromShelfAdjustment) { + private void notifyMovementBoundsChanged(boolean fromImeAdjustment) { synchronized (mService.mGlobalLock) { if (mPinnedStackListener == null) { return; @@ -371,8 +301,7 @@ class PinnedStackController { if (pinnedStack != null) { pinnedStack.getAnimationOrCurrentBounds(animatingBounds); } - mPinnedStackListener.onMovementBoundsChanged(animatingBounds, - fromImeAdjustment, fromShelfAdjustment); + mPinnedStackListener.onMovementBoundsChanged(animatingBounds, fromImeAdjustment); } catch (RemoteException e) { Slog.e(TAG_WM, "Error delivering actions changed event.", e); } @@ -391,24 +320,10 @@ class PinnedStackController { } } - /** - * Notifies listeners that the PIP animation is about to happen. - */ - private void notifyPrepareAnimation(Rect sourceRectHint, float aspectRatio, Rect stackBounds) { - if (mPinnedStackListener == null) return; - try { - mPinnedStackListener.onPrepareAnimation(sourceRectHint, aspectRatio, stackBounds); - } catch (RemoteException e) { - Slog.e(TAG_WM, "Error delivering prepare animation event.", e); - } - } - void dump(String prefix, PrintWriter pw) { pw.println(prefix + "PinnedStackController"); - pw.println(prefix + " mDefaultAspectRatio=" + mDefaultAspectRatio); pw.println(prefix + " mIsImeShowing=" + mIsImeShowing); pw.println(prefix + " mImeHeight=" + mImeHeight); - pw.println(prefix + " mIsMinimized=" + mIsMinimized); pw.println(prefix + " mAspectRatio=" + mAspectRatio); pw.println(prefix + " mMinAspectRatio=" + mMinAspectRatio); pw.println(prefix + " mMaxAspectRatio=" + mMaxAspectRatio); diff --git a/services/core/java/com/android/server/wm/RecentsAnimation.java b/services/core/java/com/android/server/wm/RecentsAnimation.java index e92bbaae71d3..adafdec40d37 100644 --- a/services/core/java/com/android/server/wm/RecentsAnimation.java +++ b/services/core/java/com/android/server/wm/RecentsAnimation.java @@ -26,8 +26,6 @@ import static android.os.Trace.TRACE_TAG_WINDOW_MANAGER; import static android.view.WindowManager.TRANSIT_NONE; import static com.android.server.wm.ActivityStackSupervisor.PRESERVE_WINDOWS; -import static com.android.server.wm.BoundsAnimationController.BOUNDS; -import static com.android.server.wm.BoundsAnimationController.FADE_IN; import static com.android.server.wm.ProtoLogGroup.WM_DEBUG_RECENTS_ANIMATIONS; import static com.android.server.wm.RecentsAnimationController.REORDER_KEEP_IN_PLACE; import static com.android.server.wm.RecentsAnimationController.REORDER_MOVE_TO_ORIGINAL_POSITION; @@ -426,11 +424,6 @@ class RecentsAnimation implements RecentsAnimationCallbacks, return; } - final DisplayContent dc = - mService.mRootWindowContainer.getDefaultDisplay().mDisplayContent; - dc.mBoundsAnimationController.setAnimationType( - controller.shouldDeferCancelUntilNextTransition() ? FADE_IN : BOUNDS); - // We defer canceling the recents animation until the next app transition in the following // cases: // 1) The next launching task is not being animated by the recents animation diff --git a/services/core/java/com/android/server/wm/RecentsAnimationController.java b/services/core/java/com/android/server/wm/RecentsAnimationController.java index 6b6b94681879..e69551a0fde4 100644 --- a/services/core/java/com/android/server/wm/RecentsAnimationController.java +++ b/services/core/java/com/android/server/wm/RecentsAnimationController.java @@ -26,7 +26,6 @@ import static android.view.WindowManager.LayoutParams.TYPE_BASE_APPLICATION; import static com.android.server.policy.WindowManagerPolicy.FINISH_LAYOUT_REDO_WALLPAPER; import static com.android.server.wm.ActivityTaskManagerInternal.APP_TRANSITION_RECENTS_ANIM; import static com.android.server.wm.AnimationAdapterProto.REMOTE; -import static com.android.server.wm.BoundsAnimationController.FADE_IN; import static com.android.server.wm.ProtoLogGroup.WM_DEBUG_RECENTS_ANIMATIONS; import static com.android.server.wm.RemoteAnimationAdapterWrapperProto.TARGET; import static com.android.server.wm.SurfaceAnimator.ANIMATION_TYPE_RECENTS; @@ -55,6 +54,7 @@ import android.view.SurfaceControl; import android.view.SurfaceControl.Transaction; import com.android.internal.annotations.VisibleForTesting; +import com.android.internal.inputmethod.SoftInputShowHideReason; import com.android.internal.util.function.pooled.PooledConsumer; import com.android.internal.util.function.pooled.PooledFunction; import com.android.internal.util.function.pooled.PooledLambda; @@ -231,7 +231,6 @@ public class RecentsAnimationController implements DeathRecipient { mCallbacks.onAnimationFinished(moveHomeToTop ? REORDER_MOVE_TO_TOP : REORDER_MOVE_TO_ORIGINAL_POSITION, sendUserLeaveHint); - mDisplayContent.mBoundsAnimationController.setAnimationType(FADE_IN); } finally { Binder.restoreCallingIdentity(token); } @@ -301,7 +300,8 @@ public class RecentsAnimationController implements DeathRecipient { final InputMethodManagerInternal inputMethodManagerInternal = LocalServices.getService(InputMethodManagerInternal.class); if (inputMethodManagerInternal != null) { - inputMethodManagerInternal.hideCurrentInputMethod(); + inputMethodManagerInternal.hideCurrentInputMethod( + SoftInputShowHideReason.HIDE_RECENTS_ANIMATION); } } finally { Binder.restoreCallingIdentity(token); @@ -309,14 +309,6 @@ public class RecentsAnimationController implements DeathRecipient { } @Override - @Deprecated - public void setCancelWithDeferredScreenshot(boolean screenshot) { - synchronized (mService.mGlobalLock) { - setDeferredCancel(true /* deferred */, screenshot); - } - } - - @Override public void setDeferCancelUntilNextTransition(boolean defer, boolean screenshot) { synchronized (mService.mGlobalLock) { setDeferredCancel(defer, screenshot); diff --git a/services/core/java/com/android/server/wm/RootWindowContainer.java b/services/core/java/com/android/server/wm/RootWindowContainer.java index aa6bdfd1aa5b..64d7db26cd7e 100644 --- a/services/core/java/com/android/server/wm/RootWindowContainer.java +++ b/services/core/java/com/android/server/wm/RootWindowContainer.java @@ -2149,19 +2149,6 @@ class RootWindowContainer extends WindowContainer<DisplayContent> mService.continueWindowLayout(); } - // TODO(b/146594635): Remove all PIP animation code from WM once SysUI handles animation. - // Notify the pinned stack controller to prepare the PiP animation, expect callback - // delivered from SystemUI to WM to start the animation. Unless we are using - // the TaskOrganizer in which case the animation will be entirely handled - // on that side. - if (mService.mTaskOrganizerController.getTaskOrganizer(WINDOWING_MODE_PINNED) - == null) { - final PinnedStackController pinnedStackController = - display.mDisplayContent.getPinnedStackController(); - pinnedStackController.prepareAnimation(sourceHintBounds, aspectRatio, - null /* stackBounds */); - } - // TODO: revisit the following statement after the animation is moved from WM to SysUI. // Update the visibility of all activities after the they have been reparented to the new // stack. This MUST run after the animation above is scheduled to ensure that the windows diff --git a/services/core/java/com/android/server/wm/Task.java b/services/core/java/com/android/server/wm/Task.java index ecfac1bc7b6e..76805e9f6342 100644 --- a/services/core/java/com/android/server/wm/Task.java +++ b/services/core/java/com/android/server/wm/Task.java @@ -111,7 +111,6 @@ import android.content.pm.PackageManager; import android.content.res.Configuration; import android.graphics.Point; import android.graphics.Rect; -import android.graphics.drawable.Icon; import android.os.Debug; import android.os.IBinder; import android.os.RemoteException; @@ -120,7 +119,6 @@ import android.os.Trace; import android.os.UserHandle; import android.provider.Settings; import android.service.voice.IVoiceInteractionSession; -import android.text.TextUtils; import android.util.DisplayMetrics; import android.util.Slog; import android.view.DisplayInfo; @@ -2910,8 +2908,7 @@ class Task extends WindowContainer<WindowContainer> { * we will have a jump at the end. */ boolean isFloating() { - return getWindowConfiguration().tasksAreFloating() - && !getStack().isAnimatingBoundsToFullscreen() && !mPreserveNonFloatingState; + return getWindowConfiguration().tasksAreFloating() && !mPreserveNonFloatingState; } /** diff --git a/services/core/java/com/android/server/wm/TaskChangeNotificationController.java b/services/core/java/com/android/server/wm/TaskChangeNotificationController.java index f715d8f3529b..96a9127344e8 100644 --- a/services/core/java/com/android/server/wm/TaskChangeNotificationController.java +++ b/services/core/java/com/android/server/wm/TaskChangeNotificationController.java @@ -37,7 +37,6 @@ class TaskChangeNotificationController { private static final int NOTIFY_TASK_STACK_CHANGE_LISTENERS_MSG = 2; private static final int NOTIFY_ACTIVITY_PINNED_LISTENERS_MSG = 3; private static final int NOTIFY_PINNED_ACTIVITY_RESTART_ATTEMPT_LISTENERS_MSG = 4; - private static final int NOTIFY_PINNED_STACK_ANIMATION_ENDED_LISTENERS_MSG = 5; private static final int NOTIFY_FORCED_RESIZABLE_MSG = 6; private static final int NOTIFY_ACTIVITY_DISMISSING_DOCKED_STACK_MSG = 7; private static final int NOTIFY_TASK_ADDED_LISTENERS_MSG = 8; @@ -48,7 +47,6 @@ class TaskChangeNotificationController { private static final int NOTIFY_TASK_REMOVAL_STARTED_LISTENERS = 13; private static final int NOTIFY_TASK_PROFILE_LOCKED_LISTENERS_MSG = 14; private static final int NOTIFY_TASK_SNAPSHOT_CHANGED_LISTENERS_MSG = 15; - private static final int NOTIFY_PINNED_STACK_ANIMATION_STARTED_LISTENERS_MSG = 16; private static final int NOTIFY_ACTIVITY_UNPINNED_LISTENERS_MSG = 17; private static final int NOTIFY_ACTIVITY_LAUNCH_ON_SECONDARY_DISPLAY_FAILED_MSG = 18; private static final int NOTIFY_ACTIVITY_LAUNCH_ON_SECONDARY_DISPLAY_REROUTED_MSG = 19; @@ -124,14 +122,6 @@ class TaskChangeNotificationController { l.onPinnedActivityRestartAttempt(m.arg1 != 0); }; - private final TaskStackConsumer mNotifyPinnedStackAnimationStarted = (l, m) -> { - l.onPinnedStackAnimationStarted(); - }; - - private final TaskStackConsumer mNotifyPinnedStackAnimationEnded = (l, m) -> { - l.onPinnedStackAnimationEnded(); - }; - private final TaskStackConsumer mNotifyActivityForcedResizable = (l, m) -> { l.onActivityForcedResizable((String) m.obj, m.arg1, m.arg2); }; @@ -233,12 +223,6 @@ class TaskChangeNotificationController { case NOTIFY_PINNED_ACTIVITY_RESTART_ATTEMPT_LISTENERS_MSG: forAllRemoteListeners(mNotifyPinnedActivityRestartAttempt, msg); break; - case NOTIFY_PINNED_STACK_ANIMATION_STARTED_LISTENERS_MSG: - forAllRemoteListeners(mNotifyPinnedStackAnimationStarted, msg); - break; - case NOTIFY_PINNED_STACK_ANIMATION_ENDED_LISTENERS_MSG: - forAllRemoteListeners(mNotifyPinnedStackAnimationEnded, msg); - break; case NOTIFY_FORCED_RESIZABLE_MSG: forAllRemoteListeners(mNotifyActivityForcedResizable, msg); break; @@ -386,24 +370,6 @@ class TaskChangeNotificationController { msg.sendToTarget(); } - /** Notifies all listeners when the pinned stack animation starts. */ - void notifyPinnedStackAnimationStarted() { - mHandler.removeMessages(NOTIFY_PINNED_STACK_ANIMATION_STARTED_LISTENERS_MSG); - final Message msg = - mHandler.obtainMessage(NOTIFY_PINNED_STACK_ANIMATION_STARTED_LISTENERS_MSG); - forAllLocalListeners(mNotifyPinnedStackAnimationStarted, msg); - msg.sendToTarget(); - } - - /** Notifies all listeners when the pinned stack animation ends. */ - void notifyPinnedStackAnimationEnded() { - mHandler.removeMessages(NOTIFY_PINNED_STACK_ANIMATION_ENDED_LISTENERS_MSG); - final Message msg = - mHandler.obtainMessage(NOTIFY_PINNED_STACK_ANIMATION_ENDED_LISTENERS_MSG); - forAllLocalListeners(mNotifyPinnedStackAnimationEnded, msg); - msg.sendToTarget(); - } - void notifyActivityDismissingDockedStack() { mHandler.removeMessages(NOTIFY_ACTIVITY_DISMISSING_DOCKED_STACK_MSG); final Message msg = mHandler.obtainMessage(NOTIFY_ACTIVITY_DISMISSING_DOCKED_STACK_MSG); diff --git a/services/core/java/com/android/server/wm/TaskOrganizerController.java b/services/core/java/com/android/server/wm/TaskOrganizerController.java index 6caa27c7fa6f..0f1e623a38f1 100644 --- a/services/core/java/com/android/server/wm/TaskOrganizerController.java +++ b/services/core/java/com/android/server/wm/TaskOrganizerController.java @@ -306,7 +306,8 @@ class TaskOrganizerController extends ITaskOrganizerController.Stub task.fillTaskInfo(mTmpTaskInfo); boolean changed = lastInfo == null || mTmpTaskInfo.topActivityType != lastInfo.topActivityType - || mTmpTaskInfo.isResizable() != lastInfo.isResizable(); + || mTmpTaskInfo.isResizable() != lastInfo.isResizable() + || mTmpTaskInfo.pictureInPictureParams != lastInfo.pictureInPictureParams; if (!(changed || force)) { return; } diff --git a/services/core/java/com/android/server/wm/TaskSnapshotController.java b/services/core/java/com/android/server/wm/TaskSnapshotController.java index 5c73f92ee6cd..f83b0522846c 100644 --- a/services/core/java/com/android/server/wm/TaskSnapshotController.java +++ b/services/core/java/com/android/server/wm/TaskSnapshotController.java @@ -18,19 +18,18 @@ package com.android.server.wm; import static android.view.WindowManager.LayoutParams.FLAG_SHOW_WALLPAPER; -import static com.android.server.wm.TaskSnapshotPersister.DISABLE_HIGH_RES_BITMAPS; import static com.android.server.wm.WindowManagerDebugConfig.DEBUG_SCREENSHOT; import static com.android.server.wm.WindowManagerDebugConfig.TAG_WITH_CLASS_NAME; import static com.android.server.wm.WindowManagerDebugConfig.TAG_WM; import android.annotation.NonNull; import android.annotation.Nullable; -import android.app.ActivityManager; import android.app.ActivityManager.TaskSnapshot; import android.content.pm.PackageManager; import android.graphics.Bitmap; import android.graphics.GraphicBuffer; import android.graphics.PixelFormat; +import android.graphics.Point; import android.graphics.RecordingCanvas; import android.graphics.Rect; import android.graphics.RenderNode; @@ -89,14 +88,6 @@ class TaskSnapshotController { @VisibleForTesting static final int SNAPSHOT_MODE_NONE = 2; - /** - * Constant for <code>scaleFactor</code> when calling {@link #snapshotTask} which is - * interpreted as using the most appropriate scale ratio for the system. - * This may yield a smaller ratio on low memory devices. - */ - @VisibleForTesting - static final float SNAPSHOT_SCALE_AUTO = -1f; - private final WindowManagerService mService; private final TaskSnapshotCache mCache; @@ -229,7 +220,7 @@ class TaskSnapshotController { @Nullable TaskSnapshot getSnapshot(int taskId, int userId, boolean restoreFromDisk, boolean isLowResolution) { return mCache.getSnapshot(taskId, userId, restoreFromDisk, isLowResolution - || DISABLE_HIGH_RES_BITMAPS); + && mPersister.enableLowResSnapshots()); } /** @@ -273,8 +264,6 @@ class TaskSnapshotController { * information from the task and populates the builder. * * @param task the task to capture - * @param scaleFraction the scale fraction between 0-1.0, or {@link #SNAPSHOT_SCALE_AUTO} - * to automatically select * @param pixelFormat the desired pixel format, or {@link PixelFormat#UNKNOWN} to * automatically select * @param builder the snapshot builder to populate @@ -282,8 +271,7 @@ class TaskSnapshotController { * @return true if the state of the task is ok to proceed */ @VisibleForTesting - boolean prepareTaskSnapshot(Task task, float scaleFraction, int pixelFormat, - TaskSnapshot.Builder builder) { + boolean prepareTaskSnapshot(Task task, int pixelFormat, TaskSnapshot.Builder builder) { if (!mService.mPolicy.isScreenOn()) { if (DEBUG_SCREENSHOT) { Slog.i(TAG_WM, "Attempted to take screenshot while display was off."); @@ -314,18 +302,6 @@ class TaskSnapshotController { builder.setId(System.currentTimeMillis()); builder.setContentInsets(getInsets(mainWindow)); - final boolean isLowRamDevice = ActivityManager.isLowRamDeviceStatic(); - - if (scaleFraction == SNAPSHOT_SCALE_AUTO) { - builder.setScaleFraction(isLowRamDevice - ? mPersister.getLowResScale() - : mHighResTaskSnapshotScale); - builder.setIsLowResolution(isLowRamDevice); - } else { - builder.setScaleFraction(scaleFraction); - builder.setIsLowResolution(scaleFraction < 1.0f); - } - final boolean isWindowTranslucent = mainWindow.getAttrs().format != PixelFormat.OPAQUE; final boolean isShowWallpaper = (mainWindow.getAttrs().flags & FLAG_SHOW_WALLPAPER) != 0; @@ -351,13 +327,23 @@ class TaskSnapshotController { @Nullable SurfaceControl.ScreenshotGraphicBuffer createTaskSnapshot(@NonNull Task task, + TaskSnapshot.Builder builder) { + Point taskSize = new Point(); + final SurfaceControl.ScreenshotGraphicBuffer taskSnapshot = createTaskSnapshot(task, + mHighResTaskSnapshotScale, builder.getPixelFormat(), taskSize); + builder.setTaskSize(taskSize); + return taskSnapshot; + } + + @Nullable + SurfaceControl.ScreenshotGraphicBuffer createTaskSnapshot(@NonNull Task task, float scaleFraction) { - return createTaskSnapshot(task, scaleFraction, PixelFormat.RGBA_8888); + return createTaskSnapshot(task, scaleFraction, PixelFormat.RGBA_8888, null); } @Nullable SurfaceControl.ScreenshotGraphicBuffer createTaskSnapshot(@NonNull Task task, - float scaleFraction, int pixelFormat) { + float scaleFraction, int pixelFormat, Point outTaskSize) { if (task.getSurfaceControl() == null) { if (DEBUG_SCREENSHOT) { Slog.w(TAG_WM, "Failed to take screenshot. No surface control for " + task); @@ -369,6 +355,10 @@ class TaskSnapshotController { final SurfaceControl.ScreenshotGraphicBuffer screenshotBuffer = SurfaceControl.captureLayers( task.getSurfaceControl(), mTmpRect, scaleFraction, pixelFormat); + if (outTaskSize != null) { + outTaskSize.x = mTmpRect.width(); + outTaskSize.y = mTmpRect.height(); + } final GraphicBuffer buffer = screenshotBuffer != null ? screenshotBuffer.getGraphicBuffer() : null; if (buffer == null || buffer.getWidth() <= 1 || buffer.getHeight() <= 1) { @@ -379,21 +369,20 @@ class TaskSnapshotController { @Nullable TaskSnapshot snapshotTask(Task task) { - return snapshotTask(task, SNAPSHOT_SCALE_AUTO, PixelFormat.UNKNOWN); + return snapshotTask(task, PixelFormat.UNKNOWN); } @Nullable - TaskSnapshot snapshotTask(Task task, float scaleFraction, int pixelFormat) { + TaskSnapshot snapshotTask(Task task, int pixelFormat) { TaskSnapshot.Builder builder = new TaskSnapshot.Builder(); - if (!prepareTaskSnapshot(task, scaleFraction, pixelFormat, builder)) { + if (!prepareTaskSnapshot(task, pixelFormat, builder)) { // Failed some pre-req. Has been logged. return null; } final SurfaceControl.ScreenshotGraphicBuffer screenshotBuffer = - createTaskSnapshot(task, builder.getScaleFraction(), - builder.getPixelFormat()); + createTaskSnapshot(task, builder); if (screenshotBuffer == null) { // Failed to acquire image. Has been logged. @@ -472,8 +461,10 @@ class TaskSnapshotController { final SystemBarBackgroundPainter decorPainter = new SystemBarBackgroundPainter(attrs.flags, attrs.privateFlags, attrs.systemUiVisibility, task.getTaskDescription(), mHighResTaskSnapshotScale, mainWindow.getRequestedInsetsState()); - final int width = (int) (task.getBounds().width() * mHighResTaskSnapshotScale); - final int height = (int) (task.getBounds().height() * mHighResTaskSnapshotScale); + final int taskWidth = task.getBounds().width(); + final int taskHeight = task.getBounds().height(); + final int width = (int) (taskWidth * mHighResTaskSnapshotScale); + final int height = (int) (taskHeight * mHighResTaskSnapshotScale); final RenderNode node = RenderNode.create("TaskSnapshotController", null); node.setLeftTopRightBottom(0, 0, width, height); @@ -494,9 +485,9 @@ class TaskSnapshotController { System.currentTimeMillis() /* id */, topChild.mActivityComponent, hwBitmap.createGraphicBufferHandle(), hwBitmap.getColorSpace(), mainWindow.getConfiguration().orientation, - mainWindow.getWindowConfiguration().getRotation(), - getInsets(mainWindow), ActivityManager.isLowRamDeviceStatic() /* isLowResolution */, - mHighResTaskSnapshotScale, false /* isRealSnapshot */, task.getWindowingMode(), + mainWindow.getWindowConfiguration().getRotation(), new Point(taskWidth, taskHeight), + getInsets(mainWindow), false /* isLowResolution */, + false /* isRealSnapshot */, task.getWindowingMode(), getSystemUiVisibility(task), false); } diff --git a/services/core/java/com/android/server/wm/TaskSnapshotLoader.java b/services/core/java/com/android/server/wm/TaskSnapshotLoader.java index 01f3427d78e1..c20ce5f40bc2 100644 --- a/services/core/java/com/android/server/wm/TaskSnapshotLoader.java +++ b/services/core/java/com/android/server/wm/TaskSnapshotLoader.java @@ -19,6 +19,7 @@ package com.android.server.wm; import static com.android.server.wm.WindowManagerDebugConfig.TAG_WITH_CLASS_NAME; import static com.android.server.wm.WindowManagerDebugConfig.TAG_WM; +import android.app.ActivityManager; import android.app.ActivityManager.TaskSnapshot; import android.content.ComponentName; import android.graphics.Bitmap; @@ -26,6 +27,7 @@ import android.graphics.Bitmap.Config; import android.graphics.BitmapFactory; import android.graphics.BitmapFactory.Options; import android.graphics.GraphicBuffer; +import android.graphics.Point; import android.graphics.Rect; import android.util.Slog; @@ -52,28 +54,110 @@ class TaskSnapshotLoader { mPersister = persister; } + static class PreRLegacySnapshotConfig { + /** + * If isPreRLegacy is {@code true}, specifies the scale the snapshot was taken at + */ + final float mScale; + + /** + * If {@code true}, always load *_reduced.jpg file, no matter what was requested + */ + final boolean mForceLoadReducedJpeg; + + PreRLegacySnapshotConfig(float scale, boolean forceLoadReducedJpeg) { + mScale = scale; + mForceLoadReducedJpeg = forceLoadReducedJpeg; + } + } + + /** + * When device is upgraded, we might be loading a legacy snapshot. In those cases, + * restore the scale based on how it was configured historically. See history of + * TaskSnapshotPersister for more information. + * + * | low_ram=false | low_ram=true + * +------------------------------------------------------------------------------+ + * O | *.jpg = 100%, *_reduced.jpg = 50% | + * | +-----------------------------------------| + * P | | *.jpg = NONE, *_reduced.jpg = 60% | + * +------------------------------------+-----------------------------------------+ + * Q | *.jpg = proto.scale, | *.jpg = NONE, | + * | *_reduced.jpg = 50% * proto.scale | *_reduced.jpg = proto.scale | + * +------------------------------------+-----------------------------------------+ + * + * @return null if Android R, otherwise a PreRLegacySnapshotConfig object + */ + PreRLegacySnapshotConfig getLegacySnapshotConfig(int taskWidth, float legacyScale, + boolean highResFileExists, boolean loadLowResolutionBitmap) { + float preRLegacyScale = 0; + boolean forceLoadReducedJpeg = false; + boolean isPreRLegacySnapshot = (taskWidth == 0); + if (!isPreRLegacySnapshot) { + return null; + } + final boolean isPreQLegacyProto = isPreRLegacySnapshot + && (Float.compare(legacyScale, 0f) == 0); + + if (isPreQLegacyProto) { + // Android O or Android P + if (ActivityManager.isLowRamDeviceStatic() && !highResFileExists) { + // Android P w/ low_ram=true + preRLegacyScale = 0.6f; + // Force bitmapFile to always be *_reduced.jpg + forceLoadReducedJpeg = true; + } else { + // Android O, OR Android P w/ low_ram=false + preRLegacyScale = loadLowResolutionBitmap ? 0.5f : 1.0f; + } + } else if (isPreRLegacySnapshot) { + // If not pre-Q but is pre-R, then it must be Android Q + if (ActivityManager.isLowRamDeviceStatic()) { + preRLegacyScale = legacyScale; + // Force bitmapFile to always be *_reduced.jpg + forceLoadReducedJpeg = true; + } else { + preRLegacyScale = + loadLowResolutionBitmap ? 0.5f * legacyScale : legacyScale; + } + } + return new PreRLegacySnapshotConfig(preRLegacyScale, forceLoadReducedJpeg); + } + /** * Loads a task from the disk. * <p> * Do not hold the window manager lock when calling this method, as we directly read data from * disk here, which might be slow. * - * @param taskId The id of the task to load. - * @param userId The id of the user the task belonged to. - * @param isLowResolution Whether to load a reduced resolution version of the snapshot. + * @param taskId The id of the task to load. + * @param userId The id of the user the task belonged to. + * @param loadLowResolutionBitmap Whether to load a low resolution resolution version of the + * snapshot. * @return The loaded {@link TaskSnapshot} or {@code null} if it couldn't be loaded. */ - TaskSnapshot loadTask(int taskId, int userId, boolean isLowResolution) { + TaskSnapshot loadTask(int taskId, int userId, boolean loadLowResolutionBitmap) { final File protoFile = mPersister.getProtoFile(taskId, userId); - final File bitmapFile = isLowResolution - ? mPersister.getLowResolutionBitmapFile(taskId, userId) - : mPersister.getHighResolutionBitmapFile(taskId, userId); - if (bitmapFile == null || !protoFile.exists() || !bitmapFile.exists()) { + if (!protoFile.exists()) { return null; } try { final byte[] bytes = Files.readAllBytes(protoFile.toPath()); final TaskSnapshotProto proto = TaskSnapshotProto.parseFrom(bytes); + final File highResBitmap = mPersister.getHighResolutionBitmapFile(taskId, userId); + + PreRLegacySnapshotConfig legacyConfig = getLegacySnapshotConfig(proto.taskWidth, + proto.legacyScale, highResBitmap.exists(), loadLowResolutionBitmap); + + boolean forceLoadReducedJpeg = + legacyConfig != null && legacyConfig.mForceLoadReducedJpeg; + File bitmapFile = (loadLowResolutionBitmap || forceLoadReducedJpeg) + ? mPersister.getLowResolutionBitmapFile(taskId, userId) : highResBitmap; + + if (!bitmapFile.exists()) { + return null; + } + final Options options = new Options(); options.inPreferredConfig = mPersister.use16BitFormat() && !proto.isTranslucent ? Config.RGB_565 @@ -99,13 +183,20 @@ class TaskSnapshotLoader { final ComponentName topActivityComponent = ComponentName.unflattenFromString( proto.topActivityComponent); - // For legacy snapshots, restore the scale based on the reduced resolution state - final float legacyScale = isLowResolution ? mPersister.getLowResScale() : 1f; - final float scale = Float.compare(proto.scale, 0f) != 0 ? proto.scale : legacyScale; - return new TaskSnapshot(proto.id, topActivityComponent, buffer, hwBitmap.getColorSpace(), - proto.orientation, proto.rotation, + + Point taskSize; + if (legacyConfig != null) { + int taskWidth = (int) ((float) hwBitmap.getWidth() / legacyConfig.mScale); + int taskHeight = (int) ((float) hwBitmap.getHeight() / legacyConfig.mScale); + taskSize = new Point(taskWidth, taskHeight); + } else { + taskSize = new Point(proto.taskWidth, proto.taskHeight); + } + + return new TaskSnapshot(proto.id, topActivityComponent, buffer, + hwBitmap.getColorSpace(), proto.orientation, proto.rotation, taskSize, new Rect(proto.insetLeft, proto.insetTop, proto.insetRight, proto.insetBottom), - isLowResolution, scale, proto.isRealSnapshot, proto.windowingMode, + loadLowResolutionBitmap, proto.isRealSnapshot, proto.windowingMode, proto.systemUiVisibility, proto.isTranslucent); } catch (IOException e) { Slog.w(TAG, "Unable to load task snapshot data for taskId=" + taskId); diff --git a/services/core/java/com/android/server/wm/TaskSnapshotPersister.java b/services/core/java/com/android/server/wm/TaskSnapshotPersister.java index 31212b8a6bd9..164d3e055e94 100644 --- a/services/core/java/com/android/server/wm/TaskSnapshotPersister.java +++ b/services/core/java/com/android/server/wm/TaskSnapshotPersister.java @@ -21,8 +21,8 @@ import static android.graphics.Bitmap.CompressFormat.JPEG; import static com.android.server.wm.WindowManagerDebugConfig.TAG_WITH_CLASS_NAME; import static com.android.server.wm.WindowManagerDebugConfig.TAG_WM; +import android.annotation.NonNull; import android.annotation.TestApi; -import android.app.ActivityManager; import android.app.ActivityManager.TaskSnapshot; import android.graphics.Bitmap; import android.graphics.Bitmap.Config; @@ -52,8 +52,6 @@ class TaskSnapshotPersister { private static final String TAG = TAG_WITH_CLASS_NAME ? "TaskSnapshotPersister" : TAG_WM; private static final String SNAPSHOTS_DIRNAME = "snapshots"; private static final String LOW_RES_FILE_POSTFIX = "_reduced"; - private static final float LOW_RAM_REDUCED_SCALE = .8f; - static final boolean DISABLE_HIGH_RES_BITMAPS = ActivityManager.isLowRamDeviceStatic(); private static final long DELAY_MS = 100; private static final int QUALITY = 95; private static final String PROTO_EXTENSION = ".proto"; @@ -71,7 +69,8 @@ class TaskSnapshotPersister { private boolean mStarted; private final Object mLock = new Object(); private final DirectoryResolver mDirectoryResolver; - private final float mLowResScale; + private final float mLowResScaleFactor; + private boolean mEnableLowResSnapshots; private final boolean mUse16BitFormat; /** @@ -83,13 +82,29 @@ class TaskSnapshotPersister { TaskSnapshotPersister(WindowManagerService service, DirectoryResolver resolver) { mDirectoryResolver = resolver; + final float highResTaskSnapshotScale = service.mContext.getResources().getFloat( + com.android.internal.R.dimen.config_highResTaskSnapshotScale); + final float lowResTaskSnapshotScale = service.mContext.getResources().getFloat( + com.android.internal.R.dimen.config_lowResTaskSnapshotScale); - if (ActivityManager.isLowRamDeviceStatic()) { - mLowResScale = LOW_RAM_REDUCED_SCALE; + if (lowResTaskSnapshotScale < 0 || 1 <= lowResTaskSnapshotScale) { + throw new RuntimeException("Low-res scale must be between 0 and 1"); + } + if (highResTaskSnapshotScale <= 0 || 1 < highResTaskSnapshotScale) { + throw new RuntimeException("High-res scale must be between 0 and 1"); + } + if (highResTaskSnapshotScale <= lowResTaskSnapshotScale) { + throw new RuntimeException("High-res scale must be greater than low-res scale"); + } + + if (lowResTaskSnapshotScale > 0) { + mLowResScaleFactor = lowResTaskSnapshotScale / highResTaskSnapshotScale; + setEnableLowResSnapshots(true); } else { - mLowResScale = service.mContext.getResources().getFloat( - com.android.internal.R.dimen.config_lowResTaskSnapshotScale); + mLowResScaleFactor = 0; + setEnableLowResSnapshots(false); } + mUse16BitFormat = service.mContext.getResources().getBoolean( com.android.internal.R.bool.config_use16BitTaskSnapshotPixelFormat); } @@ -155,13 +170,16 @@ class TaskSnapshotPersister { } } + boolean enableLowResSnapshots() { + return mEnableLowResSnapshots; + } + /** - * Gets the scaling the persister uses for low resolution task snapshots. - * - * @return the lowResBitmap scale of task snapshots when they are set to be low res + * Not to be used. Only here for testing. */ - float getLowResScale() { - return mLowResScale; + @VisibleForTesting + void setEnableLowResSnapshots(boolean enabled) { + mEnableLowResSnapshots = enabled; } /** @@ -213,14 +231,10 @@ class TaskSnapshotPersister { } File getHighResolutionBitmapFile(int taskId, int userId) { - // Full sized bitmaps are disabled on low ram devices - if (DISABLE_HIGH_RES_BITMAPS) { - Slog.wtf(TAG, "This device does not support full sized resolution bitmaps."); - return null; - } return new File(getDirectory(userId), taskId + BITMAP_EXTENSION); } + @NonNull File getLowResolutionBitmapFile(int taskId, int userId) { return new File(getDirectory(userId), taskId + LOW_RES_FILE_POSTFIX + BITMAP_EXTENSION); } @@ -234,11 +248,11 @@ class TaskSnapshotPersister { final File protoFile = getProtoFile(taskId, userId); final File bitmapLowResFile = getLowResolutionBitmapFile(taskId, userId); protoFile.delete(); - bitmapLowResFile.delete(); - - // Low ram devices do not have a full sized file to delete - if (!DISABLE_HIGH_RES_BITMAPS) { - final File bitmapFile = getHighResolutionBitmapFile(taskId, userId); + if (bitmapLowResFile.exists()) { + bitmapLowResFile.delete(); + } + final File bitmapFile = getHighResolutionBitmapFile(taskId, userId); + if (bitmapFile.exists()) { bitmapFile.delete(); } } @@ -343,6 +357,8 @@ class TaskSnapshotPersister { final TaskSnapshotProto proto = new TaskSnapshotProto(); proto.orientation = mSnapshot.getOrientation(); proto.rotation = mSnapshot.getRotation(); + proto.taskWidth = mSnapshot.getTaskSize().x; + proto.taskHeight = mSnapshot.getTaskSize().y; proto.insetLeft = mSnapshot.getContentInsets().left; proto.insetTop = mSnapshot.getContentInsets().top; proto.insetRight = mSnapshot.getContentInsets().right; @@ -352,7 +368,6 @@ class TaskSnapshotPersister { proto.systemUiVisibility = mSnapshot.getSystemUiVisibility(); proto.isTranslucent = mSnapshot.isTranslucent(); proto.topActivityComponent = mSnapshot.getTopActivityComponent().flattenToString(); - proto.scale = mSnapshot.getScale(); proto.id = mSnapshot.getId(); final byte[] bytes = TaskSnapshotProto.toByteArray(proto); final File file = getProtoFile(mTaskId, mUserId); @@ -379,39 +394,38 @@ class TaskSnapshotPersister { } final Bitmap swBitmap = bitmap.copy(Config.ARGB_8888, false /* isMutable */); - final Bitmap lowResBitmap = mSnapshot.isLowResolution() - ? swBitmap - : Bitmap.createScaledBitmap(swBitmap, - (int) (bitmap.getWidth() * mLowResScale), - (int) (bitmap.getHeight() * mLowResScale), true /* filter */); - final File lowResFile = getLowResolutionBitmapFile(mTaskId, mUserId); + final File file = getHighResolutionBitmapFile(mTaskId, mUserId); try { - FileOutputStream lowResFos = new FileOutputStream(lowResFile); - lowResBitmap.compress(JPEG, QUALITY, lowResFos); - lowResFos.close(); + FileOutputStream fos = new FileOutputStream(file); + swBitmap.compress(JPEG, QUALITY, fos); + fos.close(); } catch (IOException e) { - Slog.e(TAG, "Unable to open " + lowResFile + " for persisting.", e); + Slog.e(TAG, "Unable to open " + file + " for persisting.", e); return false; } - lowResBitmap.recycle(); - // For snapshots with lowResBitmap resolution, do not create or save full sized bitmaps - if (mSnapshot.isLowResolution()) { + if (!enableLowResSnapshots()) { swBitmap.recycle(); return true; } - final File file = getHighResolutionBitmapFile(mTaskId, mUserId); + final Bitmap lowResBitmap = Bitmap.createScaledBitmap(swBitmap, + (int) (bitmap.getWidth() * mLowResScaleFactor), + (int) (bitmap.getHeight() * mLowResScaleFactor), true /* filter */); + swBitmap.recycle(); + + final File lowResFile = getLowResolutionBitmapFile(mTaskId, mUserId); try { - FileOutputStream fos = new FileOutputStream(file); - swBitmap.compress(JPEG, QUALITY, fos); - fos.close(); + FileOutputStream lowResFos = new FileOutputStream(lowResFile); + lowResBitmap.compress(JPEG, QUALITY, lowResFos); + lowResFos.close(); } catch (IOException e) { - Slog.e(TAG, "Unable to open " + file + " for persisting.", e); + Slog.e(TAG, "Unable to open " + lowResFile + " for persisting.", e); return false; } - swBitmap.recycle(); + lowResBitmap.recycle(); + return true; } } diff --git a/services/core/java/com/android/server/wm/TaskSnapshotSurface.java b/services/core/java/com/android/server/wm/TaskSnapshotSurface.java index f4e42455087d..eb005e0f7eda 100644 --- a/services/core/java/com/android/server/wm/TaskSnapshotSurface.java +++ b/services/core/java/com/android/server/wm/TaskSnapshotSurface.java @@ -36,6 +36,7 @@ import static android.view.WindowManager.LayoutParams.FLAG_TRANSLUCENT_STATUS; import static android.view.WindowManager.LayoutParams.FLAG_WATCH_OUTSIDE_TOUCH; import static android.view.WindowManager.LayoutParams.PRIVATE_FLAG_FORCE_DRAW_BAR_BACKGROUNDS; import static android.view.WindowManager.LayoutParams.TYPE_APPLICATION_STARTING; + import static com.android.internal.policy.DecorView.NAVIGATION_BAR_COLOR_VIEW_ATTRIBUTES; import static com.android.internal.policy.DecorView.STATUS_BAR_COLOR_VIEW_ATTRIBUTES; import static com.android.internal.policy.DecorView.getColorViewLeftInset; @@ -53,9 +54,11 @@ import android.content.Context; import android.graphics.Canvas; import android.graphics.Color; import android.graphics.GraphicBuffer; +import android.graphics.Matrix; import android.graphics.Paint; import android.graphics.Point; import android.graphics.Rect; +import android.graphics.RectF; import android.os.Handler; import android.os.Looper; import android.os.Message; @@ -131,6 +134,8 @@ class TaskSnapshotSurface implements StartingSurface { private final Rect mContentInsets = new Rect(); private final Rect mFrame = new Rect(); private TaskSnapshot mSnapshot; + private final RectF mTmpSnapshotSize = new RectF(); + private final RectF mTmpDstFrame = new RectF(); private final CharSequence mTitle; private boolean mHasDrawn; private long mShownTime; @@ -141,6 +146,8 @@ class TaskSnapshotSurface implements StartingSurface { @VisibleForTesting final SystemBarBackgroundPainter mSystemBarBackgroundPainter; private final int mOrientationOnCreation; private final SurfaceControl.Transaction mTransaction; + private final Matrix mSnapshotMatrix = new Matrix(); + private final float[] mTmpFloat9 = new float[9]; static TaskSnapshotSurface create(WindowManagerService service, ActivityRecord activity, TaskSnapshot snapshot) { @@ -365,13 +372,17 @@ class TaskSnapshotSurface implements StartingSurface { frame = calculateSnapshotFrame(crop); mTransaction.setWindowCrop(mChildSurfaceControl, crop); mTransaction.setPosition(mChildSurfaceControl, frame.left, frame.top); + mTmpDstFrame.set(frame); } else { frame = null; + mTmpDstFrame.set(mFrame); } // Scale the mismatch dimensions to fill the task bounds - final float scale = 1 / mSnapshot.getScale(); - mTransaction.setMatrix(mChildSurfaceControl, scale, 0, 0, scale); + mTmpSnapshotSize.set(0, 0, buffer.getWidth(), buffer.getHeight()); + mSnapshotMatrix.setRectToRect(mTmpSnapshotSize, mTmpDstFrame, Matrix.ScaleToFit.FILL); + mTransaction.setMatrix(mChildSurfaceControl, mSnapshotMatrix, mTmpFloat9); + mTransaction.apply(); surface.attachAndQueueBufferWithColorSpace(buffer, mSnapshot.getColorSpace()); surface.release(); @@ -395,13 +406,17 @@ class TaskSnapshotSurface implements StartingSurface { rect.set(0, 0, mSnapshot.getSnapshot().getWidth(), mSnapshot.getSnapshot().getHeight()); final Rect insets = mSnapshot.getContentInsets(); + final float scaleX = (float) mSnapshot.getSnapshot().getWidth() / mSnapshot.getTaskSize().x; + final float scaleY = + (float) mSnapshot.getSnapshot().getHeight() / mSnapshot.getTaskSize().y; + // Let's remove all system decorations except the status bar, but only if the task is at the // very top of the screen. final boolean isTop = mTaskBounds.top == 0 && mFrame.top == 0; - rect.inset((int) (insets.left * mSnapshot.getScale()), - isTop ? 0 : (int) (insets.top * mSnapshot.getScale()), - (int) (insets.right * mSnapshot.getScale()), - (int) (insets.bottom * mSnapshot.getScale())); + rect.inset((int) (insets.left * scaleX), + isTop ? 0 : (int) (insets.top * scaleY), + (int) (insets.right * scaleX), + (int) (insets.bottom * scaleY)); return rect; } @@ -412,14 +427,20 @@ class TaskSnapshotSurface implements StartingSurface { */ @VisibleForTesting Rect calculateSnapshotFrame(Rect crop) { - final Rect frame = new Rect(crop); - final float scale = mSnapshot.getScale(); + final float scaleX = (float) mSnapshot.getSnapshot().getWidth() / mSnapshot.getTaskSize().x; + final float scaleY = + (float) mSnapshot.getSnapshot().getHeight() / mSnapshot.getTaskSize().y; // Rescale the frame from snapshot to window coordinate space - frame.scale(1 / scale); + final Rect frame = new Rect( + (int) (crop.left / scaleX + 0.5f), + (int) (crop.top / scaleY + 0.5f), + (int) (crop.right / scaleX + 0.5f), + (int) (crop.bottom / scaleY + 0.5f) + ); // By default, offset it to to top/left corner - frame.offsetTo((int) (-crop.left / scale), (int) (-crop.top / scale)); + frame.offsetTo((int) (-crop.left / scaleX), (int) (-crop.top / scaleY)); // However, we also need to make space for the navigation bar on the left side. final int colorViewLeftInset = getColorViewLeftInset(mStableInsets.left, diff --git a/services/core/java/com/android/server/wm/WindowContainer.java b/services/core/java/com/android/server/wm/WindowContainer.java index 7a4d0b0d9169..aaaabf2b7560 100644 --- a/services/core/java/com/android/server/wm/WindowContainer.java +++ b/services/core/java/com/android/server/wm/WindowContainer.java @@ -524,13 +524,6 @@ class WindowContainer<E extends WindowContainer> extends ConfigurationContainer< if (mSurfaceControl != null) { getPendingTransaction().remove(mSurfaceControl); - - // Merge to parent transaction to ensure the transactions on this WindowContainer are - // applied in native even if WindowContainer is removed. - if (mParent != null) { - mParent.getPendingTransaction().merge(getPendingTransaction()); - } - setSurfaceControl(null); mLastSurfacePosition.set(0, 0); scheduleAnimation(); diff --git a/services/core/java/com/android/server/wm/WindowState.java b/services/core/java/com/android/server/wm/WindowState.java index b25008373a7b..37597fbc7c45 100644 --- a/services/core/java/com/android/server/wm/WindowState.java +++ b/services/core/java/com/android/server/wm/WindowState.java @@ -1078,15 +1078,6 @@ class WindowState extends WindowContainer<WindowState> implements WindowManagerP } final ActivityStack stack = getRootTask(); - if (inPinnedWindowingMode() && stack != null - && stack.lastAnimatingBoundsWasToFullscreen()) { - // PIP edge case: When going from pinned to fullscreen, we apply a - // tempInsetFrame for the full task - but we're still at the start of the animation. - // To prevent a jump if there's a letterbox, restrict to the parent frame. - mInsetFrame.intersectUnchecked(windowFrames.mParentFrame); - windowFrames.mContainingFrame.intersectUnchecked(windowFrames.mParentFrame); - } - layoutDisplayFrame = new Rect(windowFrames.mDisplayFrame); windowFrames.mDisplayFrame.set(windowFrames.mContainingFrame); layoutXDiff = mInsetFrame.left - windowFrames.mContainingFrame.left; @@ -1343,16 +1334,6 @@ class WindowState extends WindowContainer<WindowState> implements WindowManagerP return; } - final Task task = getTask(); - // In the case of stack bound animations, the window frames will update (unlike other - // animations which just modify various transformation properties). We don't want to - // notify the client of frame changes in this case. Not only is it a lot of churn, but - // the frame may not correspond to the surface size or the onscreen area at various - // phases in the animation, and the client will become sad and confused. - if (task != null && task.getStack().isAnimatingBounds()) { - return; - } - boolean didFrameInsetsChange = setReportResizeHints(); boolean configChanged = !isLastConfigReportedToClient(); if (DEBUG_CONFIGURATION && configChanged) { diff --git a/services/core/java/com/android/server/wm/WindowStateAnimator.java b/services/core/java/com/android/server/wm/WindowStateAnimator.java index a1a9af680e92..81d0e3e88c1a 100644 --- a/services/core/java/com/android/server/wm/WindowStateAnimator.java +++ b/services/core/java/com/android/server/wm/WindowStateAnimator.java @@ -902,15 +902,9 @@ class WindowStateAnimator { boolean allowStretching = false; task.getStack().getFinalAnimationSourceHintBounds(mTmpSourceBounds); // If we don't have source bounds, we can attempt to use the content insets - // in the following scenario: - // 1. We have content insets. - // 2. We are not transitioning to full screen - // We have to be careful to check "lastAnimatingBoundsWasToFullscreen" rather than - // the mBoundsAnimating state, as we may have already left it and only be here - // because of the force-scale until resize state. + // if we have content insets. if (mTmpSourceBounds.isEmpty() && (mWin.mLastRelayoutContentInsets.width() > 0 - || mWin.mLastRelayoutContentInsets.height() > 0) - && !task.getStack().lastAnimatingBoundsWasToFullscreen()) { + || mWin.mLastRelayoutContentInsets.height() > 0)) { mTmpSourceBounds.set(task.getStack().mPreAnimationBounds); mTmpSourceBounds.inset(mWin.mLastRelayoutContentInsets); allowStretching = true; diff --git a/services/tests/servicestests/src/com/android/server/adb/AdbDebuggingManagerTest.java b/services/tests/servicestests/src/com/android/server/adb/AdbDebuggingManagerTest.java index 5a1ad8655ab0..757a2b1280f3 100644 --- a/services/tests/servicestests/src/com/android/server/adb/AdbDebuggingManagerTest.java +++ b/services/tests/servicestests/src/com/android/server/adb/AdbDebuggingManagerTest.java @@ -696,6 +696,31 @@ public final class AdbDebuggingManagerTest { mAdbKeyXmlFile.exists()); } + @Test + public void testAdbKeyStore_removeKey() throws Exception { + // Accept the test key with the 'Always allow' option selected. + runAdbTest(TEST_KEY_1, true, true, false); + runAdbTest(TEST_KEY_2, true, true, false); + + // Set the connection time to 0 to restore the original behavior. + setAllowedConnectionTime(0); + + // Verify that the key is in the adb_keys file to ensure subsequent connections are + // automatically allowed by adbd. + persistKeyStore(); + assertTrue("The key was not in the adb_keys file after persisting the keystore", + isKeyInFile(TEST_KEY_1, mAdbKeyFile)); + assertTrue("The key was not in the adb_keys file after persisting the keystore", + isKeyInFile(TEST_KEY_2, mAdbKeyFile)); + + // Now remove one of the keys and make sure the other key is still there + mKeyStore.removeKey(TEST_KEY_1); + assertFalse("The key was still in the adb_keys file after removing the key", + isKeyInFile(TEST_KEY_1, mAdbKeyFile)); + assertTrue("The key was not in the adb_keys file after removing a different key", + isKeyInFile(TEST_KEY_2, mAdbKeyFile)); + } + /** * Runs an adb test with the provided configuration. * diff --git a/services/tests/servicestests/src/com/android/server/pm/PackageParserTest.java b/services/tests/servicestests/src/com/android/server/pm/PackageParserTest.java index d2ec50001789..a19d91976307 100644 --- a/services/tests/servicestests/src/com/android/server/pm/PackageParserTest.java +++ b/services/tests/servicestests/src/com/android/server/pm/PackageParserTest.java @@ -430,13 +430,6 @@ public class PackageParserTest { ParsedProvider b ) { assertComponentsEqual(a, b); - - // Sanity check for ProviderInfo - ProviderInfo aInfo = PackageInfoUtils.generateProviderInfo(aPkg, a, 0, - new PackageUserState(), 0, mockPkgSetting(aPkg)); - ProviderInfo bInfo = PackageInfoUtils.generateProviderInfo(bPkg, b, 0, - new PackageUserState(), 0, mockPkgSetting(bPkg)); - assertApplicationInfoEqual(aInfo.applicationInfo, bInfo.applicationInfo); assertEquals(a.getName(), b.getName()); } diff --git a/services/tests/servicestests/src/com/android/server/tv/tunerresourcemanager/TunerResourceManagerServiceTest.java b/services/tests/servicestests/src/com/android/server/tv/tunerresourcemanager/TunerResourceManagerServiceTest.java new file mode 100644 index 000000000000..192c6fe4ab75 --- /dev/null +++ b/services/tests/servicestests/src/com/android/server/tv/tunerresourcemanager/TunerResourceManagerServiceTest.java @@ -0,0 +1,497 @@ +/* + * Copyright 2020 The Android Open Source Project + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ +package com.android.server.tv.tunerresourcemanager; + +import static com.google.common.truth.Truth.assertThat; + +import static org.mockito.Mockito.spy; +import static org.mockito.Mockito.when; + +import android.content.Context; +import android.content.ContextWrapper; +import android.media.tv.ITvInputManager; +import android.media.tv.TvInputManager; +import android.media.tv.TvInputService; +import android.media.tv.tuner.frontend.FrontendSettings; +import android.media.tv.tunerresourcemanager.ResourceClientProfile; +import android.media.tv.tunerresourcemanager.TunerFrontendInfo; +import android.media.tv.tunerresourcemanager.TunerFrontendRequest; +import android.media.tv.tunerresourcemanager.TunerResourceManager; +import android.os.RemoteException; +import android.util.SparseArray; + +import androidx.test.InstrumentationRegistry; +import androidx.test.filters.SmallTest; + +import org.junit.Before; +import org.junit.Test; +import org.junit.runner.RunWith; +import org.junit.runners.JUnit4; +import org.mockito.Mock; +import org.mockito.MockitoAnnotations; + +import java.util.ArrayList; +import java.util.Arrays; + +/** + * Tests for {@link TunerResourceManagerService} class. + */ +@SmallTest +@RunWith(JUnit4.class) +public class TunerResourceManagerServiceTest { + private static final String TAG = "TunerResourceManagerServiceTest"; + private Context mContextSpy; + @Mock private ITvInputManager mITvInputManagerMock; + private TunerResourceManagerService mTunerResourceManagerService; + private int mReclaimingId; + + @Before + public void setUp() throws Exception { + MockitoAnnotations.initMocks(this); + TvInputManager tvInputManager = new TvInputManager(mITvInputManagerMock, 0); + mContextSpy = spy(new ContextWrapper(InstrumentationRegistry.getTargetContext())); + when(mContextSpy.getSystemService(Context.TV_INPUT_SERVICE)).thenReturn(tvInputManager); + mTunerResourceManagerService = new TunerResourceManagerService(mContextSpy) { + @Override + protected void reclaimFrontendResource(int reclaimingId) { + mReclaimingId = reclaimingId; + } + }; + mTunerResourceManagerService.onStart(true /*isForTesting*/); + mReclaimingId = -1; + } + + @Test + public void setFrontendListTest_addFrontendResources_noExclusiveGroupId() { + // Init frontend resources. + TunerFrontendInfo[] infos = new TunerFrontendInfo[2]; + infos[0] = + new TunerFrontendInfo(0 /*id*/, FrontendSettings.TYPE_DVBT, 0 /*exclusiveGroupId*/); + infos[1] = + new TunerFrontendInfo(1 /*id*/, FrontendSettings.TYPE_DVBT, 1 /*exclusiveGroupId*/); + mTunerResourceManagerService.setFrontendInfoListInternal(infos); + + SparseArray<FrontendResource> resources = + mTunerResourceManagerService.getFrontendResources(); + assertThat(resources.size()).isEqualTo(infos.length); + for (int id = 0; id < infos.length; id++) { + FrontendResource fe = resources.get(infos[id].getId()); + assertThat(fe.getId()).isEqualTo(infos[id].getId()); + assertThat(fe.getType()).isEqualTo(infos[id].getFrontendType()); + assertThat(fe.getExclusiveGroupId()).isEqualTo(infos[id].getExclusiveGroupId()); + assertThat(fe.getExclusiveGroupMemberFeIds().size()).isEqualTo(0); + } + } + + @Test + public void setFrontendListTest_addFrontendResources_underTheSameExclusiveGroupId() { + // Init frontend resources. + TunerFrontendInfo[] infos = new TunerFrontendInfo[4]; + infos[0] = + new TunerFrontendInfo(0 /*id*/, FrontendSettings.TYPE_DVBT, 0 /*exclusiveGroupId*/); + infos[1] = + new TunerFrontendInfo(1 /*id*/, FrontendSettings.TYPE_DVBT, 1 /*exclusiveGroupId*/); + infos[2] = + new TunerFrontendInfo(2 /*id*/, FrontendSettings.TYPE_DVBS, 1 /*exclusiveGroupId*/); + infos[3] = + new TunerFrontendInfo(3 /*id*/, FrontendSettings.TYPE_ATSC, 1 /*exclusiveGroupId*/); + mTunerResourceManagerService.setFrontendInfoListInternal(infos); + + SparseArray<FrontendResource> resources = + mTunerResourceManagerService.getFrontendResources(); + assertThat(resources.size()).isEqualTo(infos.length); + for (int id = 0; id < infos.length; id++) { + FrontendResource fe = resources.get(infos[id].getId()); + assertThat(fe.getId()).isEqualTo(infos[id].getId()); + assertThat(fe.getType()).isEqualTo(infos[id].getFrontendType()); + assertThat(fe.getExclusiveGroupId()).isEqualTo(infos[id].getExclusiveGroupId()); + } + + assertThat(resources.get(0).getExclusiveGroupMemberFeIds()) + .isEqualTo(new ArrayList<Integer>()); + assertThat(resources.get(1).getExclusiveGroupMemberFeIds()) + .isEqualTo(new ArrayList<Integer>(Arrays.asList(2, 3))); + assertThat(resources.get(2).getExclusiveGroupMemberFeIds()) + .isEqualTo(new ArrayList<Integer>(Arrays.asList(1, 3))); + assertThat(resources.get(3).getExclusiveGroupMemberFeIds()) + .isEqualTo(new ArrayList<Integer>(Arrays.asList(1, 2))); + } + + @Test + public void setFrontendListTest_updateExistingFrontendResources() { + // Init frontend resources. + TunerFrontendInfo[] infos = new TunerFrontendInfo[2]; + infos[0] = + new TunerFrontendInfo(0 /*id*/, FrontendSettings.TYPE_DVBT, 1 /*exclusiveGroupId*/); + infos[1] = + new TunerFrontendInfo(1 /*id*/, FrontendSettings.TYPE_DVBS, 1 /*exclusiveGroupId*/); + + mTunerResourceManagerService.setFrontendInfoListInternal(infos); + SparseArray<FrontendResource> resources0 = + mTunerResourceManagerService.getFrontendResources(); + + mTunerResourceManagerService.setFrontendInfoListInternal(infos); + SparseArray<FrontendResource> resources1 = + mTunerResourceManagerService.getFrontendResources(); + + assertThat(resources0).isEqualTo(resources1); + } + + @Test + public void setFrontendListTest_removeFrontendResources_noExclusiveGroupId() { + // Init frontend resources. + TunerFrontendInfo[] infos0 = new TunerFrontendInfo[3]; + infos0[0] = + new TunerFrontendInfo(0 /*id*/, FrontendSettings.TYPE_DVBT, 0 /*exclusiveGroupId*/); + infos0[1] = + new TunerFrontendInfo(1 /*id*/, FrontendSettings.TYPE_DVBT, 1 /*exclusiveGroupId*/); + infos0[2] = + new TunerFrontendInfo(2 /*id*/, FrontendSettings.TYPE_DVBS, 2 /*exclusiveGroupId*/); + mTunerResourceManagerService.setFrontendInfoListInternal(infos0); + + TunerFrontendInfo[] infos1 = new TunerFrontendInfo[1]; + infos1[0] = + new TunerFrontendInfo(1 /*id*/, FrontendSettings.TYPE_DVBT, 1 /*exclusiveGroupId*/); + mTunerResourceManagerService.setFrontendInfoListInternal(infos1); + + SparseArray<FrontendResource> resources = + mTunerResourceManagerService.getFrontendResources(); + assertThat(resources.size()).isEqualTo(infos1.length); + for (int id = 0; id < infos1.length; id++) { + FrontendResource fe = resources.get(infos1[id].getId()); + assertThat(fe.getId()).isEqualTo(infos1[id].getId()); + assertThat(fe.getType()).isEqualTo(infos1[id].getFrontendType()); + assertThat(fe.getExclusiveGroupId()).isEqualTo(infos1[id].getExclusiveGroupId()); + assertThat(fe.getExclusiveGroupMemberFeIds().size()).isEqualTo(0); + } + } + + @Test + public void setFrontendListTest_removeFrontendResources_underTheSameExclusiveGroupId() { + // Init frontend resources. + TunerFrontendInfo[] infos0 = new TunerFrontendInfo[3]; + infos0[0] = + new TunerFrontendInfo(0 /*id*/, FrontendSettings.TYPE_DVBT, 0 /*exclusiveGroupId*/); + infos0[1] = + new TunerFrontendInfo(1 /*id*/, FrontendSettings.TYPE_DVBT, 1 /*exclusiveGroupId*/); + infos0[2] = + new TunerFrontendInfo(2 /*id*/, FrontendSettings.TYPE_DVBS, 1 /*exclusiveGroupId*/); + mTunerResourceManagerService.setFrontendInfoListInternal(infos0); + + TunerFrontendInfo[] infos1 = new TunerFrontendInfo[1]; + infos1[0] = + new TunerFrontendInfo(1 /*id*/, FrontendSettings.TYPE_DVBT, 1 /*exclusiveGroupId*/); + mTunerResourceManagerService.setFrontendInfoListInternal(infos1); + + SparseArray<FrontendResource> resources = + mTunerResourceManagerService.getFrontendResources(); + assertThat(resources.size()).isEqualTo(infos1.length); + for (int id = 0; id < infos1.length; id++) { + FrontendResource fe = resources.get(infos1[id].getId()); + assertThat(fe.getId()).isEqualTo(infos1[id].getId()); + assertThat(fe.getType()).isEqualTo(infos1[id].getFrontendType()); + assertThat(fe.getExclusiveGroupId()).isEqualTo(infos1[id].getExclusiveGroupId()); + assertThat(fe.getExclusiveGroupMemberFeIds().size()).isEqualTo(0); + } + } + + @Test + public void requestFrontendTest_ClientNotRegistered() { + TunerFrontendRequest request = + new TunerFrontendRequest(0 /*clientId*/, FrontendSettings.TYPE_DVBT); + int[] frontendId = new int[1]; + try { + assertThat(mTunerResourceManagerService.requestFrontendInternal(request, frontendId)) + .isFalse(); + } catch (RemoteException e) { + throw e.rethrowFromSystemServer(); + } + assertThat(frontendId[0]).isEqualTo(TunerResourceManager.INVALID_FRONTEND_ID); + } + + @Test + public void requestFrontendTest_NoFrontendWithGiveTypeAvailable() { + ResourceClientProfile profile = new ResourceClientProfile("0" /*sessionId*/, + TvInputService.PRIORITY_HINT_USE_CASE_TYPE_PLAYBACK); + int[] clientId = new int[1]; + mTunerResourceManagerService.registerClientProfileInternal( + profile, null /*listener*/, clientId); + assertThat(clientId[0]).isNotEqualTo(TunerResourceManagerService.INVALID_CLIENT_ID); + + // Init frontend resources. + TunerFrontendInfo[] infos = new TunerFrontendInfo[1]; + infos[0] = + new TunerFrontendInfo(0 /*id*/, FrontendSettings.TYPE_DVBS, 0 /*exclusiveGroupId*/); + mTunerResourceManagerService.setFrontendInfoListInternal(infos); + + TunerFrontendRequest request = + new TunerFrontendRequest(clientId[0] /*clientId*/, FrontendSettings.TYPE_DVBT); + int[] frontendId = new int[1]; + try { + assertThat(mTunerResourceManagerService.requestFrontendInternal(request, frontendId)) + .isFalse(); + } catch (RemoteException e) { + throw e.rethrowFromSystemServer(); + } + assertThat(frontendId[0]).isEqualTo(TunerResourceManager.INVALID_FRONTEND_ID); + } + + @Test + public void requestFrontendTest_FrontendWithNoExclusiveGroupAvailable() { + ResourceClientProfile profile = new ResourceClientProfile("0" /*sessionId*/, + TvInputService.PRIORITY_HINT_USE_CASE_TYPE_PLAYBACK); + int[] clientId = new int[1]; + mTunerResourceManagerService.registerClientProfileInternal( + profile, null /*listener*/, clientId); + assertThat(clientId[0]).isNotEqualTo(TunerResourceManagerService.INVALID_CLIENT_ID); + + // Init frontend resources. + TunerFrontendInfo[] infos = new TunerFrontendInfo[3]; + infos[0] = + new TunerFrontendInfo(0 /*id*/, FrontendSettings.TYPE_DVBT, 0 /*exclusiveGroupId*/); + infos[1] = + new TunerFrontendInfo(1 /*id*/, FrontendSettings.TYPE_DVBT, 1 /*exclusiveGroupId*/); + infos[2] = + new TunerFrontendInfo(2 /*id*/, FrontendSettings.TYPE_DVBS, 1 /*exclusiveGroupId*/); + mTunerResourceManagerService.setFrontendInfoListInternal(infos); + + TunerFrontendRequest request = + new TunerFrontendRequest(clientId[0] /*clientId*/, FrontendSettings.TYPE_DVBT); + int[] frontendId = new int[1]; + try { + assertThat(mTunerResourceManagerService.requestFrontendInternal(request, frontendId)) + .isTrue(); + } catch (RemoteException e) { + throw e.rethrowFromSystemServer(); + } + assertThat(frontendId[0]).isEqualTo(0); + } + + @Test + public void requestFrontendTest_FrontendWithExclusiveGroupAvailable() { + ResourceClientProfile profile0 = new ResourceClientProfile("0" /*sessionId*/, + TvInputService.PRIORITY_HINT_USE_CASE_TYPE_PLAYBACK); + ResourceClientProfile profile1 = new ResourceClientProfile("1" /*sessionId*/, + TvInputService.PRIORITY_HINT_USE_CASE_TYPE_PLAYBACK); + int[] clientId0 = new int[1]; + int[] clientId1 = new int[1]; + mTunerResourceManagerService.registerClientProfileInternal( + profile0, null /*listener*/, clientId0); + mTunerResourceManagerService.registerClientProfileInternal( + profile1, null /*listener*/, clientId1); + assertThat(clientId0[0]).isNotEqualTo(TunerResourceManagerService.INVALID_CLIENT_ID); + assertThat(clientId1[0]).isNotEqualTo(TunerResourceManagerService.INVALID_CLIENT_ID); + + // Init frontend resources. + TunerFrontendInfo[] infos = new TunerFrontendInfo[3]; + infos[0] = + new TunerFrontendInfo(0 /*id*/, FrontendSettings.TYPE_DVBT, 0 /*exclusiveGroupId*/); + infos[1] = + new TunerFrontendInfo(1 /*id*/, FrontendSettings.TYPE_DVBT, 1 /*exclusiveGroupId*/); + infos[2] = + new TunerFrontendInfo(2 /*id*/, FrontendSettings.TYPE_DVBS, 1 /*exclusiveGroupId*/); + mTunerResourceManagerService.setFrontendInfoListInternal(infos); + + int[] frontendId = new int[1]; + TunerFrontendRequest request = + new TunerFrontendRequest(clientId1[0] /*clientId*/, FrontendSettings.TYPE_DVBT); + try { + assertThat(mTunerResourceManagerService.requestFrontendInternal(request, frontendId)) + .isTrue(); + } catch (RemoteException e) { + throw e.rethrowFromSystemServer(); + } + assertThat(frontendId[0]).isEqualTo(infos[0].getId()); + + request = + new TunerFrontendRequest(clientId0[0] /*clientId*/, FrontendSettings.TYPE_DVBT); + try { + assertThat(mTunerResourceManagerService.requestFrontendInternal(request, frontendId)) + .isTrue(); + } catch (RemoteException e) { + throw e.rethrowFromSystemServer(); + } + assertThat(frontendId[0]).isEqualTo(infos[1].getId()); + assertThat(mTunerResourceManagerService.getFrontendResources().get(infos[1].getId()) + .isInUse()).isTrue(); + assertThat(mTunerResourceManagerService.getFrontendResources().get(infos[2].getId()) + .isInUse()).isTrue(); + } + + @Test + public void requestFrontendTest_NoFrontendAvailable_RequestWithLowerPriority() { + // Register clients + ResourceClientProfile[] profiles = new ResourceClientProfile[2]; + profiles[0] = new ResourceClientProfile("0" /*sessionId*/, + TvInputService.PRIORITY_HINT_USE_CASE_TYPE_PLAYBACK); + profiles[1] = new ResourceClientProfile("1" /*sessionId*/, + TvInputService.PRIORITY_HINT_USE_CASE_TYPE_PLAYBACK); + int[] clientPriorities = {100, 50}; + int[] clientId0 = new int[1]; + int[] clientId1 = new int[1]; + mTunerResourceManagerService.registerClientProfileInternal( + profiles[0], null /*listener*/, clientId0); + assertThat(clientId0[0]).isNotEqualTo(TunerResourceManagerService.INVALID_CLIENT_ID); + mTunerResourceManagerService.getClientProfiles().get(clientId0[0]) + .setPriority(clientPriorities[0]); + mTunerResourceManagerService.registerClientProfileInternal( + profiles[1], null /*listener*/, clientId1); + assertThat(clientId1[0]).isNotEqualTo(TunerResourceManagerService.INVALID_CLIENT_ID); + mTunerResourceManagerService.getClientProfiles().get(clientId1[0]) + .setPriority(clientPriorities[1]); + + // Init frontend resources. + TunerFrontendInfo[] infos = new TunerFrontendInfo[2]; + infos[0] = + new TunerFrontendInfo(0 /*id*/, FrontendSettings.TYPE_DVBT, 1 /*exclusiveGroupId*/); + infos[1] = + new TunerFrontendInfo(1 /*id*/, FrontendSettings.TYPE_DVBS, 1 /*exclusiveGroupId*/); + mTunerResourceManagerService.setFrontendInfoListInternal(infos); + + TunerFrontendRequest request = + new TunerFrontendRequest(clientId0[0] /*clientId*/, FrontendSettings.TYPE_DVBT); + int[] frontendId = new int[1]; + try { + assertThat(mTunerResourceManagerService.requestFrontendInternal(request, frontendId)) + .isTrue(); + } catch (RemoteException e) { + throw e.rethrowFromSystemServer(); + } + + request = + new TunerFrontendRequest(clientId1[0] /*clientId*/, FrontendSettings.TYPE_DVBT); + try { + assertThat(mTunerResourceManagerService.requestFrontendInternal(request, frontendId)) + .isFalse(); + } catch (RemoteException e) { + throw e.rethrowFromSystemServer(); + } + assertThat(mReclaimingId).isEqualTo(-1); + + request = + new TunerFrontendRequest(clientId1[0] /*clientId*/, FrontendSettings.TYPE_DVBS); + try { + assertThat(mTunerResourceManagerService.requestFrontendInternal(request, frontendId)) + .isFalse(); + assertThat(mReclaimingId).isEqualTo(-1); + } catch (RemoteException e) { + throw e.rethrowFromSystemServer(); + } + } + + @Test + public void requestFrontendTest_NoFrontendAvailable_RequestWithHigherPriority() { + // Register clients + ResourceClientProfile[] profiles = new ResourceClientProfile[2]; + profiles[0] = new ResourceClientProfile("0" /*sessionId*/, + TvInputService.PRIORITY_HINT_USE_CASE_TYPE_PLAYBACK); + profiles[1] = new ResourceClientProfile("1" /*sessionId*/, + TvInputService.PRIORITY_HINT_USE_CASE_TYPE_PLAYBACK); + int[] clientPriorities = {100, 500}; + int[] clientId0 = new int[1]; + int[] clientId1 = new int[1]; + mTunerResourceManagerService.registerClientProfileInternal( + profiles[0], null /*listener*/, clientId0); + assertThat(clientId0[0]).isNotEqualTo(TunerResourceManagerService.INVALID_CLIENT_ID); + mTunerResourceManagerService.getClientProfiles().get(clientId0[0]) + .setPriority(clientPriorities[0]); + mTunerResourceManagerService.registerClientProfileInternal( + profiles[1], null /*listener*/, clientId1); + assertThat(clientId1[0]).isNotEqualTo(TunerResourceManagerService.INVALID_CLIENT_ID); + mTunerResourceManagerService.getClientProfiles().get(clientId1[0]) + .setPriority(clientPriorities[1]); + + // Init frontend resources. + TunerFrontendInfo[] infos = new TunerFrontendInfo[2]; + infos[0] = + new TunerFrontendInfo(0 /*id*/, FrontendSettings.TYPE_DVBT, 1 /*exclusiveGroupId*/); + infos[1] = + new TunerFrontendInfo(1 /*id*/, FrontendSettings.TYPE_DVBS, 1 /*exclusiveGroupId*/); + mTunerResourceManagerService.setFrontendInfoListInternal(infos); + + TunerFrontendRequest request = + new TunerFrontendRequest(clientId0[0] /*clientId*/, FrontendSettings.TYPE_DVBT); + int[] frontendId = new int[1]; + try { + assertThat(mTunerResourceManagerService.requestFrontendInternal(request, frontendId)) + .isTrue(); + } catch (RemoteException e) { + throw e.rethrowFromSystemServer(); + } + assertThat(frontendId[0]).isEqualTo(infos[0].getId()); + + request = + new TunerFrontendRequest(clientId1[0] /*clientId*/, FrontendSettings.TYPE_DVBS); + try { + assertThat(mTunerResourceManagerService.requestFrontendInternal(request, frontendId)) + .isTrue(); + } catch (RemoteException e) { + throw e.rethrowFromSystemServer(); + } + assertThat(frontendId[0]).isEqualTo(infos[1].getId()); + assertThat(mTunerResourceManagerService.getFrontendResources().get(infos[0].getId()) + .isInUse()).isTrue(); + assertThat(mTunerResourceManagerService.getFrontendResources().get(infos[1].getId()) + .isInUse()).isTrue(); + assertThat(mTunerResourceManagerService.getFrontendResources() + .get(infos[0].getId()).getOwnerClientId()).isEqualTo(clientId1[0]); + assertThat(mTunerResourceManagerService.getFrontendResources() + .get(infos[1].getId()).getOwnerClientId()).isEqualTo(clientId1[0]); + assertThat(mReclaimingId).isEqualTo(clientId0[0]); + } + + @Test + public void unregisterClientTest_usingFrontend() { + // Register client + ResourceClientProfile profile = new ResourceClientProfile("0" /*sessionId*/, + TvInputService.PRIORITY_HINT_USE_CASE_TYPE_PLAYBACK); + int[] clientId = new int[1]; + mTunerResourceManagerService.registerClientProfileInternal( + profile, null /*listener*/, clientId); + assertThat(clientId[0]).isNotEqualTo(TunerResourceManagerService.INVALID_CLIENT_ID); + + // Init frontend resources. + TunerFrontendInfo[] infos = new TunerFrontendInfo[2]; + infos[0] = + new TunerFrontendInfo(0 /*id*/, FrontendSettings.TYPE_DVBT, 1 /*exclusiveGroupId*/); + infos[1] = + new TunerFrontendInfo(1 /*id*/, FrontendSettings.TYPE_DVBS, 1 /*exclusiveGroupId*/); + mTunerResourceManagerService.setFrontendInfoListInternal(infos); + + TunerFrontendRequest request = + new TunerFrontendRequest(clientId[0] /*clientId*/, FrontendSettings.TYPE_DVBT); + int[] frontendId = new int[1]; + try { + assertThat(mTunerResourceManagerService.requestFrontendInternal(request, frontendId)) + .isTrue(); + } catch (RemoteException e) { + throw e.rethrowFromSystemServer(); + } + assertThat(frontendId[0]).isEqualTo(infos[0].getId()); + assertThat(mTunerResourceManagerService.getFrontendResources().get(infos[0].getId()) + .isInUse()).isTrue(); + assertThat(mTunerResourceManagerService.getFrontendResources().get(infos[1].getId()) + .isInUse()).isTrue(); + + // Unregister client when using frontend + mTunerResourceManagerService.unregisterClientProfileInternal(clientId[0]); + assertThat(mTunerResourceManagerService.getFrontendResources().get(infos[0].getId()) + .isInUse()).isFalse(); + assertThat(mTunerResourceManagerService.getFrontendResources().get(infos[1].getId()) + .isInUse()).isFalse(); + + } +} diff --git a/services/tests/servicestests/src/com/android/server/tv/tunerresourcemanager/UseCasePriorityHintsTest.java b/services/tests/servicestests/src/com/android/server/tv/tunerresourcemanager/UseCasePriorityHintsTest.java new file mode 100644 index 000000000000..ab5665ba99fc --- /dev/null +++ b/services/tests/servicestests/src/com/android/server/tv/tunerresourcemanager/UseCasePriorityHintsTest.java @@ -0,0 +1,111 @@ +/* + * Copyright 2020 The Android Open Source Project + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ +package com.android.server.tv.tunerresourcemanager; + +import static com.google.common.truth.Truth.assertThat; + +import android.media.tv.TvInputService; +import android.util.Slog; + +import androidx.test.filters.SmallTest; + +import org.junit.Before; +import org.junit.Test; +import org.junit.runner.RunWith; +import org.junit.runners.JUnit4; +import org.xmlpull.v1.XmlPullParserException; + +import java.io.ByteArrayInputStream; +import java.io.IOException; +import java.nio.charset.StandardCharsets; + +/** + * Tests for {@link UseCasePriorityHints} class. + */ +@SmallTest +@RunWith(JUnit4.class) +public class UseCasePriorityHintsTest { + private static final String TAG = "UseCasePriorityHintsTest"; + private UseCasePriorityHints mPriorityHints; + + private final String mExampleXML = + "<!-- A sample Use Case Priority Hints xml -->" + + "<config version=\"1.0\" xmlns:xi=\"http://www.w3.org/2001/XInclude\">" + + "<useCaseDefault fgPriority=\"150\" bgPriority=\"50\"/>" + + "<useCasePreDefined type=\"USE_CASE_RECORD\" fgPriority=\"600\" bgPriority=\"500\"/>" + + "<useCasePreDefined type=\"USE_CASE_LIVE\" fgPriority=\"490\" bgPriority=\"400\"/>" + + "<useCasePreDefined type=\"USE_CASE_PLAYBACK\" fgPriority=\"480\"" + + " bgPriority=\"300\"/>" + + "<useCasePreDefined type=\"USE_CASE_BACKGROUND\" fgPriority=\"180\"" + + " bgPriority=\"100\"/>" + + "<useCaseVendor type=\"VENDOR_USE_CASE_1\" id=\"1001\" fgPriority=\"300\"" + + " bgPriority=\"80\"/>" + + "</config>"; + + @Before + public void setUp() throws Exception { + mPriorityHints = new UseCasePriorityHints(); + try { + mPriorityHints.parseInternal( + new ByteArrayInputStream(mExampleXML.getBytes(StandardCharsets.UTF_8))); + } catch (IOException | XmlPullParserException e) { + Slog.e(TAG, "Error parse xml.", e); + } + } + + @Test + public void parseTest_parseSampleXml() { + // Pre-defined foreground + assertThat(mPriorityHints.getForegroundPriority( + TvInputService.PRIORITY_HINT_USE_CASE_TYPE_BACKGROUND)).isEqualTo(180); + assertThat(mPriorityHints.getForegroundPriority( + TvInputService.PRIORITY_HINT_USE_CASE_TYPE_SCAN)).isEqualTo(150); + assertThat(mPriorityHints.getForegroundPriority( + TvInputService.PRIORITY_HINT_USE_CASE_TYPE_PLAYBACK)).isEqualTo(480); + assertThat(mPriorityHints.getForegroundPriority( + TvInputService.PRIORITY_HINT_USE_CASE_TYPE_LIVE)).isEqualTo(490); + assertThat(mPriorityHints.getForegroundPriority( + TvInputService.PRIORITY_HINT_USE_CASE_TYPE_RECORD)).isEqualTo(600); + + // Pre-defined background + assertThat(mPriorityHints.getBackgroundPriority( + TvInputService.PRIORITY_HINT_USE_CASE_TYPE_BACKGROUND)).isEqualTo(100); + assertThat(mPriorityHints.getBackgroundPriority( + TvInputService.PRIORITY_HINT_USE_CASE_TYPE_SCAN)).isEqualTo(50); + assertThat(mPriorityHints.getBackgroundPriority( + TvInputService.PRIORITY_HINT_USE_CASE_TYPE_PLAYBACK)).isEqualTo(300); + assertThat(mPriorityHints.getBackgroundPriority( + TvInputService.PRIORITY_HINT_USE_CASE_TYPE_LIVE)).isEqualTo(400); + assertThat(mPriorityHints.getBackgroundPriority( + TvInputService.PRIORITY_HINT_USE_CASE_TYPE_RECORD)).isEqualTo(500); + + // Vendor use case + assertThat(mPriorityHints.getForegroundPriority(1001)).isEqualTo(300); + assertThat(mPriorityHints.getBackgroundPriority(1001)).isEqualTo(80); + } + + @Test + public void isDefinedUseCaseTest_invalidUseCase() { + assertThat(mPriorityHints.isDefinedUseCase(1992)).isFalse(); + } + + @Test + public void isDefinedUseCaseTest_validUseCase() { + assertThat(mPriorityHints.isDefinedUseCase(1001)).isTrue(); + assertThat(mPriorityHints.isDefinedUseCase( + TvInputService.PRIORITY_HINT_USE_CASE_TYPE_RECORD)).isTrue(); + } +} diff --git a/services/tests/servicestests/src/com/android/server/usage/AppStandbyControllerTests.java b/services/tests/servicestests/src/com/android/server/usage/AppStandbyControllerTests.java index 0fdffd554b36..23613e0fccc1 100644 --- a/services/tests/servicestests/src/com/android/server/usage/AppStandbyControllerTests.java +++ b/services/tests/servicestests/src/com/android/server/usage/AppStandbyControllerTests.java @@ -76,6 +76,7 @@ import androidx.test.filters.SmallTest; import androidx.test.runner.AndroidJUnit4; import com.android.server.SystemService; +import com.android.server.usage.AppStandbyInternal.AppIdleStateChangeListener; import org.junit.Before; import org.junit.Test; @@ -87,6 +88,7 @@ import java.util.Arrays; import java.util.Collections; import java.util.List; import java.util.Set; +import java.util.concurrent.CountDownLatch; import java.util.concurrent.TimeUnit; /** @@ -127,6 +129,15 @@ public class AppStandbyControllerTests { private MyInjector mInjector; private AppStandbyController mController; + private CountDownLatch mStateChangedLatch = new CountDownLatch(1); + private AppIdleStateChangeListener mListener = new AppIdleStateChangeListener() { + @Override + public void onAppIdleStateChanged(String packageName, int userId, + boolean idle, int bucket, int reason) { + mStateChangedLatch.countDown(); + } + }; + static class MyContextWrapper extends ContextWrapper { PackageManager mockPm = mock(PackageManager.class); @@ -156,6 +167,7 @@ public class AppStandbyControllerTests { String mBoundWidgetPackage = PACKAGE_EXEMPTED_1; int[] mRunningUsers = new int[] {USER_ID}; List<UserHandle> mCrossProfileTargets = Collections.emptyList(); + boolean mDeviceIdleMode = false; MyInjector(Context context, Looper looper) { super(context, looper); @@ -251,7 +263,7 @@ public class AppStandbyControllerTests { @Override public boolean isDeviceIdleMode() { - return false; + return mDeviceIdleMode; } @Override @@ -327,6 +339,7 @@ public class AppStandbyControllerTests { controller.getAppStandbyBucket(PACKAGE_1, USER_ID, mInjector.mElapsedRealtime, false)); + controller.addListener(mListener); return controller; } @@ -1055,6 +1068,46 @@ public class AppStandbyControllerTests { STANDBY_BUCKET_WORKING_SET, getStandbyBucket(USER_ID2, mController, PACKAGE_1)); } + @Test + public void testUnexemptedSyncScheduled() throws Exception { + mStateChangedLatch = new CountDownLatch(1); + mController.addListener(mListener); + assertEquals("Test package did not start in the Never bucket", STANDBY_BUCKET_NEVER, + getStandbyBucket(mController, PACKAGE_1)); + + mController.postReportSyncScheduled(PACKAGE_1, USER_ID, false); + mStateChangedLatch.await(100, TimeUnit.MILLISECONDS); + assertEquals("Unexempted sync scheduled should bring the package out of the Never bucket", + STANDBY_BUCKET_WORKING_SET, getStandbyBucket(mController, PACKAGE_1)); + + setAndAssertBucket(PACKAGE_1, USER_ID, STANDBY_BUCKET_RARE, REASON_MAIN_FORCED_BY_SYSTEM); + + mStateChangedLatch = new CountDownLatch(1); + mController.postReportSyncScheduled(PACKAGE_1, USER_ID, false); + mStateChangedLatch.await(100, TimeUnit.MILLISECONDS); + assertEquals("Unexempted sync scheduled should not elevate a non Never bucket", + STANDBY_BUCKET_RARE, getStandbyBucket(mController, PACKAGE_1)); + } + + @Test + public void testExemptedSyncScheduled() throws Exception { + setAndAssertBucket(PACKAGE_1, USER_ID, STANDBY_BUCKET_RARE, REASON_MAIN_FORCED_BY_SYSTEM); + mInjector.mDeviceIdleMode = true; + mStateChangedLatch = new CountDownLatch(1); + mController.postReportSyncScheduled(PACKAGE_1, USER_ID, true); + mStateChangedLatch.await(100, TimeUnit.MILLISECONDS); + assertEquals("Exempted sync scheduled in doze should set bucket to working set", + STANDBY_BUCKET_WORKING_SET, getStandbyBucket(mController, PACKAGE_1)); + + setAndAssertBucket(PACKAGE_1, USER_ID, STANDBY_BUCKET_RARE, REASON_MAIN_FORCED_BY_SYSTEM); + mInjector.mDeviceIdleMode = false; + mStateChangedLatch = new CountDownLatch(1); + mController.postReportSyncScheduled(PACKAGE_1, USER_ID, true); + mStateChangedLatch.await(100, TimeUnit.MILLISECONDS); + assertEquals("Exempted sync scheduled while not in doze should set bucket to active", + STANDBY_BUCKET_ACTIVE, getStandbyBucket(mController, PACKAGE_1)); + } + private String getAdminAppsStr(int userId) { return getAdminAppsStr(userId, mController.getActiveAdminAppsForTest(userId)); } @@ -1095,4 +1148,12 @@ public class AppStandbyControllerTests { private void setActiveAdmins(int userId, String... admins) { mController.setActiveAdminApps(new ArraySet<>(Arrays.asList(admins)), userId); } + + private void setAndAssertBucket(String pkg, int user, int bucket, int reason) throws Exception { + mStateChangedLatch = new CountDownLatch(1); + mController.setAppStandbyBucket(pkg, user, bucket, reason); + mStateChangedLatch.await(100, TimeUnit.MILLISECONDS); + assertEquals("Failed to set package bucket", bucket, + getStandbyBucket(mController, PACKAGE_1)); + } } diff --git a/services/tests/wmtests/src/com/android/server/wm/ActivityStarterTests.java b/services/tests/wmtests/src/com/android/server/wm/ActivityStarterTests.java index b917e1b92fb9..049c8e1e5746 100644 --- a/services/tests/wmtests/src/com/android/server/wm/ActivityStarterTests.java +++ b/services/tests/wmtests/src/com/android/server/wm/ActivityStarterTests.java @@ -31,7 +31,6 @@ import static android.app.ActivityManager.START_TASK_TO_FRONT; import static android.app.WindowConfiguration.ACTIVITY_TYPE_STANDARD; import static android.app.WindowConfiguration.ACTIVITY_TYPE_UNDEFINED; import static android.app.WindowConfiguration.WINDOWING_MODE_FULLSCREEN; -import static android.app.WindowConfiguration.WINDOWING_MODE_PINNED; import static android.app.WindowConfiguration.WINDOWING_MODE_SPLIT_SCREEN_PRIMARY; import static android.app.WindowConfiguration.WINDOWING_MODE_SPLIT_SCREEN_SECONDARY; import static android.content.Intent.FLAG_ACTIVITY_LAUNCH_ADJACENT; @@ -50,7 +49,6 @@ import static com.android.dx.mockito.inline.extended.ExtendedMockito.spy; import static com.android.dx.mockito.inline.extended.ExtendedMockito.spyOn; import static com.android.dx.mockito.inline.extended.ExtendedMockito.times; import static com.android.dx.mockito.inline.extended.ExtendedMockito.verify; -import static com.android.server.wm.ActivityTaskManagerService.ANIMATE; import static com.android.server.wm.WindowContainer.POSITION_BOTTOM; import static com.android.server.wm.WindowContainer.POSITION_TOP; @@ -139,39 +137,6 @@ public class ActivityStarterTests extends ActivityTestsBase { } @Test - public void testUpdateLaunchBounds() { - // When in a non-resizeable stack, the task bounds should be updated. - final Task task = new TaskBuilder(mService.mStackSupervisor) - .setStack(mService.mRootWindowContainer.getDefaultDisplay().createStack( - WINDOWING_MODE_FULLSCREEN, ACTIVITY_TYPE_STANDARD, true /* onTop */)) - .build(); - final Rect bounds = new Rect(10, 10, 100, 100); - - mStarter.updateBounds(task, bounds); - assertEquals(bounds, task.getRequestedOverrideBounds()); - assertEquals(new Rect(), task.getStack().getRequestedOverrideBounds()); - - // When in a resizeable stack, the stack bounds should be updated as well. - final Task task2 = new TaskBuilder(mService.mStackSupervisor) - .setStack(mService.mRootWindowContainer.getDefaultDisplay().createStack( - WINDOWING_MODE_PINNED, ACTIVITY_TYPE_STANDARD, true /* onTop */)) - .build(); - assertThat((Object) task2.getStack()).isInstanceOf(ActivityStack.class); - mStarter.updateBounds(task2, bounds); - - verify(mService, times(1)).animateResizePinnedStack(eq(task2.getRootTaskId()), - eq(bounds), anyInt()); - - // In the case of no animation, the stack and task bounds should be set immediately. - if (!ANIMATE) { - assertEquals(bounds, task2.getStack().getRequestedOverrideBounds()); - assertEquals(bounds, task2.getRequestedOverrideBounds()); - } else { - assertEquals(new Rect(), task2.getRequestedOverrideBounds()); - } - } - - @Test public void testStartActivityPreconditions() { verifyStartActivityPreconditions(PRECONDITION_NO_CALLER_APP, START_PERMISSION_DENIED); verifyStartActivityPreconditions(PRECONDITION_NO_INTENT_COMPONENT, @@ -383,7 +348,7 @@ public class ActivityStarterTests extends ActivityTestsBase { // Never review permissions doReturn(false).when(mMockPackageManager).isPermissionsReviewRequired(any(), anyInt()); doNothing().when(mMockPackageManager).grantImplicitAccess( - anyInt(), any(), anyInt(), anyInt()); + anyInt(), any(), anyInt(), anyInt(), anyBoolean()); doNothing().when(mMockPackageManager).notifyPackageUse(anyString(), anyInt()); final Intent intent = new Intent(); diff --git a/services/tests/wmtests/src/com/android/server/wm/BoundsAnimationControllerTests.java b/services/tests/wmtests/src/com/android/server/wm/BoundsAnimationControllerTests.java deleted file mode 100644 index 1dda535cfb95..000000000000 --- a/services/tests/wmtests/src/com/android/server/wm/BoundsAnimationControllerTests.java +++ /dev/null @@ -1,627 +0,0 @@ -/* - * Copyright (C) 2017 The Android Open Source Project - * - * Licensed under the Apache License, Version 2.0 (the "License"); - * you may not use this file except in compliance with the License. - * You may obtain a copy of the License at - * - * http://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, - * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - * See the License for the specific language governing permissions and - * limitations under the License. - */ - -package com.android.server.wm; - -import static androidx.test.platform.app.InstrumentationRegistry.getInstrumentation; - -import static com.android.server.wm.BoundsAnimationController.BOUNDS; -import static com.android.server.wm.BoundsAnimationController.FADE_IN; -import static com.android.server.wm.BoundsAnimationController.NO_PIP_MODE_CHANGED_CALLBACKS; -import static com.android.server.wm.BoundsAnimationController.SCHEDULE_PIP_MODE_CHANGED_ON_END; -import static com.android.server.wm.BoundsAnimationController.SCHEDULE_PIP_MODE_CHANGED_ON_START; -import static com.android.server.wm.BoundsAnimationController.SchedulePipModeChangedState; - -import static org.junit.Assert.assertEquals; -import static org.junit.Assert.assertFalse; -import static org.junit.Assert.assertNotEquals; -import static org.junit.Assert.assertNull; -import static org.junit.Assert.assertSame; -import static org.junit.Assert.assertTrue; - -import android.animation.ValueAnimator; -import android.content.Context; -import android.graphics.Rect; -import android.os.Handler; -import android.os.Looper; -import android.platform.test.annotations.Presubmit; - -import androidx.test.annotation.UiThreadTest; -import androidx.test.filters.SmallTest; - -import com.android.server.wm.BoundsAnimationController.BoundsAnimator; -import com.android.server.wm.WindowManagerInternal.AppTransitionListener; - -import org.junit.Before; -import org.junit.Test; -import org.junit.runner.RunWith; - -/** - * Test class for {@link BoundsAnimationController} to ensure that it sends the right callbacks - * depending on the various interactions. - * - * We are really concerned about only three of the transition states [F = fullscreen, !F = floating] - * F->!F, !F->!F, and !F->F. Each animation can only be cancelled from the target mid-transition, - * or if a new animation starts on the same target. The tests below verifies that the target is - * notified of all the cases where it is animating and cancelled so that it can respond - * appropriately. - * - * Build/Install/Run: - * atest WmTests:BoundsAnimationControllerTests - */ -@SmallTest -@Presubmit -@RunWith(WindowTestRunner.class) -public class BoundsAnimationControllerTests extends WindowTestsBase { - - /** - * Mock value animator to simulate updates with. - */ - private static class MockValueAnimator extends ValueAnimator { - - private float mFraction; - - MockValueAnimator getWithValue(float fraction) { - mFraction = fraction; - return this; - } - - @Override - public Object getAnimatedValue() { - return mFraction; - } - } - - /** - * Mock app transition to fire notifications to the bounds animator. - */ - private static class MockAppTransition extends AppTransition { - - private AppTransitionListener mListener; - - MockAppTransition(Context context, WindowManagerService wm, DisplayContent displayContent) { - super(context, wm, displayContent); - } - - @Override - void registerListenerLocked(AppTransitionListener listener) { - mListener = listener; - } - - public void notifyTransitionPending() { - mListener.onAppTransitionPendingLocked(); - } - - public void notifyTransitionCancelled(int transit) { - mListener.onAppTransitionCancelledLocked(transit); - } - - public void notifyTransitionStarting(int transit) { - mListener.onAppTransitionStartingLocked(transit, 0, 0, 0); - } - - public void notifyTransitionFinished() { - mListener.onAppTransitionFinishedLocked(null); - } - } - - /** - * A test animate bounds user to track callbacks from the bounds animation. - */ - private static class TestBoundsAnimationTarget implements BoundsAnimationTarget { - - boolean mAwaitingAnimationStart; - boolean mMovedToFullscreen; - boolean mAnimationStarted; - boolean mSchedulePipModeChangedOnStart; - boolean mForcePipModeChangedCallback; - boolean mAnimationEnded; - Rect mAnimationEndFinalStackBounds; - boolean mSchedulePipModeChangedOnEnd; - boolean mBoundsUpdated; - boolean mCancelRequested; - Rect mStackBounds; - Rect mTaskBounds; - float mAlpha; - @BoundsAnimationController.AnimationType int mAnimationType; - - void initialize(Rect from) { - mAwaitingAnimationStart = true; - mMovedToFullscreen = false; - mAnimationStarted = false; - mAnimationEnded = false; - mAnimationEndFinalStackBounds = null; - mForcePipModeChangedCallback = false; - mSchedulePipModeChangedOnStart = false; - mSchedulePipModeChangedOnEnd = false; - mStackBounds = from; - mTaskBounds = null; - mBoundsUpdated = false; - } - - @Override - public boolean onAnimationStart(boolean schedulePipModeChangedCallback, - boolean forceUpdate, @BoundsAnimationController.AnimationType int animationType) { - mAwaitingAnimationStart = false; - mAnimationStarted = true; - mSchedulePipModeChangedOnStart = schedulePipModeChangedCallback; - mForcePipModeChangedCallback = forceUpdate; - mAnimationType = animationType; - return true; - } - - @Override - public boolean shouldDeferStartOnMoveToFullscreen() { - return true; - } - - @Override - public boolean setPinnedStackSize(Rect stackBounds, Rect taskBounds) { - // TODO: Once we break the runs apart, we should fail() here if this is called outside - // of onAnimationStart() and onAnimationEnd() - if (mCancelRequested) { - mCancelRequested = false; - return false; - } else { - mBoundsUpdated = true; - mStackBounds = stackBounds; - mTaskBounds = taskBounds; - return true; - } - } - - @Override - public void onAnimationEnd(boolean schedulePipModeChangedCallback, Rect finalStackBounds, - boolean moveToFullscreen) { - mAnimationEnded = true; - mAnimationEndFinalStackBounds = finalStackBounds; - mSchedulePipModeChangedOnEnd = schedulePipModeChangedCallback; - mMovedToFullscreen = moveToFullscreen; - mTaskBounds = null; - } - - @Override - public boolean setPinnedStackAlpha(float alpha) { - mAlpha = alpha; - return true; - } - } - - /** - * Drives the animations, makes common assertions along the way. - */ - private static class BoundsAnimationDriver { - - private final BoundsAnimationController mController; - private final TestBoundsAnimationTarget mTarget; - private final MockValueAnimator mMockAnimator; - - private BoundsAnimator mAnimator; - private Rect mFrom; - private Rect mTo; - private Rect mLargerBounds; - private Rect mExpectedFinalBounds; - private @BoundsAnimationController.AnimationType int mAnimationType; - - BoundsAnimationDriver(BoundsAnimationController controller, - TestBoundsAnimationTarget target, MockValueAnimator mockValueAnimator) { - mController = controller; - mTarget = target; - mMockAnimator = mockValueAnimator; - } - - BoundsAnimationDriver start(Rect from, Rect to, - @BoundsAnimationController.AnimationType int animationType) { - if (mAnimator != null) { - throw new IllegalArgumentException("Call restart() to restart an animation"); - } - - boolean fromFullscreen = from.equals(BOUNDS_FULL); - boolean toFullscreen = to.equals(BOUNDS_FULL); - - mTarget.initialize(from); - - // Started, not running - assertTrue(mTarget.mAwaitingAnimationStart); - assertFalse(mTarget.mAnimationStarted); - - startImpl(from, to, animationType); - - // Ensure that the animator is paused for the all windows drawn signal when animating - // to/from fullscreen - if (fromFullscreen || toFullscreen) { - assertTrue(mAnimator.isPaused()); - mController.onAllWindowsDrawn(); - } else { - assertTrue(!mAnimator.isPaused()); - } - - // Started and running - assertFalse(mTarget.mAwaitingAnimationStart); - assertTrue(mTarget.mAnimationStarted); - - return this; - } - - BoundsAnimationDriver restart(Rect to, boolean expectStartedAndPipModeChangedCallback) { - if (mAnimator == null) { - throw new IllegalArgumentException("Call start() to start a new animation"); - } - - BoundsAnimator oldAnimator = mAnimator; - boolean toSameBounds = mAnimator.isStarted() && to.equals(mTo); - - // Reset the animation start state - mTarget.mAnimationStarted = false; - - // Start animation - startImpl(mTarget.mStackBounds, to, BOUNDS); - - if (toSameBounds) { - // Same animator if same final bounds - assertSame(oldAnimator, mAnimator); - } - - if (expectStartedAndPipModeChangedCallback) { - // Replacing animation with pending pip mode changed callback, ensure we update - assertTrue(mTarget.mAnimationStarted); - assertTrue(mTarget.mSchedulePipModeChangedOnStart); - assertTrue(mTarget.mForcePipModeChangedCallback); - } else { - // No animation start for replacing animation - assertFalse(mTarget.mAnimationStarted); - } - mTarget.mAnimationStarted = true; - return this; - } - - private BoundsAnimationDriver startImpl(Rect from, Rect to, - @BoundsAnimationController.AnimationType int animationType) { - boolean fromFullscreen = from.equals(BOUNDS_FULL); - boolean toFullscreen = to.equals(BOUNDS_FULL); - mFrom = new Rect(from); - mTo = new Rect(to); - mExpectedFinalBounds = new Rect(to); - mLargerBounds = getLargerBounds(mFrom, mTo); - mAnimationType = animationType; - - // Start animation - final @SchedulePipModeChangedState int schedulePipModeChangedState = toFullscreen - ? SCHEDULE_PIP_MODE_CHANGED_ON_START - : fromFullscreen - ? SCHEDULE_PIP_MODE_CHANGED_ON_END - : NO_PIP_MODE_CHANGED_CALLBACKS; - mAnimator = mController.animateBoundsImpl(mTarget, from, to, DURATION, - schedulePipModeChangedState, fromFullscreen, toFullscreen, animationType); - - if (animationType == BOUNDS) { - // Original stack bounds, frozen task bounds - assertEquals(mFrom, mTarget.mStackBounds); - assertEqualSizeAtOffset(mLargerBounds, mTarget.mTaskBounds); - - // Animating to larger size - if (mFrom.equals(mLargerBounds)) { - assertFalse(mAnimator.animatingToLargerSize()); - } else if (mTo.equals(mLargerBounds)) { - assertTrue(mAnimator.animatingToLargerSize()); - } - } - - return this; - } - - BoundsAnimationDriver expectStarted(boolean schedulePipModeChanged) { - // Callback made - assertTrue(mTarget.mAnimationStarted); - - assertEquals(schedulePipModeChanged, mTarget.mSchedulePipModeChangedOnStart); - return this; - } - - BoundsAnimationDriver update(float t) { - mAnimator.onAnimationUpdate(mMockAnimator.getWithValue(t)); - - if (mAnimationType == BOUNDS) { - // Temporary stack bounds, frozen task bounds - if (t == 0f) { - assertEquals(mFrom, mTarget.mStackBounds); - } else if (t == 1f) { - assertEquals(mTo, mTarget.mStackBounds); - } else { - assertNotEquals(mFrom, mTarget.mStackBounds); - assertNotEquals(mTo, mTarget.mStackBounds); - } - assertEqualSizeAtOffset(mLargerBounds, mTarget.mTaskBounds); - } else { - assertEquals((float) mMockAnimator.getAnimatedValue(), mTarget.mAlpha, 0.01f); - } - return this; - } - - BoundsAnimationDriver cancel() { - // Cancel - mTarget.mCancelRequested = true; - mTarget.mBoundsUpdated = false; - mExpectedFinalBounds = null; - - // Update - mAnimator.onAnimationUpdate(mMockAnimator.getWithValue(0.5f)); - - // Not started, not running, cancel reset - assertFalse(mTarget.mCancelRequested); - - // Stack/task bounds not updated - assertFalse(mTarget.mBoundsUpdated); - - // Callback made - assertTrue(mTarget.mAnimationEnded); - assertNull(mTarget.mAnimationEndFinalStackBounds); - - return this; - } - - BoundsAnimationDriver end() { - mAnimator.end(); - - if (mAnimationType == BOUNDS) { - // Final stack bounds - assertEquals(mTo, mTarget.mStackBounds); - assertEquals(mExpectedFinalBounds, mTarget.mAnimationEndFinalStackBounds); - assertNull(mTarget.mTaskBounds); - } else { - assertEquals(mTarget.mAlpha, 1f, 0.01f); - } - - return this; - } - - BoundsAnimationDriver expectEnded(boolean schedulePipModeChanged, - boolean moveToFullscreen) { - // Callback made - assertTrue(mTarget.mAnimationEnded); - - assertEquals(schedulePipModeChanged, mTarget.mSchedulePipModeChangedOnEnd); - assertEquals(moveToFullscreen, mTarget.mMovedToFullscreen); - return this; - } - - private static Rect getLargerBounds(Rect r1, Rect r2) { - int r1Area = r1.width() * r1.height(); - int r2Area = r2.width() * r2.height(); - return (r1Area <= r2Area) ? r2 : r1; - } - } - - // Constants - private static final boolean SCHEDULE_PIP_MODE_CHANGED = true; - private static final boolean MOVE_TO_FULLSCREEN = true; - private static final int DURATION = 100; - - // Some dummy bounds to represent fullscreen and floating bounds - private static final Rect BOUNDS_FULL = new Rect(0, 0, 100, 100); - private static final Rect BOUNDS_FLOATING = new Rect(60, 60, 95, 95); - private static final Rect BOUNDS_SMALLER_FLOATING = new Rect(80, 80, 95, 95); - - // Common - private MockAppTransition mMockAppTransition; - private TestBoundsAnimationTarget mTarget; - private BoundsAnimationController mController; - private BoundsAnimationDriver mDriver; - - // Temp - private static final Rect sTmpRect = new Rect(); - - @Before - public void setUp() throws Exception { - final Context context = getInstrumentation().getTargetContext(); - final Handler handler = new Handler(Looper.getMainLooper()); - mMockAppTransition = new MockAppTransition(context, mWm, mDisplayContent); - mTarget = new TestBoundsAnimationTarget(); - mController = new BoundsAnimationController(context, mMockAppTransition, handler, null); - final MockValueAnimator mockValueAnimator = new MockValueAnimator(); - mDriver = new BoundsAnimationDriver(mController, mTarget, mockValueAnimator); - } - - /** BASE TRANSITIONS **/ - - @UiThreadTest - @Test - public void testFullscreenToFloatingTransition() { - mDriver.start(BOUNDS_FULL, BOUNDS_FLOATING, BOUNDS) - .expectStarted(!SCHEDULE_PIP_MODE_CHANGED) - .update(0f) - .update(0.5f) - .update(1f) - .end() - .expectEnded(SCHEDULE_PIP_MODE_CHANGED, !MOVE_TO_FULLSCREEN); - } - - @UiThreadTest - @Test - public void testFloatingToFullscreenTransition() { - mDriver.start(BOUNDS_FLOATING, BOUNDS_FULL, BOUNDS) - .expectStarted(SCHEDULE_PIP_MODE_CHANGED) - .update(0f) - .update(0.5f) - .update(1f) - .end() - .expectEnded(!SCHEDULE_PIP_MODE_CHANGED, MOVE_TO_FULLSCREEN); - } - - @UiThreadTest - @Test - public void testFloatingToSmallerFloatingTransition() { - mDriver.start(BOUNDS_FLOATING, BOUNDS_SMALLER_FLOATING, BOUNDS) - .expectStarted(!SCHEDULE_PIP_MODE_CHANGED) - .update(0f) - .update(0.5f) - .update(1f) - .end() - .expectEnded(!SCHEDULE_PIP_MODE_CHANGED, !MOVE_TO_FULLSCREEN); - } - - @UiThreadTest - @Test - public void testFloatingToLargerFloatingTransition() { - mDriver.start(BOUNDS_SMALLER_FLOATING, BOUNDS_FLOATING, BOUNDS) - .expectStarted(!SCHEDULE_PIP_MODE_CHANGED) - .update(0f) - .update(0.5f) - .update(1f) - .end() - .expectEnded(!SCHEDULE_PIP_MODE_CHANGED, !MOVE_TO_FULLSCREEN); - } - - /** F->!F w/ CANCEL **/ - - @UiThreadTest - @Test - public void testFullscreenToFloatingCancelFromTarget() { - mDriver.start(BOUNDS_FULL, BOUNDS_FLOATING, BOUNDS) - .expectStarted(!SCHEDULE_PIP_MODE_CHANGED) - .update(0.25f) - .cancel() - .expectEnded(SCHEDULE_PIP_MODE_CHANGED, !MOVE_TO_FULLSCREEN); - } - - @UiThreadTest - @Test - public void testFullscreenToFloatingCancelFromAnimationToSameBounds() { - mDriver.start(BOUNDS_FULL, BOUNDS_FLOATING, BOUNDS) - .expectStarted(!SCHEDULE_PIP_MODE_CHANGED) - .update(0.25f) - .restart(BOUNDS_FLOATING, false /* expectStartedAndPipModeChangedCallback */) - .end() - .expectEnded(SCHEDULE_PIP_MODE_CHANGED, !MOVE_TO_FULLSCREEN); - } - - @UiThreadTest - @Test - public void testFullscreenToFloatingCancelFromAnimationToFloatingBounds() { - mDriver.start(BOUNDS_FULL, BOUNDS_FLOATING, BOUNDS) - .expectStarted(!SCHEDULE_PIP_MODE_CHANGED) - .update(0.25f) - .restart(BOUNDS_SMALLER_FLOATING, - false /* expectStartedAndPipModeChangedCallback */) - .end() - .expectEnded(SCHEDULE_PIP_MODE_CHANGED, !MOVE_TO_FULLSCREEN); - } - - @UiThreadTest - @Test - public void testFullscreenToFloatingCancelFromAnimationToFullscreenBounds() { - // When animating from fullscreen and the animation is interruped, we expect the animation - // start callback to be made, with a forced pip mode change callback - mDriver.start(BOUNDS_FULL, BOUNDS_FLOATING, BOUNDS) - .expectStarted(!SCHEDULE_PIP_MODE_CHANGED) - .update(0.25f) - .restart(BOUNDS_FULL, true /* expectStartedAndPipModeChangedCallback */) - .end() - .expectEnded(!SCHEDULE_PIP_MODE_CHANGED, MOVE_TO_FULLSCREEN); - } - - /** !F->F w/ CANCEL **/ - - @UiThreadTest - @Test - public void testFloatingToFullscreenCancelFromTarget() { - mDriver.start(BOUNDS_FLOATING, BOUNDS_FULL, BOUNDS) - .expectStarted(SCHEDULE_PIP_MODE_CHANGED) - .update(0.25f) - .cancel() - .expectEnded(SCHEDULE_PIP_MODE_CHANGED, !MOVE_TO_FULLSCREEN); - } - - @UiThreadTest - @Test - public void testFloatingToFullscreenCancelFromAnimationToSameBounds() { - mDriver.start(BOUNDS_FLOATING, BOUNDS_FULL, BOUNDS) - .expectStarted(SCHEDULE_PIP_MODE_CHANGED) - .update(0.25f) - .restart(BOUNDS_FULL, false /* expectStartedAndPipModeChangedCallback */) - .end() - .expectEnded(!SCHEDULE_PIP_MODE_CHANGED, MOVE_TO_FULLSCREEN); - } - - @UiThreadTest - @Test - public void testFloatingToFullscreenCancelFromAnimationToFloatingBounds() { - mDriver.start(BOUNDS_FLOATING, BOUNDS_FULL, BOUNDS) - .expectStarted(SCHEDULE_PIP_MODE_CHANGED) - .update(0.25f) - .restart(BOUNDS_SMALLER_FLOATING, - false /* expectStartedAndPipModeChangedCallback */) - .end() - .expectEnded(SCHEDULE_PIP_MODE_CHANGED, MOVE_TO_FULLSCREEN); - } - - /** !F->!F w/ CANCEL **/ - - @UiThreadTest - @Test - public void testFloatingToSmallerFloatingCancelFromTarget() { - mDriver.start(BOUNDS_FLOATING, BOUNDS_SMALLER_FLOATING, BOUNDS) - .expectStarted(!SCHEDULE_PIP_MODE_CHANGED) - .update(0.25f) - .cancel() - .expectEnded(!SCHEDULE_PIP_MODE_CHANGED, !MOVE_TO_FULLSCREEN); - } - - @UiThreadTest - @Test - public void testFloatingToLargerFloatingCancelFromTarget() { - mDriver.start(BOUNDS_SMALLER_FLOATING, BOUNDS_FLOATING, BOUNDS) - .expectStarted(!SCHEDULE_PIP_MODE_CHANGED) - .update(0.25f) - .cancel() - .expectEnded(!SCHEDULE_PIP_MODE_CHANGED, !MOVE_TO_FULLSCREEN); - } - - @UiThreadTest - @Test - public void testFadeIn() { - mDriver.start(BOUNDS_FULL, BOUNDS_FLOATING, FADE_IN) - .expectStarted(!SCHEDULE_PIP_MODE_CHANGED) - .update(0f) - .update(0.5f) - .update(1f) - .end() - .expectEnded(SCHEDULE_PIP_MODE_CHANGED, !MOVE_TO_FULLSCREEN); - } - - /** MISC **/ - - @UiThreadTest - @Test - public void testBoundsAreCopied() { - Rect from = new Rect(0, 0, 100, 100); - Rect to = new Rect(25, 25, 75, 75); - mDriver.start(from, to, BOUNDS) - .update(0.25f) - .end(); - assertEquals(new Rect(0, 0, 100, 100), from); - assertEquals(new Rect(25, 25, 75, 75), to); - } - - /** - * @return whether the task and stack bounds would be the same if they were at the same offset. - */ - private static boolean assertEqualSizeAtOffset(Rect stackBounds, Rect taskBounds) { - sTmpRect.set(taskBounds); - sTmpRect.offsetTo(stackBounds.left, stackBounds.top); - return stackBounds.equals(sTmpRect); - } -} 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 444906977f47..d9c5c4c7dffc 100644 --- a/services/tests/wmtests/src/com/android/server/wm/RecentTasksTest.java +++ b/services/tests/wmtests/src/com/android/server/wm/RecentTasksTest.java @@ -1102,12 +1102,8 @@ public class RecentTasksTest extends ActivityTestsBase { assertSecurityException(expectCallable, () -> mService.moveTopActivityToPinnedStack(INVALID_STACK_ID, new Rect())); assertSecurityException(expectCallable, - () -> mService.animateResizePinnedStack(INVALID_STACK_ID, new Rect(), -1)); - assertSecurityException(expectCallable, () -> mService.resizeDockedStack(new Rect(), new Rect(), new Rect(), new Rect(), new Rect())); - assertSecurityException(expectCallable, - () -> mService.resizePinnedStack(new Rect(), new Rect())); assertSecurityException(expectCallable, () -> mService.getAllStackInfos()); assertSecurityException(expectCallable, () -> mService.getStackInfo(WINDOWING_MODE_UNDEFINED, ACTIVITY_TYPE_UNDEFINED)); diff --git a/services/tests/wmtests/src/com/android/server/wm/RecentsAnimationControllerTest.java b/services/tests/wmtests/src/com/android/server/wm/RecentsAnimationControllerTest.java index c9efb7018616..5c36f5c39e0c 100644 --- a/services/tests/wmtests/src/com/android/server/wm/RecentsAnimationControllerTest.java +++ b/services/tests/wmtests/src/com/android/server/wm/RecentsAnimationControllerTest.java @@ -333,7 +333,7 @@ public class RecentsAnimationControllerTest extends WindowTestsBase { assertTrue(mController.isAnimatingTask(activity.getTask())); // Assume activity transition should animate when no - // IRecentsAnimationController#setCancelWithDeferredScreenshot called. + // IRecentsAnimationController#setDeferCancelUntilNextTransition called. assertFalse(mController.shouldDeferCancelWithScreenshot()); assertTrue(activity.shouldAnimate(TRANSIT_ACTIVITY_CLOSE)); } diff --git a/services/tests/wmtests/src/com/android/server/wm/RecentsAnimationTest.java b/services/tests/wmtests/src/com/android/server/wm/RecentsAnimationTest.java index b78107e9024f..b3a253029ed0 100644 --- a/services/tests/wmtests/src/com/android/server/wm/RecentsAnimationTest.java +++ b/services/tests/wmtests/src/com/android/server/wm/RecentsAnimationTest.java @@ -208,7 +208,6 @@ public class RecentsAnimationTest extends ActivityTestsBase { @Test public void testSetLaunchTaskBehindOfTargetActivity() { DisplayContent display = mRootWindowContainer.getDefaultDisplay(); - display.mDisplayContent.mBoundsAnimationController = mock(BoundsAnimationController.class); ActivityStack homeStack = display.getRootHomeTask(); // Assume the home activity support recents. ActivityRecord targetActivity = homeStack.getTopNonFinishingActivity(); @@ -254,7 +253,6 @@ public class RecentsAnimationTest extends ActivityTestsBase { @Test public void testCancelAnimationOnVisibleStackOrderChange() { DisplayContent display = mService.mRootWindowContainer.getDefaultDisplay(); - display.mDisplayContent.mBoundsAnimationController = mock(BoundsAnimationController.class); ActivityStack fullscreenStack = display.createStack(WINDOWING_MODE_FULLSCREEN, ACTIVITY_TYPE_STANDARD, true /* onTop */); new ActivityBuilder(mService) diff --git a/services/tests/wmtests/src/com/android/server/wm/TaskSnapshotControllerTest.java b/services/tests/wmtests/src/com/android/server/wm/TaskSnapshotControllerTest.java index bd8aacb6cb96..20d9aff5f3bf 100644 --- a/services/tests/wmtests/src/com/android/server/wm/TaskSnapshotControllerTest.java +++ b/services/tests/wmtests/src/com/android/server/wm/TaskSnapshotControllerTest.java @@ -25,6 +25,7 @@ import static com.android.server.wm.TaskSnapshotController.SNAPSHOT_MODE_APP_THE import static com.android.server.wm.TaskSnapshotController.SNAPSHOT_MODE_REAL; import static org.junit.Assert.assertEquals; +import static org.junit.Assert.assertFalse; import static org.junit.Assert.assertNotEquals; import static org.junit.Assert.assertSame; import static org.junit.Assert.assertTrue; @@ -36,6 +37,7 @@ import android.content.res.Configuration; import android.graphics.ColorSpace; import android.graphics.GraphicBuffer; import android.graphics.PixelFormat; +import android.graphics.Point; import android.graphics.Rect; import android.platform.test.annotations.Presubmit; import android.util.ArraySet; @@ -138,6 +140,7 @@ public class TaskSnapshotControllerTest extends WindowTestsBase { final int orientation = Configuration.ORIENTATION_PORTRAIT; final float scaleFraction = 0.25f; final Rect contentInsets = new Rect(1, 2, 3, 4); + final Point taskSize = new Point(5, 6); try { ActivityManager.TaskSnapshot.Builder builder = @@ -147,14 +150,13 @@ public class TaskSnapshotControllerTest extends WindowTestsBase { builder.setSystemUiVisibility(systemUiVisibility); builder.setWindowingMode(windowingMode); builder.setColorSpace(sRGB); - builder.setIsLowResolution(true); builder.setOrientation(orientation); builder.setContentInsets(contentInsets); builder.setIsTranslucent(true); - builder.setScaleFraction(0.25f); builder.setSnapshot(buffer); builder.setIsRealSnapshot(true); builder.setPixelFormat(pixelFormat); + builder.setTaskSize(taskSize); // Not part of TaskSnapshot itself, used in screenshot process assertEquals(pixelFormat, builder.getPixelFormat()); @@ -165,13 +167,15 @@ public class TaskSnapshotControllerTest extends WindowTestsBase { assertEquals(systemUiVisibility, snapshot.getSystemUiVisibility()); assertEquals(windowingMode, snapshot.getWindowingMode()); assertEquals(sRGB, snapshot.getColorSpace()); - assertTrue(snapshot.isLowResolution()); + // Snapshots created with the Builder class are always high-res. The only way to get a + // low-res snapshot is to load it from the disk in TaskSnapshotLoader. + assertFalse(snapshot.isLowResolution()); assertEquals(orientation, snapshot.getOrientation()); assertEquals(contentInsets, snapshot.getContentInsets()); assertTrue(snapshot.isTranslucent()); - assertEquals(scaleFraction, builder.getScaleFraction(), 0.001f); assertSame(buffer, snapshot.getSnapshot()); assertTrue(snapshot.isRealSnapshot()); + assertEquals(taskSize, snapshot.getTaskSize()); } finally { if (buffer != null) { buffer.destroy(); @@ -188,11 +192,9 @@ public class TaskSnapshotControllerTest extends WindowTestsBase { final ActivityManager.TaskSnapshot.Builder builder = new ActivityManager.TaskSnapshot.Builder(); - final float scaleFraction = 0.8f; mWm.mTaskSnapshotController.prepareTaskSnapshot(mAppWindow.mActivityRecord.getTask(), - scaleFraction, PixelFormat.UNKNOWN, builder); + PixelFormat.UNKNOWN, builder); - assertEquals(scaleFraction, builder.getScaleFraction(), 0 /* delta */); // The pixel format should be selected automatically. assertNotEquals(PixelFormat.UNKNOWN, builder.getPixelFormat()); } diff --git a/services/tests/wmtests/src/com/android/server/wm/TaskSnapshotPersisterLoaderTest.java b/services/tests/wmtests/src/com/android/server/wm/TaskSnapshotPersisterLoaderTest.java index 0b16e5ce8b97..40f15b7e9d39 100644 --- a/services/tests/wmtests/src/com/android/server/wm/TaskSnapshotPersisterLoaderTest.java +++ b/services/tests/wmtests/src/com/android/server/wm/TaskSnapshotPersisterLoaderTest.java @@ -19,12 +19,16 @@ package com.android.server.wm; import static android.app.WindowConfiguration.WINDOWING_MODE_FULLSCREEN; import static android.app.WindowConfiguration.WINDOWING_MODE_PINNED; +import static com.android.dx.mockito.inline.extended.ExtendedMockito.mockitoSession; + import static org.junit.Assert.assertEquals; import static org.junit.Assert.assertFalse; import static org.junit.Assert.assertNotNull; import static org.junit.Assert.assertNull; import static org.junit.Assert.assertTrue; +import static org.mockito.Mockito.when; +import android.app.ActivityManager; import android.app.ActivityManager.TaskSnapshot; import android.content.res.Configuration; import android.graphics.Rect; @@ -36,10 +40,12 @@ import android.view.View; import androidx.test.filters.MediumTest; +import com.android.server.wm.TaskSnapshotLoader.PreRLegacySnapshotConfig; import com.android.server.wm.TaskSnapshotPersister.RemoveObsoleteFilesQueueItem; import org.junit.Test; import org.junit.runner.RunWith; +import org.mockito.MockitoSession; import java.io.File; import java.util.function.Predicate; @@ -55,6 +61,8 @@ import java.util.function.Predicate; @RunWith(WindowTestRunner.class) public class TaskSnapshotPersisterLoaderTest extends TaskSnapshotPersisterTestBase { + private static final float DELTA = 0.00001f; + private static final Rect TEST_INSETS = new Rect(10, 20, 30, 40); @Test @@ -148,29 +156,172 @@ public class TaskSnapshotPersisterLoaderTest extends TaskSnapshotPersisterTestBa } @Test - public void testLowResolutionPersistAndLoadSnapshot() { + public void testLegacyPLowRamConfig() throws Exception { + MockitoSession mockSession = mockitoSession() + .initMocks(this) + .mockStatic(ActivityManager.class) + .startMocking(); + + when(ActivityManager.isLowRamDeviceStatic()).thenReturn(true); + + // taskWidth and legacyScale as would be defined in the proto, and presence of a *.jpg file, + // for any P low_ram device + final int taskWidth = 0; + final float legacyScale = 0f; + final boolean hasHighResFile = false; + + PreRLegacySnapshotConfig highResConf = mLoader.getLegacySnapshotConfig( + taskWidth, legacyScale, hasHighResFile, false /* loadLowResolutionBitmap */); + assertNotNull(highResConf); + assertEquals(highResConf.mScale, 0.6f, DELTA); + assertTrue(highResConf.mForceLoadReducedJpeg); + + PreRLegacySnapshotConfig lowResConf = mLoader.getLegacySnapshotConfig( + taskWidth, legacyScale, hasHighResFile, true /* loadLowResolutionBitmap */); + assertNotNull(lowResConf); + assertEquals(lowResConf.mScale, 0.6f, DELTA); + assertTrue(lowResConf.mForceLoadReducedJpeg); + + mockSession.finishMocking(); + } + + @Test + public void testLegacyPNonLowRamConfig() throws Exception { + MockitoSession mockSession = mockitoSession() + .initMocks(this) + .mockStatic(ActivityManager.class) + .startMocking(); + + when(ActivityManager.isLowRamDeviceStatic()).thenReturn(false); + + // taskWidth and legacyScale as would be defined in the proto, and presence of a *.jpg file, + // for any O device, or a P non-low_ram device + final int taskWidth = 0; + final float legacyScale = 0f; + final boolean hasHighResFile = true; + + PreRLegacySnapshotConfig highResConf = mLoader.getLegacySnapshotConfig( + taskWidth, legacyScale, hasHighResFile, false /* loadLowResolutionBitmap */); + assertNotNull(highResConf); + assertEquals(highResConf.mScale, 1.0f, DELTA); + assertFalse(highResConf.mForceLoadReducedJpeg); + + PreRLegacySnapshotConfig lowResConf = mLoader.getLegacySnapshotConfig( + taskWidth, legacyScale, hasHighResFile, true /* loadLowResolutionBitmap */); + assertNotNull(lowResConf); + assertEquals(lowResConf.mScale, 0.5f, DELTA); + assertFalse(lowResConf.mForceLoadReducedJpeg); + + mockSession.finishMocking(); + } + + @Test + public void testLegacyQLowRamConfig() throws Exception { + MockitoSession mockSession = mockitoSession() + .initMocks(this) + .mockStatic(ActivityManager.class) + .startMocking(); + + when(ActivityManager.isLowRamDeviceStatic()).thenReturn(true); + + // taskWidth and legacyScale as would be defined in the proto, and presence of a *.jpg file, + // for any Q low_ram device + final int taskWidth = 0; + final float legacyScale = 0.6f; + final boolean hasHighResFile = false; + + PreRLegacySnapshotConfig highResConf = mLoader.getLegacySnapshotConfig( + taskWidth, legacyScale, hasHighResFile, false /* loadLowResolutionBitmap */); + assertNotNull(highResConf); + assertEquals(highResConf.mScale, legacyScale, DELTA); + assertEquals(highResConf.mScale, 0.6f, DELTA); + assertTrue(highResConf.mForceLoadReducedJpeg); + + PreRLegacySnapshotConfig lowResConf = mLoader.getLegacySnapshotConfig( + taskWidth, legacyScale, hasHighResFile, true /* loadLowResolutionBitmap */); + assertNotNull(lowResConf); + assertEquals(lowResConf.mScale, legacyScale, DELTA); + assertEquals(lowResConf.mScale, 0.6f, DELTA); + assertTrue(lowResConf.mForceLoadReducedJpeg); + + mockSession.finishMocking(); + } + + @Test + public void testLegacyQNonLowRamConfig() throws Exception { + MockitoSession mockSession = mockitoSession() + .initMocks(this) + .mockStatic(ActivityManager.class) + .startMocking(); + + when(ActivityManager.isLowRamDeviceStatic()).thenReturn(false); + + // taskWidth and legacyScale as would be defined in the proto, and presence of a *.jpg file, + // for any Q non-low_ram device + final int taskWidth = 0; + final float legacyScale = 0.8f; + final boolean hasHighResFile = true; + + PreRLegacySnapshotConfig highResConf = mLoader.getLegacySnapshotConfig( + taskWidth, legacyScale, hasHighResFile, false /* loadLowResolutionBitmap */); + assertNotNull(highResConf); + assertEquals(highResConf.mScale, legacyScale, DELTA); + assertEquals(highResConf.mScale, 0.8f, DELTA); + assertFalse(highResConf.mForceLoadReducedJpeg); + + PreRLegacySnapshotConfig lowResConf = mLoader.getLegacySnapshotConfig( + taskWidth, legacyScale, hasHighResFile, true /* loadLowResolutionBitmap */); + assertNotNull(lowResConf); + assertEquals(lowResConf.mScale, 0.5f * legacyScale, DELTA); + assertEquals(lowResConf.mScale, 0.5f * 0.8f, DELTA); + assertFalse(lowResConf.mForceLoadReducedJpeg); + + mockSession.finishMocking(); + } + + @Test + public void testNonLegacyRConfig() throws Exception { + // taskWidth and legacyScale as would be defined in the proto, and presence of a *.jpg file, + // for any R device + final int taskWidth = 1440; + final float legacyScale = 0f; + final boolean hasHighResFile = true; + + PreRLegacySnapshotConfig highResConf = mLoader.getLegacySnapshotConfig( + taskWidth, legacyScale, hasHighResFile, false /* loadLowResolutionBitmap */); + assertNull(highResConf); + + PreRLegacySnapshotConfig lowResConf = mLoader.getLegacySnapshotConfig( + taskWidth, legacyScale, hasHighResFile, true /* loadLowResolutionBitmap */); + assertNull(lowResConf); + } + + @Test + public void testDisabledLowResolutionPersistAndLoadSnapshot() { + mPersister.setEnableLowResSnapshots(false); + TaskSnapshot a = new TaskSnapshotBuilder() - .setScale(0.5f) + .setScaleFraction(0.5f) .setIsLowResolution(true) .build(); assertTrue(a.isLowResolution()); mPersister.persistSnapshot(1, mTestUserId, a); mPersister.waitForQueueEmpty(); final File[] files = new File[]{new File(FILES_DIR.getPath() + "/snapshots/1.proto"), - new File(FILES_DIR.getPath() + "/snapshots/1_reduced.jpg")}; + new File(FILES_DIR.getPath() + "/snapshots/1.jpg")}; final File[] nonExistsFiles = new File[]{ - new File(FILES_DIR.getPath() + "/snapshots/1.jpg"), + new File(FILES_DIR.getPath() + "/snapshots/1_reduced.jpg"), }; assertTrueForFiles(files, File::exists, " must exist"); assertTrueForFiles(nonExistsFiles, file -> !file.exists(), " must not exist"); - final TaskSnapshot snapshot = mLoader.loadTask(1, mTestUserId, true /* isLowResolution */); + final TaskSnapshot snapshot = mLoader.loadTask(1, mTestUserId, false /* isLowResolution */); assertNotNull(snapshot); assertEquals(TEST_INSETS, snapshot.getContentInsets()); assertNotNull(snapshot.getSnapshot()); assertEquals(Configuration.ORIENTATION_PORTRAIT, snapshot.getOrientation()); final TaskSnapshot snapshotNotExist = mLoader.loadTask(1, mTestUserId, - false /* isLowResolution */); + true /* isLowResolution */); assertNull(snapshotNotExist); } @@ -271,13 +422,11 @@ public class TaskSnapshotPersisterLoaderTest extends TaskSnapshotPersisterTestBa @Test public void testScalePersistAndLoadSnapshot() { TaskSnapshot a = new TaskSnapshotBuilder() - .setScale(0.25f) + .setScaleFraction(0.25f) .build(); TaskSnapshot b = new TaskSnapshotBuilder() - .setScale(0.75f) + .setScaleFraction(0.75f) .build(); - assertEquals(0.25f, a.getScale(), 1E-5); - assertEquals(0.75f, b.getScale(), 1E-5); mPersister.persistSnapshot(1, mTestUserId, a); mPersister.persistSnapshot(2, mTestUserId, b); mPersister.waitForQueueEmpty(); @@ -287,8 +436,6 @@ public class TaskSnapshotPersisterLoaderTest extends TaskSnapshotPersisterTestBa false /* isLowResolution */); assertNotNull(snapshotA); assertNotNull(snapshotB); - assertEquals(0.25f, snapshotA.getScale(), 1E-5); - assertEquals(0.75f, snapshotB.getScale(), 1E-5); } @Test diff --git a/services/tests/wmtests/src/com/android/server/wm/TaskSnapshotPersisterTestBase.java b/services/tests/wmtests/src/com/android/server/wm/TaskSnapshotPersisterTestBase.java index 4612dbab0a59..fa6663c06371 100644 --- a/services/tests/wmtests/src/com/android/server/wm/TaskSnapshotPersisterTestBase.java +++ b/services/tests/wmtests/src/com/android/server/wm/TaskSnapshotPersisterTestBase.java @@ -30,6 +30,7 @@ import android.graphics.Color; import android.graphics.ColorSpace; import android.graphics.GraphicBuffer; import android.graphics.PixelFormat; +import android.graphics.Point; import android.graphics.Rect; import android.os.UserManager; import android.view.Surface; @@ -87,8 +88,10 @@ class TaskSnapshotPersisterTestBase extends WindowTestsBase { * Builds a TaskSnapshot. */ static class TaskSnapshotBuilder { + private static final int SNAPSHOT_WIDTH = 100; + private static final int SNAPSHOT_HEIGHT = 100; - private float mScale = 1f; + private float mScaleFraction = 1f; private boolean mIsLowResolution = false; private boolean mIsRealSnapshot = true; private boolean mIsTranslucent = false; @@ -96,8 +99,11 @@ class TaskSnapshotPersisterTestBase extends WindowTestsBase { private int mSystemUiVisibility = 0; private int mRotation = Surface.ROTATION_0; - TaskSnapshotBuilder setScale(float scale) { - mScale = scale; + TaskSnapshotBuilder() { + } + + TaskSnapshotBuilder setScaleFraction(float scale) { + mScaleFraction = scale; return this; } @@ -132,15 +138,20 @@ class TaskSnapshotPersisterTestBase extends WindowTestsBase { } TaskSnapshot build() { - final GraphicBuffer buffer = GraphicBuffer.create(100, 100, PixelFormat.RGBA_8888, + // To satisfy existing tests, ensure the graphics buffer is always 100x100, and + // compute the ize of the task according to mScaleFraction. + Point taskSize = new Point((int) (SNAPSHOT_WIDTH / mScaleFraction), + (int) (SNAPSHOT_HEIGHT / mScaleFraction)); + final GraphicBuffer buffer = GraphicBuffer.create(SNAPSHOT_WIDTH, SNAPSHOT_HEIGHT, + PixelFormat.RGBA_8888, USAGE_HW_TEXTURE | USAGE_SW_READ_RARELY | USAGE_SW_READ_RARELY); Canvas c = buffer.lockCanvas(); c.drawColor(Color.RED); buffer.unlockCanvasAndPost(c); return new TaskSnapshot(MOCK_SNAPSHOT_ID, new ComponentName("", ""), buffer, ColorSpace.get(ColorSpace.Named.SRGB), ORIENTATION_PORTRAIT, - mRotation, TEST_INSETS, - mIsLowResolution, mScale, mIsRealSnapshot, + mRotation, taskSize, TEST_INSETS, + mIsLowResolution, mIsRealSnapshot, mWindowingMode, mSystemUiVisibility, mIsTranslucent); } } diff --git a/services/tests/wmtests/src/com/android/server/wm/TaskSnapshotSurfaceTest.java b/services/tests/wmtests/src/com/android/server/wm/TaskSnapshotSurfaceTest.java index bb0e5aec8e2e..2164de9ea191 100644 --- a/services/tests/wmtests/src/com/android/server/wm/TaskSnapshotSurfaceTest.java +++ b/services/tests/wmtests/src/com/android/server/wm/TaskSnapshotSurfaceTest.java @@ -38,6 +38,7 @@ import android.graphics.Color; import android.graphics.ColorSpace; import android.graphics.GraphicBuffer; import android.graphics.PixelFormat; +import android.graphics.Point; import android.graphics.Rect; import android.platform.test.annotations.Presubmit; import android.view.Surface; @@ -67,12 +68,22 @@ public class TaskSnapshotSurfaceTest extends WindowTestsBase { int windowFlags, Rect taskBounds) { final GraphicBuffer buffer = GraphicBuffer.create(width, height, PixelFormat.RGBA_8888, GraphicBuffer.USAGE_SW_READ_RARELY | GraphicBuffer.USAGE_SW_WRITE_NEVER); + + // Previously when constructing TaskSnapshots for this test, scale was 1.0f, so to mimic + // this behavior set the taskSize to be the same as the taskBounds width and height. The + // taskBounds passed here are assumed to be the same task bounds as when the snapshot was + // taken. We assume there is no aspect ratio mismatch between the screenshot and the + // taskBounds + assertEquals(width, taskBounds.width()); + assertEquals(height, taskBounds.height()); + Point taskSize = new Point(taskBounds.width(), taskBounds.height()); + final TaskSnapshot snapshot = new TaskSnapshot( System.currentTimeMillis(), new ComponentName("", ""), buffer, ColorSpace.get(ColorSpace.Named.SRGB), ORIENTATION_PORTRAIT, - Surface.ROTATION_0, contentInsets, false, - 1.0f, true /* isRealSnapshot */, WINDOWING_MODE_FULLSCREEN, + Surface.ROTATION_0, taskSize, contentInsets, false, + true /* isRealSnapshot */, WINDOWING_MODE_FULLSCREEN, 0 /* systemUiVisibility */, false /* isTranslucent */); mSurface = new TaskSnapshotSurface(mWm, new Window(), new SurfaceControl(), snapshot, "Test", createTaskDescription(Color.WHITE, Color.RED, Color.BLUE), sysuiVis, windowFlags, 0, @@ -152,7 +163,7 @@ public class TaskSnapshotSurfaceTest extends WindowTestsBase { @Test public void testCalculateSnapshotCrop_taskNotOnTop() { - setupSurface(100, 100, new Rect(0, 10, 0, 10), 0, 0, new Rect(0, 50, 100, 100)); + setupSurface(100, 100, new Rect(0, 10, 0, 10), 0, 0, new Rect(0, 50, 100, 150)); assertEquals(new Rect(0, 10, 100, 90), mSurface.calculateSnapshotCrop()); } diff --git a/telecomm/java/android/telecom/Connection.java b/telecomm/java/android/telecom/Connection.java index 4604cd2e2e75..5f33a3d6f1de 100755 --- a/telecomm/java/android/telecom/Connection.java +++ b/telecomm/java/android/telecom/Connection.java @@ -3358,7 +3358,6 @@ public abstract class Connection extends Conferenceable { private boolean mImmutable = false; public FailureSignalingConnection(DisconnectCause disconnectCause) { setDisconnected(disconnectCause); - mImmutable = true; } public void checkImmutable() { diff --git a/telecomm/java/android/telecom/TelecomManager.java b/telecomm/java/android/telecom/TelecomManager.java index 5d7d6490ba3e..7f4fcc0ea63c 100644 --- a/telecomm/java/android/telecom/TelecomManager.java +++ b/telecomm/java/android/telecom/TelecomManager.java @@ -330,6 +330,14 @@ public class TelecomManager { "android.telecom.extra.CALL_CREATED_TIME_MILLIS"; /** + * Optional extra for incoming and outgoing calls containing a long which specifies the Epoch + * time the call was created. + * @hide + */ + public static final String EXTRA_CALL_CREATED_EPOCH_TIME_MILLIS = + "android.telecom.extra.CALL_CREATED_EPOCH_TIME_MILLIS"; + + /** * Optional extra for incoming and outgoing calls containing a long which specifies the time * telecom began routing the call. This value is in milliseconds since boot. * @hide diff --git a/telephony/java/android/telephony/CarrierConfigManager.java b/telephony/java/android/telephony/CarrierConfigManager.java index a7e52ea21758..9d7762359f6b 100755 --- a/telephony/java/android/telephony/CarrierConfigManager.java +++ b/telephony/java/android/telephony/CarrierConfigManager.java @@ -3549,6 +3549,30 @@ public class CarrierConfigManager { public static final String KEY_SHOW_FORWARDED_NUMBER_BOOL = "show_forwarded_number_bool"; + /** + * The list of originating address of missed incoming call SMS. If the SMS has originator + * matched, the SMS will be treated as special SMS for notifying missed incoming call to the + * user. + * + * @hide + */ + public static final String KEY_MISSED_INCOMING_CALL_SMS_ORIGINATOR_STRING_ARRAY = + "missed_incoming_call_sms_originator_string_array"; + + /** + * The patterns of missed incoming call sms. This is the regular expression used for + * matching the missed incoming call's date, time, and caller id. The pattern should match + * fields for at least month, day, hour, and minute. Year is optional although it is encouraged. + * + * An usable pattern should look like this: + * ^(?<month>0[1-9]|1[012])\/(?<day>0[1-9]|1[0-9]|2[0-9]|3[0-1]) (?<hour>[0-1][0-9]|2[0-3]): + * (?<minute>[0-5][0-9])\s*(?<callerId>[0-9]+)\s*$ + * + * @hide + */ + public static final String KEY_MISSED_INCOMING_CALL_SMS_PATTERN_STRING_ARRAY = + "missed_incoming_call_sms_pattern_string_array"; + /** The default value for every variable. */ private final static PersistableBundle sDefaults; @@ -4057,6 +4081,9 @@ public class CarrierConfigManager { sDefaults.putBoolean(ENABLE_EAP_METHOD_PREFIX_BOOL, false); sDefaults.putBoolean(KEY_SHOW_FORWARDED_NUMBER_BOOL, false); sDefaults.putLong(KEY_DATA_SWITCH_VALIDATION_MIN_GAP_LONG, TimeUnit.DAYS.toMillis(1)); + sDefaults.putStringArray(KEY_MISSED_INCOMING_CALL_SMS_ORIGINATOR_STRING_ARRAY, + new String[0]); + sDefaults.putStringArray(KEY_MISSED_INCOMING_CALL_SMS_PATTERN_STRING_ARRAY, new String[0]); } /** diff --git a/telephony/java/android/telephony/TelephonyManager.java b/telephony/java/android/telephony/TelephonyManager.java index 0660776c3868..a36df49064d2 100644 --- a/telephony/java/android/telephony/TelephonyManager.java +++ b/telephony/java/android/telephony/TelephonyManager.java @@ -2788,39 +2788,22 @@ public class TelephonyManager { /** * Returns the ISO-3166 country code equivalent of the MCC (Mobile Country Code) of the current * registered operator or the cell nearby, if available. - * <p> - * The ISO-3166 country code is provided in lowercase 2 character format. - * <p> - * Note: In multi-sim, this returns a shared emergency network country iso from other - * subscription if the subscription used to create the TelephonyManager doesn't camp on - * a network due to some reason (e.g. pin/puk locked), or sim is absent in the corresponding - * slot. + * * Note: Result may be unreliable on CDMA networks (use {@link #getPhoneType()} to determine * if on a CDMA network). * <p> * @return the lowercase 2 character ISO-3166 country code, or empty string if not available. */ public String getNetworkCountryIso() { - try { - ITelephony telephony = getITelephony(); - if (telephony == null) return ""; - return telephony.getNetworkCountryIsoForPhone(getPhoneId(), - null /* no permission check */, null); - } catch (RemoteException ex) { - return ""; - } + return getNetworkCountryIso(getSlotIndex()); } /** * Returns the ISO-3166 country code equivalent of the MCC (Mobile Country Code) of the current - * registered operator or the cell nearby, if available. - * <p> - * The ISO-3166 country code is provided in lowercase 2 character format. - * <p> - * Note: In multi-sim, this returns a shared emergency network country iso from other - * subscription if the subscription used to create the TelephonyManager doesn't camp on - * a network due to some reason (e.g. pin/puk locked), or sim is absent in the corresponding - * slot. + * registered operator or the cell nearby, if available. This is same as + * {@link #getNetworkCountryIso()} but allowing specifying the SIM slot index. This is used for + * accessing network country info from the SIM slot that does not have SIM inserted. + * * Note: Result may be unreliable on CDMA networks (use {@link #getPhoneType()} to determine * if on a CDMA network). * <p> @@ -2831,22 +2814,18 @@ public class TelephonyManager { * * @throws IllegalArgumentException when the slotIndex is invalid. * - * {@hide} */ - @SystemApi - @TestApi @NonNull - @RequiresPermission(android.Manifest.permission.READ_PRIVILEGED_PHONE_STATE) public String getNetworkCountryIso(int slotIndex) { try { - if (!SubscriptionManager.isValidSlotIndex(slotIndex)) { + if (slotIndex != SubscriptionManager.DEFAULT_SIM_SLOT_INDEX + && !SubscriptionManager.isValidSlotIndex(slotIndex)) { throw new IllegalArgumentException("invalid slot index " + slotIndex); } ITelephony telephony = getITelephony(); if (telephony == null) return ""; - return telephony.getNetworkCountryIsoForPhone(slotIndex, getOpPackageName(), - getFeatureId()); + return telephony.getNetworkCountryIsoForPhone(slotIndex); } catch (RemoteException ex) { return ""; } diff --git a/telephony/java/com/android/internal/telephony/ITelephony.aidl b/telephony/java/com/android/internal/telephony/ITelephony.aidl index 861925fd66e5..af5089f8e0d9 100644 --- a/telephony/java/com/android/internal/telephony/ITelephony.aidl +++ b/telephony/java/com/android/internal/telephony/ITelephony.aidl @@ -281,7 +281,7 @@ interface ITelephony { * operator's MCC (Mobile Country Code). * @see android.telephony.TelephonyManager#getNetworkCountryIso */ - String getNetworkCountryIsoForPhone(int phoneId, String callingPkg, String callingFeatureId); + String getNetworkCountryIsoForPhone(int phoneId); /** * Returns the neighboring cell information of the device. diff --git a/wifi/java/android/net/wifi/WifiManager.java b/wifi/java/android/net/wifi/WifiManager.java index 7d4b63285eea..b1a4cac5864a 100644 --- a/wifi/java/android/net/wifi/WifiManager.java +++ b/wifi/java/android/net/wifi/WifiManager.java @@ -3769,11 +3769,19 @@ public class WifiManager { } /** - * Registers a callback for Soft AP. See {@link SoftApCallback}. Caller will receive the current - * soft AP state and number of connected devices immediately after a successful call to this API - * via callback. Note that receiving an immediate WIFI_AP_STATE_FAILED value for soft AP state - * indicates that the latest attempt to start soft AP has failed. Caller can unregister a - * previously registered callback using {@link #unregisterSoftApCallback} + * Registers a callback for Soft AP. See {@link SoftApCallback}. Caller will receive the + * following callbacks on registration: + * <ul> + * <li> {@link SoftApCallback#onStateChanged(int, int)}</li> + * <li> {@link SoftApCallback#onConnectedClientsChanged(List<WifiClient>)}</li> + * <li> {@link SoftApCallback#onInfoChanged(SoftApInfo)}</li> + * <li> {@link SoftApCallback#onCapabilityChanged(SoftApCapability)}</li> + * </ul> + * These will be dispatched on registration to provide the caller with the current state + * (and are not an indication of any current change). Note that receiving an immediate + * WIFI_AP_STATE_FAILED value for soft AP state indicates that the latest attempt to start + * soft AP has failed. Caller can unregister a previously registered callback using + * {@link #unregisterSoftApCallback} * <p> * Applications should have the * {@link android.Manifest.permission#NETWORK_SETTINGS NETWORK_SETTINGS} permission. Callers @@ -5075,7 +5083,7 @@ public class WifiManager { } /** - * Returns soft ap config from the backed up data. + * Returns soft ap config from the backed up data or null if data is invalid. * @param data byte stream in the same format produced by {@link #retrieveSoftApBackupData()} * * @hide diff --git a/wifi/java/android/net/wifi/WifiScanner.java b/wifi/java/android/net/wifi/WifiScanner.java index b4eb30b8cfe6..5e4891950466 100644 --- a/wifi/java/android/net/wifi/WifiScanner.java +++ b/wifi/java/android/net/wifi/WifiScanner.java @@ -36,6 +36,7 @@ import android.os.Parcel; import android.os.Parcelable; import android.os.RemoteException; import android.os.WorkSource; +import android.text.TextUtils; import android.util.Log; import android.util.SparseArray; @@ -744,6 +745,25 @@ public class WifiScanner { public PnoNetwork(String ssid) { this.ssid = ssid; } + + @Override + public int hashCode() { + return Objects.hash(ssid, flags, authBitField); + } + + @Override + public boolean equals(Object obj) { + if (this == obj) { + return true; + } + if (!(obj instanceof PnoNetwork)) { + return false; + } + PnoNetwork lhs = (PnoNetwork) obj; + return TextUtils.equals(this.ssid, lhs.ssid) + && this.flags == lhs.flags + && this.authBitField == lhs.authBitField; + } } /** Connected vs Disconnected PNO flag {@hide} */ |