Merge "Fix the delay time for Assistant" into rvc-dev
diff --git a/Android.bp b/Android.bp
index 11f5c41..4a1f96e 100644
--- a/Android.bp
+++ b/Android.bp
@@ -496,7 +496,6 @@
"//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 d7428cf..761e930 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.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 @@
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 a4ab31d..7a1b4f2 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 @@
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/api/test-current.txt b/api/test-current.txt
index 2d15c0e..4a9c4d6 100644
--- a/api/test-current.txt
+++ b/api/test-current.txt
@@ -118,7 +118,6 @@
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 @@
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 @@
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 @@
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 @@
}
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 {
diff --git a/core/java/android/app/ActivityTaskManager.java b/core/java/android/app/ActivityTaskManager.java
index 9ba56cf..d48b35b 100644
--- a/core/java/android/app/ActivityTaskManager.java
+++ b/core/java/android/app/ActivityTaskManager.java
@@ -362,25 +362,6 @@
}
/**
- * 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 92dd91a..0ed5aec5 100644
--- a/core/java/android/app/ActivityThread.java
+++ b/core/java/android/app/ActivityThread.java
@@ -68,6 +68,7 @@
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 @@
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 @@
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 0000000..fe13b8f
--- /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 5b61402..266a06a 100644
--- a/core/java/android/app/IActivityTaskManager.aidl
+++ b/core/java/android/app/IActivityTaskManager.aidl
@@ -98,6 +98,14 @@
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 @@
*/
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 @@
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 c33c515..1f6e4ca 100644
--- a/core/java/android/app/IApplicationThread.aidl
+++ b/core/java/android/app/IApplicationThread.aidl
@@ -30,6 +30,7 @@
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 @@
@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 37bdda0..28b28da 100644
--- a/core/java/android/app/ITaskStackListener.aidl
+++ b/core/java/android/app/ITaskStackListener.aidl
@@ -46,16 +46,6 @@
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 4369680..d04630c 100644
--- a/core/java/android/app/SystemServiceRegistry.java
+++ b/core/java/android/app/SystemServiceRegistry.java
@@ -186,7 +186,6 @@
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 @@
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 @@
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 @@
* @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 @@
* @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 @@
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 da0aadb..b892b8e 100644
--- a/core/java/android/app/TaskStackListener.java
+++ b/core/java/android/app/TaskStackListener.java
@@ -58,16 +58,6 @@
@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/content/Context.java b/core/java/android/content/Context.java
index c6e84b7..536b6c3 100644
--- a/core/java/android/content/Context.java
+++ b/core/java/android/content/Context.java
@@ -5157,6 +5157,17 @@
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 9d1c677..4c6fef2 100644
--- a/core/java/android/content/pm/ApplicationInfo.java
+++ b/core/java/android/content/pm/ApplicationInfo.java
@@ -1630,6 +1630,9 @@
@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 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 8b41c04..362098c 100644
--- a/core/java/android/content/pm/ComponentInfo.java
+++ b/core/java/android/content/pm/ComponentInfo.java
@@ -20,7 +20,6 @@
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 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 @@
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 36fa572..85c698f 100644
--- a/core/java/android/content/pm/PackageInfo.java
+++ b/core/java/android/content/pm/PackageInfo.java
@@ -439,6 +439,8 @@
@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 @@
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 @@
dest.writeInt(0);
}
dest.writeBoolean(isApex);
+ dest.restoreAllowSquashing(prevAllowSquashing);
}
public static final @android.annotation.NonNull Parcelable.Creator<PackageInfo> CREATOR
@@ -550,21 +553,5 @@
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/ProviderInfoList.aidl b/core/java/android/content/pm/ProviderInfoList.aidl
new file mode 100644
index 0000000..bb576d7
--- /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 0000000..566be2e
--- /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 1a4dac7..f0b7b5f 100644
--- a/core/java/android/os/Parcel.java
+++ b/core/java/android/os/Parcel.java
@@ -28,6 +28,7 @@
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 @@
}
/**
+ * 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 @@
}
private void freeBuffer() {
+ resetSqaushingState();
if (mOwnsNativeParcelObject) {
updateNativeSize(nativeFreeBuffer(mNativePtr));
}
@@ -3254,6 +3429,7 @@
}
private void destroy() {
+ resetSqaushingState();
if (mNativePtr != 0) {
if (mOwnsNativeParcelObject) {
nativeDestroy(mNativePtr);
@@ -3261,7 +3437,6 @@
}
mNativePtr = 0;
}
- mReadWriteHelper = null;
}
@Override
diff --git a/core/java/android/os/image/DynamicSystemClient.java b/core/java/android/os/image/DynamicSystemClient.java
index 5cb3361..50d8d80 100644
--- a/core/java/android/os/image/DynamicSystemClient.java
+++ b/core/java/android/os/image/DynamicSystemClient.java
@@ -96,9 +96,6 @@
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 @@
@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 c389b1a..dd434b4 100644
--- a/core/java/android/service/autofill/IInlineSuggestionRenderService.aidl
+++ b/core/java/android/service/autofill/IInlineSuggestionRenderService.aidl
@@ -28,5 +28,5 @@
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 29069e7..17e0456 100644
--- a/core/java/android/service/autofill/InlineSuggestionRenderService.java
+++ b/core/java/android/service/autofill/InlineSuggestionRenderService.java
@@ -23,14 +23,18 @@
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 @@
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 @@
}
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 @@
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 @@
@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 0000000..8cdd24e
--- /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 ff7cef9..41fdd0b 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 @@
* 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 de4a551..28f4929 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.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 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 @@
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 @@
* Marks this dream as windowless. Only available to doze dreams.
*
* @hide
+ *
+ * TODO: Remove @UnsupportedAppUsage.
*/
@UnsupportedAppUsage
public void setWindowless(boolean windowless) {
@@ -670,14 +678,14 @@
}
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 @@
if (mDozing) {
mDozing = false;
try {
- mSandman.stopDozing(mWindowToken);
+ mDreamManager.stopDozing(mDreamToken);
} catch (RemoteException ex) {
// system server died
}
@@ -875,14 +883,15 @@
* </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 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 @@
// 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 @@
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 @@
*
* 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);
-
- applySystemUiVisibilityFlags(
- (mLowProfile ? View.SYSTEM_UI_FLAG_LOW_PROFILE : 0),
- View.SYSTEM_UI_FLAG_LOW_PROFILE);
-
- 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;
- }
- }
- // 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;
+ mDispatchAfterOnAttachedToWindow = () -> {
+ if (mWindow != null || mWindowless) {
+ mStarted = true;
+ try {
+ onDreamingStarted();
+ } finally {
try {
- onDreamingStarted();
- } finally {
- try {
- started.sendResult(null);
- } catch (RemoteException e) {
- throw e.rethrowFromSystemServer();
- }
+ started.sendResult(null);
+ } catch (RemoteException e) {
+ throw e.rethrowFromSystemServer();
}
}
}
- });
+ };
+
+ // 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 {
+ 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();
+ }
+ }
+
+ 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 @@
/** @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 @@
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 d254ffd..6496de3 100644
--- a/core/java/android/service/dreams/IDreamManager.aidl
+++ b/core/java/android/service/dreams/IDreamManager.aidl
@@ -31,12 +31,14 @@
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 e976e18..975e75c 100644
--- a/core/java/android/service/notification/NotificationAssistantService.java
+++ b/core/java/android/service/notification/NotificationAssistantService.java
@@ -184,7 +184,7 @@
/**
* 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/view/DisplayCutout.java b/core/java/android/view/DisplayCutout.java
index 31fc161..0ab856e 100644
--- a/core/java/android/view/DisplayCutout.java
+++ b/core/java/android/view/DisplayCutout.java
@@ -554,26 +554,12 @@
*/
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 @@
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 @@
}
}
- 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 cb82f16..1cf83a3 100644
--- a/core/java/android/view/IPinnedStackController.aidl
+++ b/core/java/android/view/IPinnedStackController.aidl
@@ -25,32 +25,8 @@
* @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 d01c933..596d55a 100644
--- a/core/java/android/view/IPinnedStackListener.aidl
+++ b/core/java/android/view/IPinnedStackListener.aidl
@@ -43,8 +43,7 @@
* 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 @@
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 @@
* 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 762366e..530dffb 100644
--- a/core/java/android/view/IRecentsAnimationController.aidl
+++ b/core/java/android/view/IRecentsAnimationController.aidl
@@ -77,13 +77,6 @@
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 c91096e..235f5e1 100644
--- a/core/java/android/view/KeyEvent.java
+++ b/core/java/android/view/KeyEvent.java
@@ -1941,7 +1941,6 @@
/** @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 5700dda..e50da40 100644
--- a/core/java/android/view/inputmethod/InlineSuggestionsRequest.java
+++ b/core/java/android/view/inputmethod/InlineSuggestionsRequest.java
@@ -22,7 +22,9 @@
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 @@
*/
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 @@
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 @@
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 @@
}
@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 @@
abstract Builder setHostPackageName(@Nullable String value);
abstract Builder setHostInputToken(IBinder hostInputToken);
+
+ abstract Builder setHostDisplayId(int value);
}
@@ -145,8 +181,9 @@
@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 @@
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 @@
}
/**
+ * 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 @@
}
/**
- * 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 @@
"presentationSpecs = " + mPresentationSpecs + ", " +
"hostPackageName = " + mHostPackageName + ", " +
"supportedLocales = " + mSupportedLocales + ", " +
+ "extras = " + mExtras + ", " +
"hostInputToken = " + mHostInputToken + ", " +
- "extras = " + mExtras +
+ "hostDisplayId = " + mHostDisplayId +
" }";
}
@@ -253,8 +303,9 @@
&& 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 @@
_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 @@
/** @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 @@
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 @@
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 @@
}
@Override
- public InlineSuggestionsRequest createFromParcel(@NonNull android.os.Parcel in) {
+ public InlineSuggestionsRequest createFromParcel(@NonNull Parcel in) {
return new InlineSuggestionsRequest(in);
}
};
@@ -352,8 +407,9 @@
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 @@
}
/**
+ * 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 @@
@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 @@
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 @@
}
@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 bb40465..5cdcab0 100644
--- a/core/java/com/android/internal/accessibility/AccessibilityShortcutController.java
+++ b/core/java/com/android/internal/accessibility/AccessibilityShortcutController.java
@@ -412,8 +412,13 @@
* 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 @@
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 @@
}
/**
- * @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;
}
- 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 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;
}
- return true;
+ 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;
+ }
+ // 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 382a254b..3876976 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 @@
* Provides useful methods for debugging.
*/
public final class InputMethodDebug {
+
/**
* Not intended to be instantiated.
*/
@@ -174,4 +177,71 @@
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 0000000..79397b8
--- /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 61d2298..6062102 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 4ac51c6..490892e 100644
--- a/core/res/res/values/config.xml
+++ b/core/res/res/values/config.xml
@@ -4404,6 +4404,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 4f221d0..e1d94f50f 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 64768cf..966f495 100644
--- a/core/res/res/values/styles_device_defaults.xml
+++ b/core/res/res/values/styles_device_defaults.xml
@@ -146,7 +146,7 @@
<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 @@
<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 d595459..7690b94 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 5d9cb48..2ef0c92 100644
--- a/core/res/res/values/themes.xml
+++ b/core/res/res/values/themes.xml
@@ -701,6 +701,11 @@
<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 372b8c2..f4fbefe 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.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 @@
@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 7c2b98f..d02c6d5 100644
--- a/core/tests/coretests/src/android/view/DisplayCutoutTest.java
+++ b/core/tests/coretests/src/android/view/DisplayCutoutTest.java
@@ -230,6 +230,16 @@
}
@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 @@
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 bbf3b12..9af0ed0 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.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 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 @@
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/tv/tunerresourcemanager/ResourceClientProfile.java b/media/java/android/media/tv/tunerresourcemanager/ResourceClientProfile.java
index 6837244..598ff8f 100644
--- a/media/java/android/media/tv/tunerresourcemanager/ResourceClientProfile.java
+++ b/media/java/android/media/tv/tunerresourcemanager/ResourceClientProfile.java
@@ -78,7 +78,8 @@
* {@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 37a77be..f36f97d 100644
--- a/packages/DynamicSystemInstallationService/src/com/android/dynsystem/DynamicSystemInstallationService.java
+++ b/packages/DynamicSystemInstallationService/src/com/android/dynsystem/DynamicSystemInstallationService.java
@@ -82,6 +82,9 @@
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 @@
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 3c0f6fe..57e6808 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 @@
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 @@
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/values/dimens.xml b/packages/SystemUI/res/values/dimens.xml
index 1c2404f..9caaa9f 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 0000000..97aa512
--- /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 b1d39f5..80fd826 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.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 @@
/**
* 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/system/PinnedStackListenerForwarder.java b/packages/SystemUI/shared/src/com/android/systemui/shared/system/PinnedStackListenerForwarder.java
index 8c0ffb8..360244c 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 @@
}
@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 @@
}
@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 @@
}
}
- @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 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 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 748f356..6cd6118 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 @@
}
}
- @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 5f92b28..1c6223b 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.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 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 @@
/** @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 acce41c..cbdd3f8 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 @@
}
@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 @@
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 @@
}
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/globalactions/GlobalActionsDialog.java b/packages/SystemUI/src/com/android/systemui/globalactions/GlobalActionsDialog.java
index 9203e6d..4fba83a 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.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;
@@ -152,7 +151,7 @@
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;
@@ -198,7 +197,7 @@
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,
@@ -216,7 +215,7 @@
mDreamManager = iDreamManager;
mDevicePolicyManager = devicePolicyManager;
mLockPatternUtils = lockPatternUtils;
- mKeyguardManager = keyguardManager;
+ mKeyguardStateController = keyguardStateController;
mBroadcastDispatcher = broadcastDispatcher;
mContentResolver = contentResolver;
mResources = resources;
@@ -427,7 +426,7 @@
.startPendingIntentDismissingKeyguard(intent);
}
},
- mKeyguardManager.isDeviceLocked())
+ !mKeyguardStateController.isUnlocked())
: null;
ActionsDialog dialog = new ActionsDialog(mContext, mAdapter, panelViewController,
@@ -444,12 +443,12 @@
}
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 +1913,7 @@
}
private boolean shouldShowControls() {
- return !mKeyguardManager.isDeviceLocked()
+ return mKeyguardStateController.isUnlocked()
&& mControlsUiController.getAvailable();
}
}
diff --git a/packages/SystemUI/src/com/android/systemui/pip/BasePipManager.java b/packages/SystemUI/src/com/android/systemui/pip/BasePipManager.java
index adee7f23..38744fe 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 @@
import android.content.res.Configuration;
+import com.android.systemui.shared.recents.IPinnedStackAnimationListener;
+
import java.io.PrintWriter;
@@ -27,5 +29,7 @@
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 0000000..b5fd406
--- /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 41b3130..8c3ccaa 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.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 @@
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 @@
*/
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 @@
private Point mScreenEdgeInsets;
private int mCurrentMinSize;
- private boolean mIsMinimized;
private boolean mIsImeShowing;
private int mImeHeight;
private boolean mIsShelfShowing;
@@ -123,10 +118,6 @@
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 @@
}
/**
- * 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 @@
}
/**
- * 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 @@
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 @@
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 @@
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 @@
}
/**
- * 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 @@
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 f3e707c..6b89718 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 @@
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 @@
}
/**
- * 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 @@
}
/**
- * 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 @@
* 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 @@
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 0000000..1555153
--- /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 599c845..4fb675e 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.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 @@
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 cb94e28..e98dec0 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.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 @@
* 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 @@
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 @@
}
@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 @@
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 @@
}
@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 @@
@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 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 @@
FloatingContentCoordinator floatingContentCoordinator) {
mContext = context;
mActivityManager = ActivityManager.getService();
- mActivityTaskManager = ActivityTaskManager.getService();
try {
WindowManagerWrapper.getInstance().addPinnedStackListener(mPinnedStackListener);
@@ -243,24 +215,29 @@
}
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 @@
});
}
+ @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 c7bfc06..d660b67 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 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 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 @@
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 @@
}
private Context mContext;
- private IActivityManager mActivityManager;
private PipMediaController mMediaController;
private InputConsumerController mInputConsumerController;
@@ -146,10 +136,6 @@
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 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 c6e2852..91f539c 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 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 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 @@
/** 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 @@
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 @@
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 @@
}
/**
- * @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 @@
}
/**
- * 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 @@
* 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 @@
}
mSnapAlgorithm.applySnapFraction(normalBounds, normalMovementBounds, savedSnapFraction);
- if (minimized) {
- normalBounds = getClosestMinimizedBounds(normalBounds);
- }
-
if (immediate) {
movePip(normalBounds);
} else {
@@ -503,6 +449,7 @@
cancelAnimations();
mAnimatedBoundsPhysicsAnimator
+ .withEndActions(() -> mPipTaskOrganizer.onMotionMovementEnd(mAnimatedBounds))
.addUpdateListener(mResizePipVsyncUpdateListener)
.start();
}
@@ -594,13 +541,6 @@
}
/**
- * @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 @@
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 @@
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 @@
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 8e588e6..79a25b2 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.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 @@
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 @@
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 @@
private Runnable mShowDismissAffordance = new Runnable() {
@Override
public void run() {
- if (mEnableDimissDragToEdge) {
+ if (mEnableDismissDragToEdge) {
mDismissViewController.showDismissTarget();
}
}
@@ -112,7 +108,6 @@
// Behaviour states
private int mMenuState = MENU_STATE_NONE;
- private boolean mIsMinimized;
private boolean mIsImeShowing;
private int mImeHeight;
private int mImeOffset;
@@ -121,7 +116,6 @@
private int mMovementBoundsExtraOffsets;
private float mSavedSnapFraction = -1f;
private boolean mSendingHoverAccessibilityEvents;
- private boolean mMovementWithinMinimize;
private boolean mMovementWithinDismiss;
private PipAccessibilityInteractionConnection mConnection;
@@ -146,15 +140,7 @@
@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 @@
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 @@
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 @@
// 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 @@
}
/**
- * 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 @@
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 @@
* 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 @@
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 @@
if (touchState.startedDragging()) {
mSavedSnapFraction = -1f;
- if (mEnableDimissDragToEdge) {
+ if (mEnableDismissDragToEdge) {
mHandler.removeCallbacks(mShowDismissAffordance);
mDismissViewController.showDismissTarget();
}
@@ -662,17 +602,11 @@
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 @@
@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 @@
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 @@
}
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 @@
} 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 @@
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 487c253..cb1a218 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.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 @@
* 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 @@
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 @@
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 @@
}
@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 @@
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 @@
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 @@
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 @@
}
/**
- * 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,19 +684,28 @@
// If PIPed activity is launched again by Launcher or intent, make it fullscreen.
movePipToFullscreen();
}
-
- @Override
- public void onPinnedStackAnimationEnded() {
- if (DEBUG) Log.d(TAG, "onPinnedStackAnimationEnded()");
-
- switch (getState()) {
- case STATE_PIP_MENU:
- showPipMenu();
- break;
- }
- }
};
+ @Override
+ public void onPipTransitionStarted() { }
+
+ @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 fb89ed2..bfac85b 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.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 @@
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 @@
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 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 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 0000000..ff8ddec
--- /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 3afc460..58de95d 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.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 @@
private int mAccessibilityFromIndex;
private CharSequence mAccessibilityFromLabel;
private QSTileHost mHost;
+ private UiEventLogger mUiEventLogger = new UiEventLoggerImpl();
public TileAdapter(Context context) {
mContext = context;
@@ -436,20 +437,11 @@
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 34cad51..1cd6388 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.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 @@
}
}
+ @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 5008133..97755fc 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.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 @@
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 @@
&& 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 @@
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/tests/src/com/android/systemui/pip/PipAnimationControllerTest.java b/packages/SystemUI/tests/src/com/android/systemui/pip/PipAnimationControllerTest.java
new file mode 100644
index 0000000..6c09a46
--- /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 bc3ce8b..1dbcf10 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.TestableResources;
import android.view.DisplayInfo;
import android.view.Gravity;
-import android.view.IPinnedStackController;
import androidx.test.filters.SmallTest;
@@ -40,9 +33,6 @@
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 @@
@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 @@
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);
+ final Rect oldPosition = mPipBoundsHandler.getDestinationBounds(
+ DEFAULT_ASPECT_RATIO, EMPTY_CURRENT_BOUNDS);
- mPipBoundsHandler.onPrepareAnimation(null, 1f, null);
+ oldPosition.offset(0, -100);
+ mPipBoundsHandler.onSaveReentryBounds(componentName, oldPosition);
- 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);
+ final Rect newPosition = mPipBoundsHandler.getDestinationBounds(
+ DEFAULT_ASPECT_RATIO, EMPTY_CURRENT_BOUNDS);
- mPipBoundsHandler.onPrepareAnimation(null, 1f, null);
-
- 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 f11c42b..f37836c 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 @@
.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 @@
.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/Tethering/common/TetheringLib/Android.bp b/packages/Tethering/common/TetheringLib/Android.bp
index 1a3d5b6..5b73dd5 100644
--- a/packages/Tethering/common/TetheringLib/Android.bp
+++ b/packages/Tethering/common/TetheringLib/Android.bp
@@ -41,8 +41,7 @@
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 @@
libs: [
"framework-annotations-lib",
- "android_system_stubs_current",
],
hostdex: true, // for hiddenapi check
diff --git a/services/autofill/java/com/android/server/autofill/RemoteInlineSuggestionRenderService.java b/services/autofill/java/com/android/server/autofill/RemoteInlineSuggestionRenderService.java
index 5d5af53..7ad5632 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 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 0d8c89b..fef49d4 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 @@
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 @@
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 @@
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 @@
@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 @@
};
return new InlineSuggestion(inlineSuggestionInfo,
createInlineContentProvider(inlinePresentation, onClickAction, onErrorCallback,
- remoteRenderService, hostInputToken));
+ remoteRenderService, hostInputToken, displayId));
}
private static InlineSuggestion createInlineSuggestion(boolean isAugmented,
@@ -235,7 +238,8 @@
@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 @@
final InlineSuggestion inlineSuggestion = new InlineSuggestion(inlineSuggestionInfo,
createInlineContentProvider(inlinePresentation,
() -> onClickFactory.accept(dataset, datasetIndex), onErrorCallback,
- remoteRenderService, hostInputToken));
+ remoteRenderService, hostInputToken, displayId));
return inlineSuggestion;
}
@@ -255,7 +259,7 @@
@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 @@
createInlineContentProvider(inlinePresentation,
() -> onClickFactory.accept(null,
AutofillManager.AUTHENTICATION_ID_DATASET_ID_UNDEFINED),
- onErrorCallback, remoteRenderService, hostInputToken));
+ onErrorCallback, remoteRenderService, hostInputToken, displayId));
}
/**
@@ -291,7 +295,8 @@
@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 @@
}
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 1230bd7..1b1e06a 100644
--- a/services/core/java/android/content/pm/PackageManagerInternal.java
+++ b/services/core/java/android/content/pm/PackageManagerInternal.java
@@ -458,8 +458,8 @@
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 @@
* 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 93859b3..3148a62 100644
--- a/services/core/java/com/android/server/PinnerService.java
+++ b/services/core/java/com/android/server/PinnerService.java
@@ -345,7 +345,7 @@
@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 b1cb138..95bfbd7 100644
--- a/services/core/java/com/android/server/StorageManagerService.java
+++ b/services/core/java/com/android/server/StorageManagerService.java
@@ -4458,8 +4458,11 @@
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 6c68c31..e49357b 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 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 ca0b03df..cea3bb8 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.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 @@
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 @@
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 @@
}
@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 c7f5f63..bf79729 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 @@
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 @@
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 @@
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 fbad8de..5320453 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.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.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 @@
}
};
- 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 @@
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 @@
}
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 @@
}
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 @@
// 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 @@
// 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 @@
// 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 @@
}
};
}
-}
\ 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 3052e3c..eb0257e 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 @@
}
}
+ 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 @@
@Override // Binder call
public ComponentName[] getDreamComponents() {
- checkPermission(android.Manifest.permission.READ_DREAM_STATE);
+ return getDreamComponentsForUser(UserHandle.getCallingUserId());
+ }
- final int userId = 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 long ident = Binder.clearCallingIdentity();
try {
- return getDreamComponentsForUser(userId);
+ return DreamManagerService.this.getDreamComponentsForUser(userId);
} finally {
Binder.restoreCallingIdentity(ident);
}
@@ -526,13 +536,28 @@
}
@Override // Binder call
- public ComponentName getDefaultDreamComponent() {
- checkPermission(android.Manifest.permission.READ_DREAM_STATE);
+ 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 int userId = UserHandle.getCallingUserId();
final long ident = Binder.clearCallingIdentity();
try {
- return getDefaultDreamComponentForUser(userId);
+ 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 long ident = Binder.clearCallingIdentity();
+ try {
+ return DreamManagerService.this.getDefaultDreamComponentForUser(userId);
} finally {
Binder.restoreCallingIdentity(ident);
}
@@ -563,24 +588,25 @@
}
@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 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 eac2d24..f24699a 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.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 @@
/**
* 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 @@
}
@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 87262a8..e3c545c 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.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 @@
@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 @@
@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 @@
== 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 @@
// 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 @@
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 @@
@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 @@
+ "] doesn't match the IME package name=[" + mImePackageName
+ "].");
}
+ request.setHostDisplayId(mImeDisplayId);
mCallback.onInlineSuggestionsRequest(request, callback);
}
}
@@ -2154,7 +2235,8 @@
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 @@
}
}
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 @@
}
@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 @@
// 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 @@
}
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 @@
// 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 @@
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 @@
// 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 @@
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 @@
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 @@
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 @@
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 @@
}
long ident = Binder.clearCallingIdentity();
try {
- hideCurrentInputLocked(flags, null);
+ hideCurrentInputLocked(flags, null, SoftInputShowHideReason.HIDE_MY_SOFT_INPUT);
} finally {
Binder.restoreCallingIdentity(ident);
}
@@ -3795,7 +3887,8 @@
}
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 @@
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 @@
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 @@
}
@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 @@
p.println(" mStartInputHistory:");
mStartInputHistory.dump(pw, " ");
+
+ p.println(" mSoftInputShowHideHistory:");
+ mSoftInputShowHideHistory.dump(pw, " ");
}
p.println(" ");
@@ -5300,7 +5409,8 @@
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 4904061..1aff23b0 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.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 @@
}
@Override
- public void hideCurrentInputMethod() {
+ public void hideCurrentInputMethod(@SoftInputShowHideReason int reason) {
reportNotSupported();
}
diff --git a/services/core/java/com/android/server/pm/AppsFilter.java b/services/core/java/com/android/server/pm/AppsFilter.java
index ae6e058..0ad0b23 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 @@
* 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 85810e3..e86a42c 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.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 @@
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 @@
&& (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 cf85b0f..0eaac41 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 @@
@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 @@
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 @@
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 2d88080..4eac79c 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 @@
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 @@
@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 fe99229..7dd2e55 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 @@
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 23bdf5f..f5ce080 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 @@
/**
* @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 @@
* @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 19e6062..b40c2c1 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 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 @@
MetricsLogger mLogger;
boolean mWakeOnDpadKeyPress;
boolean mWakeOnAssistKeyPress;
+ boolean mWakeOnBackKeyPress;
private boolean mHandleVolumeKeysInWM;
@@ -1105,7 +1107,8 @@
LocalServices.getService(InputMethodManagerInternal.class);
}
if (mInputMethodManagerInternal != null) {
- mInputMethodManagerInternal.hideCurrentInputMethod();
+ mInputMethodManagerInternal.hideCurrentInputMethod(
+ SoftInputShowHideReason.HIDE_POWER_BUTTON_GO_HOME);
}
} else {
shortPressPowerGoHome();
@@ -1736,6 +1739,8 @@
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 @@
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 3c8ef6c..3f8f6bf 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 @@
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 @@
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 bad2b78..4eff954f 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 @@
* @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 @@
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 @@
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 @@
*/
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 @@
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 @@
*
* @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 @@
*
* @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 @@
*
* @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 0000000..a109265
--- /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 49a7045..cb31a50 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.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 @@
@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 + ")");
+ enforceAccessPermission();
+ synchronized (mLock) {
+ return updateClientPriorityInternal(clientId, priority, 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;
- }
-
- 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 @@
@Override
public boolean requestFrontend(@NonNull TunerFrontendRequest request,
- @NonNull int[] frontendId) throws RemoteException {
- if (DEBUG) {
- Slog.d(TAG, "requestFrontend(request=" + request + ")");
+ @NonNull int[] frontendId) throws RemoteException {
+ enforceAccessPermission();
+ if (frontendId == null) {
+ throw new RemoteException("frontendId can't be null");
}
-
- frontendId[0] = TunerResourceManager.INVALID_FRONTEND_ID;
-
- if (getContext() == null) {
- Slog.e(TAG, "Can not find context when requesting frontend");
- return false;
+ synchronized (mLock) {
+ try {
+ return requestFrontendInternal(request, frontendId);
+ } catch (RemoteException e) {
+ throw e.rethrowFromSystemServer();
+ }
}
-
- 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.");
- }
-
- 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 @@
}
@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 0000000..8c2de47
--- /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 e3b7c0a..fe34e86 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 @@
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 8130546..9bbeb72 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 @@
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 190af7a..d715ed4 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 @@
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 @@
// 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 2f1cc05..ff890ff 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.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 @@
/**
* 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 @@
// 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 @@
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 @@
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 @@
}
}
- 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 @@
});
}
- 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 @@
}
/**
- * 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 @@
}
/**
- * 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 @@
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 @@
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 @@
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 @@
|| 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 97b6388..b2d2f62 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 @@
}
}
- 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 600a125..f52c7f2 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.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 @@
int filterCallingUid;
PendingIntentRecord originatingPendingIntent;
boolean allowBackgroundActivityStart;
+ boolean isDream;
/**
* If set to {@code true}, allows this activity start to look into
@@ -387,6 +387,7 @@
filterCallingUid = UserHandle.USER_NULL;
originatingPendingIntent = null;
allowBackgroundActivityStart = false;
+ isDream = false;
}
/**
@@ -427,6 +428,7 @@
filterCallingUid = request.filterCallingUid;
originatingPendingIntent = request.originatingPendingIntent;
allowBackgroundActivityStart = request.allowBackgroundActivityStart;
+ isDream = request.isDream;
}
/**
@@ -970,7 +972,7 @@
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 @@
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 @@
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 @@
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 @@
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 @@
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 5392257..693a5e4 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.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 @@
}
@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 @@
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 @@
}
}
- @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 @@
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 @@
}
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 @@
// 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 @@
.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 8fa8119..4cce212 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 @@
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 5385e2f..0000000
--- 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 b1d5359..0000000
--- 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 0029dc8..8ccb59f 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.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 @@
final ArraySet<ActivityRecord> mClosingApps = new ArraySet<>();
final ArraySet<ActivityRecord> mChangingApps = new ArraySet<>();
final UnknownAppVisibilityController mUnknownAppVisibilityController;
- BoundsAnimationController mBoundsAnimationController;
private MetricsLogger mMetricsLogger;
@@ -977,10 +975,6 @@
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);
diff --git a/services/core/java/com/android/server/wm/DockedStackDividerController.java b/services/core/java/com/android/server/wm/DockedStackDividerController.java
index 6431e11..958c8ae 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.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 @@
// 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 668b609..e14b8ae 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 @@
* 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 @@
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 @@
// 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 @@
* 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 @@
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 @@
*/
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 @@
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 @@
void onDisplayInfoChanged(DisplayInfo displayInfo) {
synchronized (mService.mGlobalLock) {
setDisplayInfo(displayInfo);
- notifyMovementBoundsChanged(false /* fromImeAdjustment */,
- false /* fromShelfAdjustment */);
+ notifyMovementBoundsChanged(false /* fromImeAdjustment */);
}
}
@@ -269,7 +220,7 @@
mIsImeShowing = imeShowing;
mImeHeight = imeHeight;
notifyImeVisibilityChanged(imeShowing, imeHeight);
- notifyMovementBoundsChanged(true /* fromImeAdjustment */, false /* fromShelfAdjustment */);
+ notifyMovementBoundsChanged(true /* fromImeAdjustment */);
}
/**
@@ -279,10 +230,7 @@
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 @@
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 @@
}
/**
- * 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 @@
/**
* 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 @@
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 @@
}
}
- /**
- * 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 e92bbaa..adafdec 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.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 @@
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 6b6b946..e69551a 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 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.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 @@
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 @@
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 @@
}
@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 aa6bdfd..64d7db2 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 @@
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 ecfac1b..76805e9 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.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.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 @@
* 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 f715d8f..96a9127 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 @@
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 @@
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 @@
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 @@
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 @@
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 6caa27c..0f1e623 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 @@
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/WindowState.java b/services/core/java/com/android/server/wm/WindowState.java
index b250083..37597fb 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 @@
}
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 @@
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 a1a9af6..81d0e3e 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 @@
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 5a1ad86..757a2b1 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 @@
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 d2ec500..a19d919 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 @@
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 0000000..192c6fe
--- /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 0000000..ab5665b
--- /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 0fdffd5..23613e0 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.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.Collections;
import java.util.List;
import java.util.Set;
+import java.util.concurrent.CountDownLatch;
import java.util.concurrent.TimeUnit;
/**
@@ -127,6 +129,15 @@
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 @@
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 @@
@Override
public boolean isDeviceIdleMode() {
- return false;
+ return mDeviceIdleMode;
}
@Override
@@ -327,6 +339,7 @@
controller.getAppStandbyBucket(PACKAGE_1, USER_ID,
mInjector.mElapsedRealtime, false));
+ controller.addListener(mListener);
return controller;
}
@@ -1055,6 +1068,46 @@
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 @@
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 b917e1b..049c8e1 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.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.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 @@
}
@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 @@
// 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 1dda535..0000000
--- 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 4449069..d9c5c4c 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 @@
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 c9efb70..5c36f5c 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 @@
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 b78107e..b3a25302 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 @@
@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 @@
@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/telecomm/java/android/telecom/Connection.java b/telecomm/java/android/telecom/Connection.java
index 4604cd2..5f33a3d 100755
--- a/telecomm/java/android/telecom/Connection.java
+++ b/telecomm/java/android/telecom/Connection.java
@@ -3358,7 +3358,6 @@
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 5d7d649..7f4fcc0 100644
--- a/telecomm/java/android/telecom/TelecomManager.java
+++ b/telecomm/java/android/telecom/TelecomManager.java
@@ -330,6 +330,14 @@
"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 a7e52ea..9d77623 100755
--- a/telephony/java/android/telephony/CarrierConfigManager.java
+++ b/telephony/java/android/telephony/CarrierConfigManager.java
@@ -3549,6 +3549,30 @@
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 @@
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/wifi/java/android/net/wifi/WifiManager.java b/wifi/java/android/net/wifi/WifiManager.java
index 7d4b632..b1a4cac 100644
--- a/wifi/java/android/net/wifi/WifiManager.java
+++ b/wifi/java/android/net/wifi/WifiManager.java
@@ -3769,11 +3769,19 @@
}
/**
- * 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 @@
}
/**
- * 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 b4eb30b..5e48919 100644
--- a/wifi/java/android/net/wifi/WifiScanner.java
+++ b/wifi/java/android/net/wifi/WifiScanner.java
@@ -36,6 +36,7 @@
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 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} */