summaryrefslogtreecommitdiff
diff options
context:
space:
mode:
-rw-r--r--Android.bp26
-rw-r--r--api/current.txt79
-rw-r--r--api/test-current.txt2
-rw-r--r--core/java/android/content/pm/PackageInstaller.java13
-rw-r--r--core/java/android/os/BasicShellCommandHandler.java10
-rw-r--r--core/java/android/service/autofill/InlinePresentation.java4
-rw-r--r--core/java/android/service/contentcapture/ContentCaptureService.java82
-rw-r--r--core/java/android/view/WindowManagerImpl.java4
-rw-r--r--core/java/android/view/contentcapture/ContentCaptureManager.java84
-rw-r--r--core/java/android/view/inline/InlineContentView.java3
-rw-r--r--core/java/android/view/inline/InlinePresentationSpec.aidl4
-rw-r--r--core/java/android/view/inline/InlinePresentationSpec.java5
-rw-r--r--core/java/android/view/inputmethod/InlineSuggestion.java18
-rw-r--r--core/java/android/view/inputmethod/InlineSuggestionInfo.java56
-rw-r--r--core/java/android/view/inputmethod/InlineSuggestionsRequest.java148
-rw-r--r--core/java/android/widget/inline/InlineContentView.java205
-rw-r--r--core/java/android/widget/inline/InlinePresentationSpec.aidl19
-rw-r--r--core/java/android/widget/inline/InlinePresentationSpec.java293
-rw-r--r--core/java/com/android/internal/policy/DecorContext.java52
-rw-r--r--core/tests/coretests/src/com/android/internal/policy/DecorContextTest.java32
-rw-r--r--data/etc/services.core.protolog.json42
-rw-r--r--packages/SystemUI/src/com/android/systemui/media/MediaControlPanel.java59
-rw-r--r--packages/overlays/Android.mk2
-rw-r--r--services/Android.bp4
-rw-r--r--services/autofill/java/com/android/server/autofill/ui/InlineSuggestionFactory.java4
-rw-r--r--services/contentcapture/java/com/android/server/contentcapture/ContentCaptureManagerService.java78
-rw-r--r--services/core/Android.bp4
-rw-r--r--services/core/java/com/android/server/ConnectivityService.java16
-rw-r--r--services/core/java/com/android/server/IpSecService.java12
-rw-r--r--services/core/java/com/android/server/Watchdog.java1
-rw-r--r--services/core/java/com/android/server/locksettings/LockSettingsService.java46
-rw-r--r--services/core/java/com/android/server/locksettings/SyntheticPasswordManager.java45
-rw-r--r--services/core/java/com/android/server/locksettings/VersionedPasswordMetrics.java79
-rw-r--r--services/core/java/com/android/server/media/SystemMediaRoute2Provider.java23
-rw-r--r--services/core/java/com/android/server/pm/ApexManager.java38
-rw-r--r--services/core/java/com/android/server/pm/PackageManagerService.java24
-rw-r--r--services/core/java/com/android/server/pm/PackageManagerShellCommand.java117
-rw-r--r--services/core/java/com/android/server/pm/StagingManager.java41
-rw-r--r--services/core/java/com/android/server/wm/ActivityRecord.java8
-rw-r--r--services/core/java/com/android/server/wm/ActivityStack.java42
-rw-r--r--services/core/java/com/android/server/wm/ActivityStackSupervisor.java19
-rw-r--r--services/core/java/com/android/server/wm/ActivityStartController.java4
-rw-r--r--services/core/java/com/android/server/wm/ActivityTaskManagerService.java13
-rw-r--r--services/core/java/com/android/server/wm/DisplayAreaPolicy.java2
-rw-r--r--services/core/java/com/android/server/wm/DisplayContent.java1461
-rw-r--r--services/core/java/com/android/server/wm/KeyguardController.java4
-rw-r--r--services/core/java/com/android/server/wm/RecentsAnimation.java7
-rw-r--r--services/core/java/com/android/server/wm/ResetTargetTaskHelper.java4
-rw-r--r--services/core/java/com/android/server/wm/RootWindowContainer.java67
-rw-r--r--services/core/java/com/android/server/wm/Task.java4
-rw-r--r--services/core/java/com/android/server/wm/TaskContainers.java1533
-rw-r--r--services/core/java/com/android/server/wm/TaskOrganizerController.java6
-rw-r--r--services/core/java/com/android/server/wm/WindowOrganizerController.java9
-rw-r--r--services/devicepolicy/Android.bp4
-rw-r--r--services/incremental/IncrementalService.cpp23
-rw-r--r--services/incremental/ServiceWrappers.h38
-rw-r--r--services/incremental/test/IncrementalServiceTest.cpp23
-rw-r--r--services/tests/servicestests/src/com/android/server/locksettings/FakeSettings.java9
-rw-r--r--services/tests/servicestests/src/com/android/server/locksettings/LockSettingsServiceTests.java11
-rw-r--r--services/tests/servicestests/src/com/android/server/locksettings/SyntheticPasswordTests.java41
-rw-r--r--services/tests/wmtests/src/com/android/server/wm/ActivityDisplayTests.java14
-rw-r--r--services/tests/wmtests/src/com/android/server/wm/ActivityStackTests.java29
-rw-r--r--services/tests/wmtests/src/com/android/server/wm/ActivityTestsBase.java8
-rw-r--r--services/tests/wmtests/src/com/android/server/wm/DisplayAreaProviderTest.java2
-rw-r--r--services/tests/wmtests/src/com/android/server/wm/DisplayContentTests.java8
-rw-r--r--services/tests/wmtests/src/com/android/server/wm/RecentsAnimationTest.java2
-rw-r--r--services/tests/wmtests/src/com/android/server/wm/RootActivityContainerTests.java10
-rw-r--r--services/tests/wmtests/src/com/android/server/wm/SystemServicesTestRule.java4
-rw-r--r--services/tests/wmtests/src/com/android/server/wm/TestDisplayContent.java1
-rw-r--r--tests/RollbackTest/lib/src/com/android/tests/rollback/host/WatchdogEventLogger.java6
-rw-r--r--tests/net/common/java/android/net/apf/ApfCapabilitiesTest.java37
71 files changed, 3225 insertions, 2002 deletions
diff --git a/Android.bp b/Android.bp
index be1aefca39bc..30d44091b6a6 100644
--- a/Android.bp
+++ b/Android.bp
@@ -548,33 +548,9 @@ java_library {
],
}
-java_library {
- name: "framework-annotation-proc",
- srcs: [
- ":framework-all-sources",
- "core/java/**/*.logtags",
- ],
- sdk_version: "core_platform",
- libs: [
- "app-compat-annotations",
- "ext",
- "unsupportedappusage",
- ],
-
- installable: false,
- plugins: [
- "compat-changeid-annotation-processor",
- ],
- static_libs: [
- "framework-internal-utils",
- "exoplayer2-extractor",
- "android.hardware.wifi-V1.0-java-constants",
- ]
-}
-
platform_compat_config {
name: "framework-platform-compat-config",
- src: ":framework-annotation-proc",
+ src: ":framework-minus-apex",
}
// A temporary build target that is conditionally included on the bootclasspath if
diff --git a/api/current.txt b/api/current.txt
index 39d9ea969bea..d4c0e9a01243 100644
--- a/api/current.txt
+++ b/api/current.txt
@@ -43134,9 +43134,9 @@ package android.service.autofill {
}
public final class InlinePresentation implements android.os.Parcelable {
- ctor public InlinePresentation(@NonNull android.app.slice.Slice, @NonNull android.view.inline.InlinePresentationSpec, boolean);
+ ctor public InlinePresentation(@NonNull android.app.slice.Slice, @NonNull android.widget.inline.InlinePresentationSpec, boolean);
method public int describeContents();
- method @NonNull public android.view.inline.InlinePresentationSpec getInlinePresentationSpec();
+ method @NonNull public android.widget.inline.InlinePresentationSpec getInlinePresentationSpec();
method @NonNull public android.app.slice.Slice getSlice();
method public boolean isPinned();
method public void writeToParcel(@NonNull android.os.Parcel, int);
@@ -56778,38 +56778,6 @@ package android.view.contentcapture {
}
-package android.view.inline {
-
- public class InlineContentView extends android.view.ViewGroup {
- method @Nullable public android.view.SurfaceControl getSurfaceControl();
- method public boolean isZOrderedOnTop();
- method public void onLayout(boolean, int, int, int, int);
- method public void setSurfaceControlCallback(@Nullable android.view.inline.InlineContentView.SurfaceControlCallback);
- method public boolean setZOrderedOnTop(boolean);
- }
-
- public static interface InlineContentView.SurfaceControlCallback {
- method public void onCreated(@NonNull android.view.SurfaceControl);
- method public void onDestroyed(@NonNull android.view.SurfaceControl);
- }
-
- public final class InlinePresentationSpec implements android.os.Parcelable {
- method public int describeContents();
- method @NonNull public android.util.Size getMaxSize();
- method @NonNull public android.util.Size getMinSize();
- method @Nullable public android.os.Bundle getStyle();
- method public void writeToParcel(@NonNull android.os.Parcel, int);
- field @NonNull public static final android.os.Parcelable.Creator<android.view.inline.InlinePresentationSpec> CREATOR;
- }
-
- public static final class InlinePresentationSpec.Builder {
- ctor public InlinePresentationSpec.Builder(@NonNull android.util.Size, @NonNull android.util.Size);
- method @NonNull public android.view.inline.InlinePresentationSpec build();
- method @NonNull public android.view.inline.InlinePresentationSpec.Builder setStyle(@NonNull android.os.Bundle);
- }
-
-}
-
package android.view.inputmethod {
public class BaseInputConnection implements android.view.inputmethod.InputConnection {
@@ -56981,7 +56949,7 @@ package android.view.inputmethod {
public final class InlineSuggestion implements android.os.Parcelable {
method public int describeContents();
method @NonNull public android.view.inputmethod.InlineSuggestionInfo getInfo();
- method public void inflate(@NonNull android.content.Context, @NonNull android.util.Size, @NonNull java.util.concurrent.Executor, @NonNull java.util.function.Consumer<android.view.inline.InlineContentView>);
+ method public void inflate(@NonNull android.content.Context, @NonNull android.util.Size, @NonNull java.util.concurrent.Executor, @NonNull java.util.function.Consumer<android.widget.inline.InlineContentView>);
method public void writeToParcel(@NonNull android.os.Parcel, int);
field @NonNull public static final android.os.Parcelable.Creator<android.view.inputmethod.InlineSuggestion> CREATOR;
}
@@ -56989,7 +56957,7 @@ package android.view.inputmethod {
public final class InlineSuggestionInfo implements android.os.Parcelable {
method public int describeContents();
method @Nullable public String[] getAutofillHints();
- method @NonNull public android.view.inline.InlinePresentationSpec getPresentationSpec();
+ method @NonNull public android.widget.inline.InlinePresentationSpec getInlinePresentationSpec();
method @NonNull public String getSource();
method @NonNull public String getType();
method public boolean isPinned();
@@ -57005,8 +56973,8 @@ package android.view.inputmethod {
method public int describeContents();
method @Nullable public android.os.Bundle getExtras();
method @NonNull public String getHostPackageName();
+ method @NonNull public java.util.List<android.widget.inline.InlinePresentationSpec> getInlinePresentationSpecs();
method public int getMaxSuggestionCount();
- method @NonNull public java.util.List<android.view.inline.InlinePresentationSpec> getPresentationSpecs();
method @NonNull public android.os.LocaleList getSupportedLocales();
method public void writeToParcel(@NonNull android.os.Parcel, int);
field @NonNull public static final android.os.Parcelable.Creator<android.view.inputmethod.InlineSuggestionsRequest> CREATOR;
@@ -57014,10 +56982,11 @@ package android.view.inputmethod {
}
public static final class InlineSuggestionsRequest.Builder {
- ctor public InlineSuggestionsRequest.Builder(@NonNull java.util.List<android.view.inline.InlinePresentationSpec>);
- method @NonNull public android.view.inputmethod.InlineSuggestionsRequest.Builder addPresentationSpecs(@NonNull android.view.inline.InlinePresentationSpec);
+ ctor public InlineSuggestionsRequest.Builder(@NonNull java.util.List<android.widget.inline.InlinePresentationSpec>);
+ method @NonNull public android.view.inputmethod.InlineSuggestionsRequest.Builder addInlinePresentationSpecs(@NonNull android.widget.inline.InlinePresentationSpec);
method @NonNull public android.view.inputmethod.InlineSuggestionsRequest build();
method @NonNull public android.view.inputmethod.InlineSuggestionsRequest.Builder setExtras(@NonNull android.os.Bundle);
+ method @NonNull public android.view.inputmethod.InlineSuggestionsRequest.Builder setInlinePresentationSpecs(@NonNull java.util.List<android.widget.inline.InlinePresentationSpec>);
method @NonNull public android.view.inputmethod.InlineSuggestionsRequest.Builder setMaxSuggestionCount(int);
method @NonNull public android.view.inputmethod.InlineSuggestionsRequest.Builder setSupportedLocales(@NonNull android.os.LocaleList);
}
@@ -61611,6 +61580,38 @@ package android.widget {
}
+package android.widget.inline {
+
+ public class InlineContentView extends android.view.ViewGroup {
+ method @Nullable public android.view.SurfaceControl getSurfaceControl();
+ method public boolean isZOrderedOnTop();
+ method public void onLayout(boolean, int, int, int, int);
+ method public void setSurfaceControlCallback(@Nullable android.widget.inline.InlineContentView.SurfaceControlCallback);
+ method public boolean setZOrderedOnTop(boolean);
+ }
+
+ public static interface InlineContentView.SurfaceControlCallback {
+ method public void onCreated(@NonNull android.view.SurfaceControl);
+ method public void onDestroyed(@NonNull android.view.SurfaceControl);
+ }
+
+ public final class InlinePresentationSpec implements android.os.Parcelable {
+ method public int describeContents();
+ method @NonNull public android.util.Size getMaxSize();
+ method @NonNull public android.util.Size getMinSize();
+ method @Nullable public android.os.Bundle getStyle();
+ method public void writeToParcel(@NonNull android.os.Parcel, int);
+ field @NonNull public static final android.os.Parcelable.Creator<android.widget.inline.InlinePresentationSpec> CREATOR;
+ }
+
+ public static final class InlinePresentationSpec.Builder {
+ ctor public InlinePresentationSpec.Builder(@NonNull android.util.Size, @NonNull android.util.Size);
+ method @NonNull public android.widget.inline.InlinePresentationSpec build();
+ method @NonNull public android.widget.inline.InlinePresentationSpec.Builder setStyle(@NonNull android.os.Bundle);
+ }
+
+}
+
package dalvik.annotation {
@Deprecated @java.lang.annotation.Retention(java.lang.annotation.RetentionPolicy.RUNTIME) @java.lang.annotation.Target({java.lang.annotation.ElementType.ANNOTATION_TYPE}) public @interface TestTarget {
diff --git a/api/test-current.txt b/api/test-current.txt
index ba6feedc65ba..f95f8e8198c5 100644
--- a/api/test-current.txt
+++ b/api/test-current.txt
@@ -5052,7 +5052,7 @@ package android.view.inputmethod {
}
public final class InlineSuggestionInfo implements android.os.Parcelable {
- method @NonNull public static android.view.inputmethod.InlineSuggestionInfo newInlineSuggestionInfo(@NonNull android.view.inline.InlinePresentationSpec, @NonNull String, @Nullable String[], @NonNull String, boolean);
+ method @NonNull public static android.view.inputmethod.InlineSuggestionInfo newInlineSuggestionInfo(@NonNull android.widget.inline.InlinePresentationSpec, @NonNull String, @Nullable String[], @NonNull String, boolean);
}
public final class InlineSuggestionsResponse implements android.os.Parcelable {
diff --git a/core/java/android/content/pm/PackageInstaller.java b/core/java/android/content/pm/PackageInstaller.java
index 1e0b2e358e17..85a3986a65f9 100644
--- a/core/java/android/content/pm/PackageInstaller.java
+++ b/core/java/android/content/pm/PackageInstaller.java
@@ -72,7 +72,6 @@ import java.util.List;
import java.util.Objects;
import java.util.Set;
import java.util.concurrent.Executor;
-import java.util.stream.Collectors;
/**
* Offers the ability to install, upgrade, and remove applications on the
@@ -589,9 +588,15 @@ public class PackageInstaller {
* * {@link SessionInfo#isStagedSessionActive()}.
*/
public @NonNull List<SessionInfo> getActiveStagedSessions() {
- return getStagedSessions().stream()
- .filter(s -> s.isStagedSessionActive())
- .collect(Collectors.toList());
+ final List<SessionInfo> activeStagedSessions = new ArrayList<>();
+ final List<SessionInfo> stagedSessions = getStagedSessions();
+ for (int i = 0; i < stagedSessions.size(); i++) {
+ final SessionInfo sessionInfo = stagedSessions.get(i);
+ if (sessionInfo.isStagedSessionActive()) {
+ activeStagedSessions.add(sessionInfo);
+ }
+ }
+ return activeStagedSessions;
}
/**
diff --git a/core/java/android/os/BasicShellCommandHandler.java b/core/java/android/os/BasicShellCommandHandler.java
index 5bd5d61b6361..52273cb247c3 100644
--- a/core/java/android/os/BasicShellCommandHandler.java
+++ b/core/java/android/os/BasicShellCommandHandler.java
@@ -264,6 +264,16 @@ public abstract class BasicShellCommandHandler {
}
/**
+ * Returns number of arguments that haven't been processed yet.
+ */
+ public int getRemainingArgsCount() {
+ if (mArgPos >= mArgs.length) {
+ return 0;
+ }
+ return mArgs.length - mArgPos;
+ }
+
+ /**
* Return the next argument on the command line, whatever it is; if there are
* no arguments left, throws an IllegalArgumentException to report this to the user.
*/
diff --git a/core/java/android/service/autofill/InlinePresentation.java b/core/java/android/service/autofill/InlinePresentation.java
index b6a8ced29f72..cb03d2136dd4 100644
--- a/core/java/android/service/autofill/InlinePresentation.java
+++ b/core/java/android/service/autofill/InlinePresentation.java
@@ -21,7 +21,7 @@ import android.annotation.Size;
import android.app.slice.Slice;
import android.os.Parcel;
import android.os.Parcelable;
-import android.view.inline.InlinePresentationSpec;
+import android.widget.inline.InlinePresentationSpec;
import com.android.internal.util.DataClass;
@@ -233,7 +233,7 @@ public final class InlinePresentation implements Parcelable {
time = 1582753782651L,
codegenVersion = "1.0.14",
sourceFile = "frameworks/base/core/java/android/service/autofill/InlinePresentation.java",
- inputSignatures = "private final @android.annotation.NonNull android.app.slice.Slice mSlice\nprivate final @android.annotation.NonNull android.view.inline.InlinePresentationSpec mInlinePresentationSpec\nprivate final boolean mPinned\npublic @android.annotation.NonNull @android.annotation.Size(min=0L) java.lang.String[] getAutofillHints()\nclass InlinePresentation extends java.lang.Object implements [android.os.Parcelable]\n@com.android.internal.util.DataClass(genToString=true, genHiddenConstDefs=true, genEqualsHashCode=true)")
+ inputSignatures = "private final @android.annotation.NonNull android.app.slice.Slice mSlice\nprivate final @android.annotation.NonNull android.widget.inline.InlinePresentationSpec mInlinePresentationSpec\nprivate final boolean mPinned\npublic @android.annotation.NonNull @android.annotation.Size(min=0L) java.lang.String[] getAutofillHints()\nclass InlinePresentation extends java.lang.Object implements [android.os.Parcelable]\n@com.android.internal.util.DataClass(genToString=true, genHiddenConstDefs=true, genEqualsHashCode=true)")
@Deprecated
private void __metadata() {}
diff --git a/core/java/android/service/contentcapture/ContentCaptureService.java b/core/java/android/service/contentcapture/ContentCaptureService.java
index 61744e423b5b..ef55f0615ec7 100644
--- a/core/java/android/service/contentcapture/ContentCaptureService.java
+++ b/core/java/android/service/contentcapture/ContentCaptureService.java
@@ -60,7 +60,9 @@ import com.android.internal.util.Preconditions;
import java.io.FileDescriptor;
import java.io.PrintWriter;
import java.lang.ref.WeakReference;
+import java.util.HashMap;
import java.util.List;
+import java.util.Map;
import java.util.Set;
import java.util.concurrent.Executor;
import java.util.function.Consumer;
@@ -119,6 +121,9 @@ public abstract class ContentCaptureService extends Service {
*/
public static final String SERVICE_META_DATA = "android.content_capture";
+ private final LocalDataShareAdapterResourceManager mDataShareAdapterResourceManager =
+ new LocalDataShareAdapterResourceManager();
+
private Handler mHandler;
private IContentCaptureServiceCallback mCallback;
@@ -546,7 +551,8 @@ public abstract class ContentCaptureService extends Service {
Preconditions.checkNotNull(executor);
DataShareReadAdapterDelegate delegate =
- new DataShareReadAdapterDelegate(executor, adapter);
+ new DataShareReadAdapterDelegate(executor, adapter,
+ mDataShareAdapterResourceManager);
try {
callback.accept(delegate);
@@ -653,16 +659,17 @@ public abstract class ContentCaptureService extends Service {
private static class DataShareReadAdapterDelegate extends IDataShareReadAdapter.Stub {
+ private final WeakReference<LocalDataShareAdapterResourceManager> mResourceManagerReference;
private final Object mLock = new Object();
- private final WeakReference<DataShareReadAdapter> mAdapterReference;
- private final WeakReference<Executor> mExecutorReference;
- DataShareReadAdapterDelegate(Executor executor, DataShareReadAdapter adapter) {
+ DataShareReadAdapterDelegate(Executor executor, DataShareReadAdapter adapter,
+ LocalDataShareAdapterResourceManager resourceManager) {
Preconditions.checkNotNull(executor);
Preconditions.checkNotNull(adapter);
+ Preconditions.checkNotNull(resourceManager);
- mExecutorReference = new WeakReference<>(executor);
- mAdapterReference = new WeakReference<>(adapter);
+ resourceManager.initializeForDelegate(this, adapter, executor);
+ mResourceManagerReference = new WeakReference<>(resourceManager);
}
@Override
@@ -670,6 +677,10 @@ public abstract class ContentCaptureService extends Service {
throws RemoteException {
synchronized (mLock) {
executeAdapterMethodLocked(adapter -> adapter.onStart(fd), "onStart");
+
+ // Client app and Service successfully connected, so this object would be kept alive
+ // until the session has finished.
+ clearHardReferences();
}
}
@@ -678,16 +689,23 @@ public abstract class ContentCaptureService extends Service {
synchronized (mLock) {
executeAdapterMethodLocked(
adapter -> adapter.onError(errorCode), "onError");
+ clearHardReferences();
}
}
private void executeAdapterMethodLocked(Consumer<DataShareReadAdapter> adapterFn,
String methodName) {
- DataShareReadAdapter adapter = mAdapterReference.get();
- Executor executor = mExecutorReference.get();
+ LocalDataShareAdapterResourceManager resourceManager = mResourceManagerReference.get();
+ if (resourceManager == null) {
+ Slog.w(TAG, "Can't execute " + methodName + "(), resource manager has been GC'ed");
+ return;
+ }
+
+ DataShareReadAdapter adapter = resourceManager.getAdapter(this);
+ Executor executor = resourceManager.getExecutor(this);
if (adapter == null || executor == null) {
- Slog.w(TAG, "Can't execute " + methodName + "(), references have been GC'ed");
+ Slog.w(TAG, "Can't execute " + methodName + "(), references are null");
return;
}
@@ -698,5 +716,51 @@ public abstract class ContentCaptureService extends Service {
Binder.restoreCallingIdentity(identity);
}
}
+
+ private void clearHardReferences() {
+ LocalDataShareAdapterResourceManager resourceManager = mResourceManagerReference.get();
+ if (resourceManager == null) {
+ Slog.w(TAG, "Can't clear references, resource manager has been GC'ed");
+ return;
+ }
+
+ resourceManager.clearHardReferences(this);
+ }
+ }
+
+ /**
+ * Wrapper class making sure dependencies on the current application stay in the application
+ * context.
+ */
+ private static class LocalDataShareAdapterResourceManager {
+
+ // Keeping hard references to the remote objects in the current process (static context)
+ // to prevent them to be gc'ed during the lifetime of the application. This is an
+ // artifact of only operating with weak references remotely: there has to be at least 1
+ // hard reference in order for this to not be killed.
+ private Map<DataShareReadAdapterDelegate, DataShareReadAdapter>
+ mDataShareReadAdapterHardReferences = new HashMap<>();
+ private Map<DataShareReadAdapterDelegate, Executor> mExecutorHardReferences =
+ new HashMap<>();
+
+
+ void initializeForDelegate(DataShareReadAdapterDelegate delegate,
+ DataShareReadAdapter adapter, Executor executor) {
+ mDataShareReadAdapterHardReferences.put(delegate, adapter);
+ mExecutorHardReferences.remove(delegate, executor);
+ }
+
+ Executor getExecutor(DataShareReadAdapterDelegate delegate) {
+ return mExecutorHardReferences.get(delegate);
+ }
+
+ DataShareReadAdapter getAdapter(DataShareReadAdapterDelegate delegate) {
+ return mDataShareReadAdapterHardReferences.get(delegate);
+ }
+
+ void clearHardReferences(DataShareReadAdapterDelegate delegate) {
+ mDataShareReadAdapterHardReferences.remove(delegate);
+ mExecutorHardReferences.remove(delegate);
+ }
}
}
diff --git a/core/java/android/view/WindowManagerImpl.java b/core/java/android/view/WindowManagerImpl.java
index 316a5f2c88d2..561ee604aa7f 100644
--- a/core/java/android/view/WindowManagerImpl.java
+++ b/core/java/android/view/WindowManagerImpl.java
@@ -36,6 +36,7 @@ import android.os.Bundle;
import android.os.IBinder;
import android.os.RemoteException;
+import com.android.internal.annotations.VisibleForTesting;
import com.android.internal.os.IResultReceiver;
import java.util.List;
@@ -69,7 +70,8 @@ import java.util.List;
public final class WindowManagerImpl implements WindowManager {
@UnsupportedAppUsage
private final WindowManagerGlobal mGlobal = WindowManagerGlobal.getInstance();
- private final Context mContext;
+ @VisibleForTesting
+ public final Context mContext;
private final Window mParentWindow;
private IBinder mDefaultToken;
diff --git a/core/java/android/view/contentcapture/ContentCaptureManager.java b/core/java/android/view/contentcapture/ContentCaptureManager.java
index 954b83b6f9ee..edc6b1251f2c 100644
--- a/core/java/android/view/contentcapture/ContentCaptureManager.java
+++ b/core/java/android/view/contentcapture/ContentCaptureManager.java
@@ -54,6 +54,8 @@ import java.lang.annotation.Retention;
import java.lang.annotation.RetentionPolicy;
import java.lang.ref.WeakReference;
import java.util.ArrayList;
+import java.util.HashMap;
+import java.util.Map;
import java.util.Set;
import java.util.concurrent.Executor;
import java.util.function.Consumer;
@@ -360,6 +362,9 @@ public final class ContentCaptureManager {
@NonNull
private final IContentCaptureManager mService;
+ @GuardedBy("mLock")
+ private final LocalDataShareAdapterResourceManager mDataShareAdapterResourceManager;
+
@NonNull
final ContentCaptureOptions mOptions;
@@ -399,6 +404,8 @@ public final class ContentCaptureManager {
// do, then we should optimize it to run the tests after the Choreographer finishes the most
// important steps of the frame.
mHandler = Handler.createAsync(Looper.getMainLooper());
+
+ mDataShareAdapterResourceManager = new LocalDataShareAdapterResourceManager();
}
/**
@@ -681,7 +688,8 @@ public final class ContentCaptureManager {
try {
mService.shareData(request,
- new DataShareAdapterDelegate(executor, dataShareWriteAdapter));
+ new DataShareAdapterDelegate(executor, dataShareWriteAdapter,
+ mDataShareAdapterResourceManager));
} catch (RemoteException e) {
e.rethrowFromSystemServer();
}
@@ -737,40 +745,53 @@ public final class ContentCaptureManager {
private static class DataShareAdapterDelegate extends IDataShareWriteAdapter.Stub {
- private final WeakReference<DataShareWriteAdapter> mAdapterReference;
- private final WeakReference<Executor> mExecutorReference;
+ private final WeakReference<LocalDataShareAdapterResourceManager> mResourceManagerReference;
- private DataShareAdapterDelegate(Executor executor, DataShareWriteAdapter adapter) {
+ private DataShareAdapterDelegate(Executor executor, DataShareWriteAdapter adapter,
+ LocalDataShareAdapterResourceManager resourceManager) {
Preconditions.checkNotNull(executor);
Preconditions.checkNotNull(adapter);
+ Preconditions.checkNotNull(resourceManager);
- mExecutorReference = new WeakReference<>(executor);
- mAdapterReference = new WeakReference<>(adapter);
+ resourceManager.initializeForDelegate(this, adapter, executor);
+ mResourceManagerReference = new WeakReference<>(resourceManager);
}
@Override
public void write(ParcelFileDescriptor destination)
throws RemoteException {
executeAdapterMethodLocked(adapter -> adapter.onWrite(destination), "onWrite");
+
+ // Client app and Service successfully connected, so this object would be kept alive
+ // until the session has finished.
+ clearHardReferences();
}
@Override
public void error(int errorCode) throws RemoteException {
executeAdapterMethodLocked(adapter -> adapter.onError(errorCode), "onError");
+ clearHardReferences();
}
@Override
public void rejected() throws RemoteException {
executeAdapterMethodLocked(DataShareWriteAdapter::onRejected, "onRejected");
+ clearHardReferences();
}
private void executeAdapterMethodLocked(Consumer<DataShareWriteAdapter> adapterFn,
String methodName) {
- DataShareWriteAdapter adapter = mAdapterReference.get();
- Executor executor = mExecutorReference.get();
+ LocalDataShareAdapterResourceManager resourceManager = mResourceManagerReference.get();
+ if (resourceManager == null) {
+ Slog.w(TAG, "Can't execute " + methodName + "(), resource manager has been GC'ed");
+ return;
+ }
+
+ DataShareWriteAdapter adapter = resourceManager.getAdapter(this);
+ Executor executor = resourceManager.getExecutor(this);
if (adapter == null || executor == null) {
- Slog.w(TAG, "Can't execute " + methodName + "(), references have been GC'ed");
+ Slog.w(TAG, "Can't execute " + methodName + "(), references are null");
return;
}
@@ -781,5 +802,50 @@ public final class ContentCaptureManager {
Binder.restoreCallingIdentity(identity);
}
}
+
+ private void clearHardReferences() {
+ LocalDataShareAdapterResourceManager resourceManager = mResourceManagerReference.get();
+ if (resourceManager == null) {
+ Slog.w(TAG, "Can't clear references, resource manager has been GC'ed");
+ return;
+ }
+
+ resourceManager.clearHardReferences(this);
+ }
+ }
+
+ /**
+ * Wrapper class making sure dependencies on the current application stay in the application
+ * context.
+ */
+ private static class LocalDataShareAdapterResourceManager {
+
+ // Keeping hard references to the remote objects in the current process (static context)
+ // to prevent them to be gc'ed during the lifetime of the application. This is an
+ // artifact of only operating with weak references remotely: there has to be at least 1
+ // hard reference in order for this to not be killed.
+ private Map<DataShareAdapterDelegate, DataShareWriteAdapter> mWriteAdapterHardReferences =
+ new HashMap<>();
+ private Map<DataShareAdapterDelegate, Executor> mExecutorHardReferences =
+ new HashMap<>();
+
+ void initializeForDelegate(DataShareAdapterDelegate delegate, DataShareWriteAdapter adapter,
+ Executor executor) {
+ mWriteAdapterHardReferences.put(delegate, adapter);
+ mExecutorHardReferences.remove(delegate, executor);
+ }
+
+ Executor getExecutor(DataShareAdapterDelegate delegate) {
+ return mExecutorHardReferences.get(delegate);
+ }
+
+ DataShareWriteAdapter getAdapter(DataShareAdapterDelegate delegate) {
+ return mWriteAdapterHardReferences.get(delegate);
+ }
+
+ void clearHardReferences(DataShareAdapterDelegate delegate) {
+ mWriteAdapterHardReferences.remove(delegate);
+ mExecutorHardReferences.remove(delegate);
+ }
}
}
diff --git a/core/java/android/view/inline/InlineContentView.java b/core/java/android/view/inline/InlineContentView.java
index df5bc2fb0cb4..deb93a9a4414 100644
--- a/core/java/android/view/inline/InlineContentView.java
+++ b/core/java/android/view/inline/InlineContentView.java
@@ -45,6 +45,9 @@ import android.view.ViewGroup;
* under the hosting window which could be useful in some cases, e.g. animating transitions.
* At this point the inlined content will not be interactive and the touch events would
* be delivered to your app.
+ *
+ * @hide
+ * @removed
*/
public class InlineContentView extends ViewGroup {
diff --git a/core/java/android/view/inline/InlinePresentationSpec.aidl b/core/java/android/view/inline/InlinePresentationSpec.aidl
index efa46c8932b9..680ee4e14b54 100644
--- a/core/java/android/view/inline/InlinePresentationSpec.aidl
+++ b/core/java/android/view/inline/InlinePresentationSpec.aidl
@@ -16,4 +16,8 @@
package android.view.inline;
+/**
+ * @hide
+ * @removed
+ */
parcelable InlinePresentationSpec;
diff --git a/core/java/android/view/inline/InlinePresentationSpec.java b/core/java/android/view/inline/InlinePresentationSpec.java
index 3788e2b2a883..60687fbe5553 100644
--- a/core/java/android/view/inline/InlinePresentationSpec.java
+++ b/core/java/android/view/inline/InlinePresentationSpec.java
@@ -29,6 +29,9 @@ import com.android.internal.util.DataClass;
* should abide when constructing its UI. Since suggestions are inlined in a
* host application while provided by another source, they need to be consistent
* with the host's look at feel to allow building smooth and integrated UIs.
+ *
+ * @hide
+ * @removed
*/
@DataClass(genEqualsHashCode = true, genToString = true, genBuilder = true)
public final class InlinePresentationSpec implements Parcelable {
@@ -279,7 +282,7 @@ public final class InlinePresentationSpec implements Parcelable {
}
@DataClass.Generated(
- time = 1584067238741L,
+ time = 1585177087499L,
codegenVersion = "1.0.15",
sourceFile = "frameworks/base/core/java/android/view/inline/InlinePresentationSpec.java",
inputSignatures = "private final @android.annotation.NonNull android.util.Size mMinSize\nprivate final @android.annotation.NonNull android.util.Size mMaxSize\nprivate final @android.annotation.Nullable android.os.Bundle mStyle\nprivate static android.os.Bundle defaultStyle()\nclass InlinePresentationSpec extends java.lang.Object implements [android.os.Parcelable]\n@com.android.internal.util.DataClass(genEqualsHashCode=true, genToString=true, genBuilder=true)\nclass BaseBuilder extends java.lang.Object implements []")
diff --git a/core/java/android/view/inputmethod/InlineSuggestion.java b/core/java/android/view/inputmethod/InlineSuggestion.java
index ab8f36d85400..f50f0dea0466 100644
--- a/core/java/android/view/inputmethod/InlineSuggestion.java
+++ b/core/java/android/view/inputmethod/InlineSuggestion.java
@@ -29,8 +29,7 @@ import android.os.RemoteException;
import android.util.Size;
import android.util.Slog;
import android.view.SurfaceControlViewHost;
-import android.view.inline.InlineContentView;
-import android.view.inline.InlinePresentationSpec;
+import android.widget.inline.InlineContentView;
import com.android.internal.util.DataClass;
import com.android.internal.util.Parcelling;
@@ -95,9 +94,10 @@ public final class InlineSuggestion implements Parcelable {
/**
* Inflates a view with the content of this suggestion at a specific size.
- * The size must be between the {@link InlinePresentationSpec#getMinSize() min size}
- * and the {@link InlinePresentationSpec#getMaxSize() max size} of the presentation
- * spec returned by {@link InlineSuggestionInfo#getPresentationSpec()}.
+ * The size must be between the
+ * {@link android.widget.inline.InlinePresentationSpec#getMinSize() min size} and the
+ * {@link android.widget.inline.InlinePresentationSpec#getMaxSize() max size} of the
+ * presentation spec returned by {@link InlineSuggestionInfo#getInlinePresentationSpec()}.
*
* <p> The caller can attach an {@link android.view.View.OnClickListener} and/or an
* {@link android.view.View.OnLongClickListener} to the view in the
@@ -113,8 +113,8 @@ public final class InlineSuggestion implements Parcelable {
public void inflate(@NonNull Context context, @NonNull Size size,
@NonNull @CallbackExecutor Executor callbackExecutor,
@NonNull Consumer<InlineContentView> callback) {
- final Size minSize = mInfo.getPresentationSpec().getMinSize();
- final Size maxSize = mInfo.getPresentationSpec().getMaxSize();
+ final Size minSize = mInfo.getInlinePresentationSpec().getMinSize();
+ final Size maxSize = mInfo.getInlinePresentationSpec().getMaxSize();
if (size.getHeight() < minSize.getHeight() || size.getHeight() > maxSize.getHeight()
|| size.getWidth() < minSize.getWidth() || size.getWidth() > maxSize.getWidth()) {
throw new IllegalArgumentException("size not between min:"
@@ -398,10 +398,10 @@ public final class InlineSuggestion implements Parcelable {
};
@DataClass.Generated(
- time = 1584679775946L,
+ time = 1585180783541L,
codegenVersion = "1.0.15",
sourceFile = "frameworks/base/core/java/android/view/inputmethod/InlineSuggestion.java",
- inputSignatures = "private static final java.lang.String TAG\nprivate final @android.annotation.NonNull android.view.inputmethod.InlineSuggestionInfo mInfo\nprivate final @android.annotation.Nullable com.android.internal.view.inline.IInlineContentProvider mContentProvider\nprivate @com.android.internal.util.DataClass.ParcelWith(android.view.inputmethod.InlineSuggestion.InlineContentCallbackImplParceling.class) @android.annotation.Nullable android.view.inputmethod.InlineSuggestion.InlineContentCallbackImpl mInlineContentCallback\npublic static @android.annotation.TestApi @android.annotation.NonNull android.view.inputmethod.InlineSuggestion newInlineSuggestion(android.view.inputmethod.InlineSuggestionInfo)\npublic void inflate(android.content.Context,android.util.Size,java.util.concurrent.Executor,java.util.function.Consumer<android.view.inline.InlineContentView>)\nprivate synchronized android.view.inputmethod.InlineSuggestion.InlineContentCallbackImpl getInlineContentCallback(android.content.Context,java.util.concurrent.Executor,java.util.function.Consumer<android.view.inline.InlineContentView>)\nclass InlineSuggestion extends java.lang.Object implements [android.os.Parcelable]\n@com.android.internal.util.DataClass(genEqualsHashCode=true, genToString=true, genHiddenConstDefs=true, genHiddenConstructor=true)")
+ inputSignatures = "private static final java.lang.String TAG\nprivate final @android.annotation.NonNull android.view.inputmethod.InlineSuggestionInfo mInfo\nprivate final @android.annotation.Nullable com.android.internal.view.inline.IInlineContentProvider mContentProvider\nprivate @com.android.internal.util.DataClass.ParcelWith(android.view.inputmethod.InlineSuggestion.InlineContentCallbackImplParceling.class) @android.annotation.Nullable android.view.inputmethod.InlineSuggestion.InlineContentCallbackImpl mInlineContentCallback\npublic static @android.annotation.TestApi @android.annotation.NonNull android.view.inputmethod.InlineSuggestion newInlineSuggestion(android.view.inputmethod.InlineSuggestionInfo)\npublic void inflate(android.content.Context,android.util.Size,java.util.concurrent.Executor,java.util.function.Consumer<android.widget.inline.InlineContentView>)\nprivate synchronized android.view.inputmethod.InlineSuggestion.InlineContentCallbackImpl getInlineContentCallback(android.content.Context,java.util.concurrent.Executor,java.util.function.Consumer<android.widget.inline.InlineContentView>)\nclass InlineSuggestion extends java.lang.Object implements [android.os.Parcelable]\n@com.android.internal.util.DataClass(genEqualsHashCode=true, genToString=true, genHiddenConstDefs=true, genHiddenConstructor=true)")
@Deprecated
private void __metadata() {}
diff --git a/core/java/android/view/inputmethod/InlineSuggestionInfo.java b/core/java/android/view/inputmethod/InlineSuggestionInfo.java
index cb0320e966d1..fe2ce25da288 100644
--- a/core/java/android/view/inputmethod/InlineSuggestionInfo.java
+++ b/core/java/android/view/inputmethod/InlineSuggestionInfo.java
@@ -20,8 +20,9 @@ import android.annotation.NonNull;
import android.annotation.Nullable;
import android.annotation.SuppressLint;
import android.annotation.TestApi;
+import android.os.Bundle;
import android.os.Parcelable;
-import android.view.inline.InlinePresentationSpec;
+import android.widget.inline.InlinePresentationSpec;
import com.android.internal.util.DataClass;
@@ -58,7 +59,7 @@ public final class InlineSuggestionInfo implements Parcelable {
public static final @Type String TYPE_ACTION = "android:autofill:action";
/** The presentation spec to which the inflated suggestion view abides. */
- private final @NonNull InlinePresentationSpec mPresentationSpec;
+ private final @NonNull InlinePresentationSpec mInlinePresentationSpec;
/** The source from which the suggestion is provided. */
private final @NonNull @Source String mSource;
@@ -86,9 +87,26 @@ public final class InlineSuggestionInfo implements Parcelable {
return new InlineSuggestionInfo(presentationSpec, source, autofillHints, type, isPinned);
}
+ /**
+ * The presentation spec to which the inflated suggestion view abides.
+ *
+ * @hide
+ * @removed
+ */
+ public @NonNull android.view.inline.InlinePresentationSpec getPresentationSpec() {
+ final android.view.inline.InlinePresentationSpec.Builder builder =
+ new android.view.inline.InlinePresentationSpec.Builder(
+ mInlinePresentationSpec.getMinSize(), mInlinePresentationSpec.getMaxSize());
+ final Bundle style = mInlinePresentationSpec.getStyle();
+ if (style != null) {
+ builder.setStyle(style);
+ }
+ return builder.build();
+ }
- // Code below generated by codegen v1.0.14.
+
+ // Code below generated by codegen v1.0.15.
//
// DO NOT MODIFY!
// CHECKSTYLE:OFF Generated code
@@ -122,7 +140,7 @@ public final class InlineSuggestionInfo implements Parcelable {
/**
* Creates a new InlineSuggestionInfo.
*
- * @param presentationSpec
+ * @param inlinePresentationSpec
* The presentation spec to which the inflated suggestion view abides.
* @param source
* The source from which the suggestion is provided.
@@ -136,14 +154,14 @@ public final class InlineSuggestionInfo implements Parcelable {
*/
@DataClass.Generated.Member
public InlineSuggestionInfo(
- @NonNull InlinePresentationSpec presentationSpec,
+ @NonNull InlinePresentationSpec inlinePresentationSpec,
@NonNull @Source String source,
@Nullable String[] autofillHints,
@NonNull @Type String type,
boolean pinned) {
- this.mPresentationSpec = presentationSpec;
+ this.mInlinePresentationSpec = inlinePresentationSpec;
com.android.internal.util.AnnotationValidations.validate(
- NonNull.class, null, mPresentationSpec);
+ NonNull.class, null, mInlinePresentationSpec);
this.mSource = source;
if (!(java.util.Objects.equals(mSource, SOURCE_AUTOFILL))
@@ -178,8 +196,8 @@ public final class InlineSuggestionInfo implements Parcelable {
* The presentation spec to which the inflated suggestion view abides.
*/
@DataClass.Generated.Member
- public @NonNull InlinePresentationSpec getPresentationSpec() {
- return mPresentationSpec;
+ public @NonNull InlinePresentationSpec getInlinePresentationSpec() {
+ return mInlinePresentationSpec;
}
/**
@@ -221,7 +239,7 @@ public final class InlineSuggestionInfo implements Parcelable {
// String fieldNameToString() { ... }
return "InlineSuggestionInfo { " +
- "presentationSpec = " + mPresentationSpec + ", " +
+ "inlinePresentationSpec = " + mInlinePresentationSpec + ", " +
"source = " + mSource + ", " +
"autofillHints = " + java.util.Arrays.toString(mAutofillHints) + ", " +
"type = " + mType + ", " +
@@ -242,7 +260,7 @@ public final class InlineSuggestionInfo implements Parcelable {
InlineSuggestionInfo that = (InlineSuggestionInfo) o;
//noinspection PointlessBooleanExpression
return true
- && java.util.Objects.equals(mPresentationSpec, that.mPresentationSpec)
+ && java.util.Objects.equals(mInlinePresentationSpec, that.mInlinePresentationSpec)
&& java.util.Objects.equals(mSource, that.mSource)
&& java.util.Arrays.equals(mAutofillHints, that.mAutofillHints)
&& java.util.Objects.equals(mType, that.mType)
@@ -256,7 +274,7 @@ public final class InlineSuggestionInfo implements Parcelable {
// int fieldNameHashCode() { ... }
int _hash = 1;
- _hash = 31 * _hash + java.util.Objects.hashCode(mPresentationSpec);
+ _hash = 31 * _hash + java.util.Objects.hashCode(mInlinePresentationSpec);
_hash = 31 * _hash + java.util.Objects.hashCode(mSource);
_hash = 31 * _hash + java.util.Arrays.hashCode(mAutofillHints);
_hash = 31 * _hash + java.util.Objects.hashCode(mType);
@@ -274,7 +292,7 @@ public final class InlineSuggestionInfo implements Parcelable {
if (mPinned) flg |= 0x10;
if (mAutofillHints != null) flg |= 0x4;
dest.writeByte(flg);
- dest.writeTypedObject(mPresentationSpec, flags);
+ dest.writeTypedObject(mInlinePresentationSpec, flags);
dest.writeString(mSource);
if (mAutofillHints != null) dest.writeStringArray(mAutofillHints);
dest.writeString(mType);
@@ -293,14 +311,14 @@ public final class InlineSuggestionInfo implements Parcelable {
byte flg = in.readByte();
boolean pinned = (flg & 0x10) != 0;
- InlinePresentationSpec presentationSpec = (InlinePresentationSpec) in.readTypedObject(InlinePresentationSpec.CREATOR);
+ InlinePresentationSpec inlinePresentationSpec = (InlinePresentationSpec) in.readTypedObject(InlinePresentationSpec.CREATOR);
String source = in.readString();
String[] autofillHints = (flg & 0x4) == 0 ? null : in.createStringArray();
String type = in.readString();
- this.mPresentationSpec = presentationSpec;
+ this.mInlinePresentationSpec = inlinePresentationSpec;
com.android.internal.util.AnnotationValidations.validate(
- NonNull.class, null, mPresentationSpec);
+ NonNull.class, null, mInlinePresentationSpec);
this.mSource = source;
if (!(java.util.Objects.equals(mSource, SOURCE_AUTOFILL))
@@ -346,10 +364,10 @@ public final class InlineSuggestionInfo implements Parcelable {
};
@DataClass.Generated(
- time = 1582753084046L,
- codegenVersion = "1.0.14",
+ time = 1585528157244L,
+ codegenVersion = "1.0.15",
sourceFile = "frameworks/base/core/java/android/view/inputmethod/InlineSuggestionInfo.java",
- inputSignatures = "public static final @android.view.inputmethod.InlineSuggestionInfo.Source java.lang.String SOURCE_AUTOFILL\npublic static final @android.view.inputmethod.InlineSuggestionInfo.Source java.lang.String SOURCE_PLATFORM\npublic static final @android.view.inputmethod.InlineSuggestionInfo.Type java.lang.String TYPE_SUGGESTION\npublic static final @android.annotation.SuppressLint({\"IntentName\"}) @android.view.inputmethod.InlineSuggestionInfo.Type java.lang.String TYPE_ACTION\nprivate final @android.annotation.NonNull android.view.inline.InlinePresentationSpec mPresentationSpec\nprivate final @android.annotation.NonNull @android.view.inputmethod.InlineSuggestionInfo.Source java.lang.String mSource\nprivate final @android.annotation.Nullable java.lang.String[] mAutofillHints\nprivate final @android.annotation.NonNull @android.view.inputmethod.InlineSuggestionInfo.Type java.lang.String mType\nprivate final boolean mPinned\npublic static @android.annotation.TestApi @android.annotation.NonNull android.view.inputmethod.InlineSuggestionInfo newInlineSuggestionInfo(android.view.inline.InlinePresentationSpec,java.lang.String,java.lang.String[],java.lang.String,boolean)\nclass InlineSuggestionInfo extends java.lang.Object implements [android.os.Parcelable]\n@com.android.internal.util.DataClass(genEqualsHashCode=true, genToString=true, genHiddenConstDefs=true, genHiddenConstructor=true)")
+ inputSignatures = "public static final @android.view.inputmethod.InlineSuggestionInfo.Source java.lang.String SOURCE_AUTOFILL\npublic static final @android.view.inputmethod.InlineSuggestionInfo.Source java.lang.String SOURCE_PLATFORM\npublic static final @android.view.inputmethod.InlineSuggestionInfo.Type java.lang.String TYPE_SUGGESTION\npublic static final @android.annotation.SuppressLint({\"IntentName\"}) @android.view.inputmethod.InlineSuggestionInfo.Type java.lang.String TYPE_ACTION\nprivate final @android.annotation.NonNull android.widget.inline.InlinePresentationSpec mInlinePresentationSpec\nprivate final @android.annotation.NonNull @android.view.inputmethod.InlineSuggestionInfo.Source java.lang.String mSource\nprivate final @android.annotation.Nullable java.lang.String[] mAutofillHints\nprivate final @android.annotation.NonNull @android.view.inputmethod.InlineSuggestionInfo.Type java.lang.String mType\nprivate final boolean mPinned\npublic static @android.annotation.TestApi @android.annotation.NonNull android.view.inputmethod.InlineSuggestionInfo newInlineSuggestionInfo(android.widget.inline.InlinePresentationSpec,java.lang.String,java.lang.String[],java.lang.String,boolean)\npublic @android.annotation.NonNull android.view.inline.InlinePresentationSpec getPresentationSpec()\nclass InlineSuggestionInfo extends java.lang.Object implements [android.os.Parcelable]\n@com.android.internal.util.DataClass(genEqualsHashCode=true, genToString=true, genHiddenConstDefs=true, genHiddenConstructor=true)")
@Deprecated
private void __metadata() {}
diff --git a/core/java/android/view/inputmethod/InlineSuggestionsRequest.java b/core/java/android/view/inputmethod/InlineSuggestionsRequest.java
index 2945a8613bb7..61997c160b57 100644
--- a/core/java/android/view/inputmethod/InlineSuggestionsRequest.java
+++ b/core/java/android/view/inputmethod/InlineSuggestionsRequest.java
@@ -25,7 +25,7 @@ import android.os.LocaleList;
import android.os.Parcel;
import android.os.Parcelable;
import android.view.Display;
-import android.view.inline.InlinePresentationSpec;
+import android.widget.inline.InlinePresentationSpec;
import com.android.internal.util.DataClass;
import com.android.internal.util.Preconditions;
@@ -54,7 +54,7 @@ public final class InlineSuggestionsRequest implements Parcelable {
* count is larger than the number of specs in the list, then the last spec is used for the
* remainder of the suggestions. The list should not be empty.
*/
- private final @NonNull List<InlinePresentationSpec> mPresentationSpecs;
+ private final @NonNull List<InlinePresentationSpec> mInlinePresentationSpecs;
/**
* The package name of the app that requests for the inline suggestions and will host the
@@ -91,6 +91,32 @@ public final class InlineSuggestionsRequest implements Parcelable {
private int mHostDisplayId;
/**
+ * The {@link InlinePresentationSpec} for each suggestion in the response. If the max suggestion
+ * count is larger than the number of specs in the list, then the last spec is used for the
+ * remainder of the suggestions. The list should not be empty.
+ *
+ * @hide
+ * @removed
+ */
+ public @NonNull List<android.view.inline.InlinePresentationSpec> getPresentationSpecs() {
+ final ArrayList<android.view.inline.InlinePresentationSpec> convertedSpecs =
+ new ArrayList<>();
+ for (int i = 0; i < mInlinePresentationSpecs.size(); i++) {
+ final android.widget.inline.InlinePresentationSpec currSpec =
+ mInlinePresentationSpecs.get(i);
+ final android.view.inline.InlinePresentationSpec.Builder builder =
+ new android.view.inline.InlinePresentationSpec.Builder(
+ currSpec.getMinSize(), currSpec.getMaxSize());
+ final Bundle style = currSpec.getStyle();
+ if (style != null) {
+ builder.setStyle(style);
+ }
+ convertedSpecs.add(builder.build());
+ }
+ return convertedSpecs;
+ }
+
+ /**
* @hide
* @see {@link #mHostInputToken}.
*/
@@ -117,8 +143,8 @@ public final class InlineSuggestionsRequest implements Parcelable {
}
private void onConstructed() {
- Preconditions.checkState(!mPresentationSpecs.isEmpty());
- Preconditions.checkState(mMaxSuggestionCount >= mPresentationSpecs.size());
+ Preconditions.checkState(!mInlinePresentationSpecs.isEmpty());
+ Preconditions.checkState(mMaxSuggestionCount >= mInlinePresentationSpecs.size());
}
private static int defaultMaxSuggestionCount() {
@@ -148,11 +174,64 @@ public final class InlineSuggestionsRequest implements Parcelable {
return null;
}
-
-
/** @hide */
abstract static class BaseBuilder {
- abstract Builder setPresentationSpecs(@NonNull List<InlinePresentationSpec> value);
+ /**
+ * The {@link android.view.inline.InlinePresentationSpec} for each suggestion in the
+ * response. If the max suggestion count is larger than the number of specs in the list,
+ * then the last spec is used for the remainder of the suggestions.
+ * The list should not be empty.
+ *
+ * @hide
+ * @removed
+ */
+ @NonNull Builder setPresentationSpecs(
+ @NonNull List<android.view.inline.InlinePresentationSpec> specs) {
+ ((Builder) this).checkNotUsed();
+ ((Builder) this).mBuilderFieldsSet |= 0x2;
+ final ArrayList<android.widget.inline.InlinePresentationSpec> convertedSpecs =
+ new ArrayList<>();
+ for (int i = 0; i < specs.size(); i++) {
+ final android.view.inline.InlinePresentationSpec currSpec = specs.get(i);
+ final android.widget.inline.InlinePresentationSpec.Builder builder =
+ new android.widget.inline.InlinePresentationSpec.Builder(
+ currSpec.getMinSize(), currSpec.getMaxSize());
+ final Bundle style = currSpec.getStyle();
+ if (style != null) {
+ builder.setStyle(style);
+ }
+ convertedSpecs.add(builder.build());
+ }
+ ((Builder) this).mInlinePresentationSpecs = convertedSpecs;
+ return ((Builder) this);
+ }
+
+ /**
+ * @see #setPresentationSpecs
+ *
+ * @hide
+ * @removed
+ */
+ public @NonNull Builder addPresentationSpecs(
+ @NonNull android.view.inline.InlinePresentationSpec value) {
+ if (((Builder) this).mInlinePresentationSpecs == null) {
+ setPresentationSpecs(new ArrayList<>());
+ }
+
+ final android.widget.inline.InlinePresentationSpec.Builder builder =
+ new android.widget.inline.InlinePresentationSpec.Builder(
+ value.getMinSize(), value.getMaxSize());
+ final Bundle style = value.getStyle();
+ if (style != null) {
+ builder.setStyle(style);
+ }
+
+ ((Builder) this).mInlinePresentationSpecs.add(builder.build());
+ return ((Builder) this);
+ }
+
+ abstract Builder setInlinePresentationSpecs(
+ @NonNull List<android.widget.inline.InlinePresentationSpec> specs);
abstract Builder setHostPackageName(@Nullable String value);
@@ -179,16 +258,16 @@ public final class InlineSuggestionsRequest implements Parcelable {
@DataClass.Generated.Member
/* package-private */ InlineSuggestionsRequest(
int maxSuggestionCount,
- @NonNull List<InlinePresentationSpec> presentationSpecs,
+ @NonNull List<InlinePresentationSpec> inlinePresentationSpecs,
@NonNull String hostPackageName,
@NonNull LocaleList supportedLocales,
@Nullable Bundle extras,
@Nullable IBinder hostInputToken,
int hostDisplayId) {
this.mMaxSuggestionCount = maxSuggestionCount;
- this.mPresentationSpecs = presentationSpecs;
+ this.mInlinePresentationSpecs = inlinePresentationSpecs;
com.android.internal.util.AnnotationValidations.validate(
- NonNull.class, null, mPresentationSpecs);
+ NonNull.class, null, mInlinePresentationSpecs);
this.mHostPackageName = hostPackageName;
com.android.internal.util.AnnotationValidations.validate(
NonNull.class, null, mHostPackageName);
@@ -217,8 +296,8 @@ public final class InlineSuggestionsRequest implements Parcelable {
* remainder of the suggestions. The list should not be empty.
*/
@DataClass.Generated.Member
- public @NonNull List<InlinePresentationSpec> getPresentationSpecs() {
- return mPresentationSpecs;
+ public @NonNull List<InlinePresentationSpec> getInlinePresentationSpecs() {
+ return mInlinePresentationSpecs;
}
/**
@@ -278,7 +357,7 @@ public final class InlineSuggestionsRequest implements Parcelable {
return "InlineSuggestionsRequest { " +
"maxSuggestionCount = " + mMaxSuggestionCount + ", " +
- "presentationSpecs = " + mPresentationSpecs + ", " +
+ "inlinePresentationSpecs = " + mInlinePresentationSpecs + ", " +
"hostPackageName = " + mHostPackageName + ", " +
"supportedLocales = " + mSupportedLocales + ", " +
"extras = " + mExtras + ", " +
@@ -301,7 +380,7 @@ public final class InlineSuggestionsRequest implements Parcelable {
//noinspection PointlessBooleanExpression
return true
&& mMaxSuggestionCount == that.mMaxSuggestionCount
- && java.util.Objects.equals(mPresentationSpecs, that.mPresentationSpecs)
+ && java.util.Objects.equals(mInlinePresentationSpecs, that.mInlinePresentationSpecs)
&& java.util.Objects.equals(mHostPackageName, that.mHostPackageName)
&& java.util.Objects.equals(mSupportedLocales, that.mSupportedLocales)
&& java.util.Objects.equals(mExtras, that.mExtras)
@@ -317,7 +396,7 @@ public final class InlineSuggestionsRequest implements Parcelable {
int _hash = 1;
_hash = 31 * _hash + mMaxSuggestionCount;
- _hash = 31 * _hash + java.util.Objects.hashCode(mPresentationSpecs);
+ _hash = 31 * _hash + java.util.Objects.hashCode(mInlinePresentationSpecs);
_hash = 31 * _hash + java.util.Objects.hashCode(mHostPackageName);
_hash = 31 * _hash + java.util.Objects.hashCode(mSupportedLocales);
_hash = 31 * _hash + java.util.Objects.hashCode(mExtras);
@@ -337,7 +416,7 @@ public final class InlineSuggestionsRequest implements Parcelable {
if (mHostInputToken != null) flg |= 0x20;
dest.writeByte(flg);
dest.writeInt(mMaxSuggestionCount);
- dest.writeParcelableList(mPresentationSpecs, flags);
+ dest.writeParcelableList(mInlinePresentationSpecs, flags);
dest.writeString(mHostPackageName);
dest.writeTypedObject(mSupportedLocales, flags);
if (mExtras != null) dest.writeBundle(mExtras);
@@ -358,8 +437,8 @@ public final class InlineSuggestionsRequest implements Parcelable {
byte flg = in.readByte();
int maxSuggestionCount = in.readInt();
- List<InlinePresentationSpec> presentationSpecs = new ArrayList<>();
- in.readParcelableList(presentationSpecs, InlinePresentationSpec.class.getClassLoader());
+ List<InlinePresentationSpec> inlinePresentationSpecs = new ArrayList<>();
+ in.readParcelableList(inlinePresentationSpecs, InlinePresentationSpec.class.getClassLoader());
String hostPackageName = in.readString();
LocaleList supportedLocales = (LocaleList) in.readTypedObject(LocaleList.CREATOR);
Bundle extras = (flg & 0x10) == 0 ? null : in.readBundle();
@@ -367,9 +446,9 @@ public final class InlineSuggestionsRequest implements Parcelable {
int hostDisplayId = in.readInt();
this.mMaxSuggestionCount = maxSuggestionCount;
- this.mPresentationSpecs = presentationSpecs;
+ this.mInlinePresentationSpecs = inlinePresentationSpecs;
com.android.internal.util.AnnotationValidations.validate(
- NonNull.class, null, mPresentationSpecs);
+ NonNull.class, null, mInlinePresentationSpecs);
this.mHostPackageName = hostPackageName;
com.android.internal.util.AnnotationValidations.validate(
NonNull.class, null, mHostPackageName);
@@ -405,7 +484,7 @@ public final class InlineSuggestionsRequest implements Parcelable {
public static final class Builder extends BaseBuilder {
private int mMaxSuggestionCount;
- private @NonNull List<InlinePresentationSpec> mPresentationSpecs;
+ private @NonNull List<InlinePresentationSpec> mInlinePresentationSpecs;
private @NonNull String mHostPackageName;
private @NonNull LocaleList mSupportedLocales;
private @Nullable Bundle mExtras;
@@ -417,16 +496,16 @@ public final class InlineSuggestionsRequest implements Parcelable {
/**
* Creates a new Builder.
*
- * @param presentationSpecs
+ * @param inlinePresentationSpecs
* The {@link InlinePresentationSpec} for each suggestion in the response. If the max suggestion
* count is larger than the number of specs in the list, then the last spec is used for the
* remainder of the suggestions. The list should not be empty.
*/
public Builder(
- @NonNull List<InlinePresentationSpec> presentationSpecs) {
- mPresentationSpecs = presentationSpecs;
+ @NonNull List<InlinePresentationSpec> inlinePresentationSpecs) {
+ mInlinePresentationSpecs = inlinePresentationSpecs;
com.android.internal.util.AnnotationValidations.validate(
- NonNull.class, null, mPresentationSpecs);
+ NonNull.class, null, mInlinePresentationSpecs);
}
/**
@@ -447,22 +526,21 @@ public final class InlineSuggestionsRequest implements Parcelable {
* remainder of the suggestions. The list should not be empty.
*/
@DataClass.Generated.Member
- @Override
- @NonNull Builder setPresentationSpecs(@NonNull List<InlinePresentationSpec> value) {
+ public @NonNull Builder setInlinePresentationSpecs(@NonNull List<InlinePresentationSpec> value) {
checkNotUsed();
mBuilderFieldsSet |= 0x2;
- mPresentationSpecs = value;
+ mInlinePresentationSpecs = value;
return this;
}
- /** @see #setPresentationSpecs */
+ /** @see #setInlinePresentationSpecs */
@DataClass.Generated.Member
- public @NonNull Builder addPresentationSpecs(@NonNull InlinePresentationSpec value) {
+ public @NonNull Builder addInlinePresentationSpecs(@NonNull InlinePresentationSpec value) {
// You can refine this method's name by providing item's singular name, e.g.:
// @DataClass.PluralOf("item")) mItems = ...
- if (mPresentationSpecs == null) setPresentationSpecs(new ArrayList<>());
- mPresentationSpecs.add(value);
+ if (mInlinePresentationSpecs == null) setInlinePresentationSpecs(new ArrayList<>());
+ mInlinePresentationSpecs.add(value);
return this;
}
@@ -558,7 +636,7 @@ public final class InlineSuggestionsRequest implements Parcelable {
}
InlineSuggestionsRequest o = new InlineSuggestionsRequest(
mMaxSuggestionCount,
- mPresentationSpecs,
+ mInlinePresentationSpecs,
mHostPackageName,
mSupportedLocales,
mExtras,
@@ -576,10 +654,10 @@ public final class InlineSuggestionsRequest implements Parcelable {
}
@DataClass.Generated(
- time = 1584067152935L,
+ time = 1585528160885L,
codegenVersion = "1.0.15",
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.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 []")
+ inputSignatures = "public static final int SUGGESTION_COUNT_UNLIMITED\nprivate final int mMaxSuggestionCount\nprivate final @android.annotation.NonNull java.util.List<android.widget.inline.InlinePresentationSpec> mInlinePresentationSpecs\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 @android.annotation.NonNull java.util.List<android.view.inline.InlinePresentationSpec> getPresentationSpecs()\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)\n @android.annotation.NonNull android.view.inputmethod.InlineSuggestionsRequest.Builder setPresentationSpecs(java.util.List<android.view.inline.InlinePresentationSpec>)\npublic @android.annotation.NonNull android.view.inputmethod.InlineSuggestionsRequest.Builder addPresentationSpecs(android.view.inline.InlinePresentationSpec)\nabstract android.view.inputmethod.InlineSuggestionsRequest.Builder setInlinePresentationSpecs(java.util.List<android.widget.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/android/widget/inline/InlineContentView.java b/core/java/android/widget/inline/InlineContentView.java
new file mode 100644
index 000000000000..16a61fbc567d
--- /dev/null
+++ b/core/java/android/widget/inline/InlineContentView.java
@@ -0,0 +1,205 @@
+/*
+ * 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.widget.inline;
+
+import android.annotation.NonNull;
+import android.annotation.Nullable;
+import android.content.Context;
+import android.graphics.PixelFormat;
+import android.util.AttributeSet;
+import android.view.SurfaceControl;
+import android.view.SurfaceControlViewHost;
+import android.view.SurfaceHolder;
+import android.view.SurfaceView;
+import android.view.ViewGroup;
+
+/**
+ * This class represents a view that holds opaque content from another app that
+ * you can inline in your UI.
+ *
+ * <p>Since the content presented by this view is from another security domain,it is
+ * shown on a remote surface preventing the host application from accessing that content.
+ * Also the host application cannot interact with the inlined content by injecting touch
+ * events or clicking programmatically.
+ *
+ * <p>This view can be overlaid by other windows, i.e. redressed, but if this is the case
+ * the inined UI would not be interactive. Sometimes this is desirable, e.g. animating
+ * transitions.
+ *
+ * <p>By default the surface backing this view is shown on top of the hosting window such
+ * that the inlined content is interactive. However, you can temporarily move the surface
+ * under the hosting window which could be useful in some cases, e.g. animating transitions.
+ * At this point the inlined content will not be interactive and the touch events would
+ * be delivered to your app.
+ */
+public class InlineContentView extends ViewGroup {
+
+ /**
+ * Callback for observing the lifecycle of the surface control
+ * that manipulates the backing secure embedded UI surface.
+ */
+ public interface SurfaceControlCallback {
+ /**
+ * Called when the backing surface is being created.
+ *
+ * @param surfaceControl The surface control to manipulate the surface.
+ */
+ void onCreated(@NonNull SurfaceControl surfaceControl);
+
+ /**
+ * Called when the backing surface is being destroyed.
+ *
+ * @param surfaceControl The surface control to manipulate the surface.
+ */
+ void onDestroyed(@NonNull SurfaceControl surfaceControl);
+ }
+
+ private final @NonNull SurfaceHolder.Callback mSurfaceCallback = new SurfaceHolder.Callback() {
+ @Override
+ public void surfaceCreated(@NonNull SurfaceHolder holder) {
+ mSurfaceControlCallback.onCreated(mSurfaceView.getSurfaceControl());
+ }
+
+ @Override
+ public void surfaceChanged(@NonNull SurfaceHolder holder,
+ int format, int width, int height) {
+ /* do nothing */
+ }
+
+ @Override
+ public void surfaceDestroyed(@NonNull SurfaceHolder holder) {
+ mSurfaceControlCallback.onDestroyed(mSurfaceView.getSurfaceControl());
+ }
+ };
+
+ private final @NonNull SurfaceView mSurfaceView;
+
+ private @Nullable SurfaceControlCallback mSurfaceControlCallback;
+
+ /**
+ * @inheritDoc
+ *
+ * @hide
+ */
+ public InlineContentView(@NonNull Context context) {
+ this(context, null);
+ }
+
+ /**
+ * @inheritDoc
+ *
+ * @hide
+ */
+ public InlineContentView(@NonNull Context context, @Nullable AttributeSet attrs) {
+ this(context, attrs, 0);
+ }
+
+ /**
+ * @inheritDoc
+ *
+ * @hide
+ */
+ public InlineContentView(@NonNull Context context, @Nullable AttributeSet attrs,
+ int defStyleAttr) {
+ this(context, attrs, defStyleAttr, 0);
+ }
+
+ /**
+ * Gets the surface control. If the surface is not created this method
+ * returns {@code null}.
+ *
+ * @return The surface control.
+ *
+ * @see #setSurfaceControlCallback(SurfaceControlCallback)
+ */
+ public @Nullable SurfaceControl getSurfaceControl() {
+ return mSurfaceView.getSurfaceControl();
+ }
+
+ /**
+ * @inheritDoc
+ *
+ * @hide
+ */
+ public InlineContentView(@NonNull Context context, @Nullable AttributeSet attrs,
+ int defStyleAttr, int defStyleRes) {
+ super(context, attrs, defStyleAttr, defStyleRes);
+ mSurfaceView = new SurfaceView(context, attrs, defStyleAttr, defStyleRes);
+ mSurfaceView.setZOrderOnTop(true);
+ mSurfaceView.getHolder().setFormat(PixelFormat.TRANSPARENT);
+ addView(mSurfaceView);
+ }
+
+ /**
+ * Sets the embedded UI.
+ * @param surfacePackage The embedded UI.
+ *
+ * @hide
+ */
+ public void setChildSurfacePackage(
+ @Nullable SurfaceControlViewHost.SurfacePackage surfacePackage) {
+ mSurfaceView.setChildSurfacePackage(surfacePackage);
+ }
+
+ @Override
+ public void onLayout(boolean changed, int l, int t, int r, int b) {
+ mSurfaceView.layout(l, t, r, b);
+ }
+
+ /**
+ * Sets a callback to observe the lifecycle of the surface control for
+ * managing the backing surface.
+ *
+ * @param callback The callback to set or {@code null} to clear.
+ */
+ public void setSurfaceControlCallback(@Nullable SurfaceControlCallback callback) {
+ if (mSurfaceControlCallback != null) {
+ mSurfaceView.getHolder().removeCallback(mSurfaceCallback);
+ }
+ mSurfaceControlCallback = callback;
+ if (mSurfaceControlCallback != null) {
+ mSurfaceView.getHolder().addCallback(mSurfaceCallback);
+ }
+ }
+
+ /**
+ * @return Whether the surface backing this view appears on top of its parent.
+ *
+ * @see #setZOrderedOnTop(boolean)
+ */
+ public boolean isZOrderedOnTop() {
+ return mSurfaceView.isZOrderedOnTop();
+ }
+
+ /**
+ * Controls whether the backing surface is placed on top of this view's window.
+ * Normally, it is placed on top of the window, to allow interaction
+ * with the inlined UI. Via this method, you can place the surface below the
+ * window. This means that all of the contents of the window this view is in
+ * will be visible on top of its surface.
+ *
+ * <p> The Z ordering can be changed dynamically if the backing surface is
+ * created, otherwise the ordering would be applied at surface construction time.
+ *
+ * @param onTop Whether to show the surface on top of this view's window.
+ *
+ * @see #isZOrderedOnTop()
+ */
+ public boolean setZOrderedOnTop(boolean onTop) {
+ return mSurfaceView.setZOrderedOnTop(onTop, /*allowDynamicChange*/ true);
+ }
+}
diff --git a/core/java/android/widget/inline/InlinePresentationSpec.aidl b/core/java/android/widget/inline/InlinePresentationSpec.aidl
new file mode 100644
index 000000000000..c76ffe0b7fe2
--- /dev/null
+++ b/core/java/android/widget/inline/InlinePresentationSpec.aidl
@@ -0,0 +1,19 @@
+/*
+ * Copyright (C) 2019 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.widget.inline;
+
+parcelable InlinePresentationSpec;
diff --git a/core/java/android/widget/inline/InlinePresentationSpec.java b/core/java/android/widget/inline/InlinePresentationSpec.java
new file mode 100644
index 000000000000..886790268737
--- /dev/null
+++ b/core/java/android/widget/inline/InlinePresentationSpec.java
@@ -0,0 +1,293 @@
+/*
+ * Copyright (C) 2019 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.widget.inline;
+
+import android.annotation.NonNull;
+import android.annotation.Nullable;
+import android.os.Bundle;
+import android.os.Parcelable;
+import android.util.Size;
+
+import com.android.internal.util.DataClass;
+
+/**
+ * This class represents the presentation specification by which an inline suggestion
+ * should abide when constructing its UI. Since suggestions are inlined in a
+ * host application while provided by another source, they need to be consistent
+ * with the host's look at feel to allow building smooth and integrated UIs.
+ */
+@DataClass(genEqualsHashCode = true, genToString = true, genBuilder = true)
+public final class InlinePresentationSpec implements Parcelable {
+
+ /** The minimal size of the suggestion. */
+ @NonNull
+ private final Size mMinSize;
+ /** The maximal size of the suggestion. */
+ @NonNull
+ private final Size mMaxSize;
+
+ /**
+ * The extras encoding the UI style information. Defaults to {@code null} in which case the
+ * default system UI style will be used.
+ */
+ @Nullable
+ private final Bundle mStyle;
+
+ private static Bundle defaultStyle() {
+ return null;
+ }
+
+ /** @hide */
+ @DataClass.Suppress({"setMaxSize", "setMinSize"})
+ abstract static class BaseBuilder {
+ }
+
+
+
+ // Code below generated by codegen v1.0.15.
+ //
+ // DO NOT MODIFY!
+ // CHECKSTYLE:OFF Generated code
+ //
+ // To regenerate run:
+ // $ codegen $ANDROID_BUILD_TOP/frameworks/base/core/java/android/widget/inline/InlinePresentationSpec.java
+ //
+ // To exclude the generated code from IntelliJ auto-formatting enable (one-time):
+ // Settings > Editor > Code Style > Formatter Control
+ //@formatter:off
+
+
+ @DataClass.Generated.Member
+ /* package-private */ InlinePresentationSpec(
+ @NonNull Size minSize,
+ @NonNull Size maxSize,
+ @Nullable Bundle style) {
+ this.mMinSize = minSize;
+ com.android.internal.util.AnnotationValidations.validate(
+ NonNull.class, null, mMinSize);
+ this.mMaxSize = maxSize;
+ com.android.internal.util.AnnotationValidations.validate(
+ NonNull.class, null, mMaxSize);
+ this.mStyle = style;
+
+ // onConstructed(); // You can define this method to get a callback
+ }
+
+ /**
+ * The minimal size of the suggestion.
+ */
+ @DataClass.Generated.Member
+ public @NonNull Size getMinSize() {
+ return mMinSize;
+ }
+
+ /**
+ * The maximal size of the suggestion.
+ */
+ @DataClass.Generated.Member
+ public @NonNull Size getMaxSize() {
+ return mMaxSize;
+ }
+
+ /**
+ * The extras encoding the UI style information. Defaults to {@code null} in which case the
+ * default system UI style will be used.
+ */
+ @DataClass.Generated.Member
+ public @Nullable Bundle getStyle() {
+ return mStyle;
+ }
+
+ @Override
+ @DataClass.Generated.Member
+ public String toString() {
+ // You can override field toString logic by defining methods like:
+ // String fieldNameToString() { ... }
+
+ return "InlinePresentationSpec { " +
+ "minSize = " + mMinSize + ", " +
+ "maxSize = " + mMaxSize + ", " +
+ "style = " + mStyle +
+ " }";
+ }
+
+ @Override
+ @DataClass.Generated.Member
+ public boolean equals(@Nullable Object o) {
+ // You can override field equality logic by defining either of the methods like:
+ // boolean fieldNameEquals(InlinePresentationSpec other) { ... }
+ // boolean fieldNameEquals(FieldType otherValue) { ... }
+
+ if (this == o) return true;
+ if (o == null || getClass() != o.getClass()) return false;
+ @SuppressWarnings("unchecked")
+ InlinePresentationSpec that = (InlinePresentationSpec) o;
+ //noinspection PointlessBooleanExpression
+ return true
+ && java.util.Objects.equals(mMinSize, that.mMinSize)
+ && java.util.Objects.equals(mMaxSize, that.mMaxSize)
+ && java.util.Objects.equals(mStyle, that.mStyle);
+ }
+
+ @Override
+ @DataClass.Generated.Member
+ public int hashCode() {
+ // You can override field hashCode logic by defining methods like:
+ // int fieldNameHashCode() { ... }
+
+ int _hash = 1;
+ _hash = 31 * _hash + java.util.Objects.hashCode(mMinSize);
+ _hash = 31 * _hash + java.util.Objects.hashCode(mMaxSize);
+ _hash = 31 * _hash + java.util.Objects.hashCode(mStyle);
+ return _hash;
+ }
+
+ @Override
+ @DataClass.Generated.Member
+ public void writeToParcel(@NonNull android.os.Parcel dest, int flags) {
+ // You can override field parcelling by defining methods like:
+ // void parcelFieldName(Parcel dest, int flags) { ... }
+
+ byte flg = 0;
+ if (mStyle != null) flg |= 0x4;
+ dest.writeByte(flg);
+ dest.writeSize(mMinSize);
+ dest.writeSize(mMaxSize);
+ if (mStyle != null) dest.writeBundle(mStyle);
+ }
+
+ @Override
+ @DataClass.Generated.Member
+ public int describeContents() { return 0; }
+
+ /** @hide */
+ @SuppressWarnings({"unchecked", "RedundantCast"})
+ @DataClass.Generated.Member
+ /* package-private */ InlinePresentationSpec(@NonNull android.os.Parcel in) {
+ // You can override field unparcelling by defining methods like:
+ // static FieldType unparcelFieldName(Parcel in) { ... }
+
+ byte flg = in.readByte();
+ Size minSize = (Size) in.readSize();
+ Size maxSize = (Size) in.readSize();
+ Bundle style = (flg & 0x4) == 0 ? null : in.readBundle();
+
+ this.mMinSize = minSize;
+ com.android.internal.util.AnnotationValidations.validate(
+ NonNull.class, null, mMinSize);
+ this.mMaxSize = maxSize;
+ com.android.internal.util.AnnotationValidations.validate(
+ NonNull.class, null, mMaxSize);
+ this.mStyle = style;
+
+ // onConstructed(); // You can define this method to get a callback
+ }
+
+ @DataClass.Generated.Member
+ public static final @NonNull Parcelable.Creator<InlinePresentationSpec> CREATOR
+ = new Parcelable.Creator<InlinePresentationSpec>() {
+ @Override
+ public InlinePresentationSpec[] newArray(int size) {
+ return new InlinePresentationSpec[size];
+ }
+
+ @Override
+ public InlinePresentationSpec createFromParcel(@NonNull android.os.Parcel in) {
+ return new InlinePresentationSpec(in);
+ }
+ };
+
+ /**
+ * A builder for {@link InlinePresentationSpec}
+ */
+ @SuppressWarnings("WeakerAccess")
+ @DataClass.Generated.Member
+ public static final class Builder extends BaseBuilder {
+
+ private @NonNull Size mMinSize;
+ private @NonNull Size mMaxSize;
+ private @Nullable Bundle mStyle;
+
+ private long mBuilderFieldsSet = 0L;
+
+ /**
+ * Creates a new Builder.
+ *
+ * @param minSize
+ * The minimal size of the suggestion.
+ * @param maxSize
+ * The maximal size of the suggestion.
+ */
+ public Builder(
+ @NonNull Size minSize,
+ @NonNull Size maxSize) {
+ mMinSize = minSize;
+ com.android.internal.util.AnnotationValidations.validate(
+ NonNull.class, null, mMinSize);
+ mMaxSize = maxSize;
+ com.android.internal.util.AnnotationValidations.validate(
+ NonNull.class, null, mMaxSize);
+ }
+
+ /**
+ * The extras encoding the UI style information. Defaults to {@code null} in which case the
+ * default system UI style will be used.
+ */
+ @DataClass.Generated.Member
+ public @NonNull Builder setStyle(@NonNull Bundle value) {
+ checkNotUsed();
+ mBuilderFieldsSet |= 0x4;
+ mStyle = value;
+ return this;
+ }
+
+ /** Builds the instance. This builder should not be touched after calling this! */
+ public @NonNull InlinePresentationSpec build() {
+ checkNotUsed();
+ mBuilderFieldsSet |= 0x8; // Mark builder used
+
+ if ((mBuilderFieldsSet & 0x4) == 0) {
+ mStyle = defaultStyle();
+ }
+ InlinePresentationSpec o = new InlinePresentationSpec(
+ mMinSize,
+ mMaxSize,
+ mStyle);
+ return o;
+ }
+
+ private void checkNotUsed() {
+ if ((mBuilderFieldsSet & 0x8) != 0) {
+ throw new IllegalStateException(
+ "This Builder should not be reused. Use a new Builder instance instead");
+ }
+ }
+ }
+
+ @DataClass.Generated(
+ time = 1585174247896L,
+ codegenVersion = "1.0.15",
+ sourceFile = "frameworks/base/core/java/android/widget/inline/InlinePresentationSpec.java",
+ inputSignatures = "private final @android.annotation.NonNull android.util.Size mMinSize\nprivate final @android.annotation.NonNull android.util.Size mMaxSize\nprivate final @android.annotation.Nullable android.os.Bundle mStyle\nprivate static android.os.Bundle defaultStyle()\nclass InlinePresentationSpec extends java.lang.Object implements [android.os.Parcelable]\n@com.android.internal.util.DataClass(genEqualsHashCode=true, genToString=true, genBuilder=true)\nclass BaseBuilder extends java.lang.Object implements []")
+ @Deprecated
+ private void __metadata() {}
+
+
+ //@formatter:on
+ // End of generated code
+
+}
diff --git a/core/java/com/android/internal/policy/DecorContext.java b/core/java/com/android/internal/policy/DecorContext.java
index 99b4b5fb7707..f00776897f2c 100644
--- a/core/java/com/android/internal/policy/DecorContext.java
+++ b/core/java/com/android/internal/policy/DecorContext.java
@@ -41,17 +41,17 @@ import java.lang.ref.WeakReference;
public class DecorContext extends ContextThemeWrapper {
private PhoneWindow mPhoneWindow;
private WindowManager mWindowManager;
- private Resources mActivityResources;
+ private Resources mResources;
private ContentCaptureManager mContentCaptureManager;
- private WeakReference<Context> mActivityContext;
+ private WeakReference<Context> mContext;
// TODO(b/149928768): Non-activity context can be passed.
@VisibleForTesting
- public DecorContext(Context context, Context activityContext) {
- super(context.createDisplayContext(activityContext.getDisplayNoVerify()), null);
- mActivityContext = new WeakReference<>(activityContext);
- mActivityResources = activityContext.getResources();
+ public DecorContext(Context baseContext, Context context) {
+ super(baseContext.createDisplayContext(context.getDisplayNoVerify()), null);
+ mContext = new WeakReference<>(context);
+ mResources = context.getResources();
}
void setPhoneWindow(PhoneWindow phoneWindow) {
@@ -61,58 +61,56 @@ public class DecorContext extends ContextThemeWrapper {
@Override
public Object getSystemService(String name) {
+ final Context context = mContext.get();
if (Context.WINDOW_SERVICE.equals(name)) {
- if (mWindowManager == null) {
- WindowManagerImpl wm =
- (WindowManagerImpl) super.getSystemService(Context.WINDOW_SERVICE);
+ if (context != null && mWindowManager == null) {
+ WindowManagerImpl wm = (WindowManagerImpl) context.getSystemService(name);
mWindowManager = wm.createLocalWindowManager(mPhoneWindow);
}
return mWindowManager;
}
if (Context.CONTENT_CAPTURE_MANAGER_SERVICE.equals(name)) {
- if (mContentCaptureManager == null) {
- Context activityContext = mActivityContext.get();
- if (activityContext != null) {
- mContentCaptureManager = (ContentCaptureManager) activityContext
- .getSystemService(name);
- }
+ if (context != null && mContentCaptureManager == null) {
+ mContentCaptureManager = (ContentCaptureManager) context.getSystemService(name);
}
return mContentCaptureManager;
}
- return super.getSystemService(name);
+ // LayoutInflater and WallpaperManagerService should also be obtained from context
+ // instead of application context.
+ return (context != null) ? context.getSystemService(name) : super.getSystemService(name);
}
@Override
public Resources getResources() {
- Context activityContext = mActivityContext.get();
+ Context context = mContext.get();
// Attempt to update the local cached Resources from the activity context. If the activity
// is no longer around, return the old cached values.
- if (activityContext != null) {
- mActivityResources = activityContext.getResources();
+ if (context != null) {
+ mResources = context.getResources();
}
- return mActivityResources;
+ return mResources;
}
@Override
public AssetManager getAssets() {
- return mActivityResources.getAssets();
+ return mResources.getAssets();
}
@Override
public AutofillOptions getAutofillOptions() {
- Context activityContext = mActivityContext.get();
- if (activityContext != null) {
- return activityContext.getAutofillOptions();
+ Context context = mContext.get();
+ if (context != null) {
+ return context.getAutofillOptions();
}
return null;
}
@Override
public ContentCaptureOptions getContentCaptureOptions() {
- Context activityContext = mActivityContext.get();
- if (activityContext != null) {
- return activityContext.getContentCaptureOptions();
+ Context context = mContext.get();
+ if (context != null) {
+ return context.getContentCaptureOptions();
}
return null;
}
diff --git a/core/tests/coretests/src/com/android/internal/policy/DecorContextTest.java b/core/tests/coretests/src/com/android/internal/policy/DecorContextTest.java
index 3e40466e4b64..d019704fb684 100644
--- a/core/tests/coretests/src/com/android/internal/policy/DecorContextTest.java
+++ b/core/tests/coretests/src/com/android/internal/policy/DecorContextTest.java
@@ -20,19 +20,24 @@ import static android.view.Display.DEFAULT_DISPLAY;
import static org.junit.Assert.assertEquals;
+import android.app.Activity;
+import android.app.EmptyActivity;
import android.content.Context;
import android.hardware.display.DisplayManagerGlobal;
import android.platform.test.annotations.Presubmit;
import android.view.Display;
import android.view.DisplayAdjustments;
import android.view.DisplayInfo;
+import android.view.WindowManager;
+import android.view.WindowManagerImpl;
-import androidx.test.InstrumentationRegistry;
+import androidx.test.core.app.ApplicationProvider;
import androidx.test.filters.SmallTest;
+import androidx.test.rule.ActivityTestRule;
import androidx.test.runner.AndroidJUnit4;
-
import org.junit.Before;
+import org.junit.Rule;
import org.junit.Test;
import org.junit.runner.RunWith;
@@ -46,9 +51,13 @@ public final class DecorContextTest {
private Context mContext;
private static final int EXTERNAL_DISPLAY = DEFAULT_DISPLAY + 1;
+ @Rule
+ public ActivityTestRule<EmptyActivity> mActivityRule =
+ new ActivityTestRule<>(EmptyActivity.class);
+
@Before
- public void setUp() throws Exception {
- mContext = InstrumentationRegistry.getContext();
+ public void setUp() {
+ mContext = ApplicationProvider.getApplicationContext();
}
@Test
@@ -76,4 +85,19 @@ public final class DecorContextTest {
Display associatedDisplay = decorContext.getDisplay();
assertEquals(expectedDisplayId, associatedDisplay.getDisplayId());
}
+
+ @Test
+ public void testGetWindowManagerFromVisualDecorContext() throws Throwable {
+ mActivityRule.runOnUiThread(() -> {
+ Activity activity = mActivityRule.getActivity();
+ final DecorContext decorContext = new DecorContext(mContext.getApplicationContext(),
+ activity);
+ WindowManagerImpl actualWm = (WindowManagerImpl)
+ decorContext.getSystemService(WindowManager.class);
+ WindowManagerImpl expectedWm = (WindowManagerImpl)
+ activity.getSystemService(WindowManager.class);
+ // Verify that window manager is from activity not application context.
+ assertEquals(expectedWm.mContext, actualWm.mContext);
+ });
+ }
}
diff --git a/data/etc/services.core.protolog.json b/data/etc/services.core.protolog.json
index 99605ad64cb8..a871047a0602 100644
--- a/data/etc/services.core.protolog.json
+++ b/data/etc/services.core.protolog.json
@@ -193,12 +193,6 @@
"group": "WM_DEBUG_ORIENTATION",
"at": "com\/android\/server\/wm\/WindowState.java"
},
- "-1741065110": {
- "message": "No app is requesting an orientation, return %d for display id=%d",
- "level": "VERBOSE",
- "group": "WM_DEBUG_ORIENTATION",
- "at": "com\/android\/server\/wm\/DisplayContent.java"
- },
"-1730156332": {
"message": "Display id=%d rotation changed to %d from %d, lastOrientation=%d",
"level": "VERBOSE",
@@ -547,6 +541,12 @@
"group": "WM_DEBUG_APP_TRANSITIONS",
"at": "com\/android\/server\/wm\/AppTransitionController.java"
},
+ "-993446393": {
+ "message": "App is requesting an orientation, return %d for display id=%d",
+ "level": "VERBOSE",
+ "group": "WM_DEBUG_ORIENTATION",
+ "at": "com\/android\/server\/wm\/TaskContainers.java"
+ },
"-993378225": {
"message": "finishDrawingLocked: mDrawState=COMMIT_DRAW_PENDING %s in %s",
"level": "VERBOSE",
@@ -715,12 +715,6 @@
"group": "WM_DEBUG_REMOTE_ANIMATIONS",
"at": "com\/android\/server\/wm\/RemoteAnimationController.java"
},
- "-650040763": {
- "message": "rotationForOrientation(orient=%d, last=%d); user=%d %s",
- "level": "VERBOSE",
- "group": "WM_DEBUG_ORIENTATION",
- "at": "com\/android\/server\/wm\/DisplayRotation.java"
- },
"-639305784": {
"message": "Could not report config changes to the window token client.",
"level": "WARN",
@@ -1021,12 +1015,6 @@
"group": "WM_ERROR",
"at": "com\/android\/server\/wm\/WindowManagerService.java"
},
- "44438983": {
- "message": "performLayout: Activity exiting now removed %s",
- "level": "VERBOSE",
- "group": "WM_DEBUG_ADD_REMOVE",
- "at": "com\/android\/server\/wm\/DisplayContent.java"
- },
"45285419": {
"message": "startingWindow was set but startingSurface==null, couldn't remove",
"level": "VERBOSE",
@@ -1087,6 +1075,12 @@
"group": "WM_SHOW_TRANSACTIONS",
"at": "com\/android\/server\/wm\/WindowSurfaceController.java"
},
+ "137835146": {
+ "message": "No app is requesting an orientation, return %d for display id=%d",
+ "level": "VERBOSE",
+ "group": "WM_DEBUG_ORIENTATION",
+ "at": "com\/android\/server\/wm\/TaskContainers.java"
+ },
"140319294": {
"message": "IME target changed within ActivityRecord",
"level": "DEBUG",
@@ -1531,12 +1525,6 @@
"group": "WM_DEBUG_REMOTE_ANIMATIONS",
"at": "com\/android\/server\/wm\/RemoteAnimationController.java"
},
- "845234215": {
- "message": "App is requesting an orientation, return %d for display id=%d",
- "level": "VERBOSE",
- "group": "WM_DEBUG_ORIENTATION",
- "at": "com\/android\/server\/wm\/DisplayContent.java"
- },
"853091290": {
"message": "Moved stack=%s behind stack=%s",
"level": "DEBUG",
@@ -1927,6 +1915,12 @@
"group": "WM_DEBUG_STARTING_WINDOW",
"at": "com\/android\/server\/wm\/ActivityRecord.java"
},
+ "1685441447": {
+ "message": "performLayout: Activity exiting now removed %s",
+ "level": "VERBOSE",
+ "group": "WM_DEBUG_ADD_REMOVE",
+ "at": "com\/android\/server\/wm\/TaskContainers.java"
+ },
"1720229827": {
"message": "Creating animation bounds layer",
"level": "INFO",
diff --git a/packages/SystemUI/src/com/android/systemui/media/MediaControlPanel.java b/packages/SystemUI/src/com/android/systemui/media/MediaControlPanel.java
index e208ee2b4ab7..9873d240efe3 100644
--- a/packages/SystemUI/src/com/android/systemui/media/MediaControlPanel.java
+++ b/packages/SystemUI/src/com/android/systemui/media/MediaControlPanel.java
@@ -37,6 +37,7 @@ import android.util.Log;
import android.view.KeyEvent;
import android.view.LayoutInflater;
import android.view.View;
+import android.view.View.OnAttachStateChangeListener;
import android.view.ViewGroup;
import android.widget.ImageButton;
import android.widget.ImageView;
@@ -53,6 +54,8 @@ import com.android.systemui.Dependency;
import com.android.systemui.R;
import com.android.systemui.plugins.ActivityStarter;
import com.android.systemui.statusbar.NotificationMediaManager;
+import com.android.systemui.statusbar.NotificationMediaManager.MediaListener;
+import com.android.systemui.util.Assert;
import java.util.List;
import java.util.concurrent.Executor;
@@ -60,7 +63,7 @@ import java.util.concurrent.Executor;
/**
* Base media control panel for System UI
*/
-public class MediaControlPanel implements NotificationMediaManager.MediaListener {
+public class MediaControlPanel {
private static final String TAG = "MediaControlPanel";
private final NotificationMediaManager mMediaManager;
private final Executor mForegroundExecutor;
@@ -74,6 +77,7 @@ public class MediaControlPanel implements NotificationMediaManager.MediaListener
private int mForegroundColor;
private int mBackgroundColor;
protected ComponentName mRecvComponent;
+ private boolean mIsRegistered = false;
private final int[] mActionIds;
@@ -86,12 +90,34 @@ public class MediaControlPanel implements NotificationMediaManager.MediaListener
com.android.internal.R.id.action4
};
- private MediaController.Callback mSessionCallback = new MediaController.Callback() {
+ private final MediaController.Callback mSessionCallback = new MediaController.Callback() {
@Override
public void onSessionDestroyed() {
Log.d(TAG, "session destroyed");
mController.unregisterCallback(mSessionCallback);
clearControls();
+ makeInactive();
+ }
+ };
+
+ private final MediaListener mMediaListener = new MediaListener() {
+ @Override
+ public void onMetadataOrStateChanged(MediaMetadata metadata, int state) {
+ if (state == PlaybackState.STATE_NONE) {
+ clearControls();
+ makeInactive();
+ }
+ }
+ };
+
+ private final OnAttachStateChangeListener mStateListener = new OnAttachStateChangeListener() {
+ @Override
+ public void onViewAttachedToWindow(View unused) {
+ makeActive();
+ }
+ @Override
+ public void onViewDetachedFromWindow(View unused) {
+ makeInactive();
}
};
@@ -111,6 +137,12 @@ public class MediaControlPanel implements NotificationMediaManager.MediaListener
mContext = context;
LayoutInflater inflater = LayoutInflater.from(mContext);
mMediaNotifView = (LinearLayout) inflater.inflate(layoutId, parent, false);
+ // TODO(b/150854549): removeOnAttachStateChangeListener when this doesn't inflate views
+ // mStateListener shouldn't need to be unregistered since this object shares the same
+ // lifecycle with the inflated view. It would be better, however, if this controller used an
+ // attach/detach of views instead of inflating them in the constructor, which would allow
+ // mStateListener to be unregistered in detach.
+ mMediaNotifView.addOnAttachStateChangeListener(mStateListener);
mMediaManager = manager;
mActionIds = actionIds;
mForegroundExecutor = foregroundExecutor;
@@ -236,9 +268,7 @@ public class MediaControlPanel implements NotificationMediaManager.MediaListener
});
}
- // Ensure is only added once
- mMediaManager.removeCallback(this);
- mMediaManager.addCallback(this);
+ makeActive();
}
/**
@@ -422,11 +452,20 @@ public class MediaControlPanel implements NotificationMediaManager.MediaListener
btn.setVisibility(View.VISIBLE);
}
- @Override
- public void onMetadataOrStateChanged(MediaMetadata metadata, int state) {
- if (state == PlaybackState.STATE_NONE) {
- clearControls();
- mMediaManager.removeCallback(this);
+ private void makeActive() {
+ Assert.isMainThread();
+ if (!mIsRegistered) {
+ mMediaManager.addCallback(mMediaListener);
+ mIsRegistered = true;
}
}
+
+ private void makeInactive() {
+ Assert.isMainThread();
+ if (mIsRegistered) {
+ mMediaManager.removeCallback(mMediaListener);
+ mIsRegistered = false;
+ }
+ }
+
}
diff --git a/packages/overlays/Android.mk b/packages/overlays/Android.mk
index dcdb80b497d0..ef0e04456421 100644
--- a/packages/overlays/Android.mk
+++ b/packages/overlays/Android.mk
@@ -45,6 +45,8 @@ LOCAL_REQUIRED_MODULES := \
IconPackRoundedSettingsOverlay \
IconPackRoundedSystemUIOverlay \
IconPackRoundedThemePickerOverlay \
+ IconShapeFlowerOverlay \
+ IconShapeHexagonOverlay \
IconShapeRoundedRectOverlay \
IconShapeSquircleOverlay \
IconShapeTeardropOverlay \
diff --git a/services/Android.bp b/services/Android.bp
index 1ce7dcfff03b..730b9a5dc3e3 100644
--- a/services/Android.bp
+++ b/services/Android.bp
@@ -81,10 +81,6 @@ java_library {
"framework-tethering-stubs-module_libs_api",
],
- plugins: [
- "compat-changeid-annotation-processor",
- ],
-
// Uncomment to enable output of certain warnings (deprecated, unchecked)
//javacflags: ["-Xlint"],
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 2306329e689c..a86d34d4eb3e 100644
--- a/services/autofill/java/com/android/server/autofill/ui/InlineSuggestionFactory.java
+++ b/services/autofill/java/com/android/server/autofill/ui/InlineSuggestionFactory.java
@@ -35,11 +35,11 @@ import android.view.SurfaceControlViewHost;
import android.view.autofill.AutofillId;
import android.view.autofill.AutofillManager;
import android.view.autofill.AutofillValue;
-import android.view.inline.InlinePresentationSpec;
import android.view.inputmethod.InlineSuggestion;
import android.view.inputmethod.InlineSuggestionInfo;
import android.view.inputmethod.InlineSuggestionsRequest;
import android.view.inputmethod.InlineSuggestionsResponse;
+import android.widget.inline.InlinePresentationSpec;
import com.android.internal.view.inline.IInlineContentCallback;
import com.android.internal.view.inline.IInlineContentProvider;
@@ -263,7 +263,7 @@ public final class InlineSuggestionFactory {
private static InlinePresentation mergedInlinePresentation(
@NonNull InlineSuggestionsRequest request,
int index, @NonNull InlinePresentation inlinePresentation) {
- final List<InlinePresentationSpec> specs = request.getPresentationSpecs();
+ final List<InlinePresentationSpec> specs = request.getInlinePresentationSpecs();
if (specs.isEmpty()) {
return inlinePresentation;
}
diff --git a/services/contentcapture/java/com/android/server/contentcapture/ContentCaptureManagerService.java b/services/contentcapture/java/com/android/server/contentcapture/ContentCaptureManagerService.java
index 5d2b9f359381..ce539dabfac1 100644
--- a/services/contentcapture/java/com/android/server/contentcapture/ContentCaptureManagerService.java
+++ b/services/contentcapture/java/com/android/server/contentcapture/ContentCaptureManagerService.java
@@ -89,7 +89,6 @@ import java.io.IOException;
import java.io.InputStream;
import java.io.OutputStream;
import java.io.PrintWriter;
-import java.lang.ref.WeakReference;
import java.util.ArrayList;
import java.util.HashSet;
import java.util.List;
@@ -919,35 +918,24 @@ public final class ContentCaptureManagerService extends
private static class DataShareCallbackDelegate extends IDataShareCallback.Stub {
@NonNull private final DataShareRequest mDataShareRequest;
- @NonNull private final WeakReference<IDataShareWriteAdapter> mClientAdapterReference;
- @NonNull private final WeakReference<ContentCaptureManagerService> mParentServiceReference;
+ @NonNull private final IDataShareWriteAdapter mClientAdapter;
+ @NonNull private final ContentCaptureManagerService mParentService;
DataShareCallbackDelegate(@NonNull DataShareRequest dataShareRequest,
@NonNull IDataShareWriteAdapter clientAdapter,
ContentCaptureManagerService parentService) {
mDataShareRequest = dataShareRequest;
- mClientAdapterReference = new WeakReference<>(clientAdapter);
- mParentServiceReference = new WeakReference<>(parentService);
+ mClientAdapter = clientAdapter;
+ mParentService = parentService;
}
@Override
public void accept(IDataShareReadAdapter serviceAdapter) throws RemoteException {
Slog.i(TAG, "Data share request accepted by Content Capture service");
- final ContentCaptureManagerService parentService = mParentServiceReference.get();
- final IDataShareWriteAdapter clientAdapter = mClientAdapterReference.get();
- if (parentService == null || clientAdapter == null) {
- Slog.w(TAG, "Can't fulfill accept() request, because remote objects have been "
- + "GC'ed");
- return;
- }
-
- final WeakReference<IDataShareReadAdapter> serviceAdapterReference =
- new WeakReference<>(serviceAdapter);
-
Pair<ParcelFileDescriptor, ParcelFileDescriptor> clientPipe = createPipe();
if (clientPipe == null) {
- clientAdapter.error(ContentCaptureManager.DATA_SHARE_ERROR_UNKNOWN);
+ mClientAdapter.error(ContentCaptureManager.DATA_SHARE_ERROR_UNKNOWN);
serviceAdapter.error(ContentCaptureManager.DATA_SHARE_ERROR_UNKNOWN);
return;
}
@@ -959,7 +947,7 @@ public final class ContentCaptureManagerService extends
if (servicePipe == null) {
bestEffortCloseFileDescriptors(sourceIn, sinkIn);
- clientAdapter.error(ContentCaptureManager.DATA_SHARE_ERROR_UNKNOWN);
+ mClientAdapter.error(ContentCaptureManager.DATA_SHARE_ERROR_UNKNOWN);
serviceAdapter.error(ContentCaptureManager.DATA_SHARE_ERROR_UNKNOWN);
return;
}
@@ -967,9 +955,9 @@ public final class ContentCaptureManagerService extends
ParcelFileDescriptor sourceOut = servicePipe.second;
ParcelFileDescriptor sinkOut = servicePipe.first;
- parentService.mPackagesWithShareRequests.add(mDataShareRequest.getPackageName());
+ mParentService.mPackagesWithShareRequests.add(mDataShareRequest.getPackageName());
- clientAdapter.write(sourceIn);
+ mClientAdapter.write(sourceIn);
serviceAdapter.start(sinkOut);
// File descriptor received by the client app will be a copy of the current one. Close
@@ -977,7 +965,7 @@ public final class ContentCaptureManagerService extends
// current pipe.
bestEffortCloseFileDescriptor(sourceIn);
- parentService.mDataShareExecutor.execute(() -> {
+ mParentService.mDataShareExecutor.execute(() -> {
try (InputStream fis =
new ParcelFileDescriptor.AutoCloseInputStream(sinkIn);
OutputStream fos =
@@ -996,23 +984,23 @@ public final class ContentCaptureManagerService extends
} catch (IOException e) {
Slog.e(TAG, "Failed to pipe client and service streams", e);
- sendErrorSignal(mClientAdapterReference, serviceAdapterReference,
+ sendErrorSignal(mClientAdapter, serviceAdapter,
ContentCaptureManager.DATA_SHARE_ERROR_UNKNOWN);
} finally {
- synchronized (parentService.mLock) {
- parentService.mPackagesWithShareRequests
+ synchronized (mParentService.mLock) {
+ mParentService.mPackagesWithShareRequests
.remove(mDataShareRequest.getPackageName());
}
}
});
- parentService.mHandler.postDelayed(() ->
+ mParentService.mHandler.postDelayed(() ->
enforceDataSharingTtl(
sourceIn,
sinkIn,
sourceOut,
sinkOut,
- serviceAdapterReference),
+ serviceAdapter),
MAX_DATA_SHARE_FILE_DESCRIPTORS_TTL_MS);
}
@@ -1020,31 +1008,17 @@ public final class ContentCaptureManagerService extends
public void reject() throws RemoteException {
Slog.i(TAG, "Data share request rejected by Content Capture service");
- IDataShareWriteAdapter clientAdapter = mClientAdapterReference.get();
- if (clientAdapter == null) {
- Slog.w(TAG, "Can't fulfill reject() request, because remote objects have been "
- + "GC'ed");
- return;
- }
-
- clientAdapter.rejected();
+ mClientAdapter.rejected();
}
private void enforceDataSharingTtl(ParcelFileDescriptor sourceIn,
ParcelFileDescriptor sinkIn,
ParcelFileDescriptor sourceOut,
ParcelFileDescriptor sinkOut,
- WeakReference<IDataShareReadAdapter> serviceAdapterReference) {
+ IDataShareReadAdapter serviceAdapter) {
- final ContentCaptureManagerService parentService = mParentServiceReference.get();
- if (parentService == null) {
- Slog.w(TAG, "Can't enforce data sharing TTL, because remote objects have been "
- + "GC'ed");
- return;
- }
-
- synchronized (parentService.mLock) {
- parentService.mPackagesWithShareRequests
+ synchronized (mParentService.mLock) {
+ mParentService.mPackagesWithShareRequests
.remove(mDataShareRequest.getPackageName());
// Interaction finished successfully <=> all data has been written to Content
@@ -1069,7 +1043,7 @@ public final class ContentCaptureManagerService extends
bestEffortCloseFileDescriptors(sourceIn, sinkIn, sourceOut, sinkOut);
if (!finishedSuccessfully) {
- sendErrorSignal(mClientAdapterReference, serviceAdapterReference,
+ sendErrorSignal(mClientAdapter, serviceAdapter,
ContentCaptureManager.DATA_SHARE_ERROR_TIMEOUT_INTERRUPTED);
}
}
@@ -1115,19 +1089,9 @@ public final class ContentCaptureManagerService extends
}
private static void sendErrorSignal(
- WeakReference<IDataShareWriteAdapter> clientAdapterReference,
- WeakReference<IDataShareReadAdapter> serviceAdapterReference,
+ IDataShareWriteAdapter clientAdapter,
+ IDataShareReadAdapter serviceAdapter,
int errorCode) {
-
- final IDataShareWriteAdapter clientAdapter = clientAdapterReference.get();
- final IDataShareReadAdapter serviceAdapter = serviceAdapterReference.get();
-
- if (clientAdapter == null || serviceAdapter == null) {
- Slog.w(TAG, "Can't propagate error() to read/write data share adapters, because "
- + "remote objects have been GC'ed");
- return;
- }
-
try {
clientAdapter.error(errorCode);
} catch (RemoteException e) {
diff --git a/services/core/Android.bp b/services/core/Android.bp
index 7a26b21d6ac7..dcf761525382 100644
--- a/services/core/Android.bp
+++ b/services/core/Android.bp
@@ -130,10 +130,6 @@ java_library_static {
"netd_event_listener_interface-java",
"overlayable_policy_aidl-java",
],
-
- plugins: [
- "compat-changeid-annotation-processor",
- ],
}
java_genrule {
diff --git a/services/core/java/com/android/server/ConnectivityService.java b/services/core/java/com/android/server/ConnectivityService.java
index 237a961fd338..5d350be5b0c4 100644
--- a/services/core/java/com/android/server/ConnectivityService.java
+++ b/services/core/java/com/android/server/ConnectivityService.java
@@ -63,6 +63,7 @@ import android.content.ContentResolver;
import android.content.Context;
import android.content.Intent;
import android.content.IntentFilter;
+import android.content.pm.PackageManager;
import android.content.res.Configuration;
import android.database.ContentObserver;
import android.net.CaptivePortal;
@@ -5405,12 +5406,25 @@ public class ConnectivityService extends IConnectivityManager.Stub
}
}
+ private boolean checkUnsupportedStartingFrom(int version, String callingPackageName) {
+ final PackageManager pm = mContext.getPackageManager();
+ final int userId = UserHandle.getCallingUserId();
+ try {
+ final int callingVersion = pm.getApplicationInfoAsUser(
+ callingPackageName, 0 /* flags */, userId).targetSdkVersion;
+ if (callingVersion < version) return false;
+ } catch (PackageManager.NameNotFoundException e) { }
+ return true;
+ }
+
@Override
public NetworkRequest requestNetwork(NetworkCapabilities networkCapabilities,
Messenger messenger, int timeoutMs, IBinder binder, int legacyType,
@NonNull String callingPackageName) {
if (legacyType != TYPE_NONE && !checkNetworkStackPermission()) {
- throw new SecurityException("Insufficient permissions to specify legacy type");
+ if (checkUnsupportedStartingFrom(Build.VERSION_CODES.M, callingPackageName)) {
+ throw new SecurityException("Insufficient permissions to specify legacy type");
+ }
}
final int callingUid = Binder.getCallingUid();
final NetworkRequest.Type type = (networkCapabilities == null)
diff --git a/services/core/java/com/android/server/IpSecService.java b/services/core/java/com/android/server/IpSecService.java
index 9540f4336464..599485b11c72 100644
--- a/services/core/java/com/android/server/IpSecService.java
+++ b/services/core/java/com/android/server/IpSecService.java
@@ -359,10 +359,14 @@ public class IpSecService extends IIpSecService.Stub {
@VisibleForTesting
static final class UserRecord {
/* Maximum number of each type of resource that a single UID may possess */
- public static final int MAX_NUM_TUNNEL_INTERFACES = 2;
- public static final int MAX_NUM_ENCAP_SOCKETS = 2;
- public static final int MAX_NUM_TRANSFORMS = 4;
- public static final int MAX_NUM_SPIS = 8;
+
+ // Up to 4 active VPNs/IWLAN with potential soft handover.
+ public static final int MAX_NUM_TUNNEL_INTERFACES = 8;
+ public static final int MAX_NUM_ENCAP_SOCKETS = 16;
+
+ // SPIs and Transforms are both cheap, and are 1:1 correlated.
+ public static final int MAX_NUM_TRANSFORMS = 64;
+ public static final int MAX_NUM_SPIS = 64;
/**
* Store each of the OwnedResource types in an (thinly wrapped) sparse array for indexing
diff --git a/services/core/java/com/android/server/Watchdog.java b/services/core/java/com/android/server/Watchdog.java
index 056156745966..061ff42de60b 100644
--- a/services/core/java/com/android/server/Watchdog.java
+++ b/services/core/java/com/android/server/Watchdog.java
@@ -120,6 +120,7 @@ public class Watchdog extends Thread {
"android.hardware.media.c2@1.0::IComponentStore",
"android.hardware.media.omx@1.0::IOmx",
"android.hardware.media.omx@1.0::IOmxStore",
+ "android.hardware.neuralnetworks@1.0::IDevice",
"android.hardware.power.stats@1.0::IPowerStats",
"android.hardware.sensors@1.0::ISensors",
"android.hardware.vr@1.0::IVr",
diff --git a/services/core/java/com/android/server/locksettings/LockSettingsService.java b/services/core/java/com/android/server/locksettings/LockSettingsService.java
index 01af83914800..3574a6411b07 100644
--- a/services/core/java/com/android/server/locksettings/LockSettingsService.java
+++ b/services/core/java/com/android/server/locksettings/LockSettingsService.java
@@ -1016,9 +1016,14 @@ public class LockSettingsService extends ILockSettings.Stub {
}
private void enforceFrpResolved() {
- if (mInjector.settingsSecureGetInt(mContext.getContentResolver(),
- Settings.Secure.SECURE_FRP_MODE, 0, UserHandle.USER_SYSTEM) == 1) {
- throw new SecurityException("Cannot change credential while FRP is not resolved yet");
+ final ContentResolver cr = mContext.getContentResolver();
+ final boolean inSetupWizard = mInjector.settingsSecureGetInt(cr,
+ Settings.Secure.USER_SETUP_COMPLETE, 0, UserHandle.USER_SYSTEM) == 0;
+ final boolean secureFrp = mInjector.settingsSecureGetInt(cr,
+ Settings.Secure.SECURE_FRP_MODE, 0, UserHandle.USER_SYSTEM) == 1;
+ if (inSetupWizard && secureFrp) {
+ throw new SecurityException("Cannot change credential in SUW while factory reset"
+ + " protection is not resolved yet");
}
}
@@ -2161,6 +2166,13 @@ public class LockSettingsService extends ILockSettings.Stub {
}
}
+ private PasswordMetrics loadPasswordMetrics(AuthenticationToken auth, int userHandle) {
+ synchronized (mSpManager) {
+ return mSpManager.getPasswordMetrics(auth, getSyntheticPasswordHandleLocked(userHandle),
+ userHandle);
+ }
+ }
+
/**
* Call after {@link #setUserPasswordMetrics} so metrics are updated before
* reporting the password changed.
@@ -2611,7 +2623,8 @@ public class LockSettingsService extends ILockSettings.Stub {
return auth;
}
- private long getSyntheticPasswordHandleLocked(int userId) {
+ @VisibleForTesting
+ long getSyntheticPasswordHandleLocked(int userId) {
return getLong(SYNTHETIC_PASSWORD_HANDLE_KEY,
SyntheticPasswordManager.DEFAULT_HANDLE, userId);
}
@@ -2706,13 +2719,8 @@ public class LockSettingsService extends ILockSettings.Stub {
resetLockouts.add(new PendingResetLockout(userId, response.getPayload()));
}
- // TODO: Move setUserPasswordMetrics() inside onCredentialVerified(): this will require
- // LSS to store an encrypted version of the latest password metric for every user,
- // because user credential is not known when onCredentialVerified() is called during
- // a token-based unlock.
- setUserPasswordMetrics(userCredential, userId);
onCredentialVerified(authResult.authToken, challengeType, challenge, resetLockouts,
- userId);
+ PasswordMetrics.computeForCredential(userCredential), userId);
} else if (response.getResponseCode() == VerifyCredentialResponse.RESPONSE_RETRY) {
if (response.getTimeout() > 0) {
requireStrongAuth(STRONG_AUTH_REQUIRED_AFTER_LOCKOUT, userId);
@@ -2724,7 +2732,16 @@ public class LockSettingsService extends ILockSettings.Stub {
private void onCredentialVerified(AuthenticationToken authToken,
@ChallengeType int challengeType, long challenge,
- @Nullable ArrayList<PendingResetLockout> resetLockouts, int userId) {
+ @Nullable ArrayList<PendingResetLockout> resetLockouts, PasswordMetrics metrics,
+ int userId) {
+
+ if (metrics != null) {
+ synchronized (this) {
+ mUserPasswordMetrics.put(userId, metrics);
+ }
+ } else {
+ Slog.wtf(TAG, "Null metrics after credential verification");
+ }
unlockKeystore(authToken.deriveKeyStorePassword(), userId);
@@ -3118,7 +3135,9 @@ public class LockSettingsService extends ILockSettings.Stub {
// TODO: Reset biometrics lockout here. Ideally that should be self-contained inside
// onCredentialVerified(), which will require some refactoring on the current lockout
// reset logic.
- onCredentialVerified(authResult.authToken, CHALLENGE_NONE, 0, null, userId);
+
+ onCredentialVerified(authResult.authToken, CHALLENGE_NONE, 0, null,
+ loadPasswordMetrics(authResult.authToken, userId), userId);
return true;
}
@@ -3414,7 +3433,8 @@ public class LockSettingsService extends ILockSettings.Stub {
SyntheticPasswordManager.AuthenticationToken
authToken = new SyntheticPasswordManager.AuthenticationToken(spVersion);
authToken.recreateDirectly(syntheticPassword);
- onCredentialVerified(authToken, CHALLENGE_NONE, 0, null, userId);
+ onCredentialVerified(authToken, CHALLENGE_NONE, 0, null,
+ loadPasswordMetrics(authToken, userId), userId);
}
}
}
diff --git a/services/core/java/com/android/server/locksettings/SyntheticPasswordManager.java b/services/core/java/com/android/server/locksettings/SyntheticPasswordManager.java
index ac49fa293fe7..fc4634517563 100644
--- a/services/core/java/com/android/server/locksettings/SyntheticPasswordManager.java
+++ b/services/core/java/com/android/server/locksettings/SyntheticPasswordManager.java
@@ -20,6 +20,7 @@ import static com.android.internal.widget.LockPatternUtils.EscrowTokenStateChang
import android.annotation.NonNull;
import android.annotation.Nullable;
+import android.app.admin.PasswordMetrics;
import android.content.Context;
import android.content.pm.UserInfo;
import android.hardware.weaver.V1_0.IWeaver;
@@ -97,6 +98,7 @@ public class SyntheticPasswordManager {
private static final int SECDISCARDABLE_LENGTH = 16 * 1024;
private static final String PASSWORD_DATA_NAME = "pwd";
private static final String WEAVER_SLOT_NAME = "weaver";
+ private static final String PASSWORD_METRICS_NAME = "metrics";
public static final long DEFAULT_HANDLE = 0L;
private static final byte[] DEFAULT_PASSWORD = "default-password".getBytes();
@@ -132,6 +134,7 @@ public class SyntheticPasswordManager {
private static final byte[] PERSONALISATION_WEAVER_PASSWORD = "weaver-pwd".getBytes();
private static final byte[] PERSONALISATION_WEAVER_KEY = "weaver-key".getBytes();
private static final byte[] PERSONALISATION_WEAVER_TOKEN = "weaver-token".getBytes();
+ private static final byte[] PERSONALIZATION_PASSWORD_METRICS = "password-metrics".getBytes();
private static final byte[] PERSONALISATION_CONTEXT =
"android-synthetic-password-personalization-context".getBytes();
@@ -212,6 +215,11 @@ public class SyntheticPasswordManager {
return derivePassword(PERSONALIZATION_PASSWORD_HASH);
}
+ /** Derives key used to encrypt password metrics */
+ public byte[] deriveMetricsKey() {
+ return derivePassword(PERSONALIZATION_PASSWORD_METRICS);
+ }
+
/**
* Assign escrow data to this auth token. This is a prerequisite to call
* {@link AuthenticationToken#recreateFromEscrow}.
@@ -778,7 +786,7 @@ public class SyntheticPasswordManager {
synchronizeFrpPassword(pwd, 0, userId);
}
saveState(PASSWORD_DATA_NAME, pwd.toBytes(), handle, userId);
-
+ savePasswordMetrics(credential, authToken, handle, userId);
createSyntheticPasswordBlob(handle, SYNTHETIC_PASSWORD_PASSWORD_BASED, authToken,
applicationId, sid, userId);
return handle;
@@ -1061,6 +1069,12 @@ public class SyntheticPasswordManager {
// Perform verifyChallenge to refresh auth tokens for GK if user password exists.
result.gkResponse = verifyChallenge(gatekeeper, result.authToken, 0L, userId);
+
+ // Upgrade case: store the metrics if the device did not have stored metrics before, should
+ // only happen once on old synthetic password blobs.
+ if (result.authToken != null && !hasPasswordMetrics(handle, userId)) {
+ savePasswordMetrics(credential, result.authToken, handle, userId);
+ }
return result;
}
@@ -1216,6 +1230,7 @@ public class SyntheticPasswordManager {
destroySyntheticPassword(handle, userId);
destroyState(SECDISCARDABLE_NAME, handle, userId);
destroyState(PASSWORD_DATA_NAME, handle, userId);
+ destroyState(PASSWORD_METRICS_NAME, handle, userId);
}
private void destroySyntheticPassword(long handle, int userId) {
@@ -1258,6 +1273,34 @@ public class SyntheticPasswordManager {
return loadState(SECDISCARDABLE_NAME, handle, userId);
}
+ /**
+ * Retrieves the saved password metrics associated with a SP handle. Only meaningful to be
+ * called on the handle of a password-based synthetic password. A valid AuthenticationToken for
+ * the target user is required in order to be able to decrypt the encrypted password metrics on
+ * disk.
+ */
+ public @Nullable PasswordMetrics getPasswordMetrics(AuthenticationToken authToken, long handle,
+ int userId) {
+ final byte[] encrypted = loadState(PASSWORD_METRICS_NAME, handle, userId);
+ if (encrypted == null) return null;
+ final byte[] decrypted = SyntheticPasswordCrypto.decrypt(authToken.deriveMetricsKey(),
+ /* personalization= */ new byte[0], encrypted);
+ if (decrypted == null) return null;
+ return VersionedPasswordMetrics.deserialize(decrypted).getMetrics();
+ }
+
+ private void savePasswordMetrics(LockscreenCredential credential, AuthenticationToken authToken,
+ long handle, int userId) {
+ final byte[] encrypted = SyntheticPasswordCrypto.encrypt(authToken.deriveMetricsKey(),
+ /* personalization= */ new byte[0],
+ new VersionedPasswordMetrics(credential).serialize());
+ saveState(PASSWORD_METRICS_NAME, encrypted, handle, userId);
+ }
+
+ private boolean hasPasswordMetrics(long handle, int userId) {
+ return hasState(PASSWORD_METRICS_NAME, handle, userId);
+ }
+
private boolean hasState(String stateName, long handle, int userId) {
return !ArrayUtils.isEmpty(loadState(stateName, handle, userId));
}
diff --git a/services/core/java/com/android/server/locksettings/VersionedPasswordMetrics.java b/services/core/java/com/android/server/locksettings/VersionedPasswordMetrics.java
new file mode 100644
index 000000000000..b06e3814ac7c
--- /dev/null
+++ b/services/core/java/com/android/server/locksettings/VersionedPasswordMetrics.java
@@ -0,0 +1,79 @@
+/*
+ * 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.server.locksettings;
+
+import android.app.admin.PasswordMetrics;
+
+import com.android.internal.widget.LockscreenCredential;
+
+import java.nio.ByteBuffer;
+
+/**
+ * A versioned and serializable wrapper around {@link PasswordMetrics},
+ * for long-term persistence on disk.
+ */
+public class VersionedPasswordMetrics {
+ private static final int VERSION_1 = 1;
+
+ private final PasswordMetrics mMetrics;
+ private final int mVersion;
+
+ private VersionedPasswordMetrics(int version, PasswordMetrics metrics) {
+ mMetrics = metrics;
+ mVersion = version;
+ }
+
+ public VersionedPasswordMetrics(LockscreenCredential credential) {
+ this(VERSION_1, PasswordMetrics.computeForCredential(credential));
+ }
+
+ public int getVersion() {
+ return mVersion;
+ }
+
+ public PasswordMetrics getMetrics() {
+ return mMetrics;
+ }
+
+ /** Serialize object to a byte array. */
+ public byte[] serialize() {
+ final ByteBuffer buffer = ByteBuffer.allocate(Integer.BYTES * 11);
+ buffer.putInt(mVersion);
+ buffer.putInt(mMetrics.credType);
+ buffer.putInt(mMetrics.length);
+ buffer.putInt(mMetrics.letters);
+ buffer.putInt(mMetrics.upperCase);
+ buffer.putInt(mMetrics.lowerCase);
+ buffer.putInt(mMetrics.numeric);
+ buffer.putInt(mMetrics.symbols);
+ buffer.putInt(mMetrics.nonLetter);
+ buffer.putInt(mMetrics.nonNumeric);
+ buffer.putInt(mMetrics.seqLength);
+ return buffer.array();
+ }
+
+ /** Deserialize byte array to an object */
+ public static VersionedPasswordMetrics deserialize(byte[] data) {
+ final ByteBuffer buffer = ByteBuffer.allocate(data.length);
+ buffer.put(data, 0, data.length);
+ buffer.flip();
+ final int version = buffer.getInt();
+ PasswordMetrics metrics = new PasswordMetrics(buffer.getInt(), buffer.getInt(),
+ buffer.getInt(), buffer.getInt(), buffer.getInt(), buffer.getInt(), buffer.getInt(),
+ buffer.getInt(), buffer.getInt(), buffer.getInt());
+ return new VersionedPasswordMetrics(version, metrics);
+ }
+}
diff --git a/services/core/java/com/android/server/media/SystemMediaRoute2Provider.java b/services/core/java/com/android/server/media/SystemMediaRoute2Provider.java
index c69787d602e2..5b16d686e04c 100644
--- a/services/core/java/com/android/server/media/SystemMediaRoute2Provider.java
+++ b/services/core/java/com/android/server/media/SystemMediaRoute2Provider.java
@@ -103,6 +103,7 @@ class SystemMediaRoute2Provider extends MediaRoute2Provider {
}
updateDeviceRoute(newAudioRoutes);
+ // .getInstance returns null if there is no bt adapter available
mBtRouteProvider = BluetoothRouteProvider.getInstance(context, (routes) -> {
publishProviderState();
@@ -116,10 +117,12 @@ class SystemMediaRoute2Provider extends MediaRoute2Provider {
mContext.registerReceiver(new VolumeChangeReceiver(),
new IntentFilter(AudioManager.VOLUME_CHANGED_ACTION));
- mHandler.post(() -> {
- mBtRouteProvider.start();
- notifyProviderState();
- });
+ if (mBtRouteProvider != null) {
+ mHandler.post(() -> {
+ mBtRouteProvider.start();
+ notifyProviderState();
+ });
+ }
}
@Override
@@ -153,10 +156,12 @@ class SystemMediaRoute2Provider extends MediaRoute2Provider {
@Override
public void transferToRoute(long requestId, String sessionId, String routeId) {
- if (TextUtils.equals(routeId, mDeviceRoute.getId())) {
- mBtRouteProvider.transferTo(null);
- } else {
- mBtRouteProvider.transferTo(routeId);
+ if (mBtRouteProvider != null) {
+ if (TextUtils.equals(routeId, mDeviceRoute.getId())) {
+ mBtRouteProvider.transferTo(null);
+ } else {
+ mBtRouteProvider.transferTo(routeId);
+ }
}
}
@@ -294,7 +299,7 @@ class SystemMediaRoute2Provider extends MediaRoute2Provider {
mDeviceRoute = new MediaRoute2Info.Builder(mDeviceRoute)
.setVolume(newVolume)
.build();
- } else {
+ } else if (mBtRouteProvider != null) {
mBtRouteProvider.setSelectedRouteVolume(newVolume);
}
publishProviderState();
diff --git a/services/core/java/com/android/server/pm/ApexManager.java b/services/core/java/com/android/server/pm/ApexManager.java
index c97f33b5103f..142e813f66b9 100644
--- a/services/core/java/com/android/server/pm/ApexManager.java
+++ b/services/core/java/com/android/server/pm/ApexManager.java
@@ -61,11 +61,9 @@ import java.util.Collections;
import java.util.HashSet;
import java.util.Iterator;
import java.util.List;
-import java.util.Map;
import java.util.Objects;
import java.util.Set;
import java.util.concurrent.ExecutorService;
-import java.util.stream.Collectors;
/**
* ApexManager class handles communications with the apex service to perform operation and queries,
@@ -538,30 +536,42 @@ public abstract class ApexManager {
List<PackageInfo> getActivePackages() {
Preconditions.checkState(mAllPackagesCache != null,
"APEX packages have not been scanned");
- return mAllPackagesCache
- .stream()
- .filter(item -> isActive(item))
- .collect(Collectors.toList());
+ final List<PackageInfo> activePackages = new ArrayList<>();
+ for (int i = 0; i < mAllPackagesCache.size(); i++) {
+ final PackageInfo packageInfo = mAllPackagesCache.get(i);
+ if (isActive(packageInfo)) {
+ activePackages.add(packageInfo);
+ }
+ }
+ return activePackages;
}
@Override
List<PackageInfo> getFactoryPackages() {
Preconditions.checkState(mAllPackagesCache != null,
"APEX packages have not been scanned");
- return mAllPackagesCache
- .stream()
- .filter(item -> isFactory(item))
- .collect(Collectors.toList());
+ final List<PackageInfo> factoryPackages = new ArrayList<>();
+ for (int i = 0; i < mAllPackagesCache.size(); i++) {
+ final PackageInfo packageInfo = mAllPackagesCache.get(i);
+ if (isFactory(packageInfo)) {
+ factoryPackages.add(packageInfo);
+ }
+ }
+ return factoryPackages;
}
@Override
List<PackageInfo> getInactivePackages() {
Preconditions.checkState(mAllPackagesCache != null,
"APEX packages have not been scanned");
- return mAllPackagesCache
- .stream()
- .filter(item -> !isActive(item))
- .collect(Collectors.toList());
+ final List<PackageInfo> inactivePackages = new ArrayList<>();
+ for (int i = 0; i < mAllPackagesCache.size(); i++) {
+ final PackageInfo packageInfo = mAllPackagesCache.get(i);
+ if (!isActive(packageInfo)) {
+ inactivePackages.add(packageInfo);
+ }
+ }
+ return inactivePackages;
}
@Override
diff --git a/services/core/java/com/android/server/pm/PackageManagerService.java b/services/core/java/com/android/server/pm/PackageManagerService.java
index 34363c8df8f6..77a60242a6a6 100644
--- a/services/core/java/com/android/server/pm/PackageManagerService.java
+++ b/services/core/java/com/android/server/pm/PackageManagerService.java
@@ -415,7 +415,6 @@ import java.util.concurrent.atomic.AtomicInteger;
import java.util.function.BiConsumer;
import java.util.function.Consumer;
import java.util.function.Predicate;
-import java.util.stream.Collectors;
/**
* Keep track of all those APKs everywhere.
@@ -2655,6 +2654,11 @@ public class PackageManagerService extends IPackageManager.Stub
+ partition.folder);
}
}
+
+ @Override
+ public String toString() {
+ return folder.getAbsolutePath() + ":" + scanFlag;
+ }
}
public PackageManagerService(Injector injector, boolean onlyCore, boolean factoryTest) {
@@ -2757,15 +2761,19 @@ public class PackageManagerService extends IPackageManager.Stub
mApexManager = ApexManager.getInstance();
mAppsFilter = mInjector.getAppsFilter();
+ final List<ScanPartition> scanPartitions = new ArrayList<>();
+ final List<ApexManager.ActiveApexInfo> activeApexInfos = mApexManager.getActiveApexInfos();
+ for (int i = 0; i < activeApexInfos.size(); i++) {
+ final ScanPartition scanPartition = resolveApexToScanPartition(activeApexInfos.get(i));
+ if (scanPartition != null) {
+ scanPartitions.add(scanPartition);
+ }
+ }
+
mDirsToScanAsSystem = new ArrayList<>();
mDirsToScanAsSystem.addAll(SYSTEM_PARTITIONS);
- mDirsToScanAsSystem.addAll(mApexManager.getActiveApexInfos().stream()
- .map(PackageManagerService::resolveApexToScanPartition)
- .filter(Objects::nonNull).collect(Collectors.toList()));
- Slog.d(TAG,
- "Directories scanned as system partitions: [" + mDirsToScanAsSystem.stream().map(
- d -> (d.folder.getAbsolutePath() + ":" + d.scanFlag))
- .collect(Collectors.joining(",")) + "]");
+ mDirsToScanAsSystem.addAll(scanPartitions);
+ Slog.d(TAG, "Directories scanned as system partitions: " + mDirsToScanAsSystem);
// CHECKSTYLE:OFF IndentationCheck
synchronized (mInstallLock) {
diff --git a/services/core/java/com/android/server/pm/PackageManagerShellCommand.java b/services/core/java/com/android/server/pm/PackageManagerShellCommand.java
index 4d49a643485f..b4eacf6226ce 100644
--- a/services/core/java/com/android/server/pm/PackageManagerShellCommand.java
+++ b/services/core/java/com/android/server/pm/PackageManagerShellCommand.java
@@ -1278,42 +1278,7 @@ class PackageManagerShellCommand extends ShellCommand {
pw.println("Success");
return 0;
}
-
- long timeoutMs = params.timeoutMs <= 0
- ? DEFAULT_WAIT_MS
- : params.timeoutMs;
- PackageInstaller.SessionInfo si = mInterface.getPackageInstaller()
- .getSessionInfo(sessionId);
- long currentTime = System.currentTimeMillis();
- long endTime = currentTime + timeoutMs;
- // Using a loop instead of BroadcastReceiver since we can receive session update
- // broadcast only if packageInstallerName is "android". We can't always force
- // "android" as packageIntallerName, e.g, rollback auto implies
- // "-i com.android.shell".
- while (currentTime < endTime) {
- if (si != null
- && (si.isStagedSessionReady() || si.isStagedSessionFailed())) {
- break;
- }
- SystemClock.sleep(Math.min(endTime - currentTime, 100));
- currentTime = System.currentTimeMillis();
- si = mInterface.getPackageInstaller().getSessionInfo(sessionId);
- }
- if (si == null) {
- pw.println("Failure [failed to retrieve SessionInfo]");
- return 1;
- }
- if (!si.isStagedSessionReady() && !si.isStagedSessionFailed()) {
- pw.println("Failure [timed out after " + timeoutMs + " ms]");
- return 1;
- }
- if (!si.isStagedSessionReady()) {
- pw.println("Error [" + si.getStagedSessionErrorCode() + "] ["
- + si.getStagedSessionErrorMessage() + "]");
- return 1;
- }
- pw.println("Success. Reboot device to apply staged session");
- return 0;
+ return doWaitForStagedSessionRead(sessionId, params.timeoutMs, pw);
} finally {
if (abandonSession) {
try {
@@ -1324,14 +1289,92 @@ class PackageManagerShellCommand extends ShellCommand {
}
}
+ private int doWaitForStagedSessionRead(int sessionId, long timeoutMs, PrintWriter pw)
+ throws RemoteException {
+ if (timeoutMs <= 0) {
+ timeoutMs = DEFAULT_WAIT_MS;
+ }
+ PackageInstaller.SessionInfo si = mInterface.getPackageInstaller()
+ .getSessionInfo(sessionId);
+ if (si == null) {
+ pw.println("Failure [Unknown session " + sessionId + "]");
+ return 1;
+ }
+ if (!si.isStaged()) {
+ pw.println("Failure [Session " + sessionId + " is not a staged session]");
+ return 1;
+ }
+ long currentTime = System.currentTimeMillis();
+ long endTime = currentTime + timeoutMs;
+ // Using a loop instead of BroadcastReceiver since we can receive session update
+ // broadcast only if packageInstallerName is "android". We can't always force
+ // "android" as packageIntallerName, e.g, rollback auto implies
+ // "-i com.android.shell".
+ while (currentTime < endTime) {
+ if (si != null && (si.isStagedSessionReady() || si.isStagedSessionFailed())) {
+ break;
+ }
+ SystemClock.sleep(Math.min(endTime - currentTime, 100));
+ currentTime = System.currentTimeMillis();
+ si = mInterface.getPackageInstaller().getSessionInfo(sessionId);
+ }
+ if (si == null) {
+ pw.println("Failure [failed to retrieve SessionInfo]");
+ return 1;
+ }
+ if (!si.isStagedSessionReady() && !si.isStagedSessionFailed()) {
+ pw.println("Failure [timed out after " + timeoutMs + " ms]");
+ return 1;
+ }
+ if (!si.isStagedSessionReady()) {
+ pw.println("Error [" + si.getStagedSessionErrorCode() + "] ["
+ + si.getStagedSessionErrorMessage() + "]");
+ return 1;
+ }
+ pw.println("Success. Reboot device to apply staged session");
+ return 0;
+ }
+
private int runInstallAbandon() throws RemoteException {
final int sessionId = Integer.parseInt(getNextArg());
return doAbandonSession(sessionId, true /*logSuccess*/);
}
private int runInstallCommit() throws RemoteException {
+ final PrintWriter pw = getOutPrintWriter();
+ String opt;
+ boolean waitForStagedSessionReady = true;
+ long timeoutMs = -1;
+ while ((opt = getNextOption()) != null) {
+ switch (opt) {
+ case "--wait":
+ waitForStagedSessionReady = true;
+ // If there is only one remaining argument, then it represents the sessionId, we
+ // shouldn't try to parse it as timeoutMs.
+ if (getRemainingArgsCount() > 1) {
+ try {
+ timeoutMs = Long.parseLong(peekNextArg());
+ getNextArg();
+ } catch (NumberFormatException ignore) {
+ }
+ }
+ break;
+ case "--no-wait":
+ waitForStagedSessionReady = false;
+ break;
+ }
+ }
final int sessionId = Integer.parseInt(getNextArg());
- return doCommitSession(sessionId, true /*logSuccess*/);
+ if (doCommitSession(sessionId, false /*logSuccess*/) != PackageInstaller.STATUS_SUCCESS) {
+ return 1;
+ }
+ final PackageInstaller.SessionInfo si = mInterface.getPackageInstaller()
+ .getSessionInfo(sessionId);
+ if (si == null || !si.isStaged() || !waitForStagedSessionReady) {
+ pw.println("Success");
+ return 0;
+ }
+ return doWaitForStagedSessionRead(sessionId, timeoutMs, pw);
}
private int runInstallCreate() throws RemoteException {
diff --git a/services/core/java/com/android/server/pm/StagingManager.java b/services/core/java/com/android/server/pm/StagingManager.java
index 342c90719916..60292b45c0ee 100644
--- a/services/core/java/com/android/server/pm/StagingManager.java
+++ b/services/core/java/com/android/server/pm/StagingManager.java
@@ -75,13 +75,11 @@ import com.android.server.rollback.WatchdogRollbackLogger;
import java.io.File;
import java.io.IOException;
import java.util.ArrayList;
-import java.util.Arrays;
import java.util.List;
import java.util.concurrent.LinkedBlockingQueue;
import java.util.concurrent.TimeUnit;
import java.util.function.Consumer;
import java.util.function.Predicate;
-import java.util.stream.Collectors;
/**
* This class handles staged install sessions, i.e. install sessions that require packages to
@@ -222,6 +220,7 @@ public class StagingManager {
// which will be propagated to populate stagedSessionErrorMessage of this session.
final ApexInfoList apexInfoList = mApexManager.submitStagedSession(apexSessionParams);
final List<PackageInfo> result = new ArrayList<>();
+ final List<String> apexPackageNames = new ArrayList<>();
for (ApexInfo apexInfo : apexInfoList.apexInfos) {
final PackageInfo packageInfo;
int flags = PackageManager.GET_META_DATA;
@@ -245,9 +244,10 @@ public class StagingManager {
checkRequiredVersionCode(session, activePackage);
checkDowngrade(session, activePackage, packageInfo);
result.add(packageInfo);
+ apexPackageNames.add(packageInfo.packageName);
}
- Slog.d(TAG, "Session " + session.sessionId + " has following APEX packages: ["
- + result.stream().map(p -> p.packageName).collect(Collectors.joining(",")) + "]");
+ Slog.d(TAG, "Session " + session.sessionId + " has following APEX packages: "
+ + apexPackageNames);
return result;
}
@@ -313,13 +313,16 @@ public class StagingManager {
return filter.test(session);
}
synchronized (mStagedSessions) {
- return !(Arrays.stream(session.getChildSessionIds())
- // Retrieve cached sessions matching ids.
- .mapToObj(i -> mStagedSessions.get(i))
- // Filter only the ones containing APEX.
- .filter(childSession -> filter.test(childSession))
- .collect(Collectors.toList())
- .isEmpty());
+ final int[] childSessionIds = session.getChildSessionIds();
+ for (int id : childSessionIds) {
+ // Retrieve cached sessions matching ids.
+ final PackageInstallerSession s = mStagedSessions.get(id);
+ // Filter only the ones containing APEX.
+ if (filter.test(s)) {
+ return true;
+ }
+ }
+ return false;
}
}
@@ -669,15 +672,15 @@ public class StagingManager {
// contain an APK, and with those then create a new multi-package group of sessions,
// carrying over all the session parameters and unmarking them as staged. On commit the
// sessions will be installed atomically.
- final List<PackageInstallerSession> childSessions;
+ final List<PackageInstallerSession> childSessions = new ArrayList<>();
synchronized (mStagedSessions) {
- childSessions =
- Arrays.stream(session.getChildSessionIds())
- // Retrieve cached sessions matching ids.
- .mapToObj(i -> mStagedSessions.get(i))
- // Filter only the ones containing APKs.
- .filter(childSession -> !isApexSession(childSession))
- .collect(Collectors.toList());
+ final int[] childSessionIds = session.getChildSessionIds();
+ for (int id : childSessionIds) {
+ final PackageInstallerSession s = mStagedSessions.get(id);
+ if (!isApexSession(s)) {
+ childSessions.add(s);
+ }
+ }
}
if (childSessions.isEmpty()) {
// APEX-only multi-package staged session, nothing to do.
diff --git a/services/core/java/com/android/server/wm/ActivityRecord.java b/services/core/java/com/android/server/wm/ActivityRecord.java
index 29f7d523e9a0..78d6e27d067e 100644
--- a/services/core/java/com/android/server/wm/ActivityRecord.java
+++ b/services/core/java/com/android/server/wm/ActivityRecord.java
@@ -2504,7 +2504,7 @@ final class ActivityRecord extends WindowToken implements WindowManagerService.A
final DisplayContent display = stack.getDisplay();
next = display.topRunningActivity();
if (next != null) {
- display.positionStackAtTop(next.getRootTask(),
+ display.mTaskContainers.positionStackAtTop(next.getRootTask(),
false /* includingParents */, "finish-display-top");
}
}
@@ -2679,7 +2679,7 @@ final class ActivityRecord extends WindowToken implements WindowManagerService.A
final ActivityRecord next = display.topRunningActivity();
final boolean isLastStackOverEmptyHome =
next == null && stack.isFocusedStackOnDisplay()
- && display.getOrCreateRootHomeTask() != null;
+ && display.mTaskContainers.getOrCreateRootHomeTask() != null;
if (isLastStackOverEmptyHome) {
// Don't destroy activity immediately if this is the last activity on the display and
// the display contains home stack. Although there is no next activity at the moment,
@@ -4477,7 +4477,7 @@ final class ActivityRecord extends WindowToken implements WindowManagerService.A
// case where this is the top activity in a pinned stack.
final boolean isTop = this == stack.getTopNonFinishingActivity();
final boolean isTopNotPinnedStack = stack.isAttached()
- && stack.getDisplay().isTopNotPinnedStack(stack);
+ && stack.getDisplay().mTaskContainers.isTopNotPinnedStack(stack);
final boolean visibleIgnoringDisplayStatus = stack.checkKeyguardVisibility(this,
visibleIgnoringKeyguard, isTop && isTopNotPinnedStack);
@@ -7389,7 +7389,7 @@ final class ActivityRecord extends WindowToken implements WindowManagerService.A
*/
boolean isResumedActivityOnDisplay() {
final DisplayContent display = getDisplay();
- return display != null && this == display.getResumedActivity();
+ return display != null && this == display.mTaskContainers.getResumedActivity();
}
diff --git a/services/core/java/com/android/server/wm/ActivityStack.java b/services/core/java/com/android/server/wm/ActivityStack.java
index 90898597c265..64c14cf459cc 100644
--- a/services/core/java/com/android/server/wm/ActivityStack.java
+++ b/services/core/java/com/android/server/wm/ActivityStack.java
@@ -670,7 +670,7 @@ class ActivityStack extends Task {
// Since always on top is only on when the stack is freeform or pinned, the state
// can be toggled when the windowing mode changes. We must make sure the stack is
// placed properly when always on top state changes.
- display.positionStackAtTop(this, false /* includingParents */);
+ display.mTaskContainers.positionStackAtTop(this, false /* includingParents */);
}
}
@@ -742,7 +742,7 @@ class ActivityStack extends Task {
// Need to make sure windowing mode is supported. If we in the process of creating the stack
// no need to resolve the windowing mode again as it is already resolved to the right mode.
if (!creating) {
- windowingMode = display.validateWindowingMode(windowingMode,
+ windowingMode = display.mTaskContainers.validateWindowingMode(windowingMode,
null /* ActivityRecord */, topTask, getActivityType());
}
if (display.getRootSplitScreenPrimaryTask() == this
@@ -752,7 +752,8 @@ class ActivityStack extends Task {
windowingMode = mRestoreOverrideWindowingMode;
}
- final boolean alreadyInSplitScreenMode = display.isSplitScreenModeActivated();
+ final boolean alreadyInSplitScreenMode = display.mTaskContainers
+ .isSplitScreenModeActivated();
// Don't send non-resizeable notifications if the windowing mode changed was a side effect
// of us entering split-screen mode.
@@ -769,7 +770,7 @@ class ActivityStack extends Task {
// warning toast about it.
mAtmService.getTaskChangeNotificationController()
.notifyActivityDismissingDockedStack();
- display.onSplitScreenModeDismissed();
+ display.mTaskContainers.onSplitScreenModeDismissed();
}
}
@@ -861,7 +862,7 @@ class ActivityStack extends Task {
// TODO (b/78247419): Fix the rotation animation from fullscreen to minimized mode
final boolean isRecentsComponentHome =
mAtmService.getRecentTasks().isRecentsComponentHomeActivity(mCurrentUser);
- final ActivityStack recentStack = display.getOrCreateStack(
+ final ActivityStack recentStack = display.mTaskContainers.getOrCreateStack(
WINDOWING_MODE_SPLIT_SCREEN_SECONDARY,
isRecentsComponentHome ? ACTIVITY_TYPE_HOME : ACTIVITY_TYPE_RECENTS,
true /* onTop */);
@@ -1058,7 +1059,7 @@ class ActivityStack extends Task {
// cutting between them.
// TODO(b/70677280): This is a workaround until we can fix as part of b/70677280.
final ActivityStack topFullScreenStack =
- display.getTopStackInWindowingMode(WINDOWING_MODE_FULLSCREEN);
+ display.mTaskContainers.getTopStackInWindowingMode(WINDOWING_MODE_FULLSCREEN);
if (topFullScreenStack != null) {
final ActivityStack primarySplitScreenStack = display.getRootSplitScreenPrimaryTask();
if (primarySplitScreenStack != null && display.getIndexOf(topFullScreenStack)
@@ -1071,11 +1072,11 @@ class ActivityStack extends Task {
if (!isActivityTypeHome() && returnsToHomeStack()) {
// Make sure the home stack is behind this stack since that is where we should return to
// when this stack is no longer visible.
- display.moveHomeStackToFront(reason + " returnToHome");
+ display.mTaskContainers.moveHomeStackToFront(reason + " returnToHome");
}
if (isRootTask()) {
- display.positionStackAtTop(this, false /* includingParents */, reason);
+ display.mTaskContainers.positionStackAtTop(this, false /* includingParents */, reason);
}
if (task == null) {
task = this;
@@ -1092,7 +1093,7 @@ class ActivityStack extends Task {
return;
}
- getDisplay().positionStackAtBottom(this, reason);
+ getDisplay().mTaskContainers.positionStackAtBottom(this, reason);
if (task != null && task != this) {
positionChildAtBottom(task);
}
@@ -1460,7 +1461,7 @@ class ActivityStack extends Task {
boolean isTopStackOnDisplay() {
final DisplayContent display = getDisplay();
- return display != null && display.isTopStack(this);
+ return display != null && display.mTaskContainers.isTopStack(this);
}
/**
@@ -1667,7 +1668,8 @@ class ActivityStack extends Task {
*/
boolean isTopSplitScreenStack() {
return inSplitScreenWindowingMode()
- && this == getDisplay().getTopStackInWindowingMode(getWindowingMode());
+ && this == getDisplay().mTaskContainers
+ .getTopStackInWindowingMode(getWindowingMode());
}
/** @return True if the resizing of the primary-split-screen stack affects this stack size. */
@@ -1900,7 +1902,7 @@ class ActivityStack extends Task {
// If the top activity is the resumed one, nothing to do.
if (mResumedActivity == next && next.isState(RESUMED)
- && display.allResumedActivitiesComplete()) {
+ && display.mTaskContainers.allResumedActivitiesComplete()) {
// Make sure we have executed any pending transitions, since there
// should be nothing left to do at this point.
executeAppTransition(options);
@@ -1979,7 +1981,7 @@ class ActivityStack extends Task {
mStackSupervisor.setLaunchSource(next.info.applicationInfo.uid);
ActivityRecord lastResumed = null;
- final ActivityStack lastFocusedStack = display.getLastFocusedStack();
+ final ActivityStack lastFocusedStack = display.mTaskContainers.getLastFocusedStack();
if (lastFocusedStack != null && lastFocusedStack != this) {
// So, why aren't we using prev here??? See the param comment on the method. prev doesn't
// represent the last resumed activity. However, the last focus stack does if it isn't null.
@@ -1993,7 +1995,7 @@ class ActivityStack extends Task {
}
}
- boolean pausing = display.pauseBackStacks(userLeaving, next);
+ boolean pausing = display.mTaskContainers.pauseBackStacks(userLeaving, next);
if (mResumedActivity != null) {
if (DEBUG_STATES) Slog.d(TAG_STATES,
"resumeTopActivityLocked: Pausing " + mResumedActivity);
@@ -2022,7 +2024,7 @@ class ActivityStack extends Task {
}
return true;
} else if (mResumedActivity == next && next.isState(RESUMED)
- && display.allResumedActivitiesComplete()) {
+ && display.mTaskContainers.allResumedActivitiesComplete()) {
// It is possible for the activity to be resumed when we paused back stacks above if the
// next activity doesn't have to wait for pause to complete.
// So, nothing else to-do except:
@@ -2542,7 +2544,7 @@ class ActivityStack extends Task {
if (stack.isActivityTypeHome() && (top == null || !top.mVisibleRequested)) {
// If we will be focusing on the home stack next and its current top activity isn't
// visible, then use the move the home stack task to top to make the activity visible.
- stack.getDisplay().moveHomeActivityToTop(reason);
+ stack.getDisplay().mTaskContainers.moveHomeActivityToTop(reason);
return stack;
}
@@ -3304,7 +3306,7 @@ class ActivityStack extends Task {
final boolean wasResumed = topRunningActivity == task.getStack().mResumedActivity;
boolean toTop = position >= getChildCount();
- boolean includingParents = toTop || getDisplay().getNextFocusableStack(this,
+ boolean includingParents = toTop || getDisplay().mTaskContainers.getNextFocusableStack(this,
true /* ignoreCurrent */) == null;
if (WindowManagerDebugConfig.DEBUG_STACK) {
Slog.i(TAG_WM, "positionChildAt: positioning task=" + task + " at " + position);
@@ -3348,7 +3350,7 @@ class ActivityStack extends Task {
// always on top windows. Since the position the stack should be inserted into is calculated
// properly in {@link DisplayContent#getTopInsertPosition()} in both cases, we can just
// request that the stack is put at top here.
- display.positionStackAtTop(this, false /* includingParents */);
+ display.mTaskContainers.positionStackAtTop(this, false /* includingParents */);
}
/** NOTE: Should only be called from {@link Task#reparent}. */
@@ -3399,7 +3401,7 @@ class ActivityStack extends Task {
final Task task = getBottomMostTask();
setWindowingMode(WINDOWING_MODE_UNDEFINED);
- getDisplay().positionStackAtTop(this, false /* includingParents */);
+ getDisplay().mTaskContainers.positionStackAtTop(this, false /* includingParents */);
mStackSupervisor.scheduleUpdatePictureInPictureModeIfNeeded(task, this);
MetricsLoggerWrapper.logPictureInPictureFullScreen(mAtmService.mContext,
@@ -3519,7 +3521,7 @@ class ActivityStack extends Task {
// If there are other focusable stacks on the display, the z-order of the display should not
// be changed just because a task was placed at the bottom. E.g. if it is moving the topmost
// task to bottom, the next focusable stack on the same display should be focused.
- final ActivityStack nextFocusableStack = getDisplay().getNextFocusableStack(
+ final ActivityStack nextFocusableStack = getDisplay().mTaskContainers.getNextFocusableStack(
child.getStack(), true /* ignoreCurrent */);
positionChildAtBottom(child, nextFocusableStack == null /* includingParents */);
child.updateTaskMovement(true);
diff --git a/services/core/java/com/android/server/wm/ActivityStackSupervisor.java b/services/core/java/com/android/server/wm/ActivityStackSupervisor.java
index 4652f49f116c..3f8fd73c3534 100644
--- a/services/core/java/com/android/server/wm/ActivityStackSupervisor.java
+++ b/services/core/java/com/android/server/wm/ActivityStackSupervisor.java
@@ -414,14 +414,15 @@ public class ActivityStackSupervisor implements RecentTasks.Callbacks {
if (mToDisplay.getDisplayId() != stack.getDisplayId()) {
mToDisplay.moveStackToDisplay(stack, mOnTop);
} else if (mOnTop) {
- mToDisplay.positionStackAtTop(stack, false /* includingParents */);
+ mToDisplay.mTaskContainers.positionStackAtTop(stack,
+ false /* includingParents */);
} else {
- mToDisplay.positionStackAtBottom(stack);
+ mToDisplay.mTaskContainers.positionStackAtBottom(stack);
}
return;
}
- final ActivityStack toStack = mToDisplay.getOrCreateStack(
+ final ActivityStack toStack = mToDisplay.mTaskContainers.getOrCreateStack(
null, mTmpOptions, task, task.getActivityType(), mOnTop);
if (task == toStack) {
// The task was reused as the root task.
@@ -1458,7 +1459,7 @@ public class ActivityStackSupervisor implements RecentTasks.Callbacks {
|| (focusedStack != null && focusedStack.isActivityTypeRecents())) {
// We move home stack to front when we are on a fullscreen display and caller has
// requested the home activity to move with it. Or the previous stack is recents.
- display.moveHomeStackToFront(reason);
+ display.mTaskContainers.moveHomeStackToFront(reason);
}
}
@@ -1877,7 +1878,7 @@ public class ActivityStackSupervisor implements RecentTasks.Callbacks {
mStoppingActivities.remove(r);
final ActivityStack stack = r.getRootTask();
- if (stack.getDisplay().allResumedActivitiesComplete()) {
+ if (stack.getDisplay().mTaskContainers.allResumedActivitiesComplete()) {
mRootWindowContainer.ensureActivitiesVisible(null, 0, !PRESERVE_WINDOWS);
// Make sure activity & window visibility should be identical
// for all displays in this stage.
@@ -2241,7 +2242,7 @@ public class ActivityStackSupervisor implements RecentTasks.Callbacks {
final boolean isSecondaryDisplayPreferred =
(preferredDisplayId != DEFAULT_DISPLAY && preferredDisplayId != INVALID_DISPLAY);
final boolean inSplitScreenMode = actualStack != null
- && actualStack.getDisplay().isSplitScreenModeActivated();
+ && actualStack.getDisplay().mTaskContainers.isSplitScreenModeActivated();
if (((!inSplitScreenMode && preferredWindowingMode != WINDOWING_MODE_SPLIT_SCREEN_PRIMARY)
&& !isSecondaryDisplayPreferred) || !task.isActivityTypeStandardOrUndefined()) {
return;
@@ -2289,12 +2290,12 @@ public class ActivityStackSupervisor implements RecentTasks.Callbacks {
// Dismiss docked stack. If task appeared to be in docked stack but is not resizable -
// we need to move it to top of fullscreen stack, otherwise it will be covered.
final DisplayContent display = task.getStack().getDisplay();
- if (display.isSplitScreenModeActivated()) {
+ if (display.mTaskContainers.isSplitScreenModeActivated()) {
// Display a warning toast that we tried to put an app that doesn't support
// split-screen in split-screen.
mService.getTaskChangeNotificationController()
.notifyActivityDismissingDockedStack();
- display.onSplitScreenModeDismissed();
+ display.mTaskContainers.onSplitScreenModeDismissed();
display.ensureActivitiesVisible(null, 0, PRESERVE_WINDOWS,
true /* notifyClients */);
}
@@ -2612,7 +2613,7 @@ public class ActivityStackSupervisor implements RecentTasks.Callbacks {
// from whatever is started from the recents activity, so move the home stack
// forward.
// TODO (b/115289124): Multi-display supports for recents.
- mRootWindowContainer.getDefaultDisplay().moveHomeStackToFront(
+ mRootWindowContainer.getDefaultDisplay().mTaskContainers.moveHomeStackToFront(
"startActivityFromRecents");
}
diff --git a/services/core/java/com/android/server/wm/ActivityStartController.java b/services/core/java/com/android/server/wm/ActivityStartController.java
index 881dc81807f5..0a0049d1550d 100644
--- a/services/core/java/com/android/server/wm/ActivityStartController.java
+++ b/services/core/java/com/android/server/wm/ActivityStartController.java
@@ -190,8 +190,8 @@ public class ActivityStartController {
final ActivityStack homeStack;
try {
// Make sure home stack exist on display.
- homeStack = display.getOrCreateStack(WINDOWING_MODE_FULLSCREEN, ACTIVITY_TYPE_HOME,
- ON_TOP);
+ homeStack = display.mTaskContainers.getOrCreateStack(WINDOWING_MODE_FULLSCREEN,
+ ACTIVITY_TYPE_HOME, ON_TOP);
} finally {
mSupervisor.endDeferResume();
}
diff --git a/services/core/java/com/android/server/wm/ActivityTaskManagerService.java b/services/core/java/com/android/server/wm/ActivityTaskManagerService.java
index ce885ab543bc..69218164c0ad 100644
--- a/services/core/java/com/android/server/wm/ActivityTaskManagerService.java
+++ b/services/core/java/com/android/server/wm/ActivityTaskManagerService.java
@@ -2357,7 +2357,7 @@ public class ActivityTaskManagerService extends IActivityTaskManager.Stub {
}
// Convert some windowing-mode changes into root-task reparents for split-screen.
if (stack.inSplitScreenWindowingMode()) {
- stack.getDisplay().onSplitScreenModeDismissed();
+ stack.getDisplay().mTaskContainers.onSplitScreenModeDismissed();
} else {
stack.setWindowingMode(windowingMode);
@@ -2775,7 +2775,8 @@ public class ActivityTaskManagerService extends IActivityTaskManager.Stub {
}
if (toTop) {
- display.positionStackAt(POSITION_TOP, primarySplitTask, false /* includingParents */);
+ display.mTaskContainers.positionStackAt(POSITION_TOP, primarySplitTask,
+ false /* includingParents */);
}
WindowContainerTransaction wct = new WindowContainerTransaction();
wct.reparent(task.getStack().mRemoteToken, primarySplitTask.mRemoteToken, toTop);
@@ -3244,8 +3245,8 @@ public class ActivityTaskManagerService extends IActivityTaskManager.Stub {
}
final ActivityStack stack = r.getRootTask();
- final Task task = stack.getDisplay().createStack(stack.getWindowingMode(),
- stack.getActivityType(), !ON_TOP, ainfo, intent,
+ final Task task = stack.getDisplay().mTaskContainers.createStack(
+ stack.getWindowingMode(), stack.getActivityType(), !ON_TOP, ainfo, intent,
false /* createdByOrganizer */);
if (!mRecentTasks.addToBottom(task)) {
@@ -3308,10 +3309,10 @@ public class ActivityTaskManagerService extends IActivityTaskManager.Stub {
throw new IllegalArgumentException("resizeTask not allowed on task=" + task);
}
if (bounds == null && stack.getWindowingMode() == WINDOWING_MODE_FREEFORM) {
- stack = stack.getDisplay().getOrCreateStack(
+ stack = stack.getDisplay().mTaskContainers.getOrCreateStack(
WINDOWING_MODE_FULLSCREEN, stack.getActivityType(), ON_TOP);
} else if (bounds != null && stack.getWindowingMode() != WINDOWING_MODE_FREEFORM) {
- stack = stack.getDisplay().getOrCreateStack(
+ stack = stack.getDisplay().mTaskContainers.getOrCreateStack(
WINDOWING_MODE_FREEFORM, stack.getActivityType(), ON_TOP);
}
diff --git a/services/core/java/com/android/server/wm/DisplayAreaPolicy.java b/services/core/java/com/android/server/wm/DisplayAreaPolicy.java
index 0ec0c7b53875..3c083e17931d 100644
--- a/services/core/java/com/android/server/wm/DisplayAreaPolicy.java
+++ b/services/core/java/com/android/server/wm/DisplayAreaPolicy.java
@@ -19,8 +19,6 @@ package com.android.server.wm;
import android.content.res.Resources;
import android.text.TextUtils;
-import com.android.server.wm.DisplayContent.TaskContainers;
-
/**
* Policy that manages DisplayAreas.
*/
diff --git a/services/core/java/com/android/server/wm/DisplayContent.java b/services/core/java/com/android/server/wm/DisplayContent.java
index 9fc90daaccfc..4a7edee7beac 100644
--- a/services/core/java/com/android/server/wm/DisplayContent.java
+++ b/services/core/java/com/android/server/wm/DisplayContent.java
@@ -21,20 +21,13 @@ import static android.app.WindowConfiguration.ACTIVITY_TYPE_DREAM;
import static android.app.WindowConfiguration.ACTIVITY_TYPE_HOME;
import static android.app.WindowConfiguration.ACTIVITY_TYPE_RECENTS;
import static android.app.WindowConfiguration.ACTIVITY_TYPE_STANDARD;
-import static android.app.WindowConfiguration.ACTIVITY_TYPE_UNDEFINED;
import static android.app.WindowConfiguration.ROTATION_UNDEFINED;
import static android.app.WindowConfiguration.WINDOWING_MODE_FREEFORM;
import static android.app.WindowConfiguration.WINDOWING_MODE_FULLSCREEN;
-import static android.app.WindowConfiguration.WINDOWING_MODE_FULLSCREEN_OR_SPLIT_SCREEN_SECONDARY;
import static android.app.WindowConfiguration.WINDOWING_MODE_MULTI_WINDOW;
-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.app.WindowConfiguration.WINDOWING_MODE_UNDEFINED;
-import static android.app.WindowConfiguration.isSplitScreenWindowingMode;
-import static android.content.pm.ActivityInfo.SCREEN_ORIENTATION_BEHIND;
import static android.content.pm.ActivityInfo.SCREEN_ORIENTATION_UNSET;
-import static android.content.pm.ActivityInfo.SCREEN_ORIENTATION_UNSPECIFIED;
import static android.content.pm.ActivityInfo.SCREEN_ORIENTATION_USER;
import static android.content.res.Configuration.ORIENTATION_LANDSCAPE;
import static android.content.res.Configuration.ORIENTATION_PORTRAIT;
@@ -79,17 +72,12 @@ import static android.view.WindowManager.LayoutParams.TYPE_WALLPAPER;
import static android.view.WindowManager.TRANSIT_ACTIVITY_OPEN;
import static android.view.WindowManager.TRANSIT_TASK_OPEN;
import static android.view.WindowManager.TRANSIT_TASK_TO_FRONT;
-import static android.window.WindowOrganizer.DisplayAreaOrganizer.FEATURE_TASK_CONTAINER;
import static com.android.server.policy.WindowManagerPolicy.FINISH_LAYOUT_REDO_ANIM;
import static com.android.server.policy.WindowManagerPolicy.FINISH_LAYOUT_REDO_CONFIG;
import static com.android.server.policy.WindowManagerPolicy.FINISH_LAYOUT_REDO_LAYOUT;
import static com.android.server.policy.WindowManagerPolicy.FINISH_LAYOUT_REDO_WALLPAPER;
import static com.android.server.wm.ActivityStack.ActivityState.RESUMED;
-import static com.android.server.wm.ActivityStack.STACK_VISIBILITY_VISIBLE;
-import static com.android.server.wm.ActivityStackSupervisor.TAG_TASKS;
-import static com.android.server.wm.ActivityTaskManagerDebugConfig.DEBUG_STATES;
-import static com.android.server.wm.ActivityTaskManagerDebugConfig.DEBUG_TASKS;
import static com.android.server.wm.ActivityTaskManagerDebugConfig.POSTFIX_STACK;
import static com.android.server.wm.DisplayContentProto.APP_TRANSITION;
import static com.android.server.wm.DisplayContentProto.CLOSING_APPS;
@@ -108,14 +96,12 @@ import static com.android.server.wm.DisplayContentProto.ROTATION;
import static com.android.server.wm.DisplayContentProto.SCREEN_ROTATION_ANIMATION;
import static com.android.server.wm.DisplayContentProto.SINGLE_TASK_INSTANCE;
import static com.android.server.wm.DisplayContentProto.WINDOW_CONTAINER;
-import static com.android.server.wm.ProtoLogGroup.WM_DEBUG_ADD_REMOVE;
import static com.android.server.wm.ProtoLogGroup.WM_DEBUG_APP_TRANSITIONS;
import static com.android.server.wm.ProtoLogGroup.WM_DEBUG_FOCUS;
import static com.android.server.wm.ProtoLogGroup.WM_DEBUG_FOCUS_LIGHT;
import static com.android.server.wm.ProtoLogGroup.WM_DEBUG_ORIENTATION;
import static com.android.server.wm.ProtoLogGroup.WM_DEBUG_SCREEN_ON;
import static com.android.server.wm.ProtoLogGroup.WM_SHOW_TRANSACTIONS;
-import static com.android.server.wm.RootWindowContainer.TAG_STATES;
import static com.android.server.wm.WindowContainer.AnimationFlags.PARENTS;
import static com.android.server.wm.WindowContainer.AnimationFlags.TRANSITION;
import static com.android.server.wm.WindowContainerChildProto.DISPLAY_CONTENT;
@@ -155,13 +141,9 @@ import android.annotation.NonNull;
import android.annotation.Nullable;
import android.app.ActivityManager;
import android.app.ActivityManagerInternal;
-import android.app.ActivityOptions;
-import android.app.WindowConfiguration;
import android.content.Context;
-import android.content.Intent;
import android.content.pm.ActivityInfo;
import android.content.pm.ActivityInfo.ScreenOrientation;
-import android.content.pm.ApplicationInfo;
import android.content.res.CompatibilityInfo;
import android.content.res.Configuration;
import android.graphics.Bitmap;
@@ -216,7 +198,6 @@ import android.view.ViewRootImpl;
import android.view.WindowInsets;
import android.view.WindowManager;
import android.view.WindowManagerPolicyConstants.PointerEventListener;
-import android.window.WindowContainerTransaction;
import com.android.internal.annotations.VisibleForTesting;
import com.android.internal.logging.MetricsLogger;
@@ -289,7 +270,7 @@ class DisplayContent extends WindowContainer<DisplayContent.DisplayChildWindowCo
/** The containers below are the only child containers {@link #mWindowContainers} can have. */
// Contains all window containers that are related to apps (Activities)
- private final TaskContainers mTaskContainers = new TaskContainers(mWmService);
+ final TaskContainers mTaskContainers = new TaskContainers(this, mWmService);
// Contains all IME window containers. Note that the z-ordering of the IME windows will depend
// on the IME target. We mainly have this container grouping so we can keep track of all the IME
@@ -456,8 +437,6 @@ class DisplayContent extends WindowContainer<DisplayContent.DisplayChildWindowCo
private final Configuration mTmpConfiguration = new Configuration();
- private ArrayList<Task> mTmpTasks = new ArrayList<>();
-
/** Remove this display when animation on it has completed. */
private boolean mDeferredRemoval;
@@ -579,10 +558,6 @@ class DisplayContent extends WindowContainer<DisplayContent.DisplayChildWindowCo
// Last systemUiVisibility we dispatched to windows.
private int mLastDispatchedSystemUiVisibility = 0;
- private final ArrayList<ActivityStack> mTmpAlwaysOnTopStacks = new ArrayList<>();
- private final ArrayList<ActivityStack> mTmpNormalStacks = new ArrayList<>();
- private final ArrayList<ActivityStack> mTmpHomeStacks = new ArrayList<>();
-
/** Corner radius that windows should have in order to match the display. */
private final float mWindowCornerRadius;
@@ -624,7 +599,7 @@ class DisplayContent extends WindowContainer<DisplayContent.DisplayChildWindowCo
private boolean mRemoved;
/** The display can only contain one task. */
- private boolean mSingleTaskInstance;
+ boolean mSingleTaskInstance;
/**
* Non-null if the last size compatibility mode activity is using non-native screen
@@ -640,13 +615,6 @@ class DisplayContent extends WindowContainer<DisplayContent.DisplayChildWindowCo
*/
private ActivityStack mPreferredTopFocusableStack;
- /**
- * If this is the same as {@link #getFocusedStack} then the activity on the top of the focused
- * stack has been resumed. If stacks are changing position this will hold the old stack until
- * the new stack becomes resumed after which it will be set to current focused stack.
- */
- private ActivityStack mLastFocusedStack;
-
// Used in updating the display size
private Point mTmpDisplaySize = new Point();
@@ -2103,30 +2071,11 @@ class DisplayContent extends WindowContainer<DisplayContent.DisplayChildWindowCo
return mTaskContainers.getRootHomeTask();
}
- /**
- * Returns the existing home stack or creates and returns a new one if it should exist for the
- * display.
- */
- @Nullable
- ActivityStack getOrCreateRootHomeTask() {
- ActivityStack homeTask = getRootHomeTask();
- if (homeTask == null && supportsSystemDecorations() && !isUntrustedVirtualDisplay()) {
- homeTask = createStack(WINDOWING_MODE_FULLSCREEN, ACTIVITY_TYPE_HOME,
- false /* onTop */);
- }
- return homeTask;
- }
-
/** @return The primary split-screen task, and {@code null} otherwise. */
@Nullable ActivityStack getRootSplitScreenPrimaryTask() {
return mTaskContainers.getRootSplitScreenPrimaryTask();
}
- boolean isSplitScreenModeActivated() {
- Task task = getRootSplitScreenPrimaryTask();
- return task != null && task.hasChild();
- }
-
ActivityStack getRootPinnedTask() {
return mTaskContainers.getRootPinnedTask();
}
@@ -2136,14 +2085,6 @@ class DisplayContent extends WindowContainer<DisplayContent.DisplayChildWindowCo
}
/**
- * Returns the topmost stack on the display that is compatible with the input windowing mode.
- * Null is no compatible stack on the display.
- */
- ActivityStack getTopStackInWindowingMode(int windowingMode) {
- return getStack(windowingMode, ACTIVITY_TYPE_UNDEFINED);
- }
-
- /**
* Returns the topmost stack on the display that is compatible with the input windowing mode and
* activity type. Null is no compatible stack on the display.
*/
@@ -2519,11 +2460,6 @@ class DisplayContent extends WindowContainer<DisplayContent.DisplayChildWindowCo
positionDisplayAt(position, includingParents);
}
- void positionStackAt(int position, ActivityStack child, boolean includingParents) {
- mTaskContainers.positionChildAt(position, child, includingParents);
- layoutAndAssignWindowLayersIfNeeded();
- }
-
/**
* Returns true if the input point is within an app window.
*/
@@ -2591,7 +2527,7 @@ class DisplayContent extends WindowContainer<DisplayContent.DisplayChildWindowCo
}
amendWindowTapExcludeRegion(mTouchExcludeRegion);
// TODO(multi-display): Support docked stacks on secondary displays.
- if (mDisplayId == DEFAULT_DISPLAY && isSplitScreenModeActivated()) {
+ if (mDisplayId == DEFAULT_DISPLAY && mTaskContainers.isSplitScreenModeActivated()) {
mDividerControllerLocked.getTouchRegion(mTmpRect);
mTmpRegion.set(mTmpRect);
mTouchExcludeRegion.op(mTmpRegion, Op.UNION);
@@ -2855,7 +2791,8 @@ class DisplayContent extends WindowContainer<DisplayContent.DisplayChildWindowCo
final ActivityStack focusedStack = getFocusedStack();
if (focusedStack != null) {
proto.write(FOCUSED_ROOT_TASK_ID, focusedStack.getRootTaskId());
- final ActivityRecord focusedActivity = focusedStack.getDisplay().getResumedActivity();
+ final ActivityRecord focusedActivity = focusedStack.getDisplay().mTaskContainers
+ .getResumedActivity();
if (focusedActivity != null) {
focusedActivity.writeIdentifierToProto(proto, RESUMED_ACTIVITY);
}
@@ -2916,8 +2853,8 @@ class DisplayContent extends WindowContainer<DisplayContent.DisplayChildWindowCo
if (mPreferredTopFocusableStack != null) {
pw.println(prefix + "mPreferredTopFocusableStack=" + mPreferredTopFocusableStack);
}
- if (mLastFocusedStack != null) {
- pw.println(prefix + "mLastFocusedStack=" + mLastFocusedStack);
+ if (mTaskContainers.mLastFocusedStack != null) {
+ pw.println(prefix + "mLastFocusedStack=" + mTaskContainers.mLastFocusedStack);
}
if (mLosingFocus.size() > 0) {
pw.println();
@@ -3033,7 +2970,7 @@ class DisplayContent extends WindowContainer<DisplayContent.DisplayChildWindowCo
/** Returns true if the stack in the windowing mode is visible. */
boolean isStackVisible(int windowingMode) {
- final ActivityStack stack = getTopStackInWindowingMode(windowingMode);
+ final ActivityStack stack = mTaskContainers.getTopStackInWindowingMode(windowingMode);
return stack != null && stack.isVisible();
}
@@ -4260,531 +4197,6 @@ class DisplayContent extends WindowContainer<DisplayContent.DisplayChildWindowCo
}
}
- /**
- * Window container class that contains all containers on this display relating to Apps.
- * I.e Activities.
- */
- final class TaskContainers extends DisplayArea<ActivityStack> {
- /**
- * A control placed at the appropriate level for transitions to occur.
- */
- SurfaceControl mAppAnimationLayer = null;
- SurfaceControl mBoostedAppAnimationLayer = null;
- SurfaceControl mHomeAppAnimationLayer = null;
-
- /**
- * Given that the split-screen divider does not have an AppWindowToken, it
- * will have to live inside of a "NonAppWindowContainer". However, in visual Z order
- * it will need to be interleaved with some of our children, appearing on top of
- * both docked stacks but underneath any assistant stacks.
- *
- * To solve this problem we have this anchor control, which will always exist so
- * we can always assign it the correct value in our {@link #assignChildLayers}.
- * Likewise since it always exists, we can always
- * assign the divider a layer relative to it. This way we prevent linking lifecycle
- * events between tasks and the divider window.
- */
- SurfaceControl mSplitScreenDividerAnchor = null;
-
- // Cached reference to some special tasks we tend to get a lot so we don't need to loop
- // through the list to find them.
- private ActivityStack mRootHomeTask = null;
- private ActivityStack mRootPinnedTask = null;
- private ActivityStack mRootSplitScreenPrimaryTask = null;
-
- TaskContainers(WindowManagerService service) {
- super(service, Type.ANY, "TaskContainers", FEATURE_TASK_CONTAINER);
- }
-
- /**
- * Returns the topmost stack on the display that is compatible with the input windowing mode
- * and activity type. Null is no compatible stack on the display.
- */
- ActivityStack getStack(int windowingMode, int activityType) {
- if (activityType == ACTIVITY_TYPE_HOME) {
- return mRootHomeTask;
- }
- if (windowingMode == WINDOWING_MODE_PINNED) {
- return mRootPinnedTask;
- } else if (windowingMode == WINDOWING_MODE_SPLIT_SCREEN_PRIMARY) {
- return mRootSplitScreenPrimaryTask;
- }
- for (int i = mTaskContainers.getChildCount() - 1; i >= 0; --i) {
- final ActivityStack stack = mTaskContainers.getChildAt(i);
- if (activityType == ACTIVITY_TYPE_UNDEFINED
- && windowingMode == stack.getWindowingMode()) {
- // Passing in undefined type means we want to match the topmost stack with the
- // windowing mode.
- return stack;
- }
- if (stack.isCompatible(windowingMode, activityType)) {
- return stack;
- }
- }
- return null;
- }
-
- @VisibleForTesting
- ActivityStack getTopStack() {
- final int count = mTaskContainers.getChildCount();
- return count > 0 ? mTaskContainers.getChildAt(count - 1) : null;
- }
-
- int getIndexOf(ActivityStack stack) {
- return mTaskContainers.mChildren.indexOf(stack);
- }
-
- ActivityStack getRootHomeTask() {
- return mRootHomeTask;
- }
-
- ActivityStack getRootPinnedTask() {
- return mRootPinnedTask;
- }
-
- ActivityStack getRootSplitScreenPrimaryTask() {
- return mRootSplitScreenPrimaryTask;
- }
-
- ArrayList<Task> getVisibleTasks() {
- final ArrayList<Task> visibleTasks = new ArrayList<>();
- forAllTasks(task -> {
- if (task.isLeafTask() && task.isVisible()) {
- visibleTasks.add(task);
- }
- });
- return visibleTasks;
- }
-
- void onStackWindowingModeChanged(ActivityStack stack) {
- removeStackReferenceIfNeeded(stack);
- addStackReferenceIfNeeded(stack);
- if (stack == mRootPinnedTask && getTopStack() != stack) {
- // Looks like this stack changed windowing mode to pinned. Move it to the top.
- positionChildAt(POSITION_TOP, stack, false /* includingParents */);
- }
- }
-
- private void addStackReferenceIfNeeded(ActivityStack stack) {
- if (stack.isActivityTypeHome()) {
- if (mRootHomeTask != null) {
- if (!stack.isDescendantOf(mRootHomeTask)) {
- throw new IllegalArgumentException("addStackReferenceIfNeeded: home stack="
- + mRootHomeTask + " already exist on display=" + this
- + " stack=" + stack);
- }
- } else {
- mRootHomeTask = stack;
- }
- }
-
- if (!stack.isRootTask()) {
- return;
- }
- final int windowingMode = stack.getWindowingMode();
- if (windowingMode == WINDOWING_MODE_PINNED) {
- if (mRootPinnedTask != null) {
- throw new IllegalArgumentException(
- "addStackReferenceIfNeeded: pinned stack=" + mRootPinnedTask
- + " already exist on display=" + this + " stack=" + stack);
- }
- mRootPinnedTask = stack;
- } else if (windowingMode == WINDOWING_MODE_SPLIT_SCREEN_PRIMARY) {
- if (mRootSplitScreenPrimaryTask != null) {
- throw new IllegalArgumentException(
- "addStackReferenceIfNeeded: split screen primary stack="
- + mRootSplitScreenPrimaryTask
- + " already exist on display=" + this + " stack=" + stack);
- }
- mRootSplitScreenPrimaryTask = stack;
- }
- }
-
- void removeStackReferenceIfNeeded(ActivityStack stack) {
- if (stack == mRootHomeTask) {
- mRootHomeTask = null;
- } else if (stack == mRootPinnedTask) {
- mRootPinnedTask = null;
- } else if (stack == mRootSplitScreenPrimaryTask) {
- mRootSplitScreenPrimaryTask = null;
- }
- }
-
- @Override
- void addChild(ActivityStack stack, int position) {
- addStackReferenceIfNeeded(stack);
- position = findPositionForStack(position, stack, true /* adding */);
-
- super.addChild(stack, position);
- mAtmService.updateSleepIfNeededLocked();
-
- // The reparenting case is handled in WindowContainer.
- if (!stack.mReparenting) {
- setLayoutNeeded();
- }
- }
-
- @Override
- protected void removeChild(ActivityStack stack) {
- super.removeChild(stack);
- mDisplayContent.onStackRemoved(stack);
- mAtmService.updateSleepIfNeededLocked();
- removeStackReferenceIfNeeded(stack);
- }
-
- @Override
- boolean isOnTop() {
- // Considered always on top
- return true;
- }
-
- @Override
- void positionChildAt(int position, ActivityStack child, boolean includingParents) {
- final boolean moveToTop = (position == POSITION_TOP || position == getChildCount());
- final boolean moveToBottom = (position == POSITION_BOTTOM || position == 0);
- if (child.getWindowConfiguration().isAlwaysOnTop() && !moveToTop) {
- // This stack is always-on-top, override the default behavior.
- Slog.w(TAG_WM, "Ignoring move of always-on-top stack=" + this + " to bottom");
-
- // Moving to its current position, as we must call super but we don't want to
- // perform any meaningful action.
- final int currentPosition = mChildren.indexOf(child);
- super.positionChildAt(currentPosition, child, false /* includingParents */);
- return;
- }
- // We don't allow untrusted display to top when task stack moves to top,
- // until user tapping this display to change display position as top intentionally.
- if (isUntrustedVirtualDisplay() && !getParent().isOnTop()) {
- includingParents = false;
- }
- final int targetPosition = findPositionForStack(position, child, false /* adding */);
- super.positionChildAt(targetPosition, child, false /* includingParents */);
-
- if (includingParents && (moveToTop || moveToBottom)) {
- // The DisplayContent children do not re-order, but we still want to move the
- // display of this stack container because the intention of positioning is to have
- // higher z-order to gain focus.
- positionDisplayAt(moveToTop ? POSITION_TOP : POSITION_BOTTOM,
- true /* includingParents */);
- }
-
- child.updateTaskMovement(moveToTop);
-
- setLayoutNeeded();
- }
-
- /**
- * When stack is added or repositioned, find a proper position for it.
- * This will make sure that pinned stack always stays on top.
- * @param requestedPosition Position requested by caller.
- * @param stack Stack to be added or positioned.
- * @param adding Flag indicates whether we're adding a new stack or positioning an existing.
- * @return The proper position for the stack.
- */
- private int findPositionForStack(int requestedPosition, ActivityStack stack,
- boolean adding) {
- if (stack.isActivityTypeDream()) {
- return POSITION_TOP;
- }
-
- if (stack.inPinnedWindowingMode()) {
- return POSITION_TOP;
- }
-
- final int topChildPosition = mChildren.size() - 1;
- int belowAlwaysOnTopPosition = POSITION_BOTTOM;
- for (int i = topChildPosition; i >= 0; --i) {
- // Since a stack could be repositioned while being one of the child, return
- // current index if that's the same stack we are positioning and it is always on
- // top.
- final boolean sameStack = getStacks().get(i) == stack;
- if ((sameStack && stack.isAlwaysOnTop())
- || (!sameStack && !getStacks().get(i).isAlwaysOnTop())) {
- belowAlwaysOnTopPosition = i;
- break;
- }
- }
-
- // The max possible position we can insert the stack at.
- int maxPosition = POSITION_TOP;
- // The min possible position we can insert the stack at.
- int minPosition = POSITION_BOTTOM;
-
- if (stack.isAlwaysOnTop()) {
- if (hasPinnedTask()) {
- // Always-on-top stacks go below the pinned stack.
- maxPosition = getStacks().indexOf(mRootPinnedTask) - 1;
- }
- // Always-on-top stacks need to be above all other stacks.
- minPosition = belowAlwaysOnTopPosition !=
- POSITION_BOTTOM ? belowAlwaysOnTopPosition : topChildPosition;
- } else {
- // Other stacks need to be below the always-on-top stacks.
- maxPosition = belowAlwaysOnTopPosition !=
- POSITION_BOTTOM ? belowAlwaysOnTopPosition : 0;
- }
-
- // Cap the requested position to something reasonable for the previous position check
- // below.
- if (requestedPosition == POSITION_TOP) {
- requestedPosition = mChildren.size();
- } else if (requestedPosition == POSITION_BOTTOM) {
- requestedPosition = 0;
- }
-
- int targetPosition = requestedPosition;
- targetPosition = Math.min(targetPosition, maxPosition);
- targetPosition = Math.max(targetPosition, minPosition);
-
- int prevPosition = getStacks().indexOf(stack);
- // The positions we calculated above (maxPosition, minPosition) do not take into
- // consideration the following edge cases.
- // 1) We need to adjust the position depending on the value "adding".
- // 2) When we are moving a stack to another position, we also need to adjust the
- // position depending on whether the stack is moving to a higher or lower position.
- if ((targetPosition != requestedPosition) &&
- (adding || targetPosition < prevPosition)) {
- targetPosition++;
- }
-
- return targetPosition;
- }
-
- @Override
- boolean forAllWindows(ToBooleanFunction<WindowState> callback,
- boolean traverseTopToBottom) {
- if (traverseTopToBottom) {
- if (super.forAllWindows(callback, traverseTopToBottom)) {
- return true;
- }
- if (forAllExitingAppTokenWindows(callback, traverseTopToBottom)) {
- return true;
- }
- } else {
- if (forAllExitingAppTokenWindows(callback, traverseTopToBottom)) {
- return true;
- }
- if (super.forAllWindows(callback, traverseTopToBottom)) {
- return true;
- }
- }
- return false;
- }
-
- private boolean forAllExitingAppTokenWindows(ToBooleanFunction<WindowState> callback,
- boolean traverseTopToBottom) {
- // For legacy reasons we process the TaskStack.mExitingActivities first here before the
- // app tokens.
- // TODO: Investigate if we need to continue to do this or if we can just process them
- // in-order.
- if (traverseTopToBottom) {
- for (int i = mChildren.size() - 1; i >= 0; --i) {
- final List<ActivityRecord> activities = mChildren.get(i).mExitingActivities;
- for (int j = activities.size() - 1; j >= 0; --j) {
- if (activities.get(j).forAllWindowsUnchecked(callback,
- traverseTopToBottom)) {
- return true;
- }
- }
- }
- } else {
- final int count = mChildren.size();
- for (int i = 0; i < count; ++i) {
- final List<ActivityRecord> activities = mChildren.get(i).mExitingActivities;
- final int appTokensCount = activities.size();
- for (int j = 0; j < appTokensCount; j++) {
- if (activities.get(j).forAllWindowsUnchecked(callback,
- traverseTopToBottom)) {
- return true;
- }
- }
- }
- }
- return false;
- }
-
- void setExitingTokensHasVisible(boolean hasVisible) {
- for (int i = mChildren.size() - 1; i >= 0; --i) {
- final ArrayList<ActivityRecord> activities = mChildren.get(i).mExitingActivities;
- for (int j = activities.size() - 1; j >= 0; --j) {
- activities.get(j).hasVisible = hasVisible;
- }
- }
- }
-
- void removeExistingAppTokensIfPossible() {
- for (int i = mChildren.size() - 1; i >= 0; --i) {
- final ArrayList<ActivityRecord> activities = mChildren.get(i).mExitingActivities;
- for (int j = activities.size() - 1; j >= 0; --j) {
- final ActivityRecord activity = activities.get(j);
- if (!activity.hasVisible && !mClosingApps.contains(activity)
- && (!activity.mIsExiting || activity.isEmpty())) {
- // Make sure there is no animation running on this activity, so any windows
- // associated with it will be removed as soon as their animations are
- // complete.
- cancelAnimation();
- ProtoLog.v(WM_DEBUG_ADD_REMOVE,
- "performLayout: Activity exiting now removed %s", activity);
- activity.removeIfPossible();
- }
- }
- }
- }
-
- @Override
- int getOrientation(int candidate) {
- if (isStackVisible(WINDOWING_MODE_SPLIT_SCREEN_PRIMARY)) {
- // Apps and their containers are not allowed to specify an orientation while using
- // root tasks...except for the home stack if it is not resizable and currently
- // visible (top of) its root task.
- if (mRootHomeTask != null && mRootHomeTask.isVisible()) {
- final Task topMost = mRootHomeTask.getTopMostTask();
- final boolean resizable = topMost != null && topMost.isResizeable();
- if (!(resizable && mRootHomeTask.matchParentBounds())) {
- final int orientation = mRootHomeTask.getOrientation();
- if (orientation != SCREEN_ORIENTATION_UNSET) {
- return orientation;
- }
- }
- }
- return SCREEN_ORIENTATION_UNSPECIFIED;
- }
-
- final int orientation = super.getOrientation(candidate);
- if (orientation != SCREEN_ORIENTATION_UNSET
- && orientation != SCREEN_ORIENTATION_BEHIND) {
- ProtoLog.v(WM_DEBUG_ORIENTATION,
- "App is requesting an orientation, return %d for display id=%d",
- orientation, mDisplayId);
- return orientation;
- }
-
- ProtoLog.v(WM_DEBUG_ORIENTATION,
- "No app is requesting an orientation, return %d for display id=%d",
- getLastOrientation(), mDisplayId);
- // The next app has not been requested to be visible, so we keep the current orientation
- // to prevent freezing/unfreezing the display too early.
- return getLastOrientation();
- }
-
- @Override
- void assignChildLayers(SurfaceControl.Transaction t) {
- assignStackOrdering(t);
-
- for (int i = 0; i < mChildren.size(); i++) {
- final ActivityStack s = mChildren.get(i);
- s.assignChildLayers(t);
- }
- }
-
- void assignStackOrdering(SurfaceControl.Transaction t) {
- if (getParent() == null) {
- return;
- }
- mTmpAlwaysOnTopStacks.clear();
- mTmpHomeStacks.clear();
- mTmpNormalStacks.clear();
- for (int i = 0; i < mChildren.size(); ++i) {
- final ActivityStack s = mChildren.get(i);
- if (s.isAlwaysOnTop()) {
- mTmpAlwaysOnTopStacks.add(s);
- } else if (s.isActivityTypeHome()) {
- mTmpHomeStacks.add(s);
- } else {
- mTmpNormalStacks.add(s);
- }
- }
-
- int layer = 0;
- // Place home stacks to the bottom.
- for (int i = 0; i < mTmpHomeStacks.size(); i++) {
- mTmpHomeStacks.get(i).assignLayer(t, layer++);
- }
- // The home animation layer is between the home stacks and the normal stacks.
- final int layerForHomeAnimationLayer = layer++;
- int layerForSplitScreenDividerAnchor = layer++;
- int layerForAnimationLayer = layer++;
- for (int i = 0; i < mTmpNormalStacks.size(); i++) {
- final ActivityStack s = mTmpNormalStacks.get(i);
- s.assignLayer(t, layer++);
- if (s.inSplitScreenWindowingMode()) {
- // The split screen divider anchor is located above the split screen window.
- layerForSplitScreenDividerAnchor = layer++;
- }
- if (s.isTaskAnimating() || s.isAppTransitioning()) {
- // The animation layer is located above the highest animating stack and no
- // higher.
- layerForAnimationLayer = layer++;
- }
- }
- // The boosted animation layer is between the normal stacks and the always on top
- // stacks.
- final int layerForBoostedAnimationLayer = layer++;
- for (int i = 0; i < mTmpAlwaysOnTopStacks.size(); i++) {
- mTmpAlwaysOnTopStacks.get(i).assignLayer(t, layer++);
- }
-
- t.setLayer(mHomeAppAnimationLayer, layerForHomeAnimationLayer);
- t.setLayer(mAppAnimationLayer, layerForAnimationLayer);
- t.setLayer(mSplitScreenDividerAnchor, layerForSplitScreenDividerAnchor);
- t.setLayer(mBoostedAppAnimationLayer, layerForBoostedAnimationLayer);
- }
-
- @Override
- SurfaceControl getAppAnimationLayer(@AnimationLayer int animationLayer) {
- switch (animationLayer) {
- case ANIMATION_LAYER_BOOSTED:
- return mBoostedAppAnimationLayer;
- case ANIMATION_LAYER_HOME:
- return mHomeAppAnimationLayer;
- case ANIMATION_LAYER_STANDARD:
- default:
- return mAppAnimationLayer;
- }
- }
-
- SurfaceControl getSplitScreenDividerAnchor() {
- return mSplitScreenDividerAnchor;
- }
-
- @Override
- void onParentChanged(ConfigurationContainer newParent, ConfigurationContainer oldParent) {
- if (getParent() != null) {
- super.onParentChanged(newParent, oldParent, () -> {
- mAppAnimationLayer = makeChildSurface(null)
- .setName("animationLayer")
- .build();
- mBoostedAppAnimationLayer = makeChildSurface(null)
- .setName("boostedAnimationLayer")
- .build();
- mHomeAppAnimationLayer = makeChildSurface(null)
- .setName("homeAnimationLayer")
- .build();
- mSplitScreenDividerAnchor = makeChildSurface(null)
- .setName("splitScreenDividerAnchor")
- .build();
- getPendingTransaction()
- .show(mAppAnimationLayer)
- .show(mBoostedAppAnimationLayer)
- .show(mHomeAppAnimationLayer)
- .show(mSplitScreenDividerAnchor);
- });
- } else {
- super.onParentChanged(newParent, oldParent);
- mWmService.mTransactionFactory.get()
- .remove(mAppAnimationLayer)
- .remove(mBoostedAppAnimationLayer)
- .remove(mHomeAppAnimationLayer)
- .remove(mSplitScreenDividerAnchor)
- .apply();
- mAppAnimationLayer = null;
- mBoostedAppAnimationLayer = null;
- mHomeAppAnimationLayer = null;
- mSplitScreenDividerAnchor = null;
- }
- }
- }
-
private class WindowContainers extends DisplayChildWindowContainer<WindowContainer> {
private final String mName;
@@ -4963,7 +4375,8 @@ class DisplayContent extends WindowContainer<DisplayContent.DisplayChildWindowCo
private boolean skipImeWindowsDuringTraversal(DisplayContent dc) {
// We skip IME windows so they're processed just above their target, except
// in split-screen mode where we process the IME containers above the docked divider.
- return dc.mInputMethodTarget != null && !dc.isSplitScreenModeActivated();
+ return dc.mInputMethodTarget != null
+ && !dc.mTaskContainers.isSplitScreenModeActivated();
}
/** Like {@link #forAllWindows}, but ignores {@link #skipImeWindowsDuringTraversal} */
@@ -5631,111 +5044,6 @@ class DisplayContent extends WindowContainer<DisplayContent.DisplayChildWindowCo
mWmService.requestTraversal();
}
- void addStack(ActivityStack stack, int position) {
- setStackOnDisplay(stack, position);
- positionStackAt(stack, position);
- }
-
- void addStackReferenceIfNeeded(ActivityStack stack) {
- mTaskContainers.addStackReferenceIfNeeded(stack);
- }
-
- void removeStackReferenceIfNeeded(ActivityStack stack) {
- mTaskContainers.removeStackReferenceIfNeeded(stack);
- }
-
- void onStackRemoved(ActivityStack stack) {
- if (ActivityTaskManagerDebugConfig.DEBUG_STACK) {
- Slog.v(TAG_STACK, "removeStack: detaching " + stack + " from displayId=" + mDisplayId);
- }
- if (mPreferredTopFocusableStack == stack) {
- mPreferredTopFocusableStack = null;
- }
- releaseSelfIfNeeded();
- onStackOrderChanged(stack);
- }
-
- void positionStackAtTop(ActivityStack stack, boolean includingParents) {
- positionStackAtTop(stack, includingParents, null /* updateLastFocusedStackReason */);
- }
-
- void positionStackAtTop(ActivityStack stack, boolean includingParents,
- String updateLastFocusedStackReason) {
- positionStackAt(stack, getStackCount(), includingParents, updateLastFocusedStackReason);
- }
-
- void positionStackAtBottom(ActivityStack stack) {
- positionStackAtBottom(stack, null /* updateLastFocusedStackReason */);
- }
-
- void positionStackAtBottom(ActivityStack stack, String updateLastFocusedStackReason) {
- positionStackAt(stack, 0, false /* includingParents */, updateLastFocusedStackReason);
- }
-
- private void positionStackAt(ActivityStack stack, int position) {
- positionStackAt(stack, position, false /* includingParents */,
- null /* updateLastFocusedStackReason */);
- }
-
- private void positionStackAt(ActivityStack stack, int position, boolean includingParents,
- String updateLastFocusedStackReason) {
- // TODO: Keep in sync with WindowContainer.positionChildAt(), once we change that to adjust
- // the position internally, also update the logic here
- final ActivityStack prevFocusedStack = updateLastFocusedStackReason != null
- ? getFocusedStack() : null;
- final boolean wasContained = getIndexOf(stack) >= 0;
- if (mSingleTaskInstance && getStackCount() == 1 && !wasContained) {
- throw new IllegalStateException(
- "positionStackAt: Can only have one task on display=" + this);
- }
-
- final boolean movingToTop = wasContained && position >= getStackCount() - 1;
- // Reset mPreferredTopFocusableStack before positioning to top or {@link
- // ActivityStackSupervisor#updateTopResumedActivityIfNeeded()} won't update the top
- // resumed activity.
- if (movingToTop && stack.isFocusable()) {
- mPreferredTopFocusableStack = null;
- }
-
- // Since positionChildAt() is called during the creation process of pinned stacks,
- // ActivityStack#getStack() can be null.
- positionStackAt(position, stack, includingParents);
-
- // The insert position may be adjusted to non-top when there is always-on-top stack. Since
- // the original position is preferred to be top, the stack should have higher priority when
- // we are looking for top focusable stack. The condition {@code wasContained} restricts the
- // preferred stack is set only when moving an existing stack to top instead of adding a new
- // stack that may be too early (e.g. in the middle of launching or reparenting).
- if (movingToTop && stack.isFocusableAndVisible()) {
- mPreferredTopFocusableStack = stack;
- } else if (mPreferredTopFocusableStack == stack) {
- mPreferredTopFocusableStack = null;
- }
-
- if (updateLastFocusedStackReason != null) {
- final ActivityStack currentFocusedStack = getFocusedStack();
- if (currentFocusedStack != prevFocusedStack) {
- mLastFocusedStack = prevFocusedStack;
- EventLogTags.writeWmFocusedStack(mRootWindowContainer.mCurrentUser, mDisplayId,
- currentFocusedStack == null ? -1 : currentFocusedStack.getRootTaskId(),
- mLastFocusedStack == null ? -1 : mLastFocusedStack.getRootTaskId(),
- updateLastFocusedStackReason);
- }
- }
-
- onStackOrderChanged(stack);
- }
-
- ActivityStack getStack(int rootTaskId) {
- for (int i = getStackCount() - 1; i >= 0; --i) {
- final ActivityStack stack = getStackAt(i);
- if (stack.getRootTaskId() == rootTaskId) {
- return stack;
- }
- }
- return null;
- }
-
static boolean alwaysCreateStack(int windowingMode, int activityType) {
// Always create a stack for fullscreen, freeform, and split-screen-secondary windowing
// modes so that we can manage visual ordering and return types correctly.
@@ -5746,358 +5054,13 @@ class DisplayContent extends WindowContainer<DisplayContent.DisplayChildWindowCo
|| windowingMode == WINDOWING_MODE_MULTI_WINDOW);
}
- /**
- * Returns an existing stack compatible with the windowing mode and activity type or creates one
- * if a compatible stack doesn't exist.
- * @see #getOrCreateStack(int, int, boolean, Intent, Task, boolean)
- */
- ActivityStack getOrCreateStack(int windowingMode, int activityType, boolean onTop) {
- return getOrCreateStack(windowingMode, activityType, onTop, null /* intent */,
- null /* candidateTask */, false /* createdByOrganizer */);
- }
-
- /**
- * When two level tasks are required for given windowing mode and activity type, returns an
- * existing compatible root task or creates a new one.
- * For one level task, the candidate task would be reused to also be the root task or create
- * a new root task if no candidate task.
- * @see #getStack(int, int)
- * @see #createStack(int, int, boolean)
- */
- ActivityStack getOrCreateStack(int windowingMode, int activityType, boolean onTop,
- Intent intent, Task candidateTask, boolean createdByOrganizer) {
- if (!alwaysCreateStack(windowingMode, activityType)) {
- ActivityStack stack = getStack(windowingMode, activityType);
- if (stack != null) {
- return stack;
- }
- } else if (candidateTask != null) {
- final ActivityStack stack = (ActivityStack) candidateTask;
- final int position = onTop ? POSITION_TOP : POSITION_BOTTOM;
- if (isSplitScreenModeActivated()) {
- final Task splitRootSecondary = getTask(t -> t.mCreatedByOrganizer && t.isRootTask()
- && t.inSplitScreenSecondaryWindowingMode());
- if (stack.getParent() == null) {
- splitRootSecondary.addChild(stack, position);
- } else if (stack.getParent() != splitRootSecondary) {
- stack.reparent(splitRootSecondary, position);
- }
- } else if (stack.getDisplay() != this || !stack.isRootTask()) {
- if (stack.getParent() == null) {
- addStack(stack, position);
- } else {
- stack.reparent(this, onTop);
- }
- }
- // Update windowing mode if necessary, e.g. moving a pinned task to fullscreen.
- if (candidateTask.getWindowingMode() != windowingMode) {
- candidateTask.setWindowingMode(windowingMode);
- }
- return stack;
- }
- return createStack(windowingMode, activityType, onTop, null /*info*/, intent,
- createdByOrganizer);
- }
-
- /**
- * Returns an existing stack compatible with the input params or creates one
- * if a compatible stack doesn't exist.
- * @see #getOrCreateStack(int, int, boolean)
- */
- ActivityStack getOrCreateStack(@Nullable ActivityRecord r,
- @Nullable ActivityOptions options, @Nullable Task candidateTask, int activityType,
- boolean onTop) {
- // First preference is the windowing mode in the activity options if set.
- int windowingMode = (options != null)
- ? options.getLaunchWindowingMode() : WINDOWING_MODE_UNDEFINED;
- // Validate that our desired windowingMode will work under the current conditions.
- // UNDEFINED windowing mode is a valid result and means that the new stack will inherit
- // it's display's windowing mode.
- windowingMode = validateWindowingMode(windowingMode, r, candidateTask, activityType);
- return getOrCreateStack(windowingMode, activityType, onTop, null /* intent */,
- candidateTask, false /* createdByOrganizer */);
- }
-
- @VisibleForTesting
- int getNextStackId() {
- return mAtmService.mStackSupervisor.getNextTaskIdForUser();
- }
-
ActivityStack createStack(int windowingMode, int activityType, boolean onTop) {
- return createStack(windowingMode, activityType, onTop, null /* info */, null /* intent */,
- false /* createdByOrganizer */);
- }
-
- /**
- * Creates a stack matching the input windowing mode and activity type on this display.
- * @param windowingMode The windowing mode the stack should be created in. If
- * {@link WindowConfiguration#WINDOWING_MODE_UNDEFINED} then the stack will
- * inherit its parent's windowing mode.
- * @param activityType The activityType the stack should be created in. If
- * {@link WindowConfiguration#ACTIVITY_TYPE_UNDEFINED} then the stack will
- * be created in {@link WindowConfiguration#ACTIVITY_TYPE_STANDARD}.
- * @param onTop If true the stack will be created at the top of the display, else at the bottom.
- * @param info The started activity info.
- * @param intent The intent that started this task.
- * @param createdByOrganizer @{code true} if this is created by task organizer, @{code false}
- * otherwise.
- * @return The newly created stack.
- */
- ActivityStack createStack(int windowingMode, int activityType, boolean onTop, ActivityInfo info,
- Intent intent, boolean createdByOrganizer) {
- if (mSingleTaskInstance && getStackCount() > 0) {
- // Create stack on default display instead since this display can only contain 1 stack.
- // TODO: Kinda a hack, but better that having the decision at each call point. Hoping
- // this goes away once ActivityView is no longer using virtual displays.
- return mRootWindowContainer.getDefaultDisplay().createStack(
- windowingMode, activityType, onTop, info, intent, createdByOrganizer);
- }
-
- if (activityType == ACTIVITY_TYPE_UNDEFINED && !createdByOrganizer) {
- // Can't have an undefined stack type yet...so re-map to standard. Anyone that wants
- // anything else should be passing it in anyways...except for the task organizer.
- activityType = ACTIVITY_TYPE_STANDARD;
- }
-
- if (activityType != ACTIVITY_TYPE_STANDARD && activityType != ACTIVITY_TYPE_UNDEFINED) {
- // For now there can be only one stack of a particular non-standard activity type on a
- // display. So, get that ignoring whatever windowing mode it is currently in.
- ActivityStack stack = getStack(WINDOWING_MODE_UNDEFINED, activityType);
- if (stack != null) {
- throw new IllegalArgumentException("Stack=" + stack + " of activityType="
- + activityType + " already on display=" + this + ". Can't have multiple.");
- }
- }
-
- if (!isWindowingModeSupported(windowingMode, mAtmService.mSupportsMultiWindow,
- mAtmService.mSupportsSplitScreenMultiWindow,
- mAtmService.mSupportsFreeformWindowManagement,
- mAtmService.mSupportsPictureInPicture, activityType)) {
- throw new IllegalArgumentException("Can't create stack for unsupported windowingMode="
- + windowingMode);
- }
-
- final int stackId = getNextStackId();
- return createStackUnchecked(windowingMode, activityType, stackId, onTop, info, intent,
- createdByOrganizer);
- }
-
- /** @return the root task to create the next task in. */
- private Task updateLaunchRootTask(int windowingMode) {
- if (!isSplitScreenWindowingMode(windowingMode)) {
- // Only split-screen windowing modes can do this currently...
- return null;
- }
- for (int i = getStackCount() - 1; i >= 0; --i) {
- final Task t = getStackAt(i);
- if (!t.mCreatedByOrganizer || t.getRequestedOverrideWindowingMode() != windowingMode) {
- continue;
- }
- // If not already set, pick a launch root which is not the one we are launching into.
- if (mLaunchRootTask == null) {
- for (int j = 0, n = getStackCount(); j < n; ++j) {
- final Task tt = getStackAt(j);
- if (tt.mCreatedByOrganizer && tt != t) {
- mLaunchRootTask = tt;
- break;
- }
- }
- }
- return t;
- }
- return mLaunchRootTask;
+ return mTaskContainers.createStack(windowingMode, activityType, onTop, null /* info */,
+ null /* intent */, false /* createdByOrganizer */);
}
- @VisibleForTesting
- ActivityStack createStackUnchecked(int windowingMode, int activityType, int stackId,
- boolean onTop, ActivityInfo info, Intent intent, boolean createdByOrganizer) {
- if (windowingMode == WINDOWING_MODE_PINNED && activityType != ACTIVITY_TYPE_STANDARD) {
- throw new IllegalArgumentException("Stack with windowing mode cannot with non standard "
- + "activity type.");
- }
- if (info == null) {
- info = new ActivityInfo();
- info.applicationInfo = new ApplicationInfo();
- }
-
- // Task created by organizer are added as root.
- Task launchRootTask = createdByOrganizer ? null : updateLaunchRootTask(windowingMode);
- if (launchRootTask != null) {
- // Since this stack will be put into a root task, its windowingMode will be inherited.
- windowingMode = WINDOWING_MODE_UNDEFINED;
- }
-
- final ActivityStack stack = (ActivityStack) Task.create(mAtmService, stackId, activityType,
- info, intent, createdByOrganizer);
- if (launchRootTask != null) {
- launchRootTask.addChild(stack, onTop ? POSITION_TOP : POSITION_BOTTOM);
- if (onTop) {
- positionStackAtTop((ActivityStack) launchRootTask, false /* includingParents */);
- }
- } else {
- addStack(stack, onTop ? POSITION_TOP : POSITION_BOTTOM);
- stack.setWindowingMode(windowingMode, false /* animate */, false /* showRecents */,
- false /* enteringSplitScreenMode */, false /* deferEnsuringVisibility */,
- true /* creating */);
- }
- return stack;
- }
-
- /**
- * Get the preferred focusable stack in priority. If the preferred stack does not exist, find a
- * focusable and visible stack from the top of stacks in this display.
- */
ActivityStack getFocusedStack() {
- if (mPreferredTopFocusableStack != null) {
- return mPreferredTopFocusableStack;
- }
-
- for (int i = getStackCount() - 1; i >= 0; --i) {
- final ActivityStack stack = getStackAt(i);
- if (stack.isFocusableAndVisible()) {
- return stack;
- }
- }
-
- return null;
- }
-
- ActivityStack getNextFocusableStack(ActivityStack currentFocus, boolean ignoreCurrent) {
- final int currentWindowingMode = currentFocus != null
- ? currentFocus.getWindowingMode() : WINDOWING_MODE_UNDEFINED;
-
- ActivityStack candidate = null;
- for (int i = getStackCount() - 1; i >= 0; --i) {
- final ActivityStack stack = getStackAt(i);
- if (ignoreCurrent && stack == currentFocus) {
- continue;
- }
- if (!stack.isFocusableAndVisible()) {
- continue;
- }
-
- if (currentWindowingMode == WINDOWING_MODE_SPLIT_SCREEN_SECONDARY
- && candidate == null && stack.inSplitScreenPrimaryWindowingMode()) {
- // If the currently focused stack is in split-screen secondary we save off the
- // top primary split-screen stack as a candidate for focus because we might
- // prefer focus to move to an other stack to avoid primary split-screen stack
- // overlapping with a fullscreen stack when a fullscreen stack is higher in z
- // than the next split-screen stack. Assistant stack, I am looking at you...
- // We only move the focus to the primary-split screen stack if there isn't a
- // better alternative.
- candidate = stack;
- continue;
- }
- if (candidate != null && stack.inSplitScreenSecondaryWindowingMode()) {
- // Use the candidate stack since we are now at the secondary split-screen.
- return candidate;
- }
- return stack;
- }
- return candidate;
- }
-
- ActivityRecord getResumedActivity() {
- final ActivityStack focusedStack = getFocusedStack();
- if (focusedStack == null) {
- return null;
- }
- // TODO(b/111541062): Move this into ActivityStack#getResumedActivity()
- // Check if the focused stack has the resumed activity
- ActivityRecord resumedActivity = focusedStack.getResumedActivity();
- if (resumedActivity == null || resumedActivity.app == null) {
- // If there is no registered resumed activity in the stack or it is not running -
- // try to use previously resumed one.
- resumedActivity = focusedStack.mPausingActivity;
- if (resumedActivity == null || resumedActivity.app == null) {
- // If previously resumed activity doesn't work either - find the topmost running
- // activity that can be focused.
- resumedActivity = focusedStack.topRunningActivity(true /* focusableOnly */);
- }
- }
- return resumedActivity;
- }
-
- ActivityStack getLastFocusedStack() {
- return mLastFocusedStack;
- }
-
- boolean allResumedActivitiesComplete() {
- for (int stackNdx = getStackCount() - 1; stackNdx >= 0; --stackNdx) {
- final ActivityRecord r = getStackAt(stackNdx).getResumedActivity();
- if (r != null && !r.isState(RESUMED)) {
- return false;
- }
- }
- final ActivityStack currentFocusedStack = getFocusedStack();
- if (ActivityTaskManagerDebugConfig.DEBUG_STACK) {
- Slog.d(TAG_STACK, "allResumedActivitiesComplete: mLastFocusedStack changing from="
- + mLastFocusedStack + " to=" + currentFocusedStack);
- }
- mLastFocusedStack = currentFocusedStack;
- return true;
- }
-
- /**
- * Pause all activities in either all of the stacks or just the back stacks. This is done before
- * resuming a new activity and to make sure that previously active activities are
- * paused in stacks that are no longer visible or in pinned windowing mode. This does not
- * pause activities in visible stacks, so if an activity is launched within the same stack/task,
- * then we should explicitly pause that stack's top activity.
- * @param userLeaving Passed to pauseActivity() to indicate whether to call onUserLeaving().
- * @param resuming The resuming activity.
- * @return {@code true} if any activity was paused as a result of this call.
- */
- boolean pauseBackStacks(boolean userLeaving, ActivityRecord resuming) {
- boolean someActivityPaused = false;
- for (int stackNdx = getStackCount() - 1; stackNdx >= 0; --stackNdx) {
- final ActivityStack stack = getStackAt(stackNdx);
- final ActivityRecord resumedActivity = stack.getResumedActivity();
- if (resumedActivity != null
- && (stack.getVisibility(resuming) != STACK_VISIBILITY_VISIBLE
- || !stack.isTopActivityFocusable())) {
- if (DEBUG_STATES) Slog.d(TAG_STATES, "pauseBackStacks: stack=" + stack
- + " mResumedActivity=" + resumedActivity);
- someActivityPaused |= stack.startPausingLocked(userLeaving, false /* uiSleeping*/,
- resuming);
- }
- }
- return someActivityPaused;
- }
-
- /**
- * Find task for putting the Activity in.
- */
- void findTaskLocked(final ActivityRecord r, final boolean isPreferredDisplay,
- RootWindowContainer.FindTaskResult result) {
- mTmpFindTaskResult.clear();
- for (int stackNdx = getStackCount() - 1; stackNdx >= 0; --stackNdx) {
- final ActivityStack stack = getStackAt(stackNdx);
- if (!r.hasCompatibleActivityType(stack) && stack.isLeafTask()) {
- if (DEBUG_TASKS) {
- Slog.d(TAG_TASKS, "Skipping stack: (mismatch activity/stack) " + stack);
- }
- continue;
- }
-
- mTmpFindTaskResult.process(r, stack);
- // It is possible to have tasks in multiple stacks with the same root affinity, so
- // we should keep looking after finding an affinity match to see if there is a
- // better match in another stack. Also, task affinity isn't a good enough reason
- // to target a display which isn't the source of the intent, so skip any affinity
- // matches not on the specified display.
- if (mTmpFindTaskResult.mRecord != null) {
- if (mTmpFindTaskResult.mIdealMatch) {
- result.setTo(mTmpFindTaskResult);
- return;
- } else if (isPreferredDisplay) {
- // Note: since the traversing through the stacks is top down, the floating
- // tasks should always have lower priority than any affinity-matching tasks
- // in the fullscreen stacks
- result.setTo(mTmpFindTaskResult);
- }
- }
- }
+ return mTaskContainers.getFocusedStack();
}
/**
@@ -6105,249 +5068,11 @@ class DisplayContent extends WindowContainer<DisplayContent.DisplayChildWindowCo
* ACTIVITY_TYPE_STANDARD or ACTIVITY_TYPE_UNDEFINED
*/
void removeStacksInWindowingModes(int... windowingModes) {
- if (windowingModes == null || windowingModes.length == 0) {
- return;
- }
-
- // Collect the stacks that are necessary to be removed instead of performing the removal
- // by looping mStacks, so that we don't miss any stacks after the stack size changed or
- // stacks reordered.
- final ArrayList<ActivityStack> stacks = new ArrayList<>();
- for (int j = windowingModes.length - 1; j >= 0; --j) {
- final int windowingMode = windowingModes[j];
- for (int i = getStackCount() - 1; i >= 0; --i) {
- final ActivityStack stack = getStackAt(i);
- if (!stack.isActivityTypeStandardOrUndefined()) {
- continue;
- }
- if (stack.getWindowingMode() != windowingMode) {
- continue;
- }
- stacks.add(stack);
- }
- }
-
- for (int i = stacks.size() - 1; i >= 0; --i) {
- mRootWindowContainer.mStackSupervisor.removeStack(stacks.get(i));
- }
+ mTaskContainers.removeStacksInWindowingModes(windowingModes);
}
void removeStacksWithActivityTypes(int... activityTypes) {
- if (activityTypes == null || activityTypes.length == 0) {
- return;
- }
-
- // Collect the stacks that are necessary to be removed instead of performing the removal
- // by looping mStacks, so that we don't miss any stacks after the stack size changed or
- // stacks reordered.
- final ArrayList<ActivityStack> stacks = new ArrayList<>();
- for (int j = activityTypes.length - 1; j >= 0; --j) {
- final int activityType = activityTypes[j];
- for (int i = getStackCount() - 1; i >= 0; --i) {
- final ActivityStack stack = getStackAt(i);
- // Collect the root tasks that are currently being organized.
- if (stack.isOrganized()) {
- for (int k = stack.getChildCount() - 1; k >= 0; --k) {
- final ActivityStack childStack = (ActivityStack) stack.getChildAt(k);
- if (childStack.getActivityType() == activityType) {
- stacks.add(childStack);
- }
- }
- } else if (stack.getActivityType() == activityType) {
- stacks.add(stack);
- }
- }
- }
-
- for (int i = stacks.size() - 1; i >= 0; --i) {
- mRootWindowContainer.mStackSupervisor.removeStack(stacks.get(i));
- }
- }
-
- void onSplitScreenModeDismissed() {
- mAtmService.deferWindowLayout();
- try {
- mLaunchRootTask = null;
- moveSplitScreenTasksToFullScreen();
- } finally {
- final ActivityStack topFullscreenStack =
- getTopStackInWindowingMode(WINDOWING_MODE_FULLSCREEN);
- final ActivityStack homeStack = getOrCreateRootHomeTask();
- if (topFullscreenStack != null && homeStack != null && !isTopStack(homeStack)) {
- // Whenever split-screen is dismissed we want the home stack directly behind the
- // current top fullscreen stack so it shows up when the top stack is finished.
- // TODO: Would be better to use ActivityDisplay.positionChildAt() for this, however
- // ActivityDisplay doesn't have a direct controller to WM side yet. We can switch
- // once we have that.
- homeStack.moveToFront("onSplitScreenModeDismissed");
- topFullscreenStack.moveToFront("onSplitScreenModeDismissed");
- }
- mAtmService.continueWindowLayout();
- }
- }
-
- private void moveSplitScreenTasksToFullScreen() {
- final WindowContainerTransaction wct = new WindowContainerTransaction();
- mTmpTasks.clear();
- forAllTasks(task -> {
- if (task.mCreatedByOrganizer && task.inSplitScreenWindowingMode() && task.hasChild()) {
- mTmpTasks.add(task);
- }
- });
-
- for (int i = mTmpTasks.size() - 1; i >= 0; i--) {
- final Task root = mTmpTasks.get(i);
- for (int j = 0; j < root.getChildCount(); j++) {
- wct.reparent(root.getChildAt(j).mRemoteToken, null, true /* toTop */);
- }
- }
- mAtmService.mWindowOrganizerController.applyTransaction(wct);
- }
-
- /**
- * Returns true if the {@param windowingMode} is supported based on other parameters passed in.
- * @param windowingMode The windowing mode we are checking support for.
- * @param supportsMultiWindow If we should consider support for multi-window mode in general.
- * @param supportsSplitScreen If we should consider support for split-screen multi-window.
- * @param supportsFreeform If we should consider support for freeform multi-window.
- * @param supportsPip If we should consider support for picture-in-picture mutli-window.
- * @param activityType The activity type under consideration.
- * @return true if the windowing mode is supported.
- */
- private boolean isWindowingModeSupported(int windowingMode, boolean supportsMultiWindow,
- boolean supportsSplitScreen, boolean supportsFreeform, boolean supportsPip,
- int activityType) {
-
- if (windowingMode == WINDOWING_MODE_UNDEFINED
- || windowingMode == WINDOWING_MODE_FULLSCREEN) {
- return true;
- }
- if (!supportsMultiWindow) {
- return false;
- }
-
- if (windowingMode == WINDOWING_MODE_MULTI_WINDOW) {
- return true;
- }
-
- final int displayWindowingMode = getWindowingMode();
- if (windowingMode == WINDOWING_MODE_SPLIT_SCREEN_PRIMARY
- || windowingMode == WINDOWING_MODE_SPLIT_SCREEN_SECONDARY) {
- return supportsSplitScreen
- && WindowConfiguration.supportSplitScreenWindowingMode(activityType)
- // Freeform windows and split-screen windows don't mix well, so prevent
- // split windowing modes on freeform displays.
- && displayWindowingMode != WINDOWING_MODE_FREEFORM;
- }
-
- if (!supportsFreeform && windowingMode == WINDOWING_MODE_FREEFORM) {
- return false;
- }
-
- if (!supportsPip && windowingMode == WINDOWING_MODE_PINNED) {
- return false;
- }
- return true;
- }
-
- /**
- * Resolves the windowing mode that an {@link ActivityRecord} would be in if started on this
- * display with the provided parameters.
- *
- * @param r The ActivityRecord in question.
- * @param options Options to start with.
- * @param task The task within-which the activity would start.
- * @param activityType The type of activity to start.
- * @return The resolved (not UNDEFINED) windowing-mode that the activity would be in.
- */
- int resolveWindowingMode(@Nullable ActivityRecord r, @Nullable ActivityOptions options,
- @Nullable Task task, int activityType) {
-
- // First preference if the windowing mode in the activity options if set.
- int windowingMode = (options != null)
- ? options.getLaunchWindowingMode() : WINDOWING_MODE_UNDEFINED;
-
- // If windowing mode is unset, then next preference is the candidate task, then the
- // activity record.
- if (windowingMode == WINDOWING_MODE_UNDEFINED) {
- if (task != null) {
- windowingMode = task.getWindowingMode();
- }
- if (windowingMode == WINDOWING_MODE_UNDEFINED && r != null) {
- windowingMode = r.getWindowingMode();
- }
- if (windowingMode == WINDOWING_MODE_UNDEFINED) {
- // Use the display's windowing mode.
- windowingMode = getWindowingMode();
- }
- }
- windowingMode = validateWindowingMode(windowingMode, r, task, activityType);
- return windowingMode != WINDOWING_MODE_UNDEFINED
- ? windowingMode : WINDOWING_MODE_FULLSCREEN;
- }
-
- /**
- * Check that the requested windowing-mode is appropriate for the specified task and/or activity
- * on this display.
- *
- * @param windowingMode The windowing-mode to validate.
- * @param r The {@link ActivityRecord} to check against.
- * @param task The {@link Task} to check against.
- * @param activityType An activity type.
- * @return The provided windowingMode or the closest valid mode which is appropriate.
- */
- int validateWindowingMode(int windowingMode, @Nullable ActivityRecord r, @Nullable Task task,
- int activityType) {
- // Make sure the windowing mode we are trying to use makes sense for what is supported.
- boolean supportsMultiWindow = mAtmService.mSupportsMultiWindow;
- boolean supportsSplitScreen = mAtmService.mSupportsSplitScreenMultiWindow;
- boolean supportsFreeform = mAtmService.mSupportsFreeformWindowManagement;
- boolean supportsPip = mAtmService.mSupportsPictureInPicture;
- if (supportsMultiWindow) {
- if (task != null) {
- supportsMultiWindow = task.isResizeable();
- supportsSplitScreen = task.supportsSplitScreenWindowingMode();
- // TODO: Do we need to check for freeform and Pip support here?
- } else if (r != null) {
- supportsMultiWindow = r.isResizeable();
- supportsSplitScreen = r.supportsSplitScreenWindowingMode();
- supportsFreeform = r.supportsFreeform();
- supportsPip = r.supportsPictureInPicture();
- }
- }
-
- final boolean inSplitScreenMode = isSplitScreenModeActivated();
- if (!inSplitScreenMode
- && windowingMode == WINDOWING_MODE_FULLSCREEN_OR_SPLIT_SCREEN_SECONDARY) {
- // Switch to the display's windowing mode if we are not in split-screen mode and we are
- // trying to launch in split-screen secondary.
- windowingMode = WINDOWING_MODE_UNDEFINED;
- } else if (inSplitScreenMode && (windowingMode == WINDOWING_MODE_FULLSCREEN
- || windowingMode == WINDOWING_MODE_UNDEFINED)
- && supportsSplitScreen) {
- windowingMode = WINDOWING_MODE_SPLIT_SCREEN_SECONDARY;
- }
-
- if (windowingMode != WINDOWING_MODE_UNDEFINED
- && isWindowingModeSupported(windowingMode, supportsMultiWindow, supportsSplitScreen,
- supportsFreeform, supportsPip, activityType)) {
- return windowingMode;
- }
- return WINDOWING_MODE_UNDEFINED;
- }
-
- boolean isTopStack(ActivityStack stack) {
- return stack == getTopStack();
- }
-
- boolean isTopNotPinnedStack(ActivityStack stack) {
- for (int i = getStackCount() - 1; i >= 0; --i) {
- final ActivityStack current = getStackAt(i);
- if (!current.inPinnedWindowingMode()) {
- return current == stack;
- }
- }
- return false;
+ mTaskContainers.removeStacksWithActivityTypes(activityTypes);
}
ActivityRecord topRunningActivity() {
@@ -6364,37 +5089,7 @@ class DisplayContent extends WindowContainer<DisplayContent.DisplayChildWindowCo
* @return The top running activity. {@code null} if none is available.
*/
ActivityRecord topRunningActivity(boolean considerKeyguardState) {
- ActivityRecord topRunning = null;
- final ActivityStack focusedStack = getFocusedStack();
- if (focusedStack != null) {
- topRunning = focusedStack.topRunningActivity();
- }
-
- // Look in other focusable stacks.
- if (topRunning == null) {
- for (int i = getStackCount() - 1; i >= 0; --i) {
- final ActivityStack stack = getStackAt(i);
- // Only consider focusable stacks other than the current focused one.
- if (stack == focusedStack || !stack.isTopActivityFocusable()) {
- continue;
- }
- topRunning = stack.topRunningActivity();
- if (topRunning != null) {
- break;
- }
- }
- }
-
- // This activity can be considered the top running activity if we are not considering
- // the locked state, the keyguard isn't locked, or we can show when locked.
- if (topRunning != null && considerKeyguardState
- && mRootWindowContainer.mStackSupervisor.getKeyguardController()
- .isKeyguardLocked()
- && !topRunning.canShowWhenLocked()) {
- return null;
- }
-
- return topRunning;
+ return mTaskContainers.topRunningActivity(considerKeyguardState);
}
boolean updateDisplayOverrideConfigurationLocked() {
@@ -6567,7 +5262,7 @@ class DisplayContent extends WindowContainer<DisplayContent.DisplayChildWindowCo
// If default display is in split-window mode, set windowing mode of the stack
// to split-screen secondary. Otherwise, set the windowing mode to undefined by
// default to let stack inherited the windowing mode from the new display.
- final int windowingMode = toDisplay.isSplitScreenModeActivated()
+ final int windowingMode = toDisplay.mTaskContainers.isSplitScreenModeActivated()
? WINDOWING_MODE_SPLIT_SCREEN_SECONDARY
: WINDOWING_MODE_UNDEFINED;
stack.reparent(toDisplay, true /* onTop */);
@@ -6599,7 +5294,7 @@ class DisplayContent extends WindowContainer<DisplayContent.DisplayChildWindowCo
}
}
- private void releaseSelfIfNeeded() {
+ void releaseSelfIfNeeded() {
if (!mRemoved) {
return;
}
@@ -6676,80 +5371,6 @@ class DisplayContent extends WindowContainer<DisplayContent.DisplayChildWindowCo
return (index < wc.mChildren.size()) ? (ActivityStack) wc.mChildren.get(index) : null;
}
- /**
- * Adjusts the {@param stack} behind the last visible stack in the display if necessary.
- * Generally used in conjunction with {@link #moveStackBehindStack}.
- */
- // TODO(b/151575894): Remove special stack movement methods.
- void moveStackBehindBottomMostVisibleStack(ActivityStack stack) {
- if (stack.shouldBeVisible(null)) {
- // Skip if the stack is already visible
- return;
- }
-
- final boolean isRootTask = stack.isRootTask();
- if (isRootTask) {
- // Move the stack to the bottom to not affect the following visibility checks
- positionStackAtBottom(stack);
- } else {
- stack.getParent().positionChildAt(POSITION_BOTTOM, stack, false /* includingParents */);
- }
-
- // Find the next position where the stack should be placed
- final int numStacks = isRootTask ? getStackCount() : stack.getParent().getChildCount();
- for (int stackNdx = 0; stackNdx < numStacks; stackNdx++) {
- final ActivityStack s = isRootTask ? getStackAt(stackNdx)
- : (ActivityStack) stack.getParent().getChildAt(stackNdx);
- if (s == stack) {
- continue;
- }
- final int winMode = s.getWindowingMode();
- final boolean isValidWindowingMode = winMode == WINDOWING_MODE_FULLSCREEN
- || winMode == WINDOWING_MODE_SPLIT_SCREEN_SECONDARY;
- if (s.shouldBeVisible(null) && isValidWindowingMode) {
- // Move the provided stack to behind this stack
- final int position = Math.max(0, stackNdx - 1);
- if (isRootTask) {
- positionStackAt(stack, position);
- } else {
- stack.getParent().positionChildAt(position, stack, false /*includingParents */);
- }
- break;
- }
- }
- }
-
- /**
- * Moves the {@param stack} behind the given {@param behindStack} if possible. If
- * {@param behindStack} is not currently in the display, then then the stack is moved to the
- * back. Generally used in conjunction with {@link #moveStackBehindBottomMostVisibleStack}.
- */
- void moveStackBehindStack(ActivityStack stack, ActivityStack behindStack) {
- if (behindStack == null || behindStack == stack) {
- return;
- }
-
- final WindowContainer parent = stack.getParent();
- if (parent == null || parent != behindStack.getParent()) {
- return;
- }
-
- // Note that positionChildAt will first remove the given stack before inserting into the
- // list, so we need to adjust the insertion index to account for the removed index
- // TODO: Remove this logic when WindowContainer.positionChildAt() is updated to adjust the
- // position internally
- final int stackIndex = parent.mChildren.indexOf(stack);
- final int behindStackIndex = parent.mChildren.indexOf(behindStack);
- final int insertIndex = stackIndex <= behindStackIndex
- ? behindStackIndex - 1 : behindStackIndex;
- final int position = Math.max(0, insertIndex);
- if (stack.isRootTask()) {
- positionStackAt(stack, position);
- } else {
- parent.positionChildAt(position, stack, false /* includingParents */);
- }
- }
-
void ensureActivitiesVisible(ActivityRecord starting, int configChanges,
boolean preserveWindows, boolean notifyClients) {
for (int stackNdx = getStackCount() - 1; stackNdx >= 0; --stackNdx) {
@@ -6759,50 +5380,6 @@ class DisplayContent extends WindowContainer<DisplayContent.DisplayChildWindowCo
}
}
- void moveHomeStackToFront(String reason) {
- final ActivityStack homeStack = getOrCreateRootHomeTask();
- if (homeStack != null) {
- homeStack.moveToFront(reason);
- }
- }
-
- /**
- * Moves the focusable home activity to top. If there is no such activity, the home stack will
- * still move to top.
- */
- void moveHomeActivityToTop(String reason) {
- final ActivityRecord top = getHomeActivity();
- if (top == null) {
- moveHomeStackToFront(reason);
- return;
- }
- top.moveFocusableActivityToTop(reason);
- }
-
- @Nullable
- ActivityRecord getHomeActivity() {
- return getHomeActivityForUser(mRootWindowContainer.mCurrentUser);
- }
-
- @Nullable
- ActivityRecord getHomeActivityForUser(int userId) {
- final ActivityStack homeStack = getRootHomeTask();
- if (homeStack == null) {
- return null;
- }
-
- final PooledPredicate p = PooledLambda.obtainPredicate(
- DisplayContent::isHomeActivityForUser, PooledLambda.__(ActivityRecord.class),
- userId);
- final ActivityRecord r = homeStack.getActivity(p);
- p.recycle();
- return r;
- }
-
- private static boolean isHomeActivityForUser(ActivityRecord r, int userId) {
- return r.isActivityTypeHome() && (userId == UserHandle.USER_ALL || r.mUserId == userId);
- }
-
boolean isSleeping() {
return mSleeping;
}
@@ -6833,7 +5410,7 @@ class DisplayContent extends WindowContainer<DisplayContent.DisplayChildWindowCo
* Notifies of a stack order change
* @param stack The stack which triggered the order change
*/
- private void onStackOrderChanged(ActivityStack stack) {
+ void onStackOrderChanged(ActivityStack stack) {
for (int i = mStackOrderChangedCallbacks.size() - 1; i >= 0; i--) {
mStackOrderChangedCallbacks.get(i).onStackOrderChanged(stack);
}
diff --git a/services/core/java/com/android/server/wm/KeyguardController.java b/services/core/java/com/android/server/wm/KeyguardController.java
index 6b39fd2a70e3..c10b8a56fc47 100644
--- a/services/core/java/com/android/server/wm/KeyguardController.java
+++ b/services/core/java/com/android/server/wm/KeyguardController.java
@@ -407,10 +407,10 @@ class KeyguardController {
// stack since it will be complicated/risky to try to put the activity on top
// of the lock screen in the right fullscreen configuration.
final DisplayContent display = mRootWindowContainer.getDefaultDisplay();
- if (!display.isSplitScreenModeActivated()) {
+ if (!display.mTaskContainers.isSplitScreenModeActivated()) {
return;
}
- display.onSplitScreenModeDismissed();
+ display.mTaskContainers.onSplitScreenModeDismissed();
}
}
diff --git a/services/core/java/com/android/server/wm/RecentsAnimation.java b/services/core/java/com/android/server/wm/RecentsAnimation.java
index 057592c0c2fc..26b263ed6674 100644
--- a/services/core/java/com/android/server/wm/RecentsAnimation.java
+++ b/services/core/java/com/android/server/wm/RecentsAnimation.java
@@ -208,7 +208,7 @@ class RecentsAnimation implements RecentsAnimationCallbacks,
try {
if (hasExistingActivity) {
// Move the recents activity into place for the animation if it is not top most
- mDefaultDisplay.moveStackBehindBottomMostVisibleStack(targetStack);
+ mDefaultDisplay.mTaskContainers.moveStackBehindBottomMostVisibleStack(targetStack);
ProtoLog.d(WM_DEBUG_RECENTS_ANIMATIONS, "Moved stack=%s behind stack=%s",
targetStack, mDefaultDisplay.getStackAbove(targetStack));
@@ -227,7 +227,7 @@ class RecentsAnimation implements RecentsAnimationCallbacks,
targetStack = mDefaultDisplay.getStack(WINDOWING_MODE_UNDEFINED,
mTargetActivityType);
targetActivity = getTargetActivity(targetStack);
- mDefaultDisplay.moveStackBehindBottomMostVisibleStack(targetStack);
+ mDefaultDisplay.mTaskContainers.moveStackBehindBottomMostVisibleStack(targetStack);
ProtoLog.d(WM_DEBUG_RECENTS_ANIMATIONS, "Moved stack=%s behind stack=%s",
targetStack, mDefaultDisplay.getStackAbove(targetStack));
@@ -352,7 +352,8 @@ class RecentsAnimation implements RecentsAnimationCallbacks,
} else if (reorderMode == REORDER_MOVE_TO_ORIGINAL_POSITION){
// Restore the target stack to its previous position
final DisplayContent display = targetActivity.getDisplay();
- display.moveStackBehindStack(targetStack, mRestoreTargetBehindStack);
+ display.mTaskContainers.moveStackBehindStack(targetStack,
+ mRestoreTargetBehindStack);
if (WM_DEBUG_RECENTS_ANIMATIONS.isLogToAny()) {
final ActivityStack aboveTargetStack =
mDefaultDisplay.getStackAbove(targetStack);
diff --git a/services/core/java/com/android/server/wm/ResetTargetTaskHelper.java b/services/core/java/com/android/server/wm/ResetTargetTaskHelper.java
index 420997a5b82d..770c08889ab9 100644
--- a/services/core/java/com/android/server/wm/ResetTargetTaskHelper.java
+++ b/services/core/java/com/android/server/wm/ResetTargetTaskHelper.java
@@ -257,8 +257,8 @@ class ResetTargetTaskHelper {
}
if (targetTask == null) {
if (alwaysCreateTask) {
- targetTask = display.getOrCreateStack(windowingMode, activityType,
- false /* onTop */);
+ targetTask = display.mTaskContainers.getOrCreateStack(windowingMode,
+ activityType, false /* onTop */);
} else {
targetTask = mTargetStack.reuseOrCreateTask(r.info, null /*intent*/,
false /*toTop*/);
diff --git a/services/core/java/com/android/server/wm/RootWindowContainer.java b/services/core/java/com/android/server/wm/RootWindowContainer.java
index 76c16d40bf2c..342703537a32 100644
--- a/services/core/java/com/android/server/wm/RootWindowContainer.java
+++ b/services/core/java/com/android/server/wm/RootWindowContainer.java
@@ -1371,7 +1371,8 @@ class RootWindowContainer extends WindowContainer<DisplayContent>
final DisplayContent defaultDisplay = getDefaultDisplay();
- defaultDisplay.getOrCreateStack(WINDOWING_MODE_FULLSCREEN, ACTIVITY_TYPE_HOME, ON_TOP);
+ defaultDisplay.mTaskContainers.getOrCreateStack(WINDOWING_MODE_FULLSCREEN,
+ ACTIVITY_TYPE_HOME, ON_TOP);
positionChildAt(POSITION_TOP, defaultDisplay, false /* includingParents */);
}
@@ -1440,7 +1441,7 @@ class RootWindowContainer extends WindowContainer<DisplayContent>
}
ActivityRecord getDefaultDisplayHomeActivityForUser(int userId) {
- return getDisplayContent(DEFAULT_DISPLAY).getHomeActivityForUser(userId);
+ return getDisplayContent(DEFAULT_DISPLAY).mTaskContainers.getHomeActivityForUser(userId);
}
boolean startHomeOnAllDisplays(int userId, String reason) {
@@ -1636,7 +1637,7 @@ class RootWindowContainer extends WindowContainer<DisplayContent>
displayId = DEFAULT_DISPLAY;
}
- final ActivityRecord r = getDisplayContent(displayId).getHomeActivity();
+ final ActivityRecord r = getDisplayContent(displayId).mTaskContainers.getHomeActivity();
final String myReason = reason + " resumeHomeActivity";
// Only resume home activity if isn't finishing.
@@ -1837,7 +1838,8 @@ class RootWindowContainer extends WindowContainer<DisplayContent>
// focus order.
for (int i = getChildCount() - 1; i >= 0; --i) {
final DisplayContent display = getChildAt(i);
- final ActivityRecord resumedActivityOnDisplay = display.getResumedActivity();
+ final ActivityRecord resumedActivityOnDisplay = display.mTaskContainers
+ .getResumedActivity();
if (resumedActivityOnDisplay != null) {
return resumedActivityOnDisplay;
}
@@ -1970,8 +1972,8 @@ class RootWindowContainer extends WindowContainer<DisplayContent>
final int focusStackId = topFocusedStack != null
? topFocusedStack.getRootTaskId() : INVALID_TASK_ID;
// We dismiss the docked stack whenever we switch users.
- if (getDefaultDisplay().isSplitScreenModeActivated()) {
- getDefaultDisplay().onSplitScreenModeDismissed();
+ if (getDefaultDisplay().mTaskContainers.isSplitScreenModeActivated()) {
+ getDefaultDisplay().mTaskContainers.onSplitScreenModeDismissed();
}
// Also dismiss the pinned stack whenever we switch users. Removing the pinned stack will
// also cause all tasks to be moved to the fullscreen stack at a position that is
@@ -1993,7 +1995,7 @@ class RootWindowContainer extends WindowContainer<DisplayContent>
final int restoreStackId = mUserStackInFront.get(userId);
ActivityStack stack = getStack(restoreStackId);
if (stack == null) {
- stack = getDefaultDisplay().getOrCreateRootHomeTask();
+ stack = getDefaultDisplay().mTaskContainers.getOrCreateRootHomeTask();
}
final boolean homeInFront = stack.isActivityTypeHome();
if (stack.isOnHomeDisplay()) {
@@ -2016,7 +2018,7 @@ class RootWindowContainer extends WindowContainer<DisplayContent>
void updateUserStack(int userId, ActivityStack stack) {
if (userId != mCurrentUser) {
if (stack == null) {
- stack = getDefaultDisplay().getOrCreateRootHomeTask();
+ stack = getDefaultDisplay().mTaskContainers.getOrCreateRootHomeTask();
}
mUserStackInFront.put(userId, stack.getRootTaskId());
@@ -2113,8 +2115,9 @@ class RootWindowContainer extends WindowContainer<DisplayContent>
} else {
// In the case of multiple activities, we will create a new task for it and then
// move the PIP activity into the task.
- stack = display.createStack(WINDOWING_MODE_PINNED, r.getActivityType(), ON_TOP,
- r.info, r.intent, false /* createdByOrganizer */);
+ stack = display.mTaskContainers.createStack(WINDOWING_MODE_PINNED,
+ r.getActivityType(), ON_TOP, r.info, r.intent,
+ false /* createdByOrganizer */);
// There are multiple activities in the task and moving the top activity should
// reveal/leave the other activities in their original task.
@@ -2153,7 +2156,8 @@ class RootWindowContainer extends WindowContainer<DisplayContent>
// Looking up task on preferred display first
final DisplayContent preferredDisplay = getDisplayContent(preferredDisplayId);
if (preferredDisplay != null) {
- preferredDisplay.findTaskLocked(r, true /* isPreferredDisplay */, mTmpFindTaskResult);
+ preferredDisplay.mTaskContainers.findTaskLocked(r, true /* isPreferredDisplay */,
+ mTmpFindTaskResult);
if (mTmpFindTaskResult.mIdealMatch) {
return mTmpFindTaskResult.mRecord;
}
@@ -2165,7 +2169,8 @@ class RootWindowContainer extends WindowContainer<DisplayContent>
continue;
}
- display.findTaskLocked(r, false /* isPreferredDisplay */, mTmpFindTaskResult);
+ display.mTaskContainers.findTaskLocked(r, false /* isPreferredDisplay */,
+ mTmpFindTaskResult);
if (mTmpFindTaskResult.mIdealMatch) {
return mTmpFindTaskResult.mRecord;
}
@@ -2232,7 +2237,8 @@ class RootWindowContainer extends WindowContainer<DisplayContent>
resumedOnDisplay |= result;
continue;
}
- if (display.isTopStack(stack) && topRunningActivity.isState(RESUMED)) {
+ if (display.mTaskContainers.isTopStack(stack)
+ && topRunningActivity.isState(RESUMED)) {
// Kick off any lingering app transitions form the MoveTaskToFront operation,
// but only consider the top task and stack on that display.
stack.executeAppTransition(targetOptions);
@@ -2307,7 +2313,7 @@ class RootWindowContainer extends WindowContainer<DisplayContent>
protected ActivityStack getStack(int stackId) {
for (int i = getChildCount() - 1; i >= 0; --i) {
- final ActivityStack stack = getChildAt(i).getStack(stackId);
+ final ActivityStack stack = getChildAt(i).mTaskContainers.getStack(stackId);
if (stack != null) {
return stack;
}
@@ -2783,7 +2789,8 @@ class RootWindowContainer extends WindowContainer<DisplayContent>
}
final DisplayContent display = getDisplayContentOrCreate(displayId);
if (display != null) {
- stack = display.getOrCreateStack(r, options, candidateTask, activityType, onTop);
+ stack = display.mTaskContainers.getOrCreateStack(r, options, candidateTask,
+ activityType, onTop);
if (stack != null) {
return stack;
}
@@ -2805,8 +2812,8 @@ class RootWindowContainer extends WindowContainer<DisplayContent>
display = stack.getDisplay();
if (display != null && canLaunchOnDisplay(r, display.mDisplayId)) {
if (windowingMode == WindowConfiguration.WINDOWING_MODE_UNDEFINED) {
- windowingMode = display.resolveWindowingMode(r, options, candidateTask,
- activityType);
+ windowingMode = display.mTaskContainers.resolveWindowingMode(r, options,
+ candidateTask, activityType);
}
// Always allow organized tasks that created by organizer since the activity type
// of an organized task is decided by the activity type of its top child, which
@@ -2830,12 +2837,13 @@ class RootWindowContainer extends WindowContainer<DisplayContent>
if (display == null || !canLaunchOnDisplay(r, display.mDisplayId)) {
display = getDefaultDisplay();
if (windowingMode == WindowConfiguration.WINDOWING_MODE_UNDEFINED) {
- windowingMode = display.resolveWindowingMode(r, options, candidateTask,
- activityType);
+ windowingMode = display.mTaskContainers.resolveWindowingMode(r, options,
+ candidateTask, activityType);
}
}
- return display.getOrCreateStack(r, options, candidateTask, activityType, onTop);
+ return display.mTaskContainers.getOrCreateStack(r, options, candidateTask, activityType,
+ onTop);
}
/** @return true if activity record is null or can be launched on provided display. */
@@ -2895,8 +2903,8 @@ class RootWindowContainer extends WindowContainer<DisplayContent>
windowingMode = options != null ? options.getLaunchWindowingMode()
: r.getWindowingMode();
}
- windowingMode = displayContent.validateWindowingMode(windowingMode, r, candidateTask,
- r.getActivityType());
+ windowingMode = displayContent.mTaskContainers.validateWindowingMode(windowingMode, r,
+ candidateTask, r.getActivityType());
// Return the topmost valid stack on the display.
for (int i = displayContent.getStackCount() - 1; i >= 0; --i) {
@@ -2984,8 +2992,8 @@ class RootWindowContainer extends WindowContainer<DisplayContent>
// was on.
preferredDisplay = getDisplayContent(currentFocus.mPrevDisplayId);
}
- final ActivityStack preferredFocusableStack = preferredDisplay.getNextFocusableStack(
- currentFocus, ignoreCurrent);
+ final ActivityStack preferredFocusableStack = preferredDisplay.mTaskContainers
+ .getNextFocusableStack(currentFocus, ignoreCurrent);
if (preferredFocusableStack != null) {
return preferredFocusableStack;
}
@@ -3003,8 +3011,8 @@ class RootWindowContainer extends WindowContainer<DisplayContent>
// We've already checked this one
continue;
}
- final ActivityStack nextFocusableStack = display.getNextFocusableStack(currentFocus,
- ignoreCurrent);
+ final ActivityStack nextFocusableStack = display.mTaskContainers
+ .getNextFocusableStack(currentFocus, ignoreCurrent);
if (nextFocusableStack != null) {
return nextFocusableStack;
}
@@ -3416,7 +3424,8 @@ class RootWindowContainer extends WindowContainer<DisplayContent>
boolean allFocusedProcessesDiffer = true;
for (int displayNdx = 0; displayNdx < getChildCount(); ++displayNdx) {
final DisplayContent displayContent = getChildAt(displayNdx);
- final ActivityRecord resumedActivity = displayContent.getResumedActivity();
+ final ActivityRecord resumedActivity = displayContent.mTaskContainers
+ .getResumedActivity();
final WindowProcessController resumedActivityProcess =
resumedActivity == null ? null : resumedActivity.app;
@@ -3517,8 +3526,8 @@ class RootWindowContainer extends WindowContainer<DisplayContent>
printed = stack.dump(fd, pw, dumpAll, dumpClient, dumpPackage, needSep);
needSep = printed;
}
- printThisActivity(pw, displayContent.getResumedActivity(), dumpPackage, needSep,
- " ResumedActivity:");
+ printThisActivity(pw, displayContent.mTaskContainers.getResumedActivity(), dumpPackage,
+ needSep, " ResumedActivity:");
}
printed |= dumpHistoryList(fd, pw, mStackSupervisor.mFinishingActivities, " ",
diff --git a/services/core/java/com/android/server/wm/Task.java b/services/core/java/com/android/server/wm/Task.java
index 91b4ec95f8fd..10be11aa940b 100644
--- a/services/core/java/com/android/server/wm/Task.java
+++ b/services/core/java/com/android/server/wm/Task.java
@@ -1386,7 +1386,7 @@ class Task extends WindowContainer<WindowContainer> {
// A rootable task that is now being added to be the child of an organized task. Making
// sure the stack references is keep updated.
if (mTaskOrganizer != null && mCreatedByOrganizer && child.asTask() != null) {
- mDisplayContent.addStackReferenceIfNeeded((ActivityStack) child);
+ mDisplayContent.mTaskContainers.addStackReferenceIfNeeded((ActivityStack) child);
}
// Make sure the list of display UID whitelists is updated
@@ -1432,7 +1432,7 @@ class Task extends WindowContainer<WindowContainer> {
// A rootable child task that is now being removed from an organized task. Making sure
// the stack references is keep updated.
if (mTaskOrganizer != null && mCreatedByOrganizer && child.asTask() != null) {
- mDisplayContent.removeStackReferenceIfNeeded((ActivityStack) child);
+ mDisplayContent.mTaskContainers.removeStackReferenceIfNeeded((ActivityStack) child);
}
removeChild(child, "removeChild");
}
diff --git a/services/core/java/com/android/server/wm/TaskContainers.java b/services/core/java/com/android/server/wm/TaskContainers.java
new file mode 100644
index 000000000000..540bc9bbf997
--- /dev/null
+++ b/services/core/java/com/android/server/wm/TaskContainers.java
@@ -0,0 +1,1533 @@
+/*
+ * 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.server.wm;
+
+import static android.app.WindowConfiguration.ACTIVITY_TYPE_HOME;
+import static android.app.WindowConfiguration.ACTIVITY_TYPE_STANDARD;
+import static android.app.WindowConfiguration.ACTIVITY_TYPE_UNDEFINED;
+import static android.app.WindowConfiguration.WINDOWING_MODE_FREEFORM;
+import static android.app.WindowConfiguration.WINDOWING_MODE_FULLSCREEN;
+import static android.app.WindowConfiguration.WINDOWING_MODE_FULLSCREEN_OR_SPLIT_SCREEN_SECONDARY;
+import static android.app.WindowConfiguration.WINDOWING_MODE_MULTI_WINDOW;
+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.app.WindowConfiguration.WINDOWING_MODE_UNDEFINED;
+import static android.app.WindowConfiguration.isSplitScreenWindowingMode;
+import static android.content.pm.ActivityInfo.SCREEN_ORIENTATION_BEHIND;
+import static android.content.pm.ActivityInfo.SCREEN_ORIENTATION_UNSET;
+import static android.content.pm.ActivityInfo.SCREEN_ORIENTATION_UNSPECIFIED;
+import static android.window.WindowOrganizer.DisplayAreaOrganizer.FEATURE_TASK_CONTAINER;
+
+import static com.android.server.wm.ActivityStack.ActivityState.RESUMED;
+import static com.android.server.wm.ActivityStack.STACK_VISIBILITY_VISIBLE;
+import static com.android.server.wm.ActivityStackSupervisor.TAG_TASKS;
+import static com.android.server.wm.ActivityTaskManagerDebugConfig.DEBUG_STATES;
+import static com.android.server.wm.ActivityTaskManagerDebugConfig.DEBUG_TASKS;
+import static com.android.server.wm.ActivityTaskManagerService.TAG_STACK;
+import static com.android.server.wm.DisplayContent.alwaysCreateStack;
+import static com.android.server.wm.ProtoLogGroup.WM_DEBUG_ADD_REMOVE;
+import static com.android.server.wm.ProtoLogGroup.WM_DEBUG_ORIENTATION;
+import static com.android.server.wm.RootWindowContainer.TAG_STATES;
+import static com.android.server.wm.WindowManagerDebugConfig.TAG_WM;
+
+import android.annotation.Nullable;
+import android.app.ActivityOptions;
+import android.app.WindowConfiguration;
+import android.content.Intent;
+import android.content.pm.ActivityInfo;
+import android.content.pm.ApplicationInfo;
+import android.os.UserHandle;
+import android.util.Slog;
+import android.view.SurfaceControl;
+import android.window.WindowContainerTransaction;
+
+import com.android.internal.annotations.VisibleForTesting;
+import com.android.internal.util.ToBooleanFunction;
+import com.android.internal.util.function.pooled.PooledLambda;
+import com.android.internal.util.function.pooled.PooledPredicate;
+import com.android.server.protolog.common.ProtoLog;
+
+import java.util.ArrayList;
+import java.util.List;
+
+/**
+ * Window container class that contains all containers on this display relating to Apps.
+ * I.e Activities.
+ */
+final class TaskContainers extends DisplayArea<ActivityStack> {
+ private DisplayContent mDisplayContent;
+ /**
+ * A control placed at the appropriate level for transitions to occur.
+ */
+ private SurfaceControl mAppAnimationLayer;
+ private SurfaceControl mBoostedAppAnimationLayer;
+ private SurfaceControl mHomeAppAnimationLayer;
+
+ /**
+ * Given that the split-screen divider does not have an AppWindowToken, it
+ * will have to live inside of a "NonAppWindowContainer". However, in visual Z order
+ * it will need to be interleaved with some of our children, appearing on top of
+ * both docked stacks but underneath any assistant stacks.
+ *
+ * To solve this problem we have this anchor control, which will always exist so
+ * we can always assign it the correct value in our {@link #assignChildLayers}.
+ * Likewise since it always exists, we can always
+ * assign the divider a layer relative to it. This way we prevent linking lifecycle
+ * events between tasks and the divider window.
+ */
+ private SurfaceControl mSplitScreenDividerAnchor;
+
+ // Cached reference to some special tasks we tend to get a lot so we don't need to loop
+ // through the list to find them.
+ private ActivityStack mRootHomeTask;
+ private ActivityStack mRootPinnedTask;
+ private ActivityStack mRootSplitScreenPrimaryTask;
+
+ private final ArrayList<ActivityStack> mTmpAlwaysOnTopStacks = new ArrayList<>();
+ private final ArrayList<ActivityStack> mTmpNormalStacks = new ArrayList<>();
+ private final ArrayList<ActivityStack> mTmpHomeStacks = new ArrayList<>();
+
+ private ArrayList<Task> mTmpTasks = new ArrayList<>();
+
+ private ActivityTaskManagerService mAtmService;
+
+ private RootWindowContainer mRootWindowContainer;
+
+ // When non-null, new tasks get put into this root task.
+ private Task mLaunchRootTask = null;
+
+ /**
+ * A focusable stack that is purposely to be positioned at the top. Although the stack may not
+ * have the topmost index, it is used as a preferred candidate to prevent being unable to resume
+ * target stack properly when there are other focusable always-on-top stacks.
+ */
+ private ActivityStack mPreferredTopFocusableStack;
+
+ private final RootWindowContainer.FindTaskResult
+ mTmpFindTaskResult = new RootWindowContainer.FindTaskResult();
+
+ /**
+ * If this is the same as {@link #getFocusedStack} then the activity on the top of the focused
+ * stack has been resumed. If stacks are changing position this will hold the old stack until
+ * the new stack becomes resumed after which it will be set to current focused stack.
+ */
+ ActivityStack mLastFocusedStack;
+
+ TaskContainers(DisplayContent displayContent, WindowManagerService service) {
+ super(service, Type.ANY, "TaskContainers", FEATURE_TASK_CONTAINER);
+ mDisplayContent = displayContent;
+ mRootWindowContainer = service.mRoot;
+ mAtmService = service.mAtmService;
+ }
+
+ /**
+ * Returns the topmost stack on the display that is compatible with the input windowing mode
+ * and activity type. Null is no compatible stack on the display.
+ */
+ ActivityStack getStack(int windowingMode, int activityType) {
+ if (activityType == ACTIVITY_TYPE_HOME) {
+ return mRootHomeTask;
+ }
+ if (windowingMode == WINDOWING_MODE_PINNED) {
+ return mRootPinnedTask;
+ } else if (windowingMode == WINDOWING_MODE_SPLIT_SCREEN_PRIMARY) {
+ return mRootSplitScreenPrimaryTask;
+ }
+ for (int i = getChildCount() - 1; i >= 0; --i) {
+ final ActivityStack stack = getChildAt(i);
+ if (activityType == ACTIVITY_TYPE_UNDEFINED
+ && windowingMode == stack.getWindowingMode()) {
+ // Passing in undefined type means we want to match the topmost stack with the
+ // windowing mode.
+ return stack;
+ }
+ if (stack.isCompatible(windowingMode, activityType)) {
+ return stack;
+ }
+ }
+ return null;
+ }
+
+ @VisibleForTesting
+ ActivityStack getTopStack() {
+ final int count = getChildCount();
+ return count > 0 ? getChildAt(count - 1) : null;
+ }
+
+ int getIndexOf(ActivityStack stack) {
+ return mChildren.indexOf(stack);
+ }
+
+ ActivityStack getRootHomeTask() {
+ return mRootHomeTask;
+ }
+
+ ActivityStack getRootPinnedTask() {
+ return mRootPinnedTask;
+ }
+
+ ActivityStack getRootSplitScreenPrimaryTask() {
+ return mRootSplitScreenPrimaryTask;
+ }
+
+ ArrayList<Task> getVisibleTasks() {
+ final ArrayList<Task> visibleTasks = new ArrayList<>();
+ forAllTasks(task -> {
+ if (task.isLeafTask() && task.isVisible()) {
+ visibleTasks.add(task);
+ }
+ });
+ return visibleTasks;
+ }
+
+ void onStackWindowingModeChanged(ActivityStack stack) {
+ removeStackReferenceIfNeeded(stack);
+ addStackReferenceIfNeeded(stack);
+ if (stack == mRootPinnedTask && getTopStack() != stack) {
+ // Looks like this stack changed windowing mode to pinned. Move it to the top.
+ positionChildAt(POSITION_TOP, stack, false /* includingParents */);
+ }
+ }
+
+ void addStackReferenceIfNeeded(ActivityStack stack) {
+ if (stack.isActivityTypeHome()) {
+ if (mRootHomeTask != null) {
+ if (!stack.isDescendantOf(mRootHomeTask)) {
+ throw new IllegalArgumentException("addStackReferenceIfNeeded: home stack="
+ + mRootHomeTask + " already exist on display=" + this
+ + " stack=" + stack);
+ }
+ } else {
+ mRootHomeTask = stack;
+ }
+ }
+
+ if (!stack.isRootTask()) {
+ return;
+ }
+ final int windowingMode = stack.getWindowingMode();
+ if (windowingMode == WINDOWING_MODE_PINNED) {
+ if (mRootPinnedTask != null) {
+ throw new IllegalArgumentException(
+ "addStackReferenceIfNeeded: pinned stack=" + mRootPinnedTask
+ + " already exist on display=" + this + " stack=" + stack);
+ }
+ mRootPinnedTask = stack;
+ } else if (windowingMode == WINDOWING_MODE_SPLIT_SCREEN_PRIMARY) {
+ if (mRootSplitScreenPrimaryTask != null) {
+ throw new IllegalArgumentException(
+ "addStackReferenceIfNeeded: split screen primary stack="
+ + mRootSplitScreenPrimaryTask
+ + " already exist on display=" + this + " stack=" + stack);
+ }
+ mRootSplitScreenPrimaryTask = stack;
+ }
+ }
+
+ void removeStackReferenceIfNeeded(ActivityStack stack) {
+ if (stack == mRootHomeTask) {
+ mRootHomeTask = null;
+ } else if (stack == mRootPinnedTask) {
+ mRootPinnedTask = null;
+ } else if (stack == mRootSplitScreenPrimaryTask) {
+ mRootSplitScreenPrimaryTask = null;
+ }
+ }
+
+ @Override
+ void addChild(ActivityStack stack, int position) {
+ addStackReferenceIfNeeded(stack);
+ position = findPositionForStack(position, stack, true /* adding */);
+
+ super.addChild(stack, position);
+ mAtmService.updateSleepIfNeededLocked();
+
+ // The reparenting case is handled in WindowContainer.
+ if (!stack.mReparenting) {
+ mDisplayContent.setLayoutNeeded();
+ }
+ }
+
+ @Override
+ protected void removeChild(ActivityStack stack) {
+ super.removeChild(stack);
+ onStackRemoved(stack);
+ mAtmService.updateSleepIfNeededLocked();
+ removeStackReferenceIfNeeded(stack);
+ }
+
+ @Override
+ boolean isOnTop() {
+ // Considered always on top
+ return true;
+ }
+
+ @Override
+ void positionChildAt(int position, ActivityStack child, boolean includingParents) {
+ final boolean moveToTop = (position == POSITION_TOP || position == getChildCount());
+ final boolean moveToBottom = (position == POSITION_BOTTOM || position == 0);
+ if (child.getWindowConfiguration().isAlwaysOnTop() && !moveToTop) {
+ // This stack is always-on-top, override the default behavior.
+ Slog.w(TAG_WM, "Ignoring move of always-on-top stack=" + this + " to bottom");
+
+ // Moving to its current position, as we must call super but we don't want to
+ // perform any meaningful action.
+ final int currentPosition = mChildren.indexOf(child);
+ super.positionChildAt(currentPosition, child, false /* includingParents */);
+ return;
+ }
+ // We don't allow untrusted display to top when task stack moves to top,
+ // until user tapping this display to change display position as top intentionally.
+ if (mDisplayContent.isUntrustedVirtualDisplay() && !getParent().isOnTop()) {
+ includingParents = false;
+ }
+ final int targetPosition = findPositionForStack(position, child, false /* adding */);
+ super.positionChildAt(targetPosition, child, false /* includingParents */);
+
+ if (includingParents && (moveToTop || moveToBottom)) {
+ // The DisplayContent children do not re-order, but we still want to move the
+ // display of this stack container because the intention of positioning is to have
+ // higher z-order to gain focus.
+ mDisplayContent.positionDisplayAt(moveToTop ? POSITION_TOP : POSITION_BOTTOM,
+ true /* includingParents */);
+ }
+
+ child.updateTaskMovement(moveToTop);
+
+ mDisplayContent.setLayoutNeeded();
+ }
+
+ /**
+ * When stack is added or repositioned, find a proper position for it.
+ * This will make sure that pinned stack always stays on top.
+ * @param requestedPosition Position requested by caller.
+ * @param stack Stack to be added or positioned.
+ * @param adding Flag indicates whether we're adding a new stack or positioning an existing.
+ * @return The proper position for the stack.
+ */
+ private int findPositionForStack(int requestedPosition, ActivityStack stack,
+ boolean adding) {
+ if (stack.isActivityTypeDream()) {
+ return POSITION_TOP;
+ }
+
+ if (stack.inPinnedWindowingMode()) {
+ return POSITION_TOP;
+ }
+
+ final int topChildPosition = mChildren.size() - 1;
+ int belowAlwaysOnTopPosition = POSITION_BOTTOM;
+ for (int i = topChildPosition; i >= 0; --i) {
+ // Since a stack could be repositioned while being one of the child, return
+ // current index if that's the same stack we are positioning and it is always on
+ // top.
+ final boolean sameStack = mDisplayContent.getStacks().get(i) == stack;
+ if ((sameStack && stack.isAlwaysOnTop())
+ || (!sameStack && !mDisplayContent.getStacks().get(i).isAlwaysOnTop())) {
+ belowAlwaysOnTopPosition = i;
+ break;
+ }
+ }
+
+ // The max possible position we can insert the stack at.
+ int maxPosition = POSITION_TOP;
+ // The min possible position we can insert the stack at.
+ int minPosition = POSITION_BOTTOM;
+
+ if (stack.isAlwaysOnTop()) {
+ if (mDisplayContent.hasPinnedTask()) {
+ // Always-on-top stacks go below the pinned stack.
+ maxPosition = mDisplayContent.getStacks().indexOf(mRootPinnedTask) - 1;
+ }
+ // Always-on-top stacks need to be above all other stacks.
+ minPosition = belowAlwaysOnTopPosition
+ != POSITION_BOTTOM ? belowAlwaysOnTopPosition : topChildPosition;
+ } else {
+ // Other stacks need to be below the always-on-top stacks.
+ maxPosition = belowAlwaysOnTopPosition
+ != POSITION_BOTTOM ? belowAlwaysOnTopPosition : 0;
+ }
+
+ // Cap the requested position to something reasonable for the previous position check
+ // below.
+ if (requestedPosition == POSITION_TOP) {
+ requestedPosition = mChildren.size();
+ } else if (requestedPosition == POSITION_BOTTOM) {
+ requestedPosition = 0;
+ }
+
+ int targetPosition = requestedPosition;
+ targetPosition = Math.min(targetPosition, maxPosition);
+ targetPosition = Math.max(targetPosition, minPosition);
+
+ int prevPosition = mDisplayContent.getStacks().indexOf(stack);
+ // The positions we calculated above (maxPosition, minPosition) do not take into
+ // consideration the following edge cases.
+ // 1) We need to adjust the position depending on the value "adding".
+ // 2) When we are moving a stack to another position, we also need to adjust the
+ // position depending on whether the stack is moving to a higher or lower position.
+ if ((targetPosition != requestedPosition) && (adding || targetPosition < prevPosition)) {
+ targetPosition++;
+ }
+
+ return targetPosition;
+ }
+
+ @Override
+ boolean forAllWindows(ToBooleanFunction<WindowState> callback,
+ boolean traverseTopToBottom) {
+ if (traverseTopToBottom) {
+ if (super.forAllWindows(callback, traverseTopToBottom)) {
+ return true;
+ }
+ if (forAllExitingAppTokenWindows(callback, traverseTopToBottom)) {
+ return true;
+ }
+ } else {
+ if (forAllExitingAppTokenWindows(callback, traverseTopToBottom)) {
+ return true;
+ }
+ if (super.forAllWindows(callback, traverseTopToBottom)) {
+ return true;
+ }
+ }
+ return false;
+ }
+
+ private boolean forAllExitingAppTokenWindows(ToBooleanFunction<WindowState> callback,
+ boolean traverseTopToBottom) {
+ // For legacy reasons we process the TaskStack.mExitingActivities first here before the
+ // app tokens.
+ // TODO: Investigate if we need to continue to do this or if we can just process them
+ // in-order.
+ if (traverseTopToBottom) {
+ for (int i = mChildren.size() - 1; i >= 0; --i) {
+ final List<ActivityRecord> activities = mChildren.get(i).mExitingActivities;
+ for (int j = activities.size() - 1; j >= 0; --j) {
+ if (activities.get(j).forAllWindowsUnchecked(callback,
+ traverseTopToBottom)) {
+ return true;
+ }
+ }
+ }
+ } else {
+ final int count = mChildren.size();
+ for (int i = 0; i < count; ++i) {
+ final List<ActivityRecord> activities = mChildren.get(i).mExitingActivities;
+ final int appTokensCount = activities.size();
+ for (int j = 0; j < appTokensCount; j++) {
+ if (activities.get(j).forAllWindowsUnchecked(callback,
+ traverseTopToBottom)) {
+ return true;
+ }
+ }
+ }
+ }
+ return false;
+ }
+
+ void setExitingTokensHasVisible(boolean hasVisible) {
+ for (int i = mChildren.size() - 1; i >= 0; --i) {
+ final ArrayList<ActivityRecord> activities = mChildren.get(i).mExitingActivities;
+ for (int j = activities.size() - 1; j >= 0; --j) {
+ activities.get(j).hasVisible = hasVisible;
+ }
+ }
+ }
+
+ void removeExistingAppTokensIfPossible() {
+ for (int i = mChildren.size() - 1; i >= 0; --i) {
+ final ArrayList<ActivityRecord> activities = mChildren.get(i).mExitingActivities;
+ for (int j = activities.size() - 1; j >= 0; --j) {
+ final ActivityRecord activity = activities.get(j);
+ if (!activity.hasVisible && !mDisplayContent.mClosingApps.contains(activity)
+ && (!activity.mIsExiting || activity.isEmpty())) {
+ // Make sure there is no animation running on this activity, so any windows
+ // associated with it will be removed as soon as their animations are
+ // complete.
+ cancelAnimation();
+ ProtoLog.v(WM_DEBUG_ADD_REMOVE,
+ "performLayout: Activity exiting now removed %s", activity);
+ activity.removeIfPossible();
+ }
+ }
+ }
+ }
+
+ @Override
+ int getOrientation(int candidate) {
+ if (mDisplayContent.isStackVisible(WINDOWING_MODE_SPLIT_SCREEN_PRIMARY)) {
+ // Apps and their containers are not allowed to specify an orientation while using
+ // root tasks...except for the home stack if it is not resizable and currently
+ // visible (top of) its root task.
+ if (mRootHomeTask != null && mRootHomeTask.isVisible()) {
+ final Task topMost = mRootHomeTask.getTopMostTask();
+ final boolean resizable = topMost != null && topMost.isResizeable();
+ if (!(resizable && mRootHomeTask.matchParentBounds())) {
+ final int orientation = mRootHomeTask.getOrientation();
+ if (orientation != SCREEN_ORIENTATION_UNSET) {
+ return orientation;
+ }
+ }
+ }
+ return SCREEN_ORIENTATION_UNSPECIFIED;
+ }
+
+ final int orientation = super.getOrientation(candidate);
+ if (orientation != SCREEN_ORIENTATION_UNSET
+ && orientation != SCREEN_ORIENTATION_BEHIND) {
+ ProtoLog.v(WM_DEBUG_ORIENTATION,
+ "App is requesting an orientation, return %d for display id=%d",
+ orientation, mDisplayContent.mDisplayId);
+ return orientation;
+ }
+
+ ProtoLog.v(WM_DEBUG_ORIENTATION,
+ "No app is requesting an orientation, return %d for display id=%d",
+ mDisplayContent.getLastOrientation(), mDisplayContent.mDisplayId);
+ // The next app has not been requested to be visible, so we keep the current orientation
+ // to prevent freezing/unfreezing the display too early.
+ return mDisplayContent.getLastOrientation();
+ }
+
+ @Override
+ void assignChildLayers(SurfaceControl.Transaction t) {
+ assignStackOrdering(t);
+
+ for (int i = 0; i < mChildren.size(); i++) {
+ final ActivityStack s = mChildren.get(i);
+ s.assignChildLayers(t);
+ }
+ }
+
+ void assignStackOrdering(SurfaceControl.Transaction t) {
+ if (getParent() == null) {
+ return;
+ }
+ mTmpAlwaysOnTopStacks.clear();
+ mTmpHomeStacks.clear();
+ mTmpNormalStacks.clear();
+ for (int i = 0; i < mChildren.size(); ++i) {
+ final ActivityStack s = mChildren.get(i);
+ if (s.isAlwaysOnTop()) {
+ mTmpAlwaysOnTopStacks.add(s);
+ } else if (s.isActivityTypeHome()) {
+ mTmpHomeStacks.add(s);
+ } else {
+ mTmpNormalStacks.add(s);
+ }
+ }
+
+ int layer = 0;
+ // Place home stacks to the bottom.
+ for (int i = 0; i < mTmpHomeStacks.size(); i++) {
+ mTmpHomeStacks.get(i).assignLayer(t, layer++);
+ }
+ // The home animation layer is between the home stacks and the normal stacks.
+ final int layerForHomeAnimationLayer = layer++;
+ int layerForSplitScreenDividerAnchor = layer++;
+ int layerForAnimationLayer = layer++;
+ for (int i = 0; i < mTmpNormalStacks.size(); i++) {
+ final ActivityStack s = mTmpNormalStacks.get(i);
+ s.assignLayer(t, layer++);
+ if (s.inSplitScreenWindowingMode()) {
+ // The split screen divider anchor is located above the split screen window.
+ layerForSplitScreenDividerAnchor = layer++;
+ }
+ if (s.isTaskAnimating() || s.isAppTransitioning()) {
+ // The animation layer is located above the highest animating stack and no
+ // higher.
+ layerForAnimationLayer = layer++;
+ }
+ }
+ // The boosted animation layer is between the normal stacks and the always on top
+ // stacks.
+ final int layerForBoostedAnimationLayer = layer++;
+ for (int i = 0; i < mTmpAlwaysOnTopStacks.size(); i++) {
+ mTmpAlwaysOnTopStacks.get(i).assignLayer(t, layer++);
+ }
+
+ t.setLayer(mHomeAppAnimationLayer, layerForHomeAnimationLayer);
+ t.setLayer(mAppAnimationLayer, layerForAnimationLayer);
+ t.setLayer(mSplitScreenDividerAnchor, layerForSplitScreenDividerAnchor);
+ t.setLayer(mBoostedAppAnimationLayer, layerForBoostedAnimationLayer);
+ }
+
+ @Override
+ SurfaceControl getAppAnimationLayer(@AnimationLayer int animationLayer) {
+ switch (animationLayer) {
+ case ANIMATION_LAYER_BOOSTED:
+ return mBoostedAppAnimationLayer;
+ case ANIMATION_LAYER_HOME:
+ return mHomeAppAnimationLayer;
+ case ANIMATION_LAYER_STANDARD:
+ default:
+ return mAppAnimationLayer;
+ }
+ }
+
+ SurfaceControl getSplitScreenDividerAnchor() {
+ return mSplitScreenDividerAnchor;
+ }
+
+ @Override
+ void onParentChanged(ConfigurationContainer newParent, ConfigurationContainer oldParent) {
+ if (getParent() != null) {
+ super.onParentChanged(newParent, oldParent, () -> {
+ mAppAnimationLayer = makeChildSurface(null)
+ .setName("animationLayer")
+ .build();
+ mBoostedAppAnimationLayer = makeChildSurface(null)
+ .setName("boostedAnimationLayer")
+ .build();
+ mHomeAppAnimationLayer = makeChildSurface(null)
+ .setName("homeAnimationLayer")
+ .build();
+ mSplitScreenDividerAnchor = makeChildSurface(null)
+ .setName("splitScreenDividerAnchor")
+ .build();
+ getPendingTransaction()
+ .show(mAppAnimationLayer)
+ .show(mBoostedAppAnimationLayer)
+ .show(mHomeAppAnimationLayer)
+ .show(mSplitScreenDividerAnchor);
+ });
+ } else {
+ super.onParentChanged(newParent, oldParent);
+ mWmService.mTransactionFactory.get()
+ .remove(mAppAnimationLayer)
+ .remove(mBoostedAppAnimationLayer)
+ .remove(mHomeAppAnimationLayer)
+ .remove(mSplitScreenDividerAnchor)
+ .apply();
+ mAppAnimationLayer = null;
+ mBoostedAppAnimationLayer = null;
+ mHomeAppAnimationLayer = null;
+ mSplitScreenDividerAnchor = null;
+ }
+ }
+
+ void addStack(ActivityStack stack, int position) {
+ mDisplayContent.setStackOnDisplay(stack, position);
+ positionStackAt(stack, position);
+ }
+
+ void onStackRemoved(ActivityStack stack) {
+ if (ActivityTaskManagerDebugConfig.DEBUG_STACK) {
+ Slog.v(TAG_STACK, "removeStack: detaching " + stack + " from displayId="
+ + mDisplayContent.mDisplayId);
+ }
+ if (mPreferredTopFocusableStack == stack) {
+ mPreferredTopFocusableStack = null;
+ }
+ mDisplayContent.releaseSelfIfNeeded();
+ mDisplayContent.onStackOrderChanged(stack);
+ }
+
+ void positionStackAt(int position, ActivityStack child, boolean includingParents) {
+ positionChildAt(position, child, includingParents);
+ mDisplayContent.layoutAndAssignWindowLayersIfNeeded();
+ }
+
+ void positionStackAtTop(ActivityStack stack, boolean includingParents) {
+ positionStackAtTop(stack, includingParents, null /* updateLastFocusedStackReason */);
+ }
+
+ void positionStackAtTop(ActivityStack stack, boolean includingParents,
+ String updateLastFocusedStackReason) {
+ positionStackAt(stack, getStackCount(), includingParents,
+ updateLastFocusedStackReason);
+ }
+
+ void positionStackAtBottom(ActivityStack stack) {
+ positionStackAtBottom(stack, null /* updateLastFocusedStackReason */);
+ }
+
+ void positionStackAtBottom(ActivityStack stack, String updateLastFocusedStackReason) {
+ positionStackAt(stack, 0, false /* includingParents */,
+ updateLastFocusedStackReason);
+ }
+
+ void positionStackAt(ActivityStack stack, int position) {
+ positionStackAt(stack, position, false /* includingParents */,
+ null /* updateLastFocusedStackReason */);
+ }
+
+ void positionStackAt(ActivityStack stack, int position, boolean includingParents,
+ String updateLastFocusedStackReason) {
+ // TODO: Keep in sync with WindowContainer.positionChildAt(), once we change that to adjust
+ // the position internally, also update the logic here
+ final ActivityStack prevFocusedStack = updateLastFocusedStackReason != null
+ ? getFocusedStack() : null;
+ final boolean wasContained = getIndexOf(stack) >= 0;
+ if (mDisplayContent.mSingleTaskInstance && getStackCount() == 1 && !wasContained) {
+ throw new IllegalStateException(
+ "positionStackAt: Can only have one task on display=" + this);
+ }
+
+ final boolean movingToTop = wasContained && position >= getStackCount() - 1;
+ // Reset mPreferredTopFocusableStack before positioning to top or {@link
+ // ActivityStackSupervisor#updateTopResumedActivityIfNeeded()} won't update the top
+ // resumed activity.
+ if (movingToTop && stack.isFocusable()) {
+ mPreferredTopFocusableStack = null;
+ }
+
+ // Since positionChildAt() is called during the creation process of pinned stacks,
+ // ActivityStack#getStack() can be null.
+ positionStackAt(position, stack, includingParents);
+
+ // The insert position may be adjusted to non-top when there is always-on-top stack. Since
+ // the original position is preferred to be top, the stack should have higher priority when
+ // we are looking for top focusable stack. The condition {@code wasContained} restricts the
+ // preferred stack is set only when moving an existing stack to top instead of adding a new
+ // stack that may be too early (e.g. in the middle of launching or reparenting).
+ if (movingToTop && stack.isFocusableAndVisible()) {
+ mPreferredTopFocusableStack = stack;
+ } else if (mPreferredTopFocusableStack == stack) {
+ mPreferredTopFocusableStack = null;
+ }
+
+ if (updateLastFocusedStackReason != null) {
+ final ActivityStack currentFocusedStack = getFocusedStack();
+ if (currentFocusedStack != prevFocusedStack) {
+ mLastFocusedStack = prevFocusedStack;
+ EventLogTags.writeWmFocusedStack(mRootWindowContainer.mCurrentUser,
+ mDisplayContent.mDisplayId,
+ currentFocusedStack == null ? -1 : currentFocusedStack.getRootTaskId(),
+ mLastFocusedStack == null ? -1 : mLastFocusedStack.getRootTaskId(),
+ updateLastFocusedStackReason);
+ }
+ }
+
+ mDisplayContent.onStackOrderChanged(stack);
+ }
+
+ ActivityStack getStack(int rootTaskId) {
+ for (int i = getStackCount() - 1; i >= 0; --i) {
+ final ActivityStack stack = getStackAt(i);
+ if (stack.getRootTaskId() == rootTaskId) {
+ return stack;
+ }
+ }
+ return null;
+ }
+
+ /**
+ * Returns an existing stack compatible with the windowing mode and activity type or creates one
+ * if a compatible stack doesn't exist.
+ * @see #getOrCreateStack(int, int, boolean, Intent, Task, boolean)
+ */
+ ActivityStack getOrCreateStack(int windowingMode, int activityType, boolean onTop) {
+ return getOrCreateStack(windowingMode, activityType, onTop, null /* intent */,
+ null /* candidateTask */, false /* createdByOrganizer */);
+ }
+
+ /**
+ * When two level tasks are required for given windowing mode and activity type, returns an
+ * existing compatible root task or creates a new one.
+ * For one level task, the candidate task would be reused to also be the root task or create
+ * a new root task if no candidate task.
+ * @see #getStack(int, int)
+ * @see #createStack(int, int, boolean)
+ */
+ ActivityStack getOrCreateStack(int windowingMode, int activityType, boolean onTop,
+ Intent intent, Task candidateTask, boolean createdByOrganizer) {
+ if (!alwaysCreateStack(windowingMode, activityType)) {
+ ActivityStack stack = getStack(windowingMode, activityType);
+ if (stack != null) {
+ return stack;
+ }
+ } else if (candidateTask != null) {
+ final ActivityStack stack = (ActivityStack) candidateTask;
+ final int position = onTop ? POSITION_TOP : POSITION_BOTTOM;
+ if (isSplitScreenModeActivated()) {
+ final Task splitRootSecondary = getTask(t -> t.mCreatedByOrganizer && t.isRootTask()
+ && t.inSplitScreenSecondaryWindowingMode());
+ if (stack.getParent() == null) {
+ splitRootSecondary.addChild(stack, position);
+ } else if (stack.getParent() != splitRootSecondary) {
+ stack.reparent(splitRootSecondary, position);
+ }
+ } else if (stack.getDisplay() != mDisplayContent || !stack.isRootTask()) {
+ if (stack.getParent() == null) {
+ addStack(stack, position);
+ } else {
+ stack.reparent(mDisplayContent, onTop);
+ }
+ }
+ // Update windowing mode if necessary, e.g. moving a pinned task to fullscreen.
+ if (candidateTask.getWindowingMode() != windowingMode) {
+ candidateTask.setWindowingMode(windowingMode);
+ }
+ return stack;
+ }
+ return createStack(windowingMode, activityType, onTop, null /*info*/, intent,
+ createdByOrganizer);
+ }
+
+ /**
+ * Returns an existing stack compatible with the input params or creates one
+ * if a compatible stack doesn't exist.
+ * @see #getOrCreateStack(int, int, boolean)
+ */
+ ActivityStack getOrCreateStack(@Nullable ActivityRecord r,
+ @Nullable ActivityOptions options, @Nullable Task candidateTask, int activityType,
+ boolean onTop) {
+ // First preference is the windowing mode in the activity options if set.
+ int windowingMode = (options != null)
+ ? options.getLaunchWindowingMode() : WINDOWING_MODE_UNDEFINED;
+ // Validate that our desired windowingMode will work under the current conditions.
+ // UNDEFINED windowing mode is a valid result and means that the new stack will inherit
+ // it's display's windowing mode.
+ windowingMode = validateWindowingMode(windowingMode, r, candidateTask, activityType);
+ return getOrCreateStack(windowingMode, activityType, onTop, null /* intent */,
+ candidateTask, false /* createdByOrganizer */);
+ }
+
+ @VisibleForTesting
+ int getNextStackId() {
+ return mAtmService.mStackSupervisor.getNextTaskIdForUser();
+ }
+
+ ActivityStack createStack(int windowingMode, int activityType, boolean onTop) {
+ return createStack(windowingMode, activityType, onTop, null /* info */, null /* intent */,
+ false /* createdByOrganizer */);
+ }
+
+ /**
+ * Creates a stack matching the input windowing mode and activity type on this display.
+ * @param windowingMode The windowing mode the stack should be created in. If
+ * {@link WindowConfiguration#WINDOWING_MODE_UNDEFINED} then the stack will
+ * inherit its parent's windowing mode.
+ * @param activityType The activityType the stack should be created in. If
+ * {@link WindowConfiguration#ACTIVITY_TYPE_UNDEFINED} then the stack will
+ * be created in {@link WindowConfiguration#ACTIVITY_TYPE_STANDARD}.
+ * @param onTop If true the stack will be created at the top of the display, else at the bottom.
+ * @param info The started activity info.
+ * @param intent The intent that started this task.
+ * @param createdByOrganizer @{code true} if this is created by task organizer, @{code false}
+ * otherwise.
+ * @return The newly created stack.
+ */
+ ActivityStack createStack(int windowingMode, int activityType, boolean onTop, ActivityInfo info,
+ Intent intent, boolean createdByOrganizer) {
+ if (mDisplayContent.mSingleTaskInstance && getStackCount() > 0) {
+ // Create stack on default display instead since this display can only contain 1 stack.
+ // TODO: Kinda a hack, but better that having the decision at each call point. Hoping
+ // this goes away once ActivityView is no longer using virtual displays.
+ return mRootWindowContainer.getDefaultDisplay().mTaskContainers.createStack(
+ windowingMode, activityType, onTop, info, intent, createdByOrganizer);
+ }
+
+ if (activityType == ACTIVITY_TYPE_UNDEFINED && !createdByOrganizer) {
+ // Can't have an undefined stack type yet...so re-map to standard. Anyone that wants
+ // anything else should be passing it in anyways...except for the task organizer.
+ activityType = ACTIVITY_TYPE_STANDARD;
+ }
+
+ if (activityType != ACTIVITY_TYPE_STANDARD && activityType != ACTIVITY_TYPE_UNDEFINED) {
+ // For now there can be only one stack of a particular non-standard activity type on a
+ // display. So, get that ignoring whatever windowing mode it is currently in.
+ ActivityStack stack = getStack(WINDOWING_MODE_UNDEFINED, activityType);
+ if (stack != null) {
+ throw new IllegalArgumentException("Stack=" + stack + " of activityType="
+ + activityType + " already on display=" + this + ". Can't have multiple.");
+ }
+ }
+
+ if (!isWindowingModeSupported(windowingMode, mAtmService.mSupportsMultiWindow,
+ mAtmService.mSupportsSplitScreenMultiWindow,
+ mAtmService.mSupportsFreeformWindowManagement,
+ mAtmService.mSupportsPictureInPicture, activityType)) {
+ throw new IllegalArgumentException("Can't create stack for unsupported windowingMode="
+ + windowingMode);
+ }
+
+ final int stackId = getNextStackId();
+ return createStackUnchecked(windowingMode, activityType, stackId, onTop, info, intent,
+ createdByOrganizer);
+ }
+
+ /** @return the root task to create the next task in. */
+ private Task updateLaunchRootTask(int windowingMode) {
+ if (!isSplitScreenWindowingMode(windowingMode)) {
+ // Only split-screen windowing modes can do this currently...
+ return null;
+ }
+ for (int i = getStackCount() - 1; i >= 0; --i) {
+ final Task t = getStackAt(i);
+ if (!t.mCreatedByOrganizer || t.getRequestedOverrideWindowingMode() != windowingMode) {
+ continue;
+ }
+ // If not already set, pick a launch root which is not the one we are launching into.
+ if (mLaunchRootTask == null) {
+ for (int j = 0, n = getStackCount(); j < n; ++j) {
+ final Task tt = getStackAt(j);
+ if (tt.mCreatedByOrganizer && tt != t) {
+ mLaunchRootTask = tt;
+ break;
+ }
+ }
+ }
+ return t;
+ }
+ return mLaunchRootTask;
+ }
+
+ @VisibleForTesting
+ ActivityStack createStackUnchecked(int windowingMode, int activityType, int stackId,
+ boolean onTop, ActivityInfo info, Intent intent, boolean createdByOrganizer) {
+ if (windowingMode == WINDOWING_MODE_PINNED && activityType != ACTIVITY_TYPE_STANDARD) {
+ throw new IllegalArgumentException("Stack with windowing mode cannot with non standard "
+ + "activity type.");
+ }
+ if (info == null) {
+ info = new ActivityInfo();
+ info.applicationInfo = new ApplicationInfo();
+ }
+
+ // Task created by organizer are added as root.
+ Task launchRootTask = createdByOrganizer ? null : updateLaunchRootTask(windowingMode);
+ if (launchRootTask != null) {
+ // Since this stack will be put into a root task, its windowingMode will be inherited.
+ windowingMode = WINDOWING_MODE_UNDEFINED;
+ }
+
+ final ActivityStack stack = (ActivityStack) Task.create(mAtmService, stackId, activityType,
+ info, intent, createdByOrganizer);
+ if (launchRootTask != null) {
+ launchRootTask.addChild(stack, onTop ? POSITION_TOP : POSITION_BOTTOM);
+ if (onTop) {
+ positionStackAtTop((ActivityStack) launchRootTask, false /* includingParents */);
+ }
+ } else {
+ addStack(stack, onTop ? POSITION_TOP : POSITION_BOTTOM);
+ stack.setWindowingMode(windowingMode, false /* animate */, false /* showRecents */,
+ false /* enteringSplitScreenMode */, false /* deferEnsuringVisibility */,
+ true /* creating */);
+ }
+ return stack;
+ }
+
+ /**
+ * Get the preferred focusable stack in priority. If the preferred stack does not exist, find a
+ * focusable and visible stack from the top of stacks in this display.
+ */
+ ActivityStack getFocusedStack() {
+ if (mPreferredTopFocusableStack != null) {
+ return mPreferredTopFocusableStack;
+ }
+
+ for (int i = getStackCount() - 1; i >= 0; --i) {
+ final ActivityStack stack = getStackAt(i);
+ if (stack.isFocusableAndVisible()) {
+ return stack;
+ }
+ }
+
+ return null;
+ }
+
+ ActivityStack getNextFocusableStack(ActivityStack currentFocus, boolean ignoreCurrent) {
+ final int currentWindowingMode = currentFocus != null
+ ? currentFocus.getWindowingMode() : WINDOWING_MODE_UNDEFINED;
+
+ ActivityStack candidate = null;
+ for (int i = getStackCount() - 1; i >= 0; --i) {
+ final ActivityStack stack = getStackAt(i);
+ if (ignoreCurrent && stack == currentFocus) {
+ continue;
+ }
+ if (!stack.isFocusableAndVisible()) {
+ continue;
+ }
+
+ if (currentWindowingMode == WINDOWING_MODE_SPLIT_SCREEN_SECONDARY
+ && candidate == null && stack.inSplitScreenPrimaryWindowingMode()) {
+ // If the currently focused stack is in split-screen secondary we save off the
+ // top primary split-screen stack as a candidate for focus because we might
+ // prefer focus to move to an other stack to avoid primary split-screen stack
+ // overlapping with a fullscreen stack when a fullscreen stack is higher in z
+ // than the next split-screen stack. Assistant stack, I am looking at you...
+ // We only move the focus to the primary-split screen stack if there isn't a
+ // better alternative.
+ candidate = stack;
+ continue;
+ }
+ if (candidate != null && stack.inSplitScreenSecondaryWindowingMode()) {
+ // Use the candidate stack since we are now at the secondary split-screen.
+ return candidate;
+ }
+ return stack;
+ }
+ return candidate;
+ }
+
+ ActivityRecord getResumedActivity() {
+ final ActivityStack focusedStack = getFocusedStack();
+ if (focusedStack == null) {
+ return null;
+ }
+ // TODO(b/111541062): Move this into ActivityStack#getResumedActivity()
+ // Check if the focused stack has the resumed activity
+ ActivityRecord resumedActivity = focusedStack.getResumedActivity();
+ if (resumedActivity == null || resumedActivity.app == null) {
+ // If there is no registered resumed activity in the stack or it is not running -
+ // try to use previously resumed one.
+ resumedActivity = focusedStack.mPausingActivity;
+ if (resumedActivity == null || resumedActivity.app == null) {
+ // If previously resumed activity doesn't work either - find the topmost running
+ // activity that can be focused.
+ resumedActivity = focusedStack.topRunningActivity(true /* focusableOnly */);
+ }
+ }
+ return resumedActivity;
+ }
+
+ ActivityStack getLastFocusedStack() {
+ return mLastFocusedStack;
+ }
+
+ boolean allResumedActivitiesComplete() {
+ for (int stackNdx = getStackCount() - 1; stackNdx >= 0; --stackNdx) {
+ final ActivityRecord r = getStackAt(stackNdx).getResumedActivity();
+ if (r != null && !r.isState(RESUMED)) {
+ return false;
+ }
+ }
+ final ActivityStack currentFocusedStack = getFocusedStack();
+ if (ActivityTaskManagerDebugConfig.DEBUG_STACK) {
+ Slog.d(TAG_STACK, "allResumedActivitiesComplete: mLastFocusedStack changing from="
+ + mLastFocusedStack + " to=" + currentFocusedStack);
+ }
+ mLastFocusedStack = currentFocusedStack;
+ return true;
+ }
+
+ /**
+ * Pause all activities in either all of the stacks or just the back stacks. This is done before
+ * resuming a new activity and to make sure that previously active activities are
+ * paused in stacks that are no longer visible or in pinned windowing mode. This does not
+ * pause activities in visible stacks, so if an activity is launched within the same stack/task,
+ * then we should explicitly pause that stack's top activity.
+ * @param userLeaving Passed to pauseActivity() to indicate whether to call onUserLeaving().
+ * @param resuming The resuming activity.
+ * @return {@code true} if any activity was paused as a result of this call.
+ */
+ boolean pauseBackStacks(boolean userLeaving, ActivityRecord resuming) {
+ boolean someActivityPaused = false;
+ for (int stackNdx = getStackCount() - 1; stackNdx >= 0; --stackNdx) {
+ final ActivityStack stack = getStackAt(stackNdx);
+ final ActivityRecord resumedActivity = stack.getResumedActivity();
+ if (resumedActivity != null
+ && (stack.getVisibility(resuming) != STACK_VISIBILITY_VISIBLE
+ || !stack.isTopActivityFocusable())) {
+ if (DEBUG_STATES) {
+ Slog.d(TAG_STATES, "pauseBackStacks: stack=" + stack
+ + " mResumedActivity=" + resumedActivity);
+ }
+ someActivityPaused |= stack.startPausingLocked(userLeaving, false /* uiSleeping*/,
+ resuming);
+ }
+ }
+ return someActivityPaused;
+ }
+
+ /**
+ * Find task for putting the Activity in.
+ */
+ void findTaskLocked(final ActivityRecord r, final boolean isPreferredDisplay,
+ RootWindowContainer.FindTaskResult result) {
+ mTmpFindTaskResult.clear();
+ for (int stackNdx = getStackCount() - 1; stackNdx >= 0; --stackNdx) {
+ final ActivityStack stack = getStackAt(stackNdx);
+ if (!r.hasCompatibleActivityType(stack) && stack.isLeafTask()) {
+ if (DEBUG_TASKS) {
+ Slog.d(TAG_TASKS, "Skipping stack: (mismatch activity/stack) " + stack);
+ }
+ continue;
+ }
+
+ mTmpFindTaskResult.process(r, stack);
+ // It is possible to have tasks in multiple stacks with the same root affinity, so
+ // we should keep looking after finding an affinity match to see if there is a
+ // better match in another stack. Also, task affinity isn't a good enough reason
+ // to target a display which isn't the source of the intent, so skip any affinity
+ // matches not on the specified display.
+ if (mTmpFindTaskResult.mRecord != null) {
+ if (mTmpFindTaskResult.mIdealMatch) {
+ result.setTo(mTmpFindTaskResult);
+ return;
+ } else if (isPreferredDisplay) {
+ // Note: since the traversing through the stacks is top down, the floating
+ // tasks should always have lower priority than any affinity-matching tasks
+ // in the fullscreen stacks
+ result.setTo(mTmpFindTaskResult);
+ }
+ }
+ }
+ }
+
+ /**
+ * Removes stacks in the input windowing modes from the system if they are of activity type
+ * ACTIVITY_TYPE_STANDARD or ACTIVITY_TYPE_UNDEFINED
+ */
+ void removeStacksInWindowingModes(int... windowingModes) {
+ if (windowingModes == null || windowingModes.length == 0) {
+ return;
+ }
+
+ // Collect the stacks that are necessary to be removed instead of performing the removal
+ // by looping mStacks, so that we don't miss any stacks after the stack size changed or
+ // stacks reordered.
+ final ArrayList<ActivityStack> stacks = new ArrayList<>();
+ for (int j = windowingModes.length - 1; j >= 0; --j) {
+ final int windowingMode = windowingModes[j];
+ for (int i = getStackCount() - 1; i >= 0; --i) {
+ final ActivityStack stack = getStackAt(i);
+ if (!stack.isActivityTypeStandardOrUndefined()) {
+ continue;
+ }
+ if (stack.getWindowingMode() != windowingMode) {
+ continue;
+ }
+ stacks.add(stack);
+ }
+ }
+
+ for (int i = stacks.size() - 1; i >= 0; --i) {
+ mRootWindowContainer.mStackSupervisor.removeStack(stacks.get(i));
+ }
+ }
+
+ void removeStacksWithActivityTypes(int... activityTypes) {
+ if (activityTypes == null || activityTypes.length == 0) {
+ return;
+ }
+
+ // Collect the stacks that are necessary to be removed instead of performing the removal
+ // by looping mStacks, so that we don't miss any stacks after the stack size changed or
+ // stacks reordered.
+ final ArrayList<ActivityStack> stacks = new ArrayList<>();
+ for (int j = activityTypes.length - 1; j >= 0; --j) {
+ final int activityType = activityTypes[j];
+ for (int i = getStackCount() - 1; i >= 0; --i) {
+ final ActivityStack stack = getStackAt(i);
+ // Collect the root tasks that are currently being organized.
+ if (stack.isOrganized()) {
+ for (int k = stack.getChildCount() - 1; k >= 0; --k) {
+ final ActivityStack childStack = (ActivityStack) stack.getChildAt(k);
+ if (childStack.getActivityType() == activityType) {
+ stacks.add(childStack);
+ }
+ }
+ } else if (stack.getActivityType() == activityType) {
+ stacks.add(stack);
+ }
+ }
+ }
+
+ for (int i = stacks.size() - 1; i >= 0; --i) {
+ mRootWindowContainer.mStackSupervisor.removeStack(stacks.get(i));
+ }
+ }
+
+ void onSplitScreenModeDismissed() {
+ mAtmService.deferWindowLayout();
+ try {
+ mLaunchRootTask = null;
+ moveSplitScreenTasksToFullScreen();
+ } finally {
+ final ActivityStack topFullscreenStack =
+ getTopStackInWindowingMode(WINDOWING_MODE_FULLSCREEN);
+ final ActivityStack homeStack = getOrCreateRootHomeTask();
+ if (topFullscreenStack != null && homeStack != null && !isTopStack(homeStack)) {
+ // Whenever split-screen is dismissed we want the home stack directly behind the
+ // current top fullscreen stack so it shows up when the top stack is finished.
+ // TODO: Would be better to use ActivityDisplay.positionChildAt() for this, however
+ // ActivityDisplay doesn't have a direct controller to WM side yet. We can switch
+ // once we have that.
+ homeStack.moveToFront("onSplitScreenModeDismissed");
+ topFullscreenStack.moveToFront("onSplitScreenModeDismissed");
+ }
+ mAtmService.continueWindowLayout();
+ }
+ }
+
+ private void moveSplitScreenTasksToFullScreen() {
+ final WindowContainerTransaction wct = new WindowContainerTransaction();
+ mTmpTasks.clear();
+ forAllTasks(task -> {
+ if (task.mCreatedByOrganizer && task.inSplitScreenWindowingMode() && task.hasChild()) {
+ mTmpTasks.add(task);
+ }
+ });
+
+ for (int i = mTmpTasks.size() - 1; i >= 0; i--) {
+ final Task root = mTmpTasks.get(i);
+ for (int j = 0; j < root.getChildCount(); j++) {
+ wct.reparent(root.getChildAt(j).mRemoteToken, null, true /* toTop */);
+ }
+ }
+ mAtmService.mWindowOrganizerController.applyTransaction(wct);
+ }
+
+ /**
+ * Returns true if the {@param windowingMode} is supported based on other parameters passed in.
+ * @param windowingMode The windowing mode we are checking support for.
+ * @param supportsMultiWindow If we should consider support for multi-window mode in general.
+ * @param supportsSplitScreen If we should consider support for split-screen multi-window.
+ * @param supportsFreeform If we should consider support for freeform multi-window.
+ * @param supportsPip If we should consider support for picture-in-picture mutli-window.
+ * @param activityType The activity type under consideration.
+ * @return true if the windowing mode is supported.
+ */
+ private boolean isWindowingModeSupported(int windowingMode, boolean supportsMultiWindow,
+ boolean supportsSplitScreen, boolean supportsFreeform, boolean supportsPip,
+ int activityType) {
+
+ if (windowingMode == WINDOWING_MODE_UNDEFINED
+ || windowingMode == WINDOWING_MODE_FULLSCREEN) {
+ return true;
+ }
+ if (!supportsMultiWindow) {
+ return false;
+ }
+
+ if (windowingMode == WINDOWING_MODE_MULTI_WINDOW) {
+ return true;
+ }
+
+ final int displayWindowingMode = getWindowingMode();
+ if (windowingMode == WINDOWING_MODE_SPLIT_SCREEN_PRIMARY
+ || windowingMode == WINDOWING_MODE_SPLIT_SCREEN_SECONDARY) {
+ return supportsSplitScreen
+ && WindowConfiguration.supportSplitScreenWindowingMode(activityType)
+ // Freeform windows and split-screen windows don't mix well, so prevent
+ // split windowing modes on freeform displays.
+ && displayWindowingMode != WINDOWING_MODE_FREEFORM;
+ }
+
+ if (!supportsFreeform && windowingMode == WINDOWING_MODE_FREEFORM) {
+ return false;
+ }
+
+ if (!supportsPip && windowingMode == WINDOWING_MODE_PINNED) {
+ return false;
+ }
+ return true;
+ }
+
+ /**
+ * Resolves the windowing mode that an {@link ActivityRecord} would be in if started on this
+ * display with the provided parameters.
+ *
+ * @param r The ActivityRecord in question.
+ * @param options Options to start with.
+ * @param task The task within-which the activity would start.
+ * @param activityType The type of activity to start.
+ * @return The resolved (not UNDEFINED) windowing-mode that the activity would be in.
+ */
+ int resolveWindowingMode(@Nullable ActivityRecord r, @Nullable ActivityOptions options,
+ @Nullable Task task, int activityType) {
+
+ // First preference if the windowing mode in the activity options if set.
+ int windowingMode = (options != null)
+ ? options.getLaunchWindowingMode() : WINDOWING_MODE_UNDEFINED;
+
+ // If windowing mode is unset, then next preference is the candidate task, then the
+ // activity record.
+ if (windowingMode == WINDOWING_MODE_UNDEFINED) {
+ if (task != null) {
+ windowingMode = task.getWindowingMode();
+ }
+ if (windowingMode == WINDOWING_MODE_UNDEFINED && r != null) {
+ windowingMode = r.getWindowingMode();
+ }
+ if (windowingMode == WINDOWING_MODE_UNDEFINED) {
+ // Use the display's windowing mode.
+ windowingMode = getWindowingMode();
+ }
+ }
+ windowingMode = validateWindowingMode(windowingMode, r, task, activityType);
+ return windowingMode != WINDOWING_MODE_UNDEFINED
+ ? windowingMode : WINDOWING_MODE_FULLSCREEN;
+ }
+
+ /**
+ * Check that the requested windowing-mode is appropriate for the specified task and/or activity
+ * on this display.
+ *
+ * @param windowingMode The windowing-mode to validate.
+ * @param r The {@link ActivityRecord} to check against.
+ * @param task The {@link Task} to check against.
+ * @param activityType An activity type.
+ * @return The provided windowingMode or the closest valid mode which is appropriate.
+ */
+ int validateWindowingMode(int windowingMode, @Nullable ActivityRecord r, @Nullable Task task,
+ int activityType) {
+ // Make sure the windowing mode we are trying to use makes sense for what is supported.
+ boolean supportsMultiWindow = mAtmService.mSupportsMultiWindow;
+ boolean supportsSplitScreen = mAtmService.mSupportsSplitScreenMultiWindow;
+ boolean supportsFreeform = mAtmService.mSupportsFreeformWindowManagement;
+ boolean supportsPip = mAtmService.mSupportsPictureInPicture;
+ if (supportsMultiWindow) {
+ if (task != null) {
+ supportsMultiWindow = task.isResizeable();
+ supportsSplitScreen = task.supportsSplitScreenWindowingMode();
+ // TODO: Do we need to check for freeform and Pip support here?
+ } else if (r != null) {
+ supportsMultiWindow = r.isResizeable();
+ supportsSplitScreen = r.supportsSplitScreenWindowingMode();
+ supportsFreeform = r.supportsFreeform();
+ supportsPip = r.supportsPictureInPicture();
+ }
+ }
+
+ final boolean inSplitScreenMode = isSplitScreenModeActivated();
+ if (!inSplitScreenMode
+ && windowingMode == WINDOWING_MODE_FULLSCREEN_OR_SPLIT_SCREEN_SECONDARY) {
+ // Switch to the display's windowing mode if we are not in split-screen mode and we are
+ // trying to launch in split-screen secondary.
+ windowingMode = WINDOWING_MODE_UNDEFINED;
+ } else if (inSplitScreenMode && (windowingMode == WINDOWING_MODE_FULLSCREEN
+ || windowingMode == WINDOWING_MODE_UNDEFINED)
+ && supportsSplitScreen) {
+ windowingMode = WINDOWING_MODE_SPLIT_SCREEN_SECONDARY;
+ }
+
+ if (windowingMode != WINDOWING_MODE_UNDEFINED
+ && isWindowingModeSupported(windowingMode, supportsMultiWindow, supportsSplitScreen,
+ supportsFreeform, supportsPip, activityType)) {
+ return windowingMode;
+ }
+ return WINDOWING_MODE_UNDEFINED;
+ }
+
+ boolean isTopStack(ActivityStack stack) {
+ return stack == getTopStack();
+ }
+
+ boolean isTopNotPinnedStack(ActivityStack stack) {
+ for (int i = getStackCount() - 1; i >= 0; --i) {
+ final ActivityStack current = getStackAt(i);
+ if (!current.inPinnedWindowingMode()) {
+ return current == stack;
+ }
+ }
+ return false;
+ }
+
+ /**
+ * Returns the top running activity in the focused stack. In the case the focused stack has no
+ * such activity, the next focusable stack on this display is returned.
+ *
+ * @param considerKeyguardState Indicates whether the locked state should be considered. if
+ * {@code true} and the keyguard is locked, only activities that
+ * can be shown on top of the keyguard will be considered.
+ * @return The top running activity. {@code null} if none is available.
+ */
+ ActivityRecord topRunningActivity(boolean considerKeyguardState) {
+ ActivityRecord topRunning = null;
+ final ActivityStack focusedStack = getFocusedStack();
+ if (focusedStack != null) {
+ topRunning = focusedStack.topRunningActivity();
+ }
+
+ // Look in other focusable stacks.
+ if (topRunning == null) {
+ for (int i = getStackCount() - 1; i >= 0; --i) {
+ final ActivityStack stack = getStackAt(i);
+ // Only consider focusable stacks other than the current focused one.
+ if (stack == focusedStack || !stack.isTopActivityFocusable()) {
+ continue;
+ }
+ topRunning = stack.topRunningActivity();
+ if (topRunning != null) {
+ break;
+ }
+ }
+ }
+
+ // This activity can be considered the top running activity if we are not considering
+ // the locked state, the keyguard isn't locked, or we can show when locked.
+ if (topRunning != null && considerKeyguardState
+ && mRootWindowContainer.mStackSupervisor.getKeyguardController()
+ .isKeyguardLocked()
+ && !topRunning.canShowWhenLocked()) {
+ return null;
+ }
+
+ return topRunning;
+ }
+
+ protected int getStackCount() {
+ return mChildren.size();
+ }
+
+ protected ActivityStack getStackAt(int index) {
+ return mChildren.get(index);
+ }
+
+ /**
+ * Returns the existing home stack or creates and returns a new one if it should exist for the
+ * display.
+ */
+ @Nullable
+ ActivityStack getOrCreateRootHomeTask() {
+ ActivityStack homeTask = getRootHomeTask();
+ if (homeTask == null && mDisplayContent.supportsSystemDecorations()
+ && !mDisplayContent.isUntrustedVirtualDisplay()) {
+ homeTask = createStack(WINDOWING_MODE_FULLSCREEN, ACTIVITY_TYPE_HOME,
+ false /* onTop */);
+ }
+ return homeTask;
+ }
+
+ boolean isSplitScreenModeActivated() {
+ Task task = getRootSplitScreenPrimaryTask();
+ return task != null && task.hasChild();
+ }
+
+ /**
+ * Returns the topmost stack on the display that is compatible with the input windowing mode.
+ * Null is no compatible stack on the display.
+ */
+ ActivityStack getTopStackInWindowingMode(int windowingMode) {
+ return getStack(windowingMode, ACTIVITY_TYPE_UNDEFINED);
+ }
+
+ void moveHomeStackToFront(String reason) {
+ final ActivityStack homeStack = getOrCreateRootHomeTask();
+ if (homeStack != null) {
+ homeStack.moveToFront(reason);
+ }
+ }
+
+ /**
+ * Moves the focusable home activity to top. If there is no such activity, the home stack will
+ * still move to top.
+ */
+ void moveHomeActivityToTop(String reason) {
+ final ActivityRecord top = getHomeActivity();
+ if (top == null) {
+ moveHomeStackToFront(reason);
+ return;
+ }
+ top.moveFocusableActivityToTop(reason);
+ }
+
+ @Nullable
+ ActivityRecord getHomeActivity() {
+ return getHomeActivityForUser(mRootWindowContainer.mCurrentUser);
+ }
+
+ @Nullable
+ ActivityRecord getHomeActivityForUser(int userId) {
+ final ActivityStack homeStack = getRootHomeTask();
+ if (homeStack == null) {
+ return null;
+ }
+
+ final PooledPredicate p = PooledLambda.obtainPredicate(
+ TaskContainers::isHomeActivityForUser, PooledLambda.__(ActivityRecord.class),
+ userId);
+ final ActivityRecord r = homeStack.getActivity(p);
+ p.recycle();
+ return r;
+ }
+
+ private static boolean isHomeActivityForUser(ActivityRecord r, int userId) {
+ return r.isActivityTypeHome() && (userId == UserHandle.USER_ALL || r.mUserId == userId);
+ }
+
+ /**
+ * Adjusts the {@param stack} behind the last visible stack in the display if necessary.
+ * Generally used in conjunction with {@link #moveStackBehindStack}.
+ */
+ // TODO(b/151575894): Remove special stack movement methods.
+ void moveStackBehindBottomMostVisibleStack(ActivityStack stack) {
+ if (stack.shouldBeVisible(null)) {
+ // Skip if the stack is already visible
+ return;
+ }
+
+ final boolean isRootTask = stack.isRootTask();
+ if (isRootTask) {
+ // Move the stack to the bottom to not affect the following visibility checks
+ positionStackAtBottom(stack);
+ } else {
+ stack.getParent().positionChildAt(POSITION_BOTTOM, stack, false /* includingParents */);
+ }
+
+ // Find the next position where the stack should be placed
+ final int numStacks = isRootTask ? getStackCount() : stack.getParent().getChildCount();
+ for (int stackNdx = 0; stackNdx < numStacks; stackNdx++) {
+ final ActivityStack s = isRootTask ? getStackAt(stackNdx)
+ : (ActivityStack) stack.getParent().getChildAt(stackNdx);
+ if (s == stack) {
+ continue;
+ }
+ final int winMode = s.getWindowingMode();
+ final boolean isValidWindowingMode = winMode == WINDOWING_MODE_FULLSCREEN
+ || winMode == WINDOWING_MODE_SPLIT_SCREEN_SECONDARY;
+ if (s.shouldBeVisible(null) && isValidWindowingMode) {
+ // Move the provided stack to behind this stack
+ final int position = Math.max(0, stackNdx - 1);
+ if (isRootTask) {
+ positionStackAt(stack, position);
+ } else {
+ stack.getParent().positionChildAt(position, stack, false /*includingParents */);
+ }
+ break;
+ }
+ }
+ }
+
+ /**
+ * Moves the {@param stack} behind the given {@param behindStack} if possible. If
+ * {@param behindStack} is not currently in the display, then then the stack is moved to the
+ * back. Generally used in conjunction with {@link #moveStackBehindBottomMostVisibleStack}.
+ */
+ void moveStackBehindStack(ActivityStack stack, ActivityStack behindStack) {
+ if (behindStack == null || behindStack == stack) {
+ return;
+ }
+
+ final WindowContainer parent = stack.getParent();
+ if (parent == null || parent != behindStack.getParent()) {
+ return;
+ }
+
+ // Note that positionChildAt will first remove the given stack before inserting into the
+ // list, so we need to adjust the insertion index to account for the removed index
+ // TODO: Remove this logic when WindowContainer.positionChildAt() is updated to adjust the
+ // position internally
+ final int stackIndex = parent.mChildren.indexOf(stack);
+ final int behindStackIndex = parent.mChildren.indexOf(behindStack);
+ final int insertIndex = stackIndex <= behindStackIndex
+ ? behindStackIndex - 1 : behindStackIndex;
+ final int position = Math.max(0, insertIndex);
+ if (stack.isRootTask()) {
+ positionStackAt(stack, position);
+ } else {
+ parent.positionChildAt(position, stack, false /* includingParents */);
+ }
+ }
+}
diff --git a/services/core/java/com/android/server/wm/TaskOrganizerController.java b/services/core/java/com/android/server/wm/TaskOrganizerController.java
index 8a896f52eea7..15b483cd0c49 100644
--- a/services/core/java/com/android/server/wm/TaskOrganizerController.java
+++ b/services/core/java/com/android/server/wm/TaskOrganizerController.java
@@ -255,9 +255,9 @@ class TaskOrganizerController extends ITaskOrganizerController.Stub {
return null;
}
- final Task task = display.getOrCreateStack(windowingMode, ACTIVITY_TYPE_UNDEFINED,
- false /* onTop */, new Intent(), null /* candidateTask */,
- true /* createdByOrganizer */);
+ final Task task = display.mTaskContainers.getOrCreateStack(windowingMode,
+ ACTIVITY_TYPE_UNDEFINED, false /* onTop */, new Intent(),
+ null /* candidateTask */, true /* createdByOrganizer */);
RunningTaskInfo out = task.getTaskInfo();
mLastSentTaskInfos.put(task, out);
return out;
diff --git a/services/core/java/com/android/server/wm/WindowOrganizerController.java b/services/core/java/com/android/server/wm/WindowOrganizerController.java
index 5f21e1799958..7eccf085f2f7 100644
--- a/services/core/java/com/android/server/wm/WindowOrganizerController.java
+++ b/services/core/java/com/android/server/wm/WindowOrganizerController.java
@@ -252,9 +252,10 @@ class WindowOrganizerController extends IWindowOrganizerController.Stub
final ActivityStack rootTask =
(ActivityStack) (newParent != null ? newParent : task.getRootTask());
if (hop.getToTop()) {
- as.getDisplay().positionStackAtTop(rootTask, false /* includingParents */);
+ as.getDisplay().mTaskContainers.positionStackAtTop(rootTask,
+ false /* includingParents */);
} else {
- as.getDisplay().positionStackAtBottom(rootTask);
+ as.getDisplay().mTaskContainers.positionStackAtBottom(rootTask);
}
}
} else {
@@ -264,9 +265,9 @@ class WindowOrganizerController extends IWindowOrganizerController.Stub
// Ugh, of course ActivityStack has its own special reorder logic...
if (task.isRootTask()) {
if (hop.getToTop()) {
- dc.positionStackAtTop(as, false /* includingParents */);
+ dc.mTaskContainers.positionStackAtTop(as, false /* includingParents */);
} else {
- dc.positionStackAtBottom(as);
+ dc.mTaskContainers.positionStackAtBottom(as);
}
} else {
task.getParent().positionChildAt(
diff --git a/services/devicepolicy/Android.bp b/services/devicepolicy/Android.bp
index cdbe77a3d64c..2f6592bd33b0 100644
--- a/services/devicepolicy/Android.bp
+++ b/services/devicepolicy/Android.bp
@@ -13,8 +13,4 @@ java_library_static {
"services.core",
"app-compat-annotations",
],
-
- plugins: [
- "compat-changeid-annotation-processor",
- ],
}
diff --git a/services/incremental/IncrementalService.cpp b/services/incremental/IncrementalService.cpp
index ae27c7aabdda..25da8fe4a2e8 100644
--- a/services/incremental/IncrementalService.cpp
+++ b/services/incremental/IncrementalService.cpp
@@ -157,7 +157,6 @@ std::string makeBindMdName() {
IncrementalService::IncFsMount::~IncFsMount() {
incrementalService.mDataLoaderManager->destroyDataLoader(mountId);
- control.reset();
LOG(INFO) << "Unmounting and cleaning up mount " << mountId << " with root '" << root << '\'';
for (auto&& [target, _] : bindPoints) {
LOG(INFO) << "\tbind: " << target;
@@ -424,9 +423,10 @@ StorageId IncrementalService::createStorage(
LOG(ERROR) << "Vold::mountIncFs() returned invalid control parcel.";
return kInvalidStorageId;
}
- control.cmd = controlParcel.cmd.release().release();
- control.pendingReads = controlParcel.pendingReads.release().release();
- control.logs = controlParcel.log.release().release();
+ int cmd = controlParcel.cmd.release().release();
+ int pendingReads = controlParcel.pendingReads.release().release();
+ int logs = controlParcel.log.release().release();
+ control = mIncFs->createControl(cmd, pendingReads, logs);
}
std::unique_lock l(mLock);
@@ -965,16 +965,17 @@ bool IncrementalService::mountExistingImage(std::string_view root, std::string_v
auto mountTarget = path::join(root, constants().mount);
const auto backing = path::join(root, constants().backing);
- IncFsMount::Control control;
IncrementalFileSystemControlParcel controlParcel;
auto status = mVold->mountIncFs(backing, mountTarget, 0, &controlParcel);
if (!status.isOk()) {
LOG(ERROR) << "Vold::mountIncFs() failed: " << status.toString8();
return false;
}
- control.cmd = controlParcel.cmd.release().release();
- control.pendingReads = controlParcel.pendingReads.release().release();
- control.logs = controlParcel.log.release().release();
+
+ int cmd = controlParcel.cmd.release().release();
+ int pendingReads = controlParcel.pendingReads.release().release();
+ int logs = controlParcel.log.release().release();
+ IncFsMount::Control control = mIncFs->createControl(cmd, pendingReads, logs);
auto ifs = std::make_shared<IncFsMount>(std::string(root), -1, std::move(control), *this);
@@ -1084,10 +1085,10 @@ bool IncrementalService::prepareDataLoader(IncrementalService::IncFsMount& ifs,
}
FileSystemControlParcel fsControlParcel;
fsControlParcel.incremental = aidl::make_nullable<IncrementalFileSystemControlParcel>();
- fsControlParcel.incremental->cmd.reset(base::unique_fd(::dup(ifs.control.cmd)));
+ fsControlParcel.incremental->cmd.reset(base::unique_fd(::dup(ifs.control.cmd())));
fsControlParcel.incremental->pendingReads.reset(
- base::unique_fd(::dup(ifs.control.pendingReads)));
- fsControlParcel.incremental->log.reset(base::unique_fd(::dup(ifs.control.logs)));
+ base::unique_fd(::dup(ifs.control.pendingReads())));
+ fsControlParcel.incremental->log.reset(base::unique_fd(::dup(ifs.control.logs())));
sp<IncrementalDataLoaderListener> listener =
new IncrementalDataLoaderListener(*this,
externalListener ? *externalListener
diff --git a/services/incremental/ServiceWrappers.h b/services/incremental/ServiceWrappers.h
index 5349ebff5052..c70a47d40c4e 100644
--- a/services/incremental/ServiceWrappers.h
+++ b/services/incremental/ServiceWrappers.h
@@ -66,15 +66,17 @@ public:
class IncFsWrapper {
public:
virtual ~IncFsWrapper() = default;
- virtual ErrorCode makeFile(Control control, std::string_view path, int mode, FileId id,
+ virtual Control createControl(IncFsFd cmd, IncFsFd pendingReads, IncFsFd logs) const = 0;
+ virtual ErrorCode makeFile(const Control& control, std::string_view path, int mode, FileId id,
NewFileParams params) const = 0;
- virtual ErrorCode makeDir(Control control, std::string_view path, int mode) const = 0;
- virtual RawMetadata getMetadata(Control control, FileId fileid) const = 0;
- virtual RawMetadata getMetadata(Control control, std::string_view path) const = 0;
- virtual FileId getFileId(Control control, std::string_view path) const = 0;
- virtual ErrorCode link(Control control, std::string_view from, std::string_view to) const = 0;
- virtual ErrorCode unlink(Control control, std::string_view path) const = 0;
- virtual base::unique_fd openWrite(Control control, FileId id) const = 0;
+ virtual ErrorCode makeDir(const Control& control, std::string_view path, int mode) const = 0;
+ virtual RawMetadata getMetadata(const Control& control, FileId fileid) const = 0;
+ virtual RawMetadata getMetadata(const Control& control, std::string_view path) const = 0;
+ virtual FileId getFileId(const Control& control, std::string_view path) const = 0;
+ virtual ErrorCode link(const Control& control, std::string_view from,
+ std::string_view to) const = 0;
+ virtual ErrorCode unlink(const Control& control, std::string_view path) const = 0;
+ virtual base::unique_fd openWrite(const Control& control, FileId id) const = 0;
virtual ErrorCode writeBlocks(Span<const DataBlock> blocks) const = 0;
};
@@ -149,29 +151,33 @@ class RealIncFs : public IncFsWrapper {
public:
RealIncFs() = default;
~RealIncFs() = default;
- ErrorCode makeFile(Control control, std::string_view path, int mode, FileId id,
+ Control createControl(IncFsFd cmd, IncFsFd pendingReads, IncFsFd logs) const override {
+ return incfs::createControl(cmd, pendingReads, logs);
+ }
+ ErrorCode makeFile(const Control& control, std::string_view path, int mode, FileId id,
NewFileParams params) const override {
return incfs::makeFile(control, path, mode, id, params);
}
- ErrorCode makeDir(Control control, std::string_view path, int mode) const override {
+ ErrorCode makeDir(const Control& control, std::string_view path, int mode) const override {
return incfs::makeDir(control, path, mode);
}
- RawMetadata getMetadata(Control control, FileId fileid) const override {
+ RawMetadata getMetadata(const Control& control, FileId fileid) const override {
return incfs::getMetadata(control, fileid);
}
- RawMetadata getMetadata(Control control, std::string_view path) const override {
+ RawMetadata getMetadata(const Control& control, std::string_view path) const override {
return incfs::getMetadata(control, path);
}
- FileId getFileId(Control control, std::string_view path) const override {
+ FileId getFileId(const Control& control, std::string_view path) const override {
return incfs::getFileId(control, path);
}
- ErrorCode link(Control control, std::string_view from, std::string_view to) const override {
+ ErrorCode link(const Control& control, std::string_view from,
+ std::string_view to) const override {
return incfs::link(control, from, to);
}
- ErrorCode unlink(Control control, std::string_view path) const override {
+ ErrorCode unlink(const Control& control, std::string_view path) const override {
return incfs::unlink(control, path);
}
- base::unique_fd openWrite(Control control, FileId id) const override {
+ base::unique_fd openWrite(const Control& control, FileId id) const override {
return base::unique_fd{incfs::openWrite(control, id)};
}
ErrorCode writeBlocks(Span<const DataBlock> blocks) const override {
diff --git a/services/incremental/test/IncrementalServiceTest.cpp b/services/incremental/test/IncrementalServiceTest.cpp
index f5b88d93c8de..c4b4d1746cbe 100644
--- a/services/incremental/test/IncrementalServiceTest.cpp
+++ b/services/incremental/test/IncrementalServiceTest.cpp
@@ -164,22 +164,23 @@ private:
class MockIncFs : public IncFsWrapper {
public:
+ MOCK_CONST_METHOD3(createControl, Control(IncFsFd cmd, IncFsFd pendingReads, IncFsFd logs));
MOCK_CONST_METHOD5(makeFile,
- ErrorCode(Control control, std::string_view path, int mode, FileId id,
+ ErrorCode(const Control& control, std::string_view path, int mode, FileId id,
NewFileParams params));
- MOCK_CONST_METHOD3(makeDir, ErrorCode(Control control, std::string_view path, int mode));
- MOCK_CONST_METHOD2(getMetadata, RawMetadata(Control control, FileId fileid));
- MOCK_CONST_METHOD2(getMetadata, RawMetadata(Control control, std::string_view path));
- MOCK_CONST_METHOD2(getFileId, FileId(Control control, std::string_view path));
+ MOCK_CONST_METHOD3(makeDir, ErrorCode(const Control& control, std::string_view path, int mode));
+ MOCK_CONST_METHOD2(getMetadata, RawMetadata(const Control& control, FileId fileid));
+ MOCK_CONST_METHOD2(getMetadata, RawMetadata(const Control& control, std::string_view path));
+ MOCK_CONST_METHOD2(getFileId, FileId(const Control& control, std::string_view path));
MOCK_CONST_METHOD3(link,
- ErrorCode(Control control, std::string_view from, std::string_view to));
- MOCK_CONST_METHOD2(unlink, ErrorCode(Control control, std::string_view path));
- MOCK_CONST_METHOD2(openWrite, base::unique_fd(Control control, FileId id));
+ ErrorCode(const Control& control, std::string_view from, std::string_view to));
+ MOCK_CONST_METHOD2(unlink, ErrorCode(const Control& control, std::string_view path));
+ MOCK_CONST_METHOD2(openWrite, base::unique_fd(const Control& control, FileId id));
MOCK_CONST_METHOD1(writeBlocks, ErrorCode(Span<const DataBlock> blocks));
void makeFileFails() { ON_CALL(*this, makeFile(_, _, _, _, _)).WillByDefault(Return(-1)); }
void makeFileSuccess() { ON_CALL(*this, makeFile(_, _, _, _, _)).WillByDefault(Return(0)); }
- RawMetadata getMountInfoMetadata(Control control, std::string_view path) {
+ RawMetadata getMountInfoMetadata(const Control& control, std::string_view path) {
metadata::Mount m;
m.mutable_storage()->set_id(100);
m.mutable_loader()->set_package_name("com.test");
@@ -189,13 +190,13 @@ public:
m.mutable_loader()->release_package_name();
return {metadata.begin(), metadata.end()};
}
- RawMetadata getStorageMetadata(Control control, std::string_view path) {
+ RawMetadata getStorageMetadata(const Control& control, std::string_view path) {
metadata::Storage st;
st.set_id(100);
auto metadata = st.SerializeAsString();
return {metadata.begin(), metadata.end()};
}
- RawMetadata getBindPointMetadata(Control control, std::string_view path) {
+ RawMetadata getBindPointMetadata(const Control& control, std::string_view path) {
metadata::BindPoint bp;
std::string destPath = "dest";
std::string srcPath = "src";
diff --git a/services/tests/servicestests/src/com/android/server/locksettings/FakeSettings.java b/services/tests/servicestests/src/com/android/server/locksettings/FakeSettings.java
index c5e924be2612..2bcd653a5476 100644
--- a/services/tests/servicestests/src/com/android/server/locksettings/FakeSettings.java
+++ b/services/tests/servicestests/src/com/android/server/locksettings/FakeSettings.java
@@ -23,6 +23,7 @@ public class FakeSettings {
private int mDeviceProvisioned;
private int mSecureFrpMode;
+ private int mUserSetupComplete;
public void setDeviceProvisioned(boolean provisioned) {
mDeviceProvisioned = provisioned ? 1 : 0;
@@ -32,6 +33,10 @@ public class FakeSettings {
mSecureFrpMode = secure ? 1 : 0;
}
+ public void setUserSetupComplete(boolean complete) {
+ mUserSetupComplete = complete ? 1 : 0;
+ }
+
public int globalGetInt(String keyName) {
switch (keyName) {
case Settings.Global.DEVICE_PROVISIONED:
@@ -46,6 +51,10 @@ public class FakeSettings {
if (Settings.Secure.SECURE_FRP_MODE.equals(keyName) && userId == UserHandle.USER_SYSTEM) {
return mSecureFrpMode;
}
+ if (Settings.Secure.USER_SETUP_COMPLETE.equals(keyName)
+ && userId == UserHandle.USER_SYSTEM) {
+ return mUserSetupComplete;
+ }
return defaultValue;
}
}
diff --git a/services/tests/servicestests/src/com/android/server/locksettings/LockSettingsServiceTests.java b/services/tests/servicestests/src/com/android/server/locksettings/LockSettingsServiceTests.java
index 661ce113e81e..07d7830c9b0f 100644
--- a/services/tests/servicestests/src/com/android/server/locksettings/LockSettingsServiceTests.java
+++ b/services/tests/servicestests/src/com/android/server/locksettings/LockSettingsServiceTests.java
@@ -417,7 +417,8 @@ public class LockSettingsServiceTests extends BaseLockSettingsServiceTests {
}
@Test
- public void testCredentialChangeNotPossibleInSecureFrpMode() {
+ public void testCredentialChangeNotPossibleInSecureFrpModeDuringSuw() {
+ mSettings.setUserSetupComplete(false);
mSettings.setSecureFrpMode(true);
try {
mService.setLockCredential(newPassword("1234"), nonePassword(), PRIMARY_USER_ID);
@@ -425,6 +426,14 @@ public class LockSettingsServiceTests extends BaseLockSettingsServiceTests {
} catch (SecurityException e) { }
}
+ @Test
+ public void testCredentialChangePossibleInSecureFrpModeAfterSuw() {
+ mSettings.setUserSetupComplete(true);
+ mSettings.setSecureFrpMode(true);
+ assertTrue(mService.setLockCredential(newPassword("1234"), nonePassword(),
+ PRIMARY_USER_ID));
+ }
+
private void testCreateCredential(int userId, LockscreenCredential credential)
throws RemoteException {
assertTrue(mService.setLockCredential(credential, nonePassword(), userId));
diff --git a/services/tests/servicestests/src/com/android/server/locksettings/SyntheticPasswordTests.java b/services/tests/servicestests/src/com/android/server/locksettings/SyntheticPasswordTests.java
index 8982925ce42d..ba851992cbad 100644
--- a/services/tests/servicestests/src/com/android/server/locksettings/SyntheticPasswordTests.java
+++ b/services/tests/servicestests/src/com/android/server/locksettings/SyntheticPasswordTests.java
@@ -53,6 +53,7 @@ import org.junit.Test;
import org.junit.runner.RunWith;
import org.mockito.ArgumentCaptor;
+import java.io.File;
import java.util.ArrayList;
@@ -104,7 +105,7 @@ public class SyntheticPasswordTests extends BaseLockSettingsServiceTests {
protected void initializeCredentialUnderSP(LockscreenCredential password, int userId)
throws RemoteException {
enableSyntheticPassword();
- mService.setLockCredential(password, nonePassword(), userId);
+ assertTrue(mService.setLockCredential(password, nonePassword(), userId));
assertEquals(CREDENTIAL_TYPE_PASSWORD, mService.getCredentialType(userId));
assertTrue(mService.isSyntheticPasswordBasedCredential(userId));
}
@@ -492,6 +493,44 @@ public class SyntheticPasswordTests extends BaseLockSettingsServiceTests {
verify(mAuthSecretService, never()).primaryUserCredential(any(ArrayList.class));
}
+ @Test
+ public void testUnlockUserWithToken() throws Exception {
+ LockscreenCredential password = newPassword("password");
+ byte[] token = "some-high-entropy-secure-token".getBytes();
+ initializeCredentialUnderSP(password, PRIMARY_USER_ID);
+ // Disregard any reportPasswordChanged() invocations as part of credential setup.
+ flushHandlerTasks();
+ reset(mDevicePolicyManager);
+
+ long handle = mLocalService.addEscrowToken(token, PRIMARY_USER_ID, null);
+ mService.verifyCredential(password, 0, PRIMARY_USER_ID).getResponseCode();
+ assertTrue(mLocalService.isEscrowTokenActive(handle, PRIMARY_USER_ID));
+
+ mService.onCleanupUser(PRIMARY_USER_ID);
+ assertNull(mLocalService.getUserPasswordMetrics(PRIMARY_USER_ID));
+
+ assertTrue(mLocalService.unlockUserWithToken(handle, token, PRIMARY_USER_ID));
+ assertEquals(PasswordMetrics.computeForCredential(password),
+ mLocalService.getUserPasswordMetrics(PRIMARY_USER_ID));
+ }
+
+ @Test
+ public void testPasswordChange_NoOrphanedFilesLeft() throws Exception {
+ LockscreenCredential password = newPassword("password");
+ initializeCredentialUnderSP(password, PRIMARY_USER_ID);
+ assertTrue(mService.setLockCredential(password, password, PRIMARY_USER_ID));
+
+ String handleString = String.format("%016x",
+ mService.getSyntheticPasswordHandleLocked(PRIMARY_USER_ID));
+ File directory = mStorage.getSyntheticPasswordDirectoryForUser(PRIMARY_USER_ID);
+ for (File file : directory.listFiles()) {
+ String[] parts = file.getName().split("\\.");
+ if (!parts[0].equals(handleString) && !parts[0].equals("0000000000000000")) {
+ fail("Orphaned state left: " + file.getName());
+ }
+ }
+ }
+
// b/62213311
//TODO: add non-migration work profile case, and unify/un-unify transition.
//TODO: test token after user resets password
diff --git a/services/tests/wmtests/src/com/android/server/wm/ActivityDisplayTests.java b/services/tests/wmtests/src/com/android/server/wm/ActivityDisplayTests.java
index 4f84ee17b087..05604b2b9aeb 100644
--- a/services/tests/wmtests/src/com/android/server/wm/ActivityDisplayTests.java
+++ b/services/tests/wmtests/src/com/android/server/wm/ActivityDisplayTests.java
@@ -69,11 +69,11 @@ public class ActivityDisplayTests extends ActivityTestsBase {
stack.moveToFront("moveStackToFront");
// After moving the stack to front, the previous focused should be the last focused.
assertTrue(stack.isFocusedStackOnDisplay());
- assertEquals(prevFocusedStack, display.getLastFocusedStack());
+ assertEquals(prevFocusedStack, display.mTaskContainers.getLastFocusedStack());
stack.moveToBack("moveStackToBack", null /* task */);
// After moving the stack to back, the stack should be the last focused.
- assertEquals(stack, display.getLastFocusedStack());
+ assertEquals(stack, display.mTaskContainers.getLastFocusedStack());
}
/**
@@ -225,7 +225,7 @@ public class ActivityDisplayTests extends ActivityTestsBase {
final ActivityRecord activity = new ActivityBuilder(mService).setCreateTask(true)
.setStack(alwaysOnTopStack).build();
alwaysOnTopStack.setAlwaysOnTop(true);
- display.positionStackAtTop(alwaysOnTopStack, false /* includingParents */);
+ display.mTaskContainers.positionStackAtTop(alwaysOnTopStack, false /* includingParents */);
assertTrue(alwaysOnTopStack.isAlwaysOnTop());
// Ensure always on top state is synced to the children of the stack.
assertTrue(alwaysOnTopStack.getTopNonFinishingActivity().isAlwaysOnTop());
@@ -239,7 +239,8 @@ public class ActivityDisplayTests extends ActivityTestsBase {
final ActivityStack anotherAlwaysOnTopStack = display.createStack(
WINDOWING_MODE_FREEFORM, ACTIVITY_TYPE_STANDARD, true /* onTop */);
anotherAlwaysOnTopStack.setAlwaysOnTop(true);
- display.positionStackAtTop(anotherAlwaysOnTopStack, false /* includingParents */);
+ display.mTaskContainers.positionStackAtTop(anotherAlwaysOnTopStack,
+ false /* includingParents */);
assertTrue(anotherAlwaysOnTopStack.isAlwaysOnTop());
int topPosition = display.getStackCount() - 1;
// Ensure the new alwaysOnTop stack is put below the pinned stack, but on top of the
@@ -255,7 +256,8 @@ public class ActivityDisplayTests extends ActivityTestsBase {
assertEquals(nonAlwaysOnTopStack, display.getStackAt(topPosition - 3));
anotherAlwaysOnTopStack.setAlwaysOnTop(false);
- display.positionStackAtTop(anotherAlwaysOnTopStack, false /* includingParents */);
+ display.mTaskContainers.positionStackAtTop(anotherAlwaysOnTopStack,
+ false /* includingParents */);
assertFalse(anotherAlwaysOnTopStack.isAlwaysOnTop());
// Ensure, when always on top is turned off for a stack, the stack is put just below all
// other always on top stacks.
@@ -300,7 +302,7 @@ public class ActivityDisplayTests extends ActivityTestsBase {
// Reordering stacks while removing stacks.
doAnswer(invocation -> {
- display.positionStackAtTop(stack3, false);
+ display.mTaskContainers.positionStackAtTop(stack3, false);
return true;
}).when(mSupervisor).removeTask(eq(task4), anyBoolean(), anyBoolean(), any());
diff --git a/services/tests/wmtests/src/com/android/server/wm/ActivityStackTests.java b/services/tests/wmtests/src/com/android/server/wm/ActivityStackTests.java
index 12934ee8bb78..71ca8781b7f9 100644
--- a/services/tests/wmtests/src/com/android/server/wm/ActivityStackTests.java
+++ b/services/tests/wmtests/src/com/android/server/wm/ActivityStackTests.java
@@ -613,7 +613,7 @@ public class ActivityStackTests extends ActivityTestsBase {
// Ensure that we don't move the home stack if it is already behind the top fullscreen stack
int homeStackIndex = mDefaultDisplay.getIndexOf(homeStack);
assertEquals(fullscreenStack, mDefaultDisplay.getStackAbove(homeStack));
- mDefaultDisplay.moveStackBehindBottomMostVisibleStack(homeStack);
+ mDefaultDisplay.mTaskContainers.moveStackBehindBottomMostVisibleStack(homeStack);
assertEquals(homeStackIndex, mDefaultDisplay.getIndexOf(homeStack));
}
@@ -632,7 +632,7 @@ public class ActivityStackTests extends ActivityTestsBase {
// Ensure that we don't move the home stack if it is already behind the top fullscreen stack
int homeStackIndex = mDefaultDisplay.getIndexOf(homeStack);
assertEquals(fullscreenStack, mDefaultDisplay.getStackAbove(homeStack));
- mDefaultDisplay.moveStackBehindBottomMostVisibleStack(homeStack);
+ mDefaultDisplay.mTaskContainers.moveStackBehindBottomMostVisibleStack(homeStack);
assertEquals(homeStackIndex, mDefaultDisplay.getIndexOf(homeStack));
}
@@ -651,7 +651,7 @@ public class ActivityStackTests extends ActivityTestsBase {
// Ensure we don't move the home stack if it is already on top
int homeStackIndex = mDefaultDisplay.getIndexOf(homeStack);
assertNull(mDefaultDisplay.getStackAbove(homeStack));
- mDefaultDisplay.moveStackBehindBottomMostVisibleStack(homeStack);
+ mDefaultDisplay.mTaskContainers.moveStackBehindBottomMostVisibleStack(homeStack);
assertEquals(homeStackIndex, mDefaultDisplay.getIndexOf(homeStack));
}
@@ -677,7 +677,7 @@ public class ActivityStackTests extends ActivityTestsBase {
// Ensure that we move the home stack behind the bottom most fullscreen stack, ignoring the
// pinned stack
assertEquals(fullscreenStack1, mDefaultDisplay.getStackAbove(homeStack));
- mDefaultDisplay.moveStackBehindBottomMostVisibleStack(homeStack);
+ mDefaultDisplay.mTaskContainers.moveStackBehindBottomMostVisibleStack(homeStack);
assertEquals(fullscreenStack2, mDefaultDisplay.getStackAbove(homeStack));
}
@@ -702,7 +702,7 @@ public class ActivityStackTests extends ActivityTestsBase {
// Ensure that we move the home stack behind the bottom most non-translucent fullscreen
// stack
assertEquals(fullscreenStack1, mDefaultDisplay.getStackAbove(homeStack));
- mDefaultDisplay.moveStackBehindBottomMostVisibleStack(homeStack);
+ mDefaultDisplay.mTaskContainers.moveStackBehindBottomMostVisibleStack(homeStack);
assertEquals(fullscreenStack1, mDefaultDisplay.getStackAbove(homeStack));
}
@@ -725,7 +725,7 @@ public class ActivityStackTests extends ActivityTestsBase {
// Ensure we don't move the home stack behind itself
int homeStackIndex = mDefaultDisplay.getIndexOf(homeStack);
- mDefaultDisplay.moveStackBehindStack(homeStack, homeStack);
+ mDefaultDisplay.mTaskContainers.moveStackBehindStack(homeStack, homeStack);
assertEquals(homeStackIndex, mDefaultDisplay.getIndexOf(homeStack));
}
@@ -748,13 +748,13 @@ public class ActivityStackTests extends ActivityTestsBase {
final ActivityStack homeStack = createStackForShouldBeVisibleTest(mDefaultDisplay,
WINDOWING_MODE_FULLSCREEN, ACTIVITY_TYPE_HOME, true /* onTop */);
- mDefaultDisplay.moveStackBehindStack(homeStack, fullscreenStack1);
+ mDefaultDisplay.mTaskContainers.moveStackBehindStack(homeStack, fullscreenStack1);
assertEquals(fullscreenStack1, mDefaultDisplay.getStackAbove(homeStack));
- mDefaultDisplay.moveStackBehindStack(homeStack, fullscreenStack2);
+ mDefaultDisplay.mTaskContainers.moveStackBehindStack(homeStack, fullscreenStack2);
assertEquals(fullscreenStack2, mDefaultDisplay.getStackAbove(homeStack));
- mDefaultDisplay.moveStackBehindStack(homeStack, fullscreenStack4);
+ mDefaultDisplay.mTaskContainers.moveStackBehindStack(homeStack, fullscreenStack4);
assertEquals(fullscreenStack4, mDefaultDisplay.getStackAbove(homeStack));
- mDefaultDisplay.moveStackBehindStack(homeStack, fullscreenStack2);
+ mDefaultDisplay.mTaskContainers.moveStackBehindStack(homeStack, fullscreenStack2);
assertEquals(fullscreenStack2, mDefaultDisplay.getStackAbove(homeStack));
}
@@ -845,9 +845,10 @@ public class ActivityStackTests extends ActivityTestsBase {
// Home stack and activity are created in ActivityTestsBase#setupActivityManagerService
stack = mDefaultDisplay.getStack(WINDOWING_MODE_FULLSCREEN, ACTIVITY_TYPE_HOME);
if (onTop) {
- mDefaultDisplay.positionStackAtTop(stack, false /* includingParents */);
+ mDefaultDisplay.mTaskContainers.positionStackAtTop(stack,
+ false /* includingParents */);
} else {
- mDefaultDisplay.positionStackAtBottom(stack);
+ mDefaultDisplay.mTaskContainers.positionStackAtBottom(stack);
}
} else {
stack = new StackBuilder(mRootWindowContainer)
@@ -1090,7 +1091,7 @@ public class ActivityStackTests extends ActivityTestsBase {
mDefaultDisplay.registerStackOrderChangedListener(listener);
try {
mStack.mReparenting = true;
- mDefaultDisplay.addStack(mStack, 0);
+ mDefaultDisplay.mTaskContainers.addStack(mStack, 0);
} finally {
mDefaultDisplay.unregisterStackOrderChangedListener(listener);
}
@@ -1105,7 +1106,7 @@ public class ActivityStackTests extends ActivityTestsBase {
mDefaultDisplay, WINDOWING_MODE_FULLSCREEN, ACTIVITY_TYPE_STANDARD,
true /* onTop */);
mDefaultDisplay.registerStackOrderChangedListener(listener);
- mDefaultDisplay.positionStackAtBottom(fullscreenStack1);
+ mDefaultDisplay.mTaskContainers.positionStackAtBottom(fullscreenStack1);
} finally {
mDefaultDisplay.unregisterStackOrderChangedListener(listener);
}
diff --git a/services/tests/wmtests/src/com/android/server/wm/ActivityTestsBase.java b/services/tests/wmtests/src/com/android/server/wm/ActivityTestsBase.java
index 716369d49036..9240b2222cd6 100644
--- a/services/tests/wmtests/src/com/android/server/wm/ActivityTestsBase.java
+++ b/services/tests/wmtests/src/com/android/server/wm/ActivityTestsBase.java
@@ -462,9 +462,11 @@ class ActivityTestsBase extends SystemServiceTestsBase {
}
ActivityStack build() {
- final int stackId = mStackId >= 0 ? mStackId : mDisplay.getNextStackId();
- final ActivityStack stack = mDisplay.createStackUnchecked(mWindowingMode,
- mActivityType, stackId, mOnTop, mInfo, mIntent, false /* createdByOrganizer */);
+ final int stackId = mStackId >= 0 ? mStackId
+ : mDisplay.mTaskContainers.getNextStackId();
+ final ActivityStack stack = mDisplay.mTaskContainers.createStackUnchecked(
+ mWindowingMode, mActivityType, stackId, mOnTop, mInfo, mIntent,
+ false /* createdByOrganizer */);
final ActivityStackSupervisor supervisor = mRootWindowContainer.mStackSupervisor;
if (mCreateActivity) {
diff --git a/services/tests/wmtests/src/com/android/server/wm/DisplayAreaProviderTest.java b/services/tests/wmtests/src/com/android/server/wm/DisplayAreaProviderTest.java
index 31206315618e..32d7a0773b5e 100644
--- a/services/tests/wmtests/src/com/android/server/wm/DisplayAreaProviderTest.java
+++ b/services/tests/wmtests/src/com/android/server/wm/DisplayAreaProviderTest.java
@@ -78,7 +78,7 @@ public class DisplayAreaProviderTest {
@Override
public DisplayAreaPolicy instantiate(WindowManagerService wmService, DisplayContent content,
DisplayArea.Root root, DisplayArea<? extends WindowContainer> imeContainer,
- DisplayContent.TaskContainers taskContainers) {
+ TaskContainers taskContainers) {
throw new RuntimeException("test stub");
}
}
diff --git a/services/tests/wmtests/src/com/android/server/wm/DisplayContentTests.java b/services/tests/wmtests/src/com/android/server/wm/DisplayContentTests.java
index 218c8169e7e6..5b96c4372abc 100644
--- a/services/tests/wmtests/src/com/android/server/wm/DisplayContentTests.java
+++ b/services/tests/wmtests/src/com/android/server/wm/DisplayContentTests.java
@@ -1088,7 +1088,7 @@ public class DisplayContentTests extends WindowTestsBase {
}
assertNull(defaultDisplay.getRootHomeTask());
- assertNotNull(defaultDisplay.getOrCreateRootHomeTask());
+ assertNotNull(defaultDisplay.mTaskContainers.getOrCreateRootHomeTask());
}
@Test
@@ -1104,7 +1104,7 @@ public class DisplayContentTests extends WindowTestsBase {
}
assertNull(display.getRootHomeTask());
- assertNotNull(display.getOrCreateRootHomeTask());
+ assertNotNull(display.mTaskContainers.getOrCreateRootHomeTask());
}
@Test
@@ -1113,7 +1113,7 @@ public class DisplayContentTests extends WindowTestsBase {
doReturn(false).when(display).supportsSystemDecorations();
assertNull(display.getRootHomeTask());
- assertNull(display.getOrCreateRootHomeTask());
+ assertNull(display.mTaskContainers.getOrCreateRootHomeTask());
}
@Test
@@ -1122,7 +1122,7 @@ public class DisplayContentTests extends WindowTestsBase {
doReturn(true).when(display).isUntrustedVirtualDisplay();
assertNull(display.getRootHomeTask());
- assertNull(display.getOrCreateRootHomeTask());
+ assertNull(display.mTaskContainers.getOrCreateRootHomeTask());
}
private boolean isOptionsPanelAtRight(int displayId) {
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 b3a253029ed0..cfb5bc7d7759 100644
--- a/services/tests/wmtests/src/com/android/server/wm/RecentsAnimationTest.java
+++ b/services/tests/wmtests/src/com/android/server/wm/RecentsAnimationTest.java
@@ -119,7 +119,7 @@ public class RecentsAnimationTest extends ActivityTestsBase {
final DisplayContent defaultDisplay = mRootWindowContainer.getDefaultDisplay();
final ActivityStack homeStack =
defaultDisplay.getStack(WINDOWING_MODE_FULLSCREEN, ACTIVITY_TYPE_HOME);
- defaultDisplay.positionStackAtTop(homeStack, false /* includingParents */);
+ defaultDisplay.mTaskContainers.positionStackAtTop(homeStack, false /* includingParents */);
ActivityRecord topRunningHomeActivity = homeStack.topRunningActivity();
if (topRunningHomeActivity == null) {
topRunningHomeActivity = new ActivityBuilder(mService)
diff --git a/services/tests/wmtests/src/com/android/server/wm/RootActivityContainerTests.java b/services/tests/wmtests/src/com/android/server/wm/RootActivityContainerTests.java
index e841e434ea82..dc354a73b624 100644
--- a/services/tests/wmtests/src/com/android/server/wm/RootActivityContainerTests.java
+++ b/services/tests/wmtests/src/com/android/server/wm/RootActivityContainerTests.java
@@ -325,7 +325,8 @@ public class RootActivityContainerTests extends ActivityTestsBase {
mSupervisor.findTaskToMoveToFront(targetTask, 0, ActivityOptions.makeBasic(), reason,
false);
- verify(display).moveHomeStackToFront(contains(reason));
+ final TaskContainers taskContainers = display.mTaskContainers;
+ verify(taskContainers).moveHomeStackToFront(contains(reason));
}
/**
@@ -352,7 +353,8 @@ public class RootActivityContainerTests extends ActivityTestsBase {
mSupervisor.findTaskToMoveToFront(targetTask, 0, ActivityOptions.makeBasic(), reason,
false);
- verify(display, never()).moveHomeStackToFront(contains(reason));
+ final TaskContainers taskContainers = display.mTaskContainers;
+ verify(taskContainers, never()).moveHomeStackToFront(contains(reason));
}
/**
@@ -367,7 +369,7 @@ public class RootActivityContainerTests extends ActivityTestsBase {
ACTIVITY_TYPE_STANDARD, false /* onTop */));
final Task task = new TaskBuilder(mSupervisor).setStack(targetStack).build();
final ActivityRecord activity = new ActivityBuilder(mService).setTask(task).build();
- display.positionStackAtBottom(targetStack);
+ display.mTaskContainers.positionStackAtBottom(targetStack);
// Assume the stack is not at the topmost position (e.g. behind always-on-top stacks) but it
// is the current top focused stack.
@@ -470,7 +472,7 @@ public class RootActivityContainerTests extends ActivityTestsBase {
final Task task = new TaskBuilder(mSupervisor).setStack(targetStack).build();
final ActivityRecord activity = new ActivityBuilder(mService).setTask(task).build();
activity.setState(ActivityState.RESUMED, "test");
- display.positionStackAtBottom(targetStack);
+ display.mTaskContainers.positionStackAtBottom(targetStack);
// Assume the stack is at the topmost position
assertFalse(targetStack.isTopStackOnDisplay());
diff --git a/services/tests/wmtests/src/com/android/server/wm/SystemServicesTestRule.java b/services/tests/wmtests/src/com/android/server/wm/SystemServicesTestRule.java
index 091f493b4687..8c8d3f1242cb 100644
--- a/services/tests/wmtests/src/com/android/server/wm/SystemServicesTestRule.java
+++ b/services/tests/wmtests/src/com/android/server/wm/SystemServicesTestRule.java
@@ -316,7 +316,9 @@ public class SystemServicesTestRule implements TestRule {
// that the default display is in fullscreen mode.
display.setDisplayWindowingMode(WINDOWING_MODE_FULLSCREEN);
spyOn(display);
- final ActivityStack homeStack = display.getStack(
+ final TaskContainers taskContainer = display.mTaskContainers;
+ spyOn(taskContainer);
+ final ActivityStack homeStack = taskContainer.getStack(
WINDOWING_MODE_FULLSCREEN, ACTIVITY_TYPE_HOME);
spyOn(homeStack);
}
diff --git a/services/tests/wmtests/src/com/android/server/wm/TestDisplayContent.java b/services/tests/wmtests/src/com/android/server/wm/TestDisplayContent.java
index fc8cc96d224c..18737c2b4bb7 100644
--- a/services/tests/wmtests/src/com/android/server/wm/TestDisplayContent.java
+++ b/services/tests/wmtests/src/com/android/server/wm/TestDisplayContent.java
@@ -43,6 +43,7 @@ class TestDisplayContent extends DisplayContent {
// hard-code to FULLSCREEN for tests.
setWindowingMode(WINDOWING_MODE_FULLSCREEN);
spyOn(this);
+ spyOn(mTaskContainers);
final DisplayRotation displayRotation = getDisplayRotation();
spyOn(displayRotation);
diff --git a/tests/RollbackTest/lib/src/com/android/tests/rollback/host/WatchdogEventLogger.java b/tests/RollbackTest/lib/src/com/android/tests/rollback/host/WatchdogEventLogger.java
index 317d67e3791e..88731504eafe 100644
--- a/tests/RollbackTest/lib/src/com/android/tests/rollback/host/WatchdogEventLogger.java
+++ b/tests/RollbackTest/lib/src/com/android/tests/rollback/host/WatchdogEventLogger.java
@@ -36,8 +36,10 @@ public class WatchdogEventLogger {
}
public void stop() {
- mReceiver.stop();
- mReceiver.clear();
+ if (mReceiver != null) {
+ mReceiver.stop();
+ mReceiver.clear();
+ }
}
/**
diff --git a/tests/net/common/java/android/net/apf/ApfCapabilitiesTest.java b/tests/net/common/java/android/net/apf/ApfCapabilitiesTest.java
index f4f804aff08d..84805442e5c7 100644
--- a/tests/net/common/java/android/net/apf/ApfCapabilitiesTest.java
+++ b/tests/net/common/java/android/net/apf/ApfCapabilitiesTest.java
@@ -21,17 +21,31 @@ import static com.android.testutils.ParcelUtilsKt.assertParcelSane;
import static org.junit.Assert.assertEquals;
import static org.junit.Assert.assertFalse;
import static org.junit.Assert.assertNotEquals;
+import static org.junit.Assert.assertNotNull;
import static org.junit.Assert.assertTrue;
+import android.content.Context;
+
+import androidx.test.InstrumentationRegistry;
import androidx.test.filters.SmallTest;
import androidx.test.runner.AndroidJUnit4;
+import org.junit.Before;
import org.junit.Test;
import org.junit.runner.RunWith;
+import java.util.Arrays;
+
@RunWith(AndroidJUnit4.class)
@SmallTest
public class ApfCapabilitiesTest {
+ private Context mContext;
+
+ @Before
+ public void setUp() {
+ mContext = InstrumentationRegistry.getContext();
+ }
+
@Test
public void testConstructAndParcel() {
final ApfCapabilities caps = new ApfCapabilities(123, 456, 789);
@@ -59,4 +73,27 @@ public class ApfCapabilitiesTest {
caps = new ApfCapabilities(4 /* apfVersionSupported */, 5, 6);
assertTrue(caps.hasDataAccess());
}
+
+ @Test
+ public void testGetApfDrop8023Frames() {
+ // Get com.android.internal.R.bool.config_apfDrop802_3Frames. The test cannot directly
+ // use R.bool.config_apfDrop802_3Frames because that is not a stable resource ID.
+ final int resId = mContext.getResources().getIdentifier("config_apfDrop802_3Frames",
+ "bool", "android");
+ final boolean shouldDrop8023Frames = mContext.getResources().getBoolean(resId);
+ final boolean actual = ApfCapabilities.getApfDrop8023Frames();
+ assertEquals(shouldDrop8023Frames, actual);
+ }
+
+ @Test
+ public void testGetApfEtherTypeBlackList() {
+ // Get com.android.internal.R.array.config_apfEthTypeBlackList. The test cannot directly
+ // use R.array.config_apfEthTypeBlackList because that is not a stable resource ID.
+ final int resId = mContext.getResources().getIdentifier("config_apfEthTypeBlackList",
+ "array", "android");
+ final int[] blacklistedEtherTypeArray = mContext.getResources().getIntArray(resId);
+ final int[] actual = ApfCapabilities.getApfEtherTypeBlackList();
+ assertNotNull(actual);
+ assertTrue(Arrays.equals(blacklistedEtherTypeArray, actual));
+ }
}