diff options
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)); + } } |