diff options
89 files changed, 5398 insertions, 5210 deletions
diff --git a/config/hiddenapi-light-greylist.txt b/config/hiddenapi-light-greylist.txt index 0bfcadd04553..c193e894be0b 100644 --- a/config/hiddenapi-light-greylist.txt +++ b/config/hiddenapi-light-greylist.txt @@ -2615,7 +2615,6 @@ Lcom/android/internal/telephony/dataconnection/DataConnection;->initConnection(L Lcom/android/internal/telephony/dataconnection/DataConnection;->log(Ljava/lang/String;)V Lcom/android/internal/telephony/dataconnection/DataConnection;->mActivatingState:Lcom/android/internal/telephony/dataconnection/DataConnection$DcActivatingState; Lcom/android/internal/telephony/dataconnection/DataConnection;->mActiveState:Lcom/android/internal/telephony/dataconnection/DataConnection$DcActiveState; -Lcom/android/internal/telephony/dataconnection/DataConnection;->mApnContexts:Ljava/util/HashMap; Lcom/android/internal/telephony/dataconnection/DataConnection;->mConnectionParams:Lcom/android/internal/telephony/dataconnection/DataConnection$ConnectionParams; Lcom/android/internal/telephony/dataconnection/DataConnection;->mDataRegState:I Lcom/android/internal/telephony/dataconnection/DataConnection;->mDcFailCause:Lcom/android/internal/telephony/dataconnection/DcFailCause; diff --git a/core/java/android/os/PowerManager.java b/core/java/android/os/PowerManager.java index 1c1db68babc3..894015ff5a92 100644 --- a/core/java/android/os/PowerManager.java +++ b/core/java/android/os/PowerManager.java @@ -1008,7 +1008,8 @@ public final class PowerManager { * progress, does nothing. Unlike {@link #nap(long)}, this does not put device to sleep when * dream ends. * </p><p> - * Requires the {@link android.Manifest.permission#WRITE_DREAM_STATE} permission. + * Requires the {@link android.Manifest.permission#READ_DREAM_STATE} and + * {@link android.Manifest.permission#WRITE_DREAM_STATE} permissions. * </p> * * @param time The time when the request to nap was issued, in the @@ -1019,7 +1020,9 @@ public final class PowerManager { * @hide */ @SystemApi - @RequiresPermission(android.Manifest.permission.WRITE_DREAM_STATE) + @RequiresPermission(allOf = { + android.Manifest.permission.READ_DREAM_STATE, + android.Manifest.permission.WRITE_DREAM_STATE }) public void dream(long time) { Sandman.startDreamByUserRequest(mContext); } diff --git a/core/java/com/android/internal/widget/ResolverDrawerLayout.java b/core/java/com/android/internal/widget/ResolverDrawerLayout.java index 7635a727ae85..b7e656bc9278 100644 --- a/core/java/com/android/internal/widget/ResolverDrawerLayout.java +++ b/core/java/com/android/internal/widget/ResolverDrawerLayout.java @@ -814,7 +814,14 @@ public class ResolverDrawerLayout extends ViewGroup { final View child = getChildAt(i); final LayoutParams lp = (LayoutParams) child.getLayoutParams(); if (lp.alwaysShow && child.getVisibility() != GONE) { - measureChildWithMargins(child, widthSpec, widthPadding, heightSpec, heightUsed); + if (lp.maxHeight != -1) { + final int remainingHeight = heightSize - heightUsed; + measureChildWithMargins(child, widthSpec, widthPadding, + MeasureSpec.makeMeasureSpec(lp.maxHeight, MeasureSpec.AT_MOST), + lp.maxHeight > remainingHeight ? lp.maxHeight - remainingHeight : 0); + } else { + measureChildWithMargins(child, widthSpec, widthPadding, heightSpec, heightUsed); + } heightUsed += child.getMeasuredHeight(); } } @@ -824,9 +831,17 @@ public class ResolverDrawerLayout extends ViewGroup { // And now the rest. for (int i = 0; i < childCount; i++) { final View child = getChildAt(i); + final LayoutParams lp = (LayoutParams) child.getLayoutParams(); if (!lp.alwaysShow && child.getVisibility() != GONE) { - measureChildWithMargins(child, widthSpec, widthPadding, heightSpec, heightUsed); + if (lp.maxHeight != -1) { + final int remainingHeight = heightSize - heightUsed; + measureChildWithMargins(child, widthSpec, widthPadding, + MeasureSpec.makeMeasureSpec(lp.maxHeight, MeasureSpec.AT_MOST), + lp.maxHeight > remainingHeight ? lp.maxHeight - remainingHeight : 0); + } else { + measureChildWithMargins(child, widthSpec, widthPadding, heightSpec, heightUsed); + } heightUsed += child.getMeasuredHeight(); } } @@ -938,6 +953,7 @@ public class ResolverDrawerLayout extends ViewGroup { public boolean alwaysShow; public boolean ignoreOffset; public boolean hasNestedScrollIndicator; + public int maxHeight; public LayoutParams(Context c, AttributeSet attrs) { super(c, attrs); @@ -953,6 +969,8 @@ public class ResolverDrawerLayout extends ViewGroup { hasNestedScrollIndicator = a.getBoolean( R.styleable.ResolverDrawerLayout_LayoutParams_layout_hasNestedScrollIndicator, false); + maxHeight = a.getDimensionPixelSize( + R.styleable.ResolverDrawerLayout_LayoutParams_layout_maxHeight, -1); a.recycle(); } @@ -965,6 +983,7 @@ public class ResolverDrawerLayout extends ViewGroup { this.alwaysShow = source.alwaysShow; this.ignoreOffset = source.ignoreOffset; this.hasNestedScrollIndicator = source.hasNestedScrollIndicator; + this.maxHeight = source.maxHeight; } public LayoutParams(MarginLayoutParams source) { diff --git a/core/jni/android_media_AudioSystem.cpp b/core/jni/android_media_AudioSystem.cpp index 2c0158a03434..adab8e2bae39 100644 --- a/core/jni/android_media_AudioSystem.cpp +++ b/core/jni/android_media_AudioSystem.cpp @@ -2032,6 +2032,35 @@ static jint android_media_AudioSystem_get_FCC_8(JNIEnv *env, jobject thiz) { return FCC_8; } +static jint +android_media_AudioSystem_setAssistantUid(JNIEnv *env, jobject thiz, jint uid) +{ + status_t status = AudioSystem::setAssistantUid(uid); + return (jint)nativeToJavaStatus(status); +} + +static jint +android_media_AudioSystem_setA11yServicesUids(JNIEnv *env, jobject thiz, jintArray uids) { + std::vector<uid_t> nativeUidsVector; + + if (uids != nullptr) { + jsize len = env->GetArrayLength(uids); + + if (len > 0) { + int *nativeUids = nullptr; + nativeUids = env->GetIntArrayElements(uids, 0); + if (nativeUids != nullptr) { + for (size_t i = 0; i < len; i++) { + nativeUidsVector.push_back(nativeUids[i]); + } + env->ReleaseIntArrayElements(uids, nativeUids, 0); + } + } + } + status_t status = AudioSystem::setA11yServicesUids(nativeUidsVector); + return (jint)nativeToJavaStatus(status); +} + // ---------------------------------------------------------------------------- static const JNINativeMethod gMethods[] = { @@ -2092,6 +2121,8 @@ static const JNINativeMethod gMethods[] = { {"getMicrophones", "(Ljava/util/ArrayList;)I", (void *)android_media_AudioSystem_getMicrophones}, {"getSurroundFormats", "(Ljava/util/Map;Z)I", (void *)android_media_AudioSystem_getSurroundFormats}, {"setSurroundFormatEnabled", "(IZ)I", (void *)android_media_AudioSystem_setSurroundFormatEnabled}, + {"setAssistantUid", "(I)I", (void *)android_media_AudioSystem_setAssistantUid}, + {"setA11yServicesUids", "([I)I", (void *)android_media_AudioSystem_setA11yServicesUids}, }; static const JNINativeMethod gEventHandlerMethods[] = { diff --git a/core/res/res/values/attrs.xml b/core/res/res/values/attrs.xml index 6c4861b34429..470e1ccf36e8 100644 --- a/core/res/res/values/attrs.xml +++ b/core/res/res/values/attrs.xml @@ -8803,6 +8803,7 @@ <attr name="layout_ignoreOffset" format="boolean" /> <attr name="layout_gravity" /> <attr name="layout_hasNestedScrollIndicator" format="boolean" /> + <attr name="layout_maxHeight" /> </declare-styleable> <!-- @hide --> diff --git a/core/tests/coretests/src/com/android/internal/app/ResolverActivityTest.java b/core/tests/coretests/src/com/android/internal/app/ResolverActivityTest.java index 9fcb06ee54f6..404c99c82a4c 100644 --- a/core/tests/coretests/src/com/android/internal/app/ResolverActivityTest.java +++ b/core/tests/coretests/src/com/android/internal/app/ResolverActivityTest.java @@ -103,6 +103,49 @@ public class ResolverActivityTest { } @Test + public void setMaxHeight() throws Exception { + Intent sendIntent = createSendImageIntent(); + List<ResolvedComponentInfo> resolvedComponentInfos = createResolvedComponentsForTest(2); + + when(sOverrides.resolverListController.getResolversForIntent(Mockito.anyBoolean(), + Mockito.anyBoolean(), + Mockito.isA(List.class))).thenReturn(resolvedComponentInfos); + waitForIdle(); + + final ResolverWrapperActivity activity = mActivityRule.launchActivity(sendIntent); + final View resolverList = activity.findViewById(R.id.resolver_list); + final int initialResolverHeight = resolverList.getHeight(); + + activity.runOnUiThread(() -> { + ResolverDrawerLayout layout = (ResolverDrawerLayout) + activity.findViewById( + R.id.contentPanel); + ((ResolverDrawerLayout.LayoutParams) resolverList.getLayoutParams()).maxHeight + = initialResolverHeight - 1; + // Force a relayout + layout.invalidate(); + layout.requestLayout(); + }); + waitForIdle(); + assertThat("Drawer should be capped at maxHeight", + resolverList.getHeight() == (initialResolverHeight - 1)); + + activity.runOnUiThread(() -> { + ResolverDrawerLayout layout = (ResolverDrawerLayout) + activity.findViewById( + R.id.contentPanel); + ((ResolverDrawerLayout.LayoutParams) resolverList.getLayoutParams()).maxHeight + = initialResolverHeight + 1; + // Force a relayout + layout.invalidate(); + layout.requestLayout(); + }); + waitForIdle(); + assertThat("Drawer should not change height if its height is less than maxHeight", + resolverList.getHeight() == initialResolverHeight); + } + + @Test public void setShowAtTopToTrue() throws Exception { Intent sendIntent = createSendImageIntent(); List<ResolvedComponentInfo> resolvedComponentInfos = createResolvedComponentsForTest(2); diff --git a/libs/hwui/Android.bp b/libs/hwui/Android.bp index 17d2db71ab58..4a5b61a2d6cd 100644 --- a/libs/hwui/Android.bp +++ b/libs/hwui/Android.bp @@ -45,9 +45,6 @@ cc_defaults { ], product_variables: { - device_uses_hwc2: { - cflags: ["-DUSE_HWC2"], - }, eng: { lto: { never: true, diff --git a/libs/hwui/JankTracker.cpp b/libs/hwui/JankTracker.cpp index e7ae7675f0b8..ccbb6c10d3af 100644 --- a/libs/hwui/JankTracker.cpp +++ b/libs/hwui/JankTracker.cpp @@ -82,7 +82,6 @@ static FrameInfoIndex sFrameStart = FrameInfoIndex::IntendedVsync; JankTracker::JankTracker(ProfileDataContainer* globalData, const DisplayInfo& displayInfo) { mGlobalData = globalData; nsecs_t frameIntervalNanos = static_cast<nsecs_t>(1_s / displayInfo.fps); -#if USE_HWC2 nsecs_t sfOffset = frameIntervalNanos - (displayInfo.presentationDeadline - 1_ms); nsecs_t offsetDelta = sfOffset - displayInfo.appVsyncOffset; // There are two different offset cases. If the offsetDelta is positive @@ -96,7 +95,6 @@ JankTracker::JankTracker(ProfileDataContainer* globalData, const DisplayInfo& di // return due to the staggering of VSYNC-app & VSYNC-sf. mDequeueTimeForgiveness = offsetDelta + 4_ms; } -#endif setFrameInterval(frameIntervalNanos); } diff --git a/media/java/android/media/AudioSystem.java b/media/java/android/media/AudioSystem.java index 1ebe059ad44b..082a375470ee 100644 --- a/media/java/android/media/AudioSystem.java +++ b/media/java/android/media/AudioSystem.java @@ -924,6 +924,15 @@ public class AudioSystem public static native int setSurroundFormatEnabled(int audioFormat, boolean enabled); + /** + * Communicate UID of active assistant to audio policy service. + */ + public static native int setAssistantUid(int uid); + /** + * Communicate UIDs of active accessibility services to audio policy service. + */ + public static native int setA11yServicesUids(int[] uids); + // Items shared with audio service /** diff --git a/media/java/android/media/CallbackDataSourceDesc.java b/media/java/android/media/CallbackDataSourceDesc.java index 82273da7097d..e22203dab4bc 100644 --- a/media/java/android/media/CallbackDataSourceDesc.java +++ b/media/java/android/media/CallbackDataSourceDesc.java @@ -20,9 +20,11 @@ import android.annotation.NonNull; /** * @hide - * Structure for file data source descriptor. + * Structure of data source descriptor for sources using callback. * - * Used by {@link MediaPlayer2#setDataSource(CallbackDataSourceDesc)} + * Used by {@link MediaPlayer2#setDataSource(DataSourceDesc)}, + * {@link MediaPlayer2#setNextDataSource(DataSourceDesc)} and + * {@link MediaPlayer2#setNextDataSources(List<DataSourceDesc>)} * to set data source for playback. * * <p>Users should use {@link Builder} to create {@link CallbackDataSourceDesc}. diff --git a/media/java/android/media/DataSourceDesc.java b/media/java/android/media/DataSourceDesc.java index 360af34bc722..702034e987ca 100644 --- a/media/java/android/media/DataSourceDesc.java +++ b/media/java/android/media/DataSourceDesc.java @@ -22,7 +22,9 @@ import android.annotation.NonNull; * @hide * Base class of data source descriptor. * - * Used by {@link MediaPlayer2#setDataSource(DataSourceDesc)} + * Used by {@link MediaPlayer2#setDataSource(DataSourceDesc)}, + * {@link MediaPlayer2#setNextDataSource(DataSourceDesc)} and + * {@link MediaPlayer2#setNextDataSources(List<DataSourceDesc>)} * to set data source for playback. * * <p>Users should use subclasses' builder to change {@link DataSourceDesc}. @@ -30,7 +32,7 @@ import android.annotation.NonNull; */ public class DataSourceDesc { // intentionally less than long.MAX_VALUE - public static final long LONG_MAX = 0x7ffffffffffffffL; + static final long LONG_MAX = 0x7ffffffffffffffL; // keep consistent with native code public static final long LONG_MAX_TIME_MS = LONG_MAX / 1000; @@ -46,6 +48,19 @@ public class DataSourceDesc { } /** + * Releases the resources held by this {@code DataSourceDesc} object. + */ + void close() { + } + + // Have to declare protected for finalize() since it is protected + // in the base class Object. + @Override + protected void finalize() throws Throwable { + close(); + } + + /** * Return the media Id of data source. * @return the media Id of data source */ diff --git a/media/java/android/media/FileDataSourceDesc.java b/media/java/android/media/FileDataSourceDesc.java index 9e80975e032c..763a81f53765 100644 --- a/media/java/android/media/FileDataSourceDesc.java +++ b/media/java/android/media/FileDataSourceDesc.java @@ -17,20 +17,26 @@ package android.media; import android.annotation.NonNull; +import android.os.ParcelFileDescriptor; +import android.util.Log; -import java.io.FileDescriptor; +import java.io.IOException; /** * @hide - * Structure for data source descriptor. + * Structure of data source descriptor for sources using file descriptor. * - * Used by {@link MediaPlayer2#setDataSource(DataSourceDesc)} + * Used by {@link MediaPlayer2#setDataSource(DataSourceDesc)}, + * {@link MediaPlayer2#setNextDataSource(DataSourceDesc)} and + * {@link MediaPlayer2#setNextDataSources(List<DataSourceDesc>)} * to set data source for playback. * * <p>Users should use {@link Builder} to create {@link FileDataSourceDesc}. * */ public class FileDataSourceDesc extends DataSourceDesc { + private static final String TAG = "FileDataSourceDesc"; + /** * Used when the length of file descriptor is unknown. * @@ -38,34 +44,61 @@ public class FileDataSourceDesc extends DataSourceDesc { */ public static final long FD_LENGTH_UNKNOWN = LONG_MAX; - private FileDescriptor mFD; + private ParcelFileDescriptor mPFD; private long mOffset = 0; private long mLength = FD_LENGTH_UNKNOWN; private FileDataSourceDesc() { + super(); + } + + /** + * Releases the resources held by this {@code FileDataSourceDesc} object. + */ + @Override + void close() { + super.close(); + closeFD(); + } + + /** + * Releases the file descriptor held by this {@code FileDataSourceDesc} object. + */ + void closeFD() { + synchronized (this) { + if (mPFD != null) { + try { + mPFD.close(); + } catch (IOException e) { + Log.e(TAG, "failed to close pfd: " + e); + } + + mPFD = null; + } + } } /** - * Return the FileDescriptor of this data source. - * @return the FileDescriptor of this data source + * Return the ParcelFileDescriptor of this data source. + * @return the ParcelFileDescriptor of this data source */ - public FileDescriptor getFileDescriptor() { - return mFD; + public ParcelFileDescriptor getParcelFileDescriptor() { + return mPFD; } /** - * Return the offset associated with the FileDescriptor of this data source. + * Return the offset associated with the ParcelFileDescriptor of this data source. * It's meaningful only when it has been set by the {@link Builder}. - * @return the offset associated with the FileDescriptor of this data source + * @return the offset associated with the ParcelFileDescriptor of this data source */ public long getOffset() { return mOffset; } /** - * Return the content length associated with the FileDescriptor of this data source. + * Return the content length associated with the ParcelFileDescriptor of this data source. * {@link #FD_LENGTH_UNKNOWN} means same as the length of source content. - * @return the content length associated with the FileDescriptor of this data source + * @return the content length associated with the ParcelFileDescriptor of this data source */ public long getLength() { return mLength; @@ -78,7 +111,7 @@ public class FileDataSourceDesc extends DataSourceDesc { * * <pre class="prettyprint"> * FileDataSourceDesc newDSD = new FileDataSourceDesc.Builder() - * .setDataSource(fd, 0, srcLength) + * .setDataSource(pfd, 0, srcLength) * .setStartPosition(1000) * .setEndPosition(15000) * .build(); @@ -86,7 +119,7 @@ public class FileDataSourceDesc extends DataSourceDesc { * </pre> */ public static class Builder extends BuilderBase<Builder> { - private FileDescriptor mFD; + private ParcelFileDescriptor mPFD; private long mOffset = 0; private long mLength = FD_LENGTH_UNKNOWN; @@ -107,7 +140,7 @@ public class FileDataSourceDesc extends DataSourceDesc { if (dsd == null) { return; // use default } - mFD = dsd.mFD; + mPFD = dsd.mPFD; mOffset = dsd.mOffset; mLength = dsd.mLength; } @@ -122,7 +155,7 @@ public class FileDataSourceDesc extends DataSourceDesc { public @NonNull FileDataSourceDesc build() { FileDataSourceDesc dsd = new FileDataSourceDesc(); super.build(dsd); - dsd.mFD = mFD; + dsd.mPFD = mPFD; dsd.mOffset = mOffset; dsd.mLength = mLength; @@ -130,38 +163,46 @@ public class FileDataSourceDesc extends DataSourceDesc { } /** - * Sets the data source (FileDescriptor) to use. The FileDescriptor must be - * seekable (N.B. a LocalSocket is not seekable). It is the caller's responsibility - * to close the file descriptor after the source has been used. + * Sets the data source (ParcelFileDescriptor) to use. The ParcelFileDescriptor must be + * seekable (N.B. a LocalSocket is not seekable). When the {@link FileDataSourceDesc} + * created by this builder is passed to {@link MediaPlayer2} via + * {@link MediaPlayer2#setDataSource(DataSourceDesc)}, + * {@link MediaPlayer2#setNextDataSource(DataSourceDesc)} or + * {@link MediaPlayer2#setNextDataSources(List<DataSourceDesc>)}, MediaPlayer2 will + * close the ParcelFileDescriptor. * - * @param fd the FileDescriptor for the file to play + * @param pfd the ParcelFileDescriptor for the file to play * @return the same Builder instance. - * @throws NullPointerException if fd is null. + * @throws NullPointerException if pfd is null. */ - public @NonNull Builder setDataSource(@NonNull FileDescriptor fd) { - Media2Utils.checkArgument(fd != null, "fd cannot be null."); + public @NonNull Builder setDataSource(@NonNull ParcelFileDescriptor pfd) { + Media2Utils.checkArgument(pfd != null, "pfd cannot be null."); resetDataSource(); - mFD = fd; + mPFD = pfd; return this; } /** - * Sets the data source (FileDescriptor) to use. The FileDescriptor must be - * seekable (N.B. a LocalSocket is not seekable). It is the caller's responsibility - * to close the file descriptor after the source has been used. + * Sets the data source (ParcelFileDescriptor) to use. The ParcelFileDescriptor must be + * seekable (N.B. a LocalSocket is not seekable). When the {@link FileDataSourceDesc} + * created by this builder is passed to {@link MediaPlayer2} via + * {@link MediaPlayer2#setDataSource(DataSourceDesc)}, + * {@link MediaPlayer2#setNextDataSource(DataSourceDesc)} or + * {@link MediaPlayer2#setNextDataSources(List<DataSourceDesc>)}, MediaPlayer2 will + * close the ParcelFileDescriptor. * * Any negative number for offset is treated as 0. * Any negative number for length is treated as maximum length of the data source. * - * @param fd the FileDescriptor for the file to play + * @param pfd the ParcelFileDescriptor for the file to play * @param offset the offset into the file where the data to be played starts, in bytes * @param length the length in bytes of the data to be played * @return the same Builder instance. - * @throws NullPointerException if fd is null. + * @throws NullPointerException if pfd is null. */ public @NonNull Builder setDataSource( - @NonNull FileDescriptor fd, long offset, long length) { - Media2Utils.checkArgument(fd != null, "fd cannot be null."); + @NonNull ParcelFileDescriptor pfd, long offset, long length) { + Media2Utils.checkArgument(pfd != null, "pfd cannot be null."); if (offset < 0) { offset = 0; } @@ -169,14 +210,14 @@ public class FileDataSourceDesc extends DataSourceDesc { length = FD_LENGTH_UNKNOWN; } resetDataSource(); - mFD = fd; + mPFD = pfd; mOffset = offset; mLength = length; return this; } private void resetDataSource() { - mFD = null; + mPFD = null; mOffset = 0; mLength = FD_LENGTH_UNKNOWN; } diff --git a/media/java/android/media/MediaPlayer2.java b/media/java/android/media/MediaPlayer2.java index 267dab9c46a1..0f2604e3f2ce 100644 --- a/media/java/android/media/MediaPlayer2.java +++ b/media/java/android/media/MediaPlayer2.java @@ -36,6 +36,7 @@ import android.os.Handler; import android.os.HandlerThread; import android.os.Looper; import android.os.Message; +import android.os.ParcelFileDescriptor; import android.os.PersistableBundle; import android.os.PowerManager; import android.util.Log; @@ -412,6 +413,9 @@ public class MediaPlayer2 implements AutoCloseable mHandlerThread = null; } + setCurrentSourceInfo(null); + clearNextSourceInfos(); + // Modular DRM clean up mOnDrmConfigHelper = null; synchronized (mDrmEventCbLock) { @@ -457,10 +461,8 @@ public class MediaPlayer2 implements AutoCloseable synchronized (mDrmEventCbLock) { mDrmEventCallbackRecords.clear(); } - synchronized (mSrcLock) { - mCurrentSourceInfo = null; - mNextSourceInfos.clear(); - } + setCurrentSourceInfo(null); + clearNextSourceInfos(); synchronized (mTaskLock) { mPendingTasks.clear(); @@ -685,6 +687,8 @@ public class MediaPlayer2 implements AutoCloseable /** * Sets the data source as described by a DataSourceDesc. + * When the data source is of {@link FileDataSourceDesc} type, the {@link ParcelFileDescriptor} + * in the {@link FileDataSourceDesc} will be closed by the player. * * @param dsd the descriptor of data source you want to play * @return a token which can be used to cancel the operation later with {@link #cancelCommand}. @@ -696,13 +700,17 @@ public class MediaPlayer2 implements AutoCloseable void process() throws IOException { Media2Utils.checkArgument(dsd != null, "the DataSourceDesc cannot be null"); int state = getState(); - if (state != PLAYER_STATE_ERROR && state != PLAYER_STATE_IDLE) { - throw new IllegalStateException("called in wrong state " + state); - } + try { + if (state != PLAYER_STATE_ERROR && state != PLAYER_STATE_IDLE) { + throw new IllegalStateException("called in wrong state " + state); + } - synchronized (mSrcLock) { - mCurrentSourceInfo = new SourceInfo(dsd); - handleDataSource(true /* isCurrent */, dsd, mCurrentSourceInfo.mId); + synchronized (mSrcLock) { + setCurrentSourceInfo(new SourceInfo(dsd)); + handleDataSource(true /* isCurrent */, dsd, mCurrentSourceInfo.mId); + } + } finally { + dsd.close(); } } }); @@ -711,6 +719,8 @@ public class MediaPlayer2 implements AutoCloseable /** * Sets a single data source as described by a DataSourceDesc which will be played * after current data source is finished. + * When the data source is of {@link FileDataSourceDesc} type, the {@link ParcelFileDescriptor} + * in the {@link FileDataSourceDesc} will be closed by the player. * * @param dsd the descriptor of data source you want to play after current one * @return a token which can be used to cancel the operation later with {@link #cancelCommand}. @@ -722,7 +732,7 @@ public class MediaPlayer2 implements AutoCloseable void process() { Media2Utils.checkArgument(dsd != null, "the DataSourceDesc cannot be null"); synchronized (mSrcLock) { - mNextSourceInfos.clear(); + clearNextSourceInfos(); mNextSourceInfos.add(new SourceInfo(dsd)); } prepareNextDataSource(); @@ -732,6 +742,8 @@ public class MediaPlayer2 implements AutoCloseable /** * Sets a list of data sources to be played sequentially after current data source is done. + * When the data source is of {@link FileDataSourceDesc} type, the {@link ParcelFileDescriptor} + * in the {@link FileDataSourceDesc} will be closed by the player. * * @param dsds the list of data sources you want to play after current one * @return a token which can be used to cancel the operation later with {@link #cancelCommand}. @@ -744,17 +756,15 @@ public class MediaPlayer2 implements AutoCloseable if (dsds == null || dsds.size() == 0) { throw new IllegalArgumentException("data source list cannot be null or empty."); } - for (DataSourceDesc dsd : dsds) { - if (dsd == null) { - throw new IllegalArgumentException( - "DataSourceDesc in the source list cannot be null."); - } - } synchronized (mSrcLock) { - mNextSourceInfos.clear(); + clearNextSourceInfos(); for (DataSourceDesc dsd : dsds) { - mNextSourceInfos.add(new SourceInfo(dsd)); + if (dsd != null) { + mNextSourceInfos.add(new SourceInfo(dsd)); + } else { + Log.w(TAG, "DataSourceDesc in the source list shall not be null."); + } } } prepareNextDataSource(); @@ -771,7 +781,7 @@ public class MediaPlayer2 implements AutoCloseable return addTask(new Task(CALL_COMPLETED_CLEAR_NEXT_DATA_SOURCES, false) { @Override void process() { - mNextSourceInfos.clear(); + clearNextSourceInfos(); } }); } @@ -802,7 +812,7 @@ public class MediaPlayer2 implements AutoCloseable FileDataSourceDesc fileDSD = (FileDataSourceDesc) dsd; handleDataSource(isCurrent, srcId, - fileDSD.getFileDescriptor(), + fileDSD.getParcelFileDescriptor(), fileDSD.getOffset(), fileDSD.getLength(), fileDSD.getStartPosition(), @@ -886,7 +896,7 @@ public class MediaPlayer2 implements AutoCloseable if (afd.getDeclaredLength() < 0) { handleDataSource(isCurrent, srcId, - afd.getFileDescriptor(), + ParcelFileDescriptor.dup(afd.getFileDescriptor()), 0, DataSourceDesc.LONG_MAX, startPos, @@ -894,7 +904,7 @@ public class MediaPlayer2 implements AutoCloseable } else { handleDataSource(isCurrent, srcId, - afd.getFileDescriptor(), + ParcelFileDescriptor.dup(afd.getFileDescriptor()), afd.getStartOffset(), afd.getDeclaredLength(), startPos, @@ -960,7 +970,8 @@ public class MediaPlayer2 implements AutoCloseable if (file.exists()) { FileInputStream is = new FileInputStream(file); FileDescriptor fd = is.getFD(); - handleDataSource(isCurrent, srcId, fd, 0, DataSourceDesc.LONG_MAX, startPos, endPos); + handleDataSource(isCurrent, srcId, ParcelFileDescriptor.dup(fd), + 0, DataSourceDesc.LONG_MAX, startPos, endPos); is.close(); } else { throw new IOException("handleDataSource failed."); @@ -984,9 +995,10 @@ public class MediaPlayer2 implements AutoCloseable */ private void handleDataSource( boolean isCurrent, long srcId, - FileDescriptor fd, long offset, long length, + ParcelFileDescriptor pfd, long offset, long length, long startPos, long endPos) throws IOException { - nativeHandleDataSourceFD(isCurrent, srcId, fd, offset, length, startPos, endPos); + nativeHandleDataSourceFD(isCurrent, srcId, pfd.getFileDescriptor(), offset, length, + startPos, endPos); } private native void nativeHandleDataSourceFD(boolean isCurrent, long srcId, @@ -1037,7 +1049,10 @@ public class MediaPlayer2 implements AutoCloseable MEDIA_ERROR, MEDIA_ERROR_IO, MEDIA_ERROR_UNKNOWN, null); mTaskHandler.handleMessage(msg, nextSource.mId); - mNextSourceInfos.poll(); + SourceInfo nextSourceInfo = mNextSourceInfos.poll(); + if (nextSource != null) { + nextSourceInfo.close(); + } return prepareNextDataSource(); } } @@ -1058,7 +1073,7 @@ public class MediaPlayer2 implements AutoCloseable SourceInfo nextSourceInfo = mNextSourceInfos.peek(); if (nextSourceInfo.mStateAsNextSource == NEXT_SOURCE_STATE_PREPARED) { // Switch to next source only when it has been prepared. - mCurrentSourceInfo = mNextSourceInfos.poll(); + setCurrentSourceInfo(mNextSourceInfos.poll()); long srcId = mCurrentSourceInfo.mId; try { @@ -4490,6 +4505,7 @@ public class MediaPlayer2 implements AutoCloseable final DataSourceDesc mDSD; final long mId = mSrcIdGenerator.getAndIncrement(); AtomicInteger mBufferedPercentage = new AtomicInteger(0); + boolean mClosed = false; // m*AsNextSource (below) only applies to pending data sources in the playlist; // the meanings of mCurrentSourceInfo.{mStateAsNextSource,mPlayPendingAsNextSource} @@ -4501,6 +4517,17 @@ public class MediaPlayer2 implements AutoCloseable this.mDSD = dsd; } + void close() { + synchronized (this) { + if (!mClosed) { + if (mDSD != null) { + mDSD.close(); + } + mClosed = true; + } + } + } + @Override public String toString() { return String.format("%s(%d)", SourceInfo.class.getName(), mId); @@ -4531,6 +4558,26 @@ public class MediaPlayer2 implements AutoCloseable return nextSourceInfo != null && nextSourceInfo.mId == srcId; } + private void setCurrentSourceInfo(SourceInfo newSourceInfo) { + synchronized (mSrcLock) { + if (mCurrentSourceInfo != null) { + mCurrentSourceInfo.close(); + } + mCurrentSourceInfo = newSourceInfo; + } + } + + private void clearNextSourceInfos() { + synchronized (mSrcLock) { + for (SourceInfo sourceInfo : mNextSourceInfos) { + if (sourceInfo != null) { + sourceInfo.close(); + } + } + mNextSourceInfos.clear(); + } + } + public static final class MetricsConstants { private MetricsConstants() {} diff --git a/media/java/android/media/UriDataSourceDesc.java b/media/java/android/media/UriDataSourceDesc.java index d4a43a1d1c41..6a83dab14aa4 100644 --- a/media/java/android/media/UriDataSourceDesc.java +++ b/media/java/android/media/UriDataSourceDesc.java @@ -31,9 +31,11 @@ import java.util.Map; /** * @hide - * Structure for data source descriptor. + * Structure of data source descriptor for sources using URI. * - * Used by {@link MediaPlayer2#setDataSource(UriDataSourceDesc)} + * Used by {@link MediaPlayer2#setDataSource(DataSourceDesc)}, + * {@link MediaPlayer2#setNextDataSource(DataSourceDesc)} and + * {@link MediaPlayer2#setNextDataSources(List<DataSourceDesc>)} * to set data source for playback. * * <p>Users should use {@link Builder} to change {@link UriDataSourceDesc}. diff --git a/native/android/libandroid.map.txt b/native/android/libandroid.map.txt index e7e8384c5567..207508e18a8b 100644 --- a/native/android/libandroid.map.txt +++ b/native/android/libandroid.map.txt @@ -144,6 +144,7 @@ LIBANDROID { AHardwareBuffer_describe; # introduced=26 AHardwareBuffer_fromHardwareBuffer; # introduced=26 AHardwareBuffer_getNativeHandle; # introduced=26 + AHardwareBuffer_isSupported; # introduced=29 AHardwareBuffer_lock; # introduced=26 AHardwareBuffer_recvHandleFromUnixSocket; # introduced=26 AHardwareBuffer_release; # introduced=26 diff --git a/packages/SystemUI/src/com/android/systemui/bubbles/BubbleController.java b/packages/SystemUI/src/com/android/systemui/bubbles/BubbleController.java index c6dcfc7356be..416cc594051b 100644 --- a/packages/SystemUI/src/com/android/systemui/bubbles/BubbleController.java +++ b/packages/SystemUI/src/com/android/systemui/bubbles/BubbleController.java @@ -23,10 +23,10 @@ import static android.view.ViewGroup.LayoutParams.WRAP_CONTENT; import static com.android.systemui.bubbles.BubbleMovementHelper.EDGE_OVERLAP; import android.app.Notification; -import android.app.NotificationManager; import android.content.Context; import android.graphics.Point; import android.graphics.Rect; +import android.provider.Settings; import android.service.notification.StatusBarNotification; import android.view.ViewGroup; import android.view.WindowManager; @@ -57,6 +57,11 @@ public class BubbleController { // When a bubble is dismissed, recreate it as a notification public static final boolean DEBUG_DEMOTE_TO_NOTIF = false; + // Secure settings + private static final String ENABLE_AUTO_BUBBLE_MESSAGES = "experiment_autobubble_messaging"; + private static final String ENABLE_AUTO_BUBBLE_ONGOING = "experiment_autobubble_ongoing"; + private static final String ENABLE_AUTO_BUBBLE_ALL = "experiment_autobubble_all"; + private Context mContext; private BubbleDismissListener mDismissListener; private BubbleStateChangeListener mStateChangeListener; @@ -318,11 +323,15 @@ public class BubbleController { /** * Whether the notification should bubble or not. */ - public static boolean shouldAutoBubble(NotificationData.Entry entry, int priority, - boolean canAppOverlay) { - if (!DEBUG_ENABLE_AUTO_BUBBLE || entry.isBubbleDismissed()) { + public static boolean shouldAutoBubble(Context context, NotificationData.Entry entry) { + if (entry.isBubbleDismissed()) { return false; } + + boolean autoBubbleMessages = shouldAutoBubbleMessages(context) || DEBUG_ENABLE_AUTO_BUBBLE; + boolean autoBubbleOngoing = shouldAutoBubbleOngoing(context) || DEBUG_ENABLE_AUTO_BUBBLE; + boolean autoBubbleAll = shouldAutoBubbleAll(context) || DEBUG_ENABLE_AUTO_BUBBLE; + StatusBarNotification n = entry.notification; boolean hasRemoteInput = false; if (n.getNotification().actions != null) { @@ -333,12 +342,28 @@ public class BubbleController { } } } + Class<? extends Notification.Style> style = n.getNotification().getNotificationStyle(); - boolean shouldBubble = priority >= NotificationManager.IMPORTANCE_HIGH - || Notification.MessagingStyle.class.equals(style) + boolean isMessageType = Notification.MessagingStyle.class.equals(style) || Notification.CATEGORY_MESSAGE.equals(n.getNotification().category) - || hasRemoteInput - || canAppOverlay; - return shouldBubble && !entry.isBubbleDismissed(); + || hasRemoteInput; + return (isMessageType && autoBubbleMessages) + || (n.isOngoing() && autoBubbleOngoing) + || autoBubbleAll; + } + + private static boolean shouldAutoBubbleMessages(Context context) { + return Settings.Secure.getInt(context.getContentResolver(), + ENABLE_AUTO_BUBBLE_MESSAGES, 0) != 0; + } + + private static boolean shouldAutoBubbleOngoing(Context context) { + return Settings.Secure.getInt(context.getContentResolver(), + ENABLE_AUTO_BUBBLE_ONGOING, 0) != 0; + } + + private static boolean shouldAutoBubbleAll(Context context) { + return Settings.Secure.getInt(context.getContentResolver(), + ENABLE_AUTO_BUBBLE_ALL, 0) != 0; } } diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/notification/NotificationEntryManager.java b/packages/SystemUI/src/com/android/systemui/statusbar/notification/NotificationEntryManager.java index e33372957011..7970f166d8a6 100644 --- a/packages/SystemUI/src/com/android/systemui/statusbar/notification/NotificationEntryManager.java +++ b/packages/SystemUI/src/com/android/systemui/statusbar/notification/NotificationEntryManager.java @@ -24,7 +24,6 @@ import static com.android.systemui.statusbar.notification.row.NotificationInflat import android.annotation.Nullable; import android.app.Notification; -import android.app.NotificationChannel; import android.app.NotificationManager; import android.app.PendingIntent; import android.content.Context; @@ -766,7 +765,7 @@ public class NotificationEntryManager implements Dumpable, NotificationInflater. } NotificationData.Entry entry = new NotificationData.Entry(sbn, ranking); - if (shouldAutoBubble(entry)) { + if (BubbleController.shouldAutoBubble(getContext(), entry)) { entry.setIsBubble(true); } @@ -1207,17 +1206,6 @@ public class NotificationEntryManager implements Dumpable, NotificationInflater. } } - - /** - * Whether a bubble is appropriate to auto-bubble or not. - */ - private boolean shouldAutoBubble(NotificationData.Entry entry) { - int priority = mNotificationData.getImportance(entry.key); - NotificationChannel channel = mNotificationData.getChannel(entry.key); - boolean canAppOverlay = channel != null && channel.canOverlayApps(); - return BubbleController.shouldAutoBubble(entry, priority, canAppOverlay); - } - /** * Callback for NotificationEntryManager. */ diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/notification/row/NotificationGutsManager.java b/packages/SystemUI/src/com/android/systemui/statusbar/notification/row/NotificationGutsManager.java index 7895a8e7831f..3b407b5f8a65 100644 --- a/packages/SystemUI/src/com/android/systemui/statusbar/notification/row/NotificationGutsManager.java +++ b/packages/SystemUI/src/com/android/systemui/statusbar/notification/row/NotificationGutsManager.java @@ -18,10 +18,7 @@ package com.android.systemui.statusbar.notification.row; import static android.app.AppOpsManager.OP_CAMERA; import static android.app.AppOpsManager.OP_RECORD_AUDIO; import static android.app.AppOpsManager.OP_SYSTEM_ALERT_WINDOW; -import static android.service.notification.NotificationListenerService.Ranking - .USER_SENTIMENT_NEGATIVE; - -import static com.android.systemui.statusbar.notification.row.NotificationInfo.ACTION_NONE; +import static android.service.notification.NotificationListenerService.Ranking.USER_SENTIMENT_NEGATIVE; import android.app.INotificationManager; import android.app.NotificationChannel; @@ -43,7 +40,6 @@ import android.view.accessibility.AccessibilityManager; import com.android.internal.annotations.VisibleForTesting; import com.android.internal.logging.MetricsLogger; import com.android.internal.logging.nano.MetricsProto; -import com.android.internal.statusbar.IStatusBarService; import com.android.systemui.Dependency; import com.android.systemui.Dumpable; import com.android.systemui.plugins.ActivityStarter; @@ -188,13 +184,7 @@ public class NotificationGutsManager implements Dumpable, NotificationLifetimeEx } else if (gutsView instanceof AppOpsInfo) { initializeAppOpsInfo(row, (AppOpsInfo) gutsView); } else if (gutsView instanceof NotificationInfo) { - int action; - if (item instanceof NotificationMenuRow.NotificationInfoMenuItem) { - action = ((NotificationMenuRow.NotificationInfoMenuItem) item).mAction; - } else { - action = ACTION_NONE; - } - initializeNotificationInfo(row, (NotificationInfo) gutsView, action); + initializeNotificationInfo(row, (NotificationInfo) gutsView); } return true; } catch (Exception e) { @@ -253,13 +243,11 @@ public class NotificationGutsManager implements Dumpable, NotificationLifetimeEx * Sets up the {@link NotificationInfo} inside the notification row's guts. * @param row view to set up the guts for * @param notificationInfoView view to set up/bind within {@code row} - * @param action The action to take immediately upon binding, if any. */ @VisibleForTesting void initializeNotificationInfo( final ExpandableNotificationRow row, - NotificationInfo notificationInfoView, - @NotificationInfo.NotificationInfoAction int action) throws Exception { + NotificationInfo notificationInfoView) throws Exception { NotificationGuts guts = row.getGuts(); StatusBarNotification sbn = row.getStatusBarNotification(); String packageName = sbn.getPackageName(); @@ -303,8 +291,7 @@ public class NotificationGutsManager implements Dumpable, NotificationLifetimeEx isForBlockingHelper, row.getEntry().userSentiment == USER_SENTIMENT_NEGATIVE, row.getEntry().noisy, - row.getEntry().importance, - action); + row.getEntry().importance); } diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/notification/row/NotificationInfo.java b/packages/SystemUI/src/com/android/systemui/statusbar/notification/row/NotificationInfo.java index 0d36d2c2f77c..213ac704b06a 100644 --- a/packages/SystemUI/src/com/android/systemui/statusbar/notification/row/NotificationInfo.java +++ b/packages/SystemUI/src/com/android/systemui/statusbar/notification/row/NotificationInfo.java @@ -187,14 +187,13 @@ public class NotificationInfo extends LinearLayout implements NotificationGuts.G boolean isDeviceProvisioned, boolean isNonblockable, boolean isNoisy, - int importance, - @NotificationInfoAction int action) + int importance) throws RemoteException { bindNotification(pm, iNotificationManager, pkg, notificationChannel, numUniqueChannelsInRow, sbn, checkSaveListener, onSettingsClick, onAppSettingsClick, isDeviceProvisioned, isNonblockable, false /* isBlockingHelper */, false /* isUserSentimentNegative */, isNoisy, - importance, action); + importance); } public void bindNotification( @@ -212,8 +211,7 @@ public class NotificationInfo extends LinearLayout implements NotificationGuts.G boolean isForBlockingHelper, boolean isUserSentimentNegative, boolean isNoisy, - int importance, - @NotificationInfoAction int action) + int importance) throws RemoteException { mINotificationManager = iNotificationManager; mMetricsLogger = Dependency.get(MetricsLogger.class); @@ -255,10 +253,6 @@ public class NotificationInfo extends LinearLayout implements NotificationGuts.G bindHeader(); bindPrompt(); bindButtons(); - - if (action != ACTION_NONE) { - swapContent(action, false /* don't animate */); - } } private void bindHeader() throws RemoteException { diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/notification/row/NotificationMenuRow.java b/packages/SystemUI/src/com/android/systemui/statusbar/notification/row/NotificationMenuRow.java index b6ff6fc7f0e5..948d2a5e2a18 100644 --- a/packages/SystemUI/src/com/android/systemui/statusbar/notification/row/NotificationMenuRow.java +++ b/packages/SystemUI/src/com/android/systemui/statusbar/notification/row/NotificationMenuRow.java @@ -17,9 +17,6 @@ package com.android.systemui.statusbar.notification.row; import static com.android.systemui.SwipeHelper.SWIPED_FAR_ENOUGH_SIZE_FRACTION; -import static com.android.systemui.statusbar.notification.row.NotificationInfo.ACTION_BLOCK; -import static com.android.systemui.statusbar.notification.row.NotificationInfo.ACTION_NONE; -import static com.android.systemui.statusbar.notification.row.NotificationInfo.ACTION_TOGGLE_SILENT; import android.animation.Animator; import android.animation.AnimatorListenerAdapter; @@ -46,7 +43,6 @@ import com.android.systemui.plugins.statusbar.NotificationMenuRowPlugin; import com.android.systemui.statusbar.AlphaOptimizedImageView; import com.android.systemui.statusbar.notification.NotificationUtils; import com.android.systemui.statusbar.notification.row.NotificationGuts.GutsContent; -import com.android.systemui.statusbar.notification.row.NotificationInfo.NotificationInfoAction; import com.android.systemui.statusbar.notification.stack.NotificationStackScrollLayout; import java.util.ArrayList; @@ -73,7 +69,7 @@ public class NotificationMenuRow implements NotificationMenuRowPlugin, View.OnCl private Context mContext; private FrameLayout mMenuContainer; - private NotificationInfoMenuItem mInfoItem; + private NotificationMenuItem mInfoItem; private MenuItem mAppOpsItem; private MenuItem mSnoozeItem; private ArrayList<MenuItem> mLeftMenuItems; @@ -248,36 +244,30 @@ public class NotificationMenuRow implements NotificationMenuRowPlugin, View.OnCl if (!isForeground) { // Only show snooze for non-foreground notifications mSnoozeItem = createSnoozeItem(mContext); - mLeftMenuItems.add(mSnoozeItem); } - mInfoItem = createInfoItem(mContext); - if (!NotificationUtils.useNewInterruptionModel(mContext)) { - mLeftMenuItems.add(mInfoItem); - } - mAppOpsItem = createAppOpsItem(mContext); - mLeftMenuItems.add(mAppOpsItem); - if (NotificationUtils.useNewInterruptionModel(mContext)) { - if (!mParent.getIsNonblockable()) { - mRightMenuItems.add(createBlockItem(mContext, mInfoItem.getGutsView())); - } - // TODO(kprevas): this is duplicated logic - // but it's currently spread across NotificationGutsManager and NotificationInfo. - // Try to consolidate and reuse here. - boolean canToggleSilent = !mParent.getIsNonblockable() - && !isForeground - && mParent.getEntry().noisy; - if (canToggleSilent) { - int channelImportance = mParent.getEntry().channel.getImportance(); - int effectiveImportance = - channelImportance == NotificationManager.IMPORTANCE_UNSPECIFIED - ? mParent.getEntry().importance : channelImportance; - mRightMenuItems.add(createToggleSilentItem(mContext, mInfoItem.getGutsView(), - effectiveImportance < NotificationManager.IMPORTANCE_DEFAULT)); + int channelImportance = mParent.getEntry().channel.getImportance(); + int effectiveImportance = + channelImportance == NotificationManager.IMPORTANCE_UNSPECIFIED + ? mParent.getEntry().importance : channelImportance; + mInfoItem = createInfoItem(mContext, + effectiveImportance < NotificationManager.IMPORTANCE_DEFAULT); + } else { + mInfoItem = createInfoItem(mContext); + } + + if (!NotificationUtils.useNewInterruptionModel(mContext)) { + if (!isForeground) { + mRightMenuItems.add(mSnoozeItem); } + mRightMenuItems.add(mInfoItem); + mRightMenuItems.add(mAppOpsItem); + mLeftMenuItems.addAll(mRightMenuItems); } else { - mRightMenuItems.addAll(mLeftMenuItems); + mRightMenuItems.add(mInfoItem); + mRightMenuItems.add(mAppOpsItem); + mRightMenuItems.add(mSnoozeItem); } populateMenuViews(); @@ -634,13 +624,24 @@ public class NotificationMenuRow implements NotificationMenuRowPlugin, View.OnCl return snooze; } - static NotificationInfoMenuItem createInfoItem(Context context) { + static NotificationMenuItem createInfoItem(Context context) { Resources res = context.getResources(); String infoDescription = res.getString(R.string.notification_menu_gear_description); NotificationInfo infoContent = (NotificationInfo) LayoutInflater.from(context).inflate( R.layout.notification_info, null, false); - return new NotificationInfoMenuItem(context, infoDescription, infoContent, - R.drawable.ic_settings, ACTION_NONE); + return new NotificationMenuItem(context, infoDescription, infoContent, + R.drawable.ic_settings); + } + + static NotificationMenuItem createInfoItem(Context context, boolean isCurrentlySilent) { + Resources res = context.getResources(); + String infoDescription = res.getString(R.string.notification_menu_gear_description); + NotificationInfo infoContent = (NotificationInfo) LayoutInflater.from(context).inflate( + R.layout.notification_info, null, false); + int iconResId = isCurrentlySilent + ? R.drawable.ic_notifications_alert + : R.drawable.ic_notifications_silence; + return new NotificationMenuItem(context, infoDescription, infoContent, iconResId); } static MenuItem createAppOpsItem(Context context) { @@ -651,29 +652,6 @@ public class NotificationMenuRow implements NotificationMenuRowPlugin, View.OnCl return info; } - private static MenuItem createBlockItem(Context context, NotificationInfo gutsView) { - return new NotificationInfoMenuItem( - context, - context.getResources().getString(R.string.inline_stop_button), - gutsView, - R.drawable.ic_notification_block, - ACTION_BLOCK); - } - - private static MenuItem createToggleSilentItem(Context context, NotificationInfo gutsView, - boolean isCurrentlySilent) { - return new NotificationInfoMenuItem( - context, - isCurrentlySilent - ? context.getResources().getString(R.string.inline_silent_button_alert) - : context.getResources().getString(R.string.inline_silent_button_silent), - gutsView, - isCurrentlySilent - ? R.drawable.ic_notifications_alert - : R.drawable.ic_notifications_silence, - ACTION_TOGGLE_SILENT); - } - private void addMenuView(MenuItem item, ViewGroup parent) { View menuView = item.getMenuView(); if (menuView != null) { @@ -789,23 +767,4 @@ public class NotificationMenuRow implements NotificationMenuRowPlugin, View.OnCl return mContentDescription; } } - - /** A {@link NotificationMenuItem} with an associated {@link NotificationInfoAction}. */ - public static class NotificationInfoMenuItem extends NotificationMenuItem { - - @NotificationInfoAction - int mAction; - - public NotificationInfoMenuItem(Context context, String contentDescription, - NotificationInfo content, int iconResId, - @NotificationInfoAction int action) { - super(context, contentDescription, content, iconResId); - this.mAction = action; - } - - @Override - public NotificationInfo getGutsView() { - return (NotificationInfo) super.getGutsView(); - } - } } diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/phone/NavigationBackAction.java b/packages/SystemUI/src/com/android/systemui/statusbar/phone/NavigationBackAction.java index 1002f9e45b3c..b83ebc7f8ea1 100644 --- a/packages/SystemUI/src/com/android/systemui/statusbar/phone/NavigationBackAction.java +++ b/packages/SystemUI/src/com/android/systemui/statusbar/phone/NavigationBackAction.java @@ -16,8 +16,6 @@ package com.android.systemui.statusbar.phone; -import static com.android.systemui.shared.system.NavigationBarCompat.HIT_TARGET_HOME; - import android.annotation.NonNull; import android.hardware.input.InputManager; import android.os.Handler; @@ -35,10 +33,8 @@ import com.android.systemui.recents.OverviewProxyService; */ public class NavigationBackAction extends NavigationGestureAction { - private static final String PULL_HOME_GO_BACK_PROP = "quickstepcontroller_homegoesback"; private static final String BACK_AFTER_END_PROP = "quickstepcontroller_homegoesbackwhenend"; - private static final String NAVBAR_EXPERIMENTS_DISABLED = "navbarexperiments_disabled"; private static final long BACK_BUTTON_FADE_OUT_ALPHA = 60; private static final long BACK_GESTURE_POLL_TIMEOUT = 1000; @@ -60,23 +56,13 @@ public class NavigationBackAction extends NavigationGestureAction { } @Override - public int requiresTouchDownHitTarget() { - return HIT_TARGET_HOME; - } - - @Override - public boolean requiresDragWithHitTarget() { - return true; - } - - @Override public boolean canPerformAction() { return mProxySender.getBackButtonAlpha() > 0; } @Override public boolean isEnabled() { - return swipeHomeGoBackGestureEnabled(); + return !getGlobalBoolean(NavigationPrototypeController.NAVBAR_EXPERIMENTS_DISABLED); } @Override @@ -110,13 +96,8 @@ public class NavigationBackAction extends NavigationGestureAction { mNavigationBarView.performHapticFeedback(HapticFeedbackConstants.VIRTUAL_KEY); } - private boolean swipeHomeGoBackGestureEnabled() { - return !getGlobalBoolean(NAVBAR_EXPERIMENTS_DISABLED) - && getGlobalBoolean(PULL_HOME_GO_BACK_PROP); - } - private boolean shouldExecuteBackOnUp() { - return !getGlobalBoolean(NAVBAR_EXPERIMENTS_DISABLED) + return !getGlobalBoolean(NavigationPrototypeController.NAVBAR_EXPERIMENTS_DISABLED) && getGlobalBoolean(BACK_AFTER_END_PROP); } diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/phone/NavigationBarView.java b/packages/SystemUI/src/com/android/systemui/statusbar/phone/NavigationBarView.java index 5db43eae8443..33d022c83c16 100644 --- a/packages/SystemUI/src/com/android/systemui/statusbar/phone/NavigationBarView.java +++ b/packages/SystemUI/src/com/android/systemui/statusbar/phone/NavigationBarView.java @@ -77,6 +77,8 @@ import com.android.systemui.shared.plugins.PluginManager; import com.android.systemui.shared.system.ActivityManagerWrapper; import com.android.systemui.shared.system.NavigationBarCompat; import com.android.systemui.shared.system.WindowManagerWrapper; +import com.android.systemui.statusbar.phone.NavigationPrototypeController.GestureAction; +import com.android.systemui.statusbar.phone.NavigationPrototypeController.OnPrototypeChangedListener; import com.android.systemui.statusbar.policy.DeadZone; import com.android.systemui.statusbar.policy.KeyButtonDrawable; @@ -146,6 +148,8 @@ public class NavigationBarView extends FrameLayout implements PluginListener<Nav private RecentsOnboarding mRecentsOnboarding; private NotificationPanelView mPanelView; + private NavigationPrototypeController mPrototypeController; + private NavigationGestureAction[] mDefaultGestureMap; private QuickScrubAction mQuickScrubAction; private QuickStepAction mQuickStepAction; private NavigationBackAction mBackAction; @@ -261,6 +265,18 @@ public class NavigationBarView extends FrameLayout implements PluginListener<Nav } }; + private OnPrototypeChangedListener mPrototypeListener = new OnPrototypeChangedListener() { + @Override + public void onGestureRemap(int[] actions) { + updateNavigationGestures(); + } + + @Override + public void onBackButtonVisibilityChanged(boolean visible) { + getBackButton().setVisibility(visible ? VISIBLE : GONE); + } + }; + public NavigationBarView(Context context, AttributeSet attrs) { super(context, attrs); @@ -309,6 +325,14 @@ public class NavigationBarView extends FrameLayout implements PluginListener<Nav mQuickScrubAction = new QuickScrubAction(this, mOverviewProxyService); mQuickStepAction = new QuickStepAction(this, mOverviewProxyService); mBackAction = new NavigationBackAction(this, mOverviewProxyService); + mDefaultGestureMap = new NavigationGestureAction[] { + mQuickStepAction, null /* swipeDownAction*/, null /* swipeLeftAction */, + mQuickScrubAction + }; + + mPrototypeController = new NavigationPrototypeController(mHandler, mContext); + mPrototypeController.register(); + mPrototypeController.setOnPrototypeChangedListener(mPrototypeListener); } public BarTransitions getBarTransitions() { @@ -323,8 +347,32 @@ public class NavigationBarView extends FrameLayout implements PluginListener<Nav mPanelView = panel; if (mGestureHelper instanceof QuickStepController) { ((QuickStepController) mGestureHelper).setComponents(this); - ((QuickStepController) mGestureHelper).setGestureActions(mQuickStepAction, - null /* swipeDownAction*/, mBackAction, mQuickScrubAction); + updateNavigationGestures(); + } + } + + private void updateNavigationGestures() { + if (mGestureHelper instanceof QuickStepController) { + final int[] assignedMap = mPrototypeController.getGestureActionMap(); + ((QuickStepController) mGestureHelper).setGestureActions( + getNavigationActionFromType(assignedMap[0], mDefaultGestureMap[0]), + getNavigationActionFromType(assignedMap[1], mDefaultGestureMap[1]), + getNavigationActionFromType(assignedMap[2], mDefaultGestureMap[2]), + getNavigationActionFromType(assignedMap[3], mDefaultGestureMap[3])); + } + } + + private NavigationGestureAction getNavigationActionFromType(@GestureAction int actionType, + NavigationGestureAction defaultAction) { + switch(actionType) { + case NavigationPrototypeController.ACTION_QUICKSTEP: + return mQuickStepAction; + case NavigationPrototypeController.ACTION_QUICKSCRUB: + return mQuickScrubAction; + case NavigationPrototypeController.ACTION_BACK: + return mBackAction; + default: + return defaultAction; } } @@ -1043,6 +1091,7 @@ public class NavigationBarView extends FrameLayout implements PluginListener<Nav if (mGestureHelper != null) { mGestureHelper.destroy(); } + mPrototypeController.unregister(); setUpSwipeUpOnboarding(false); for (int i = 0; i < mButtonDispatchers.size(); ++i) { mButtonDispatchers.valueAt(i).onDestroy(); diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/phone/NavigationPrototypeController.java b/packages/SystemUI/src/com/android/systemui/statusbar/phone/NavigationPrototypeController.java new file mode 100644 index 000000000000..e8c0bf13644f --- /dev/null +++ b/packages/SystemUI/src/com/android/systemui/statusbar/phone/NavigationPrototypeController.java @@ -0,0 +1,142 @@ +/* + * Copyright (C) 2008 The Android Open Source Project + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +package com.android.systemui.statusbar.phone; + +import android.annotation.IntDef; +import android.content.Context; +import android.database.ContentObserver; +import android.net.Uri; +import android.os.Handler; +import android.provider.Settings; +import android.provider.Settings.SettingNotFoundException; + +import android.util.Log; +import java.lang.annotation.Retention; +import java.lang.annotation.RetentionPolicy; + +/** + * Coordinates with the prototype settings plugin app that uses Settings.Global to allow different + * prototypes to run in the system. The class will handle communication changes from the settings + * app and call back to listeners. + */ +public class NavigationPrototypeController extends ContentObserver { + private static final String HIDE_BACK_BUTTON_SETTING = "quickstepcontroller_hideback"; + + static final String NAVBAR_EXPERIMENTS_DISABLED = "navbarexperiments_disabled"; + private final String GESTURE_MATCH_SETTING = "quickstepcontroller_gesture_match_map"; + + @Retention(RetentionPolicy.SOURCE) + @IntDef({ACTION_DEFAULT, ACTION_QUICKSTEP, ACTION_QUICKSCRUB, ACTION_BACK}) + @interface GestureAction {} + static final int ACTION_DEFAULT = 0; + static final int ACTION_QUICKSTEP = 1; + static final int ACTION_QUICKSCRUB = 2; + static final int ACTION_BACK = 3; + + private OnPrototypeChangedListener mListener; + + /** + * Each index corresponds to a different action set in QuickStepController + * {@see updateSwipeLTRBackSetting} + */ + private int[] mActionMap = new int[4]; + + private final Context mContext; + + public NavigationPrototypeController(Handler handler, Context context) { + super(handler); + mContext = context; + updateSwipeLTRBackSetting(); + } + + public void setOnPrototypeChangedListener(OnPrototypeChangedListener listener) { + mListener = listener; + } + + /** + * Observe all the settings to react to from prototype settings + */ + public void register() { + registerObserver(HIDE_BACK_BUTTON_SETTING); + registerObserver(GESTURE_MATCH_SETTING); + } + + /** + * Disable observing settings to react to from prototype settings + */ + public void unregister() { + mContext.getContentResolver().unregisterContentObserver(this); + } + + @Override + public void onChange(boolean selfChange, Uri uri) { + super.onChange(selfChange, uri); + if (!selfChange && mListener != null) { + try { + final String path = uri.getPath(); + if (path.endsWith(GESTURE_MATCH_SETTING)) { + // Get the settings gesture map corresponding to each action + // {@see updateSwipeLTRBackSetting} + updateSwipeLTRBackSetting(); + mListener.onGestureRemap(mActionMap); + } else if (path.endsWith(HIDE_BACK_BUTTON_SETTING)) { + mListener.onBackButtonVisibilityChanged( + !getGlobalBool(HIDE_BACK_BUTTON_SETTING)); + } + } catch (SettingNotFoundException e) { + e.printStackTrace(); + } + } + } + + /** + * Retrieve the action map to apply to the quick step controller + * @return an action map + */ + int[] getGestureActionMap() { + return mActionMap; + } + + /** + * Since Settings.Global cannot pass arrays, use a string to represent each character as a + * gesture map to actions corresponding to {@see GestureAction}. The number is represented as: + * Number: [up] [down] [left] [right] + */ + private void updateSwipeLTRBackSetting() { + String value = Settings.Global.getString(mContext.getContentResolver(), + GESTURE_MATCH_SETTING); + if (value != null) { + for (int i = 0; i < mActionMap.length; ++i) { + mActionMap[i] = Character.getNumericValue(value.charAt(i)); + } + } + } + + private boolean getGlobalBool(String name) throws SettingNotFoundException { + return Settings.Global.getInt(mContext.getContentResolver(), name) == 1; + } + + private void registerObserver(String name) { + mContext.getContentResolver() + .registerContentObserver(Settings.Global.getUriFor(name), false, this); + } + + public interface OnPrototypeChangedListener { + void onGestureRemap(@GestureAction int[] actions); + void onBackButtonVisibilityChanged(boolean visible); + } +} diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/phone/QuickStepController.java b/packages/SystemUI/src/com/android/systemui/statusbar/phone/QuickStepController.java index 0eff4d4adff5..497fdfb2deb1 100644 --- a/packages/SystemUI/src/com/android/systemui/statusbar/phone/QuickStepController.java +++ b/packages/SystemUI/src/com/android/systemui/statusbar/phone/QuickStepController.java @@ -30,9 +30,9 @@ import static com.android.systemui.shared.system.NavigationBarCompat.HIT_TARGET_ import android.annotation.Nullable; import android.content.Context; -import android.content.res.Resources; import android.graphics.Canvas; import android.graphics.Matrix; +import android.graphics.Rect; import android.os.RemoteException; import android.provider.Settings; import android.util.Log; @@ -100,6 +100,7 @@ public class QuickStepController implements GestureHelper { private NavigationGestureAction mCurrentAction; private NavigationGestureAction[] mGestureActions = new NavigationGestureAction[MAX_GESTURES]; + private final Rect mLastLayoutRect = new Rect(); private final OverviewProxyService mOverviewEventSender; private final Context mContext; private final StatusBar mStatusBar; @@ -107,7 +108,6 @@ public class QuickStepController implements GestureHelper { private final Matrix mTransformLocalMatrix = new Matrix(); public QuickStepController(Context context) { - final Resources res = context.getResources(); mContext = context; mStatusBar = SysUiServiceProvider.getComponent(context, StatusBar.class); mOverviewEventSender = Dependency.get(OverviewProxyService.class); @@ -142,6 +142,8 @@ public class QuickStepController implements GestureHelper { if (action != null) { action.setBarState(true, mNavBarPosition, mDragHPositive, mDragVPositive); action.onDarkIntensityChange(mDarkIntensity); + action.onLayout(true /* changed */, mLastLayoutRect.left, mLastLayoutRect.top, + mLastLayoutRect.right, mLastLayoutRect.bottom); } } } @@ -382,6 +384,7 @@ public class QuickStepController implements GestureHelper { action.onLayout(changed, left, top, right, bottom); } } + mLastLayoutRect.set(left, top, right, bottom); } @Override diff --git a/packages/SystemUI/tests/src/com/android/systemui/statusbar/notification/row/NotificationGutsManagerTest.java b/packages/SystemUI/tests/src/com/android/systemui/statusbar/notification/row/NotificationGutsManagerTest.java index 279796919879..84bfae650ce3 100644 --- a/packages/SystemUI/tests/src/com/android/systemui/statusbar/notification/row/NotificationGutsManagerTest.java +++ b/packages/SystemUI/tests/src/com/android/systemui/statusbar/notification/row/NotificationGutsManagerTest.java @@ -20,8 +20,7 @@ import static android.app.AppOpsManager.OP_CAMERA; import static android.app.AppOpsManager.OP_RECORD_AUDIO; import static android.app.AppOpsManager.OP_SYSTEM_ALERT_WINDOW; import static android.app.NotificationManager.IMPORTANCE_DEFAULT; -import static android.service.notification.NotificationListenerService.Ranking - .USER_SENTIMENT_NEGATIVE; +import static android.service.notification.NotificationListenerService.Ranking.USER_SENTIMENT_NEGATIVE; import static junit.framework.Assert.assertNotNull; import static junit.framework.Assert.assertNull; @@ -54,7 +53,6 @@ import android.support.test.filters.SmallTest; import android.testing.AndroidTestingRunner; import android.testing.TestableLooper; import android.util.ArraySet; -import android.util.Log; import android.view.View; import com.android.systemui.SysuiTestCase; @@ -63,8 +61,7 @@ import com.android.systemui.statusbar.NotificationPresenter; import com.android.systemui.statusbar.NotificationTestHelper; import com.android.systemui.statusbar.notification.NotificationData; import com.android.systemui.statusbar.notification.NotificationEntryManager; -import com.android.systemui.statusbar.notification.row.NotificationGutsManager - .OnSettingsClickListener; +import com.android.systemui.statusbar.notification.row.NotificationGutsManager.OnSettingsClickListener; import com.android.systemui.statusbar.notification.stack.NotificationStackScrollLayout; import com.android.systemui.statusbar.policy.DeviceProvisionedController; @@ -298,8 +295,7 @@ public class NotificationGutsManagerTest extends SysuiTestCase { when(row.getIsNonblockable()).thenReturn(false); StatusBarNotification statusBarNotification = row.getStatusBarNotification(); - mGutsManager.initializeNotificationInfo(row, notificationInfoView, - NotificationInfo.ACTION_NONE); + mGutsManager.initializeNotificationInfo(row, notificationInfoView); verify(notificationInfoView).bindNotification( any(PackageManager.class), @@ -316,8 +312,7 @@ public class NotificationGutsManagerTest extends SysuiTestCase { eq(true) /* isForBlockingHelper */, eq(true) /* isUserSentimentNegative */, eq(false) /*isNoisy */, - eq(0), - eq(NotificationInfo.ACTION_NONE)); + eq(0)); } @Test @@ -329,8 +324,7 @@ public class NotificationGutsManagerTest extends SysuiTestCase { when(row.getIsNonblockable()).thenReturn(false); StatusBarNotification statusBarNotification = row.getStatusBarNotification(); - mGutsManager.initializeNotificationInfo(row, notificationInfoView, - NotificationInfo.ACTION_NONE); + mGutsManager.initializeNotificationInfo(row, notificationInfoView); verify(notificationInfoView).bindNotification( any(PackageManager.class), @@ -347,8 +341,7 @@ public class NotificationGutsManagerTest extends SysuiTestCase { eq(false) /* isForBlockingHelper */, eq(true) /* isUserSentimentNegative */, eq(false) /*isNoisy */, - eq(0), - eq(NotificationInfo.ACTION_NONE)); + eq(0)); } @Test @@ -361,8 +354,7 @@ public class NotificationGutsManagerTest extends SysuiTestCase { when(row.getIsNonblockable()).thenReturn(false); StatusBarNotification statusBarNotification = row.getStatusBarNotification(); - mGutsManager.initializeNotificationInfo(row, notificationInfoView, - NotificationInfo.ACTION_NONE); + mGutsManager.initializeNotificationInfo(row, notificationInfoView); verify(notificationInfoView).bindNotification( any(PackageManager.class), @@ -379,8 +371,7 @@ public class NotificationGutsManagerTest extends SysuiTestCase { eq(true) /* isForBlockingHelper */, eq(true) /* isUserSentimentNegative */, eq(true) /*isNoisy */, - eq(0), - eq(NotificationInfo.ACTION_NONE)); + eq(0)); } @Test @@ -393,8 +384,7 @@ public class NotificationGutsManagerTest extends SysuiTestCase { when(row.getIsNonblockable()).thenReturn(false); StatusBarNotification statusBarNotification = row.getStatusBarNotification(); - mGutsManager.initializeNotificationInfo(row, notificationInfoView, - NotificationInfo.ACTION_NONE); + mGutsManager.initializeNotificationInfo(row, notificationInfoView); verify(notificationInfoView).bindNotification( any(PackageManager.class), @@ -411,8 +401,7 @@ public class NotificationGutsManagerTest extends SysuiTestCase { eq(true) /* isForBlockingHelper */, eq(true) /* isUserSentimentNegative */, eq(false) /*isNoisy */, - eq(IMPORTANCE_DEFAULT), - eq(NotificationInfo.ACTION_NONE)); + eq(IMPORTANCE_DEFAULT)); } @Test @@ -425,8 +414,7 @@ public class NotificationGutsManagerTest extends SysuiTestCase { StatusBarNotification statusBarNotification = row.getStatusBarNotification(); when(mDeviceProvisionedController.isDeviceProvisioned()).thenReturn(true); - mGutsManager.initializeNotificationInfo(row, notificationInfoView, - NotificationInfo.ACTION_NONE); + mGutsManager.initializeNotificationInfo(row, notificationInfoView); verify(notificationInfoView).bindNotification( any(PackageManager.class), @@ -443,8 +431,7 @@ public class NotificationGutsManagerTest extends SysuiTestCase { eq(false) /* isForBlockingHelper */, eq(true) /* isUserSentimentNegative */, eq(false) /*isNoisy */, - eq(0), - eq(NotificationInfo.ACTION_NONE)); + eq(0)); } @Test @@ -456,8 +443,7 @@ public class NotificationGutsManagerTest extends SysuiTestCase { when(row.getIsNonblockable()).thenReturn(false); StatusBarNotification statusBarNotification = row.getStatusBarNotification(); - mGutsManager.initializeNotificationInfo(row, notificationInfoView, - NotificationInfo.ACTION_BLOCK); + mGutsManager.initializeNotificationInfo(row, notificationInfoView); verify(notificationInfoView).bindNotification( any(PackageManager.class), @@ -474,8 +460,7 @@ public class NotificationGutsManagerTest extends SysuiTestCase { eq(true) /* isForBlockingHelper */, eq(true) /* isUserSentimentNegative */, eq(false) /*isNoisy */, - eq(0), - eq(NotificationInfo.ACTION_BLOCK)); + eq(0)); } @Test diff --git a/packages/SystemUI/tests/src/com/android/systemui/statusbar/notification/row/NotificationInfoTest.java b/packages/SystemUI/tests/src/com/android/systemui/statusbar/notification/row/NotificationInfoTest.java index 985827a9cd54..3dd493f0cd44 100644 --- a/packages/SystemUI/tests/src/com/android/systemui/statusbar/notification/row/NotificationInfoTest.java +++ b/packages/SystemUI/tests/src/com/android/systemui/statusbar/notification/row/NotificationInfoTest.java @@ -187,7 +187,7 @@ public class NotificationInfoTest extends SysuiTestCase { when(mMockPackageManager.getApplicationLabel(any())).thenReturn("App Name"); mNotificationInfo.bindNotification(mMockPackageManager, mMockINotificationManager, TEST_PACKAGE_NAME, mNotificationChannel, 1, mSbn, null, null, null, true, false, - false, IMPORTANCE_DEFAULT, NotificationInfo.ACTION_NONE); + false, IMPORTANCE_DEFAULT); final TextView textView = mNotificationInfo.findViewById(R.id.pkgname); assertTrue(textView.getText().toString().contains("App Name")); assertEquals(VISIBLE, mNotificationInfo.findViewById(R.id.header).getVisibility()); @@ -200,7 +200,7 @@ public class NotificationInfoTest extends SysuiTestCase { .thenReturn(iconDrawable); mNotificationInfo.bindNotification(mMockPackageManager, mMockINotificationManager, TEST_PACKAGE_NAME, mNotificationChannel, 1, mSbn, null, null, null, true, false, - false, IMPORTANCE_DEFAULT, NotificationInfo.ACTION_NONE); + false, IMPORTANCE_DEFAULT); final ImageView iconView = mNotificationInfo.findViewById(R.id.pkgicon); assertEquals(iconDrawable, iconView.getDrawable()); } @@ -209,7 +209,7 @@ public class NotificationInfoTest extends SysuiTestCase { public void testBindNotification_noDelegate() throws Exception { mNotificationInfo.bindNotification(mMockPackageManager, mMockINotificationManager, TEST_PACKAGE_NAME, mNotificationChannel, 1, mSbn, null, null, null, true, false, - false, IMPORTANCE_DEFAULT, NotificationInfo.ACTION_NONE); + false, IMPORTANCE_DEFAULT); final TextView nameView = mNotificationInfo.findViewById(R.id.delegate_name); assertEquals(GONE, nameView.getVisibility()); final TextView dividerView = mNotificationInfo.findViewById(R.id.pkg_divider); @@ -228,7 +228,7 @@ public class NotificationInfoTest extends SysuiTestCase { mNotificationInfo.bindNotification(mMockPackageManager, mMockINotificationManager, TEST_PACKAGE_NAME, mNotificationChannel, 1, mSbn, null, null, null, true, false, - false, IMPORTANCE_DEFAULT, NotificationInfo.ACTION_NONE); + false, IMPORTANCE_DEFAULT); final TextView nameView = mNotificationInfo.findViewById(R.id.delegate_name); assertEquals(VISIBLE, nameView.getVisibility()); assertTrue(nameView.getText().toString().contains("Other")); @@ -240,7 +240,7 @@ public class NotificationInfoTest extends SysuiTestCase { public void testBindNotification_GroupNameHiddenIfNoGroup() throws Exception { mNotificationInfo.bindNotification(mMockPackageManager, mMockINotificationManager, TEST_PACKAGE_NAME, mNotificationChannel, 1, mSbn, null, null, null, true, false, - false, IMPORTANCE_DEFAULT, NotificationInfo.ACTION_NONE); + false, IMPORTANCE_DEFAULT); final TextView groupNameView = mNotificationInfo.findViewById(R.id.group_name); assertEquals(GONE, groupNameView.getVisibility()); final TextView groupDividerView = mNotificationInfo.findViewById(R.id.pkg_group_divider); @@ -257,7 +257,7 @@ public class NotificationInfoTest extends SysuiTestCase { .thenReturn(notificationChannelGroup); mNotificationInfo.bindNotification(mMockPackageManager, mMockINotificationManager, TEST_PACKAGE_NAME, mNotificationChannel, 1, mSbn, null, null, null, true, false, - false, IMPORTANCE_DEFAULT, NotificationInfo.ACTION_NONE); + false, IMPORTANCE_DEFAULT); final TextView groupNameView = mNotificationInfo.findViewById(R.id.group_name); assertEquals(View.VISIBLE, groupNameView.getVisibility()); assertEquals("Test Group Name", groupNameView.getText()); @@ -269,7 +269,7 @@ public class NotificationInfoTest extends SysuiTestCase { public void testBindNotification_SetsTextChannelName() throws Exception { mNotificationInfo.bindNotification(mMockPackageManager, mMockINotificationManager, TEST_PACKAGE_NAME, mNotificationChannel, 1, mSbn, null, null, null, true, false, - false, IMPORTANCE_DEFAULT, NotificationInfo.ACTION_NONE); + false, IMPORTANCE_DEFAULT); final TextView textView = mNotificationInfo.findViewById(R.id.channel_name); assertEquals(TEST_CHANNEL_NAME, textView.getText()); } @@ -278,7 +278,7 @@ public class NotificationInfoTest extends SysuiTestCase { public void testBindNotification_DefaultChannelDoesNotUseChannelName() throws Exception { mNotificationInfo.bindNotification(mMockPackageManager, mMockINotificationManager, TEST_PACKAGE_NAME, mDefaultNotificationChannel, 1, mSbn, null, null, null, true, - false, false, IMPORTANCE_DEFAULT, NotificationInfo.ACTION_NONE); + false, false, IMPORTANCE_DEFAULT); final TextView textView = mNotificationInfo.findViewById(R.id.channel_name); assertEquals(GONE, textView.getVisibility()); } @@ -291,7 +291,7 @@ public class NotificationInfoTest extends SysuiTestCase { eq(TEST_PACKAGE_NAME), eq(TEST_UID), anyBoolean())).thenReturn(10); mNotificationInfo.bindNotification(mMockPackageManager, mMockINotificationManager, TEST_PACKAGE_NAME, mDefaultNotificationChannel, 1, mSbn, null, null, null, true, - false, false, IMPORTANCE_DEFAULT, NotificationInfo.ACTION_NONE); + false, false, IMPORTANCE_DEFAULT); final TextView textView = mNotificationInfo.findViewById(R.id.channel_name); assertEquals(VISIBLE, textView.getVisibility()); } @@ -300,7 +300,7 @@ public class NotificationInfoTest extends SysuiTestCase { public void testBindNotification_UnblockablePackageUsesChannelName() throws Exception { mNotificationInfo.bindNotification(mMockPackageManager, mMockINotificationManager, TEST_PACKAGE_NAME, mNotificationChannel, 1, mSbn, null, null, null, true, true, - false, IMPORTANCE_DEFAULT, NotificationInfo.ACTION_NONE); + false, IMPORTANCE_DEFAULT); final TextView textView = mNotificationInfo.findViewById(R.id.channel_name); assertEquals(VISIBLE, textView.getVisibility()); } @@ -309,7 +309,7 @@ public class NotificationInfoTest extends SysuiTestCase { public void testBindNotification_BlockButton() throws Exception { mNotificationInfo.bindNotification(mMockPackageManager, mMockINotificationManager, TEST_PACKAGE_NAME, mNotificationChannel, 1, mSbn, null, null, null, true, false, - false, IMPORTANCE_DEFAULT, NotificationInfo.ACTION_NONE); + false, IMPORTANCE_DEFAULT); final View block = mNotificationInfo.findViewById(R.id.block); final View toggleSilent = mNotificationInfo.findViewById(R.id.toggle_silent); final View minimize = mNotificationInfo.findViewById(R.id.minimize); @@ -323,7 +323,7 @@ public class NotificationInfoTest extends SysuiTestCase { mNotificationChannel.setImportance(IMPORTANCE_DEFAULT); mNotificationInfo.bindNotification(mMockPackageManager, mMockINotificationManager, TEST_PACKAGE_NAME, mNotificationChannel, 1, mSbn, null, null, null, true, false, - true, IMPORTANCE_DEFAULT, NotificationInfo.ACTION_NONE); + true, IMPORTANCE_DEFAULT); final TextView toggleSilent = mNotificationInfo.findViewById(R.id.toggle_silent); assertEquals(VISIBLE, toggleSilent.getVisibility()); assertEquals( @@ -335,7 +335,7 @@ public class NotificationInfoTest extends SysuiTestCase { mNotificationChannel.setImportance(IMPORTANCE_LOW); mNotificationInfo.bindNotification(mMockPackageManager, mMockINotificationManager, TEST_PACKAGE_NAME, mNotificationChannel, 1, mSbn, null, null, null, true, false, - true, IMPORTANCE_LOW, NotificationInfo.ACTION_NONE); + true, IMPORTANCE_LOW); final TextView toggleSilent = mNotificationInfo.findViewById(R.id.toggle_silent); assertEquals(VISIBLE, toggleSilent.getVisibility()); assertEquals( @@ -347,7 +347,7 @@ public class NotificationInfoTest extends SysuiTestCase { mNotificationChannel.setImportance(IMPORTANCE_UNSPECIFIED); mNotificationInfo.bindNotification(mMockPackageManager, mMockINotificationManager, TEST_PACKAGE_NAME, mNotificationChannel, 1, mSbn, null, null, null, true, false, - true, IMPORTANCE_DEFAULT, NotificationInfo.ACTION_NONE); + true, IMPORTANCE_DEFAULT); final TextView toggleSilent = mNotificationInfo.findViewById(R.id.toggle_silent); assertEquals(VISIBLE, toggleSilent.getVisibility()); assertEquals( @@ -360,7 +360,7 @@ public class NotificationInfoTest extends SysuiTestCase { mNotificationChannel.setImportance(IMPORTANCE_UNSPECIFIED); mNotificationInfo.bindNotification(mMockPackageManager, mMockINotificationManager, TEST_PACKAGE_NAME, mNotificationChannel, 1, mSbn, null, null, null, true, false, - true, IMPORTANCE_LOW, NotificationInfo.ACTION_NONE); + true, IMPORTANCE_LOW); final TextView toggleSilent = mNotificationInfo.findViewById(R.id.toggle_silent); assertEquals(VISIBLE, toggleSilent.getVisibility()); assertEquals( @@ -372,7 +372,7 @@ public class NotificationInfoTest extends SysuiTestCase { mSbn.getNotification().flags = Notification.FLAG_FOREGROUND_SERVICE; mNotificationInfo.bindNotification(mMockPackageManager, mMockINotificationManager, TEST_PACKAGE_NAME, mNotificationChannel, 1, mSbn, null, null, null, true, false, - false, IMPORTANCE_DEFAULT, NotificationInfo.ACTION_NONE); + false, IMPORTANCE_DEFAULT); final View block = mNotificationInfo.findViewById(R.id.block); final View minimize = mNotificationInfo.findViewById(R.id.minimize); assertEquals(GONE, block.getVisibility()); @@ -387,7 +387,7 @@ public class NotificationInfoTest extends SysuiTestCase { (View v, NotificationChannel c, int appUid) -> { assertEquals(mNotificationChannel, c); latch.countDown(); - }, null, true, false, false, IMPORTANCE_DEFAULT, NotificationInfo.ACTION_NONE); + }, null, true, false, false, IMPORTANCE_DEFAULT); final View settingsButton = mNotificationInfo.findViewById(R.id.info); settingsButton.performClick(); @@ -399,7 +399,7 @@ public class NotificationInfoTest extends SysuiTestCase { public void testBindNotification_SettingsButtonInvisibleWhenNoClickListener() throws Exception { mNotificationInfo.bindNotification(mMockPackageManager, mMockINotificationManager, TEST_PACKAGE_NAME, mNotificationChannel, 1, mSbn, null, null, null, true, false, - false, IMPORTANCE_DEFAULT, NotificationInfo.ACTION_NONE); + false, IMPORTANCE_DEFAULT); final View settingsButton = mNotificationInfo.findViewById(R.id.info); assertTrue(settingsButton.getVisibility() != View.VISIBLE); } @@ -411,7 +411,7 @@ public class NotificationInfoTest extends SysuiTestCase { TEST_PACKAGE_NAME, mNotificationChannel, 1, mSbn, null, (View v, NotificationChannel c, int appUid) -> { assertEquals(mNotificationChannel, c); - }, null, false, false, false, IMPORTANCE_DEFAULT, NotificationInfo.ACTION_NONE); + }, null, false, false, false, IMPORTANCE_DEFAULT); final View settingsButton = mNotificationInfo.findViewById(R.id.info); assertTrue(settingsButton.getVisibility() != View.VISIBLE); } @@ -420,11 +420,11 @@ public class NotificationInfoTest extends SysuiTestCase { public void testBindNotification_SettingsButtonReappearsAfterSecondBind() throws Exception { mNotificationInfo.bindNotification(mMockPackageManager, mMockINotificationManager, TEST_PACKAGE_NAME, mNotificationChannel, 1, mSbn, null, null, null, true, false, - false, IMPORTANCE_DEFAULT, NotificationInfo.ACTION_NONE); + false, IMPORTANCE_DEFAULT); mNotificationInfo.bindNotification(mMockPackageManager, mMockINotificationManager, TEST_PACKAGE_NAME, mNotificationChannel, 1, mSbn, null, (View v, NotificationChannel c, int appUid) -> { - }, null, true, false, false, IMPORTANCE_DEFAULT, NotificationInfo.ACTION_NONE); + }, null, true, false, false, IMPORTANCE_DEFAULT); final View settingsButton = mNotificationInfo.findViewById(R.id.info); assertEquals(View.VISIBLE, settingsButton.getVisibility()); } @@ -433,7 +433,7 @@ public class NotificationInfoTest extends SysuiTestCase { public void testLogBlockingHelperCounter_doesntLogForNormalGutsView() throws Exception { mNotificationInfo.bindNotification(mMockPackageManager, mMockINotificationManager, TEST_PACKAGE_NAME, mNotificationChannel, 1, mSbn, null, null, null, true, false, - false, IMPORTANCE_DEFAULT, NotificationInfo.ACTION_NONE); + false, IMPORTANCE_DEFAULT); mNotificationInfo.logBlockingHelperCounter("HowCanNotifsBeRealIfAppsArent"); verify(mMetricsLogger, times(0)).count(anyString(), anyInt()); } @@ -442,7 +442,7 @@ public class NotificationInfoTest extends SysuiTestCase { public void testLogBlockingHelperCounter_logsForBlockingHelper() throws Exception { mNotificationInfo.bindNotification(mMockPackageManager, mMockINotificationManager, TEST_PACKAGE_NAME, mNotificationChannel, 1, mSbn, null, null, null, false, true, - true, true, false, IMPORTANCE_DEFAULT, NotificationInfo.ACTION_NONE); + true, true, false, IMPORTANCE_DEFAULT); mNotificationInfo.logBlockingHelperCounter("HowCanNotifsBeRealIfAppsArent"); verify(mMetricsLogger, times(1)).count(anyString(), anyInt()); } @@ -455,7 +455,7 @@ public class NotificationInfoTest extends SysuiTestCase { (View v, NotificationChannel c, int appUid) -> { assertEquals(null, c); latch.countDown(); - }, null, true, true, false, IMPORTANCE_DEFAULT, NotificationInfo.ACTION_NONE); + }, null, true, true, false, IMPORTANCE_DEFAULT); mNotificationInfo.findViewById(R.id.info).performClick(); // Verify that listener was triggered. @@ -468,7 +468,7 @@ public class NotificationInfoTest extends SysuiTestCase { throws Exception { mNotificationInfo.bindNotification(mMockPackageManager, mMockINotificationManager, TEST_PACKAGE_NAME, mNotificationChannel, MULTIPLE_CHANNEL_COUNT, mSbn, null, null, - null, true, true, false, IMPORTANCE_DEFAULT, NotificationInfo.ACTION_NONE); + null, true, true, false, IMPORTANCE_DEFAULT); final TextView channelNameView = mNotificationInfo.findViewById(R.id.channel_name); assertEquals(GONE, channelNameView.getVisibility()); @@ -479,7 +479,7 @@ public class NotificationInfoTest extends SysuiTestCase { public void testStopInvisibleIfBundleFromDifferentChannels() throws Exception { mNotificationInfo.bindNotification(mMockPackageManager, mMockINotificationManager, TEST_PACKAGE_NAME, mNotificationChannel, MULTIPLE_CHANNEL_COUNT, mSbn, null, null, - null, true, true, false, IMPORTANCE_DEFAULT, NotificationInfo.ACTION_NONE); + null, true, true, false, IMPORTANCE_DEFAULT); final TextView blockView = mNotificationInfo.findViewById(R.id.block); assertEquals(GONE, blockView.getVisibility()); } @@ -488,7 +488,7 @@ public class NotificationInfoTest extends SysuiTestCase { public void testbindNotification_BlockingHelper() throws Exception { mNotificationInfo.bindNotification(mMockPackageManager, mMockINotificationManager, TEST_PACKAGE_NAME, mNotificationChannel, 1, mSbn, null, null, null, false, false, - true, true, false, IMPORTANCE_DEFAULT, NotificationInfo.ACTION_NONE); + true, true, false, IMPORTANCE_DEFAULT); final TextView view = mNotificationInfo.findViewById(R.id.block_prompt); assertEquals(View.VISIBLE, view.getVisibility()); assertEquals(mContext.getString(R.string.inline_blocking_helper), view.getText()); @@ -498,7 +498,7 @@ public class NotificationInfoTest extends SysuiTestCase { public void testbindNotification_UnblockableTextVisibleWhenAppUnblockable() throws Exception { mNotificationInfo.bindNotification(mMockPackageManager, mMockINotificationManager, TEST_PACKAGE_NAME, mNotificationChannel, 1, mSbn, null, null, null, true, true, - false, IMPORTANCE_DEFAULT, NotificationInfo.ACTION_NONE); + false, IMPORTANCE_DEFAULT); final TextView view = mNotificationInfo.findViewById(R.id.block_prompt); assertEquals(View.VISIBLE, view.getVisibility()); assertEquals(mContext.getString(R.string.notification_unblockable_desc), @@ -509,7 +509,7 @@ public class NotificationInfoTest extends SysuiTestCase { public void testBindNotification_DoesNotUpdateNotificationChannel() throws Exception { mNotificationInfo.bindNotification(mMockPackageManager, mMockINotificationManager, TEST_PACKAGE_NAME, mNotificationChannel, 1, mSbn, null, null, null, true, false, - false, IMPORTANCE_DEFAULT, NotificationInfo.ACTION_NONE); + false, IMPORTANCE_DEFAULT); mTestableLooper.processAllMessages(); verify(mMockINotificationManager, never()).updateNotificationChannelForPackage( anyString(), eq(TEST_UID), any()); @@ -520,7 +520,7 @@ public class NotificationInfoTest extends SysuiTestCase { mNotificationChannel.setImportance(IMPORTANCE_LOW); mNotificationInfo.bindNotification(mMockPackageManager, mMockINotificationManager, TEST_PACKAGE_NAME, mNotificationChannel, 1, mSbn, null, null, null, true, false, - false, IMPORTANCE_DEFAULT, NotificationInfo.ACTION_NONE); + false, IMPORTANCE_DEFAULT); mNotificationInfo.findViewById(R.id.block).performClick(); mTestableLooper.processAllMessages(); @@ -534,7 +534,7 @@ public class NotificationInfoTest extends SysuiTestCase { mNotificationChannel.setImportance(IMPORTANCE_LOW); mNotificationInfo.bindNotification(mMockPackageManager, mMockINotificationManager, TEST_PACKAGE_NAME, mNotificationChannel, 1, mSbn, null, null, null, true, false, - false, IMPORTANCE_DEFAULT, NotificationInfo.ACTION_NONE); + false, IMPORTANCE_DEFAULT); mNotificationInfo.findViewById(R.id.minimize).performClick(); mTestableLooper.processAllMessages(); @@ -548,7 +548,7 @@ public class NotificationInfoTest extends SysuiTestCase { mNotificationChannel.setImportance(IMPORTANCE_DEFAULT); mNotificationInfo.bindNotification(mMockPackageManager, mMockINotificationManager, TEST_PACKAGE_NAME, mNotificationChannel, 1, mSbn, null, null, null, true, false, - true, IMPORTANCE_DEFAULT, NotificationInfo.ACTION_NONE); + true, IMPORTANCE_DEFAULT); mNotificationInfo.findViewById(R.id.toggle_silent).performClick(); mTestableLooper.processAllMessages(); @@ -562,7 +562,7 @@ public class NotificationInfoTest extends SysuiTestCase { mNotificationChannel.setImportance(IMPORTANCE_LOW); mNotificationInfo.bindNotification(mMockPackageManager, mMockINotificationManager, TEST_PACKAGE_NAME, mNotificationChannel, 1, mSbn, null, null, null, true, false, - true, IMPORTANCE_DEFAULT, NotificationInfo.ACTION_NONE); + true, IMPORTANCE_DEFAULT); mNotificationInfo.findViewById(R.id.toggle_silent).performClick(); mTestableLooper.processAllMessages(); @@ -576,7 +576,7 @@ public class NotificationInfoTest extends SysuiTestCase { int originalImportance = mNotificationChannel.getImportance(); mNotificationInfo.bindNotification(mMockPackageManager, mMockINotificationManager, TEST_PACKAGE_NAME, mNotificationChannel, 1, mSbn, null, null, null, true, false, - false, IMPORTANCE_DEFAULT, NotificationInfo.ACTION_NONE); + false, IMPORTANCE_DEFAULT); mNotificationInfo.handleCloseControls(true, false); mTestableLooper.processAllMessages(); @@ -591,7 +591,7 @@ public class NotificationInfoTest extends SysuiTestCase { mNotificationChannel.setImportance(IMPORTANCE_UNSPECIFIED); mNotificationInfo.bindNotification(mMockPackageManager, mMockINotificationManager, TEST_PACKAGE_NAME, mNotificationChannel, 1, mSbn, null, null, null, true, false, - false, IMPORTANCE_DEFAULT, NotificationInfo.ACTION_NONE); + false, IMPORTANCE_DEFAULT); mNotificationInfo.handleCloseControls(true, false); @@ -609,8 +609,8 @@ public class NotificationInfoTest extends SysuiTestCase { TEST_PACKAGE_NAME, mNotificationChannel /* notificationChannel */, 10 /* numUniqueChannelsInRow */, mSbn, null /* checkSaveListener */, null /* onSettingsClick */, null /* onAppSettingsClick */ , - true, false /* isNonblockable */, false /* isNoisy */, IMPORTANCE_DEFAULT, - NotificationInfo.ACTION_NONE); + true, false /* isNonblockable */, false /* isNoisy */, IMPORTANCE_DEFAULT + ); mNotificationInfo.findViewById(R.id.block).performClick(); waitForUndoButton(); @@ -631,8 +631,8 @@ public class NotificationInfoTest extends SysuiTestCase { TEST_PACKAGE_NAME, mNotificationChannel /* notificationChannel */, 10 /* numUniqueChannelsInRow */, mSbn, null /* checkSaveListener */, null /* onSettingsClick */, null /* onAppSettingsClick */, - true, false /* isNonblockable */, false /* isNoisy */, IMPORTANCE_DEFAULT, - NotificationInfo.ACTION_NONE); + true, false /* isNonblockable */, false /* isNoisy */, IMPORTANCE_DEFAULT + ); mNotificationInfo.findViewById(R.id.block).performClick(); waitForUndoButton(); @@ -653,8 +653,7 @@ public class NotificationInfoTest extends SysuiTestCase { null /* onSettingsClick */, null /* onAppSettingsClick */ , true /* provisioned */, false /* isNonblockable */, true /* isForBlockingHelper */, - true /* isUserSentimentNegative */, false /* isNoisy */, IMPORTANCE_DEFAULT, - NotificationInfo.ACTION_NONE); + true /* isUserSentimentNegative */, false /* isNoisy */, IMPORTANCE_DEFAULT); NotificationGuts guts = spy(new NotificationGuts(mContext, null)); when(guts.getWindowToken()).thenReturn(mock(IBinder.class)); @@ -682,8 +681,7 @@ public class NotificationInfoTest extends SysuiTestCase { 10 /* numUniqueChannelsInRow */, mSbn, listener /* checkSaveListener */, null /* onSettingsClick */, null /* onAppSettingsClick */ , true /* provisioned */, false /* isNonblockable */, true /* isForBlockingHelper */, - true /* isUserSentimentNegative */, false /* isNoisy */, IMPORTANCE_DEFAULT, - NotificationInfo.ACTION_NONE); + true /* isUserSentimentNegative */, false /* isNoisy */, IMPORTANCE_DEFAULT); NotificationGuts guts = spy(new NotificationGuts(mContext, null)); when(guts.getWindowToken()).thenReturn(mock(IBinder.class)); @@ -712,7 +710,7 @@ public class NotificationInfoTest extends SysuiTestCase { null /* onSettingsClick */, null /* onAppSettingsClick */ , false /* isNonblockable */, true /* isForBlockingHelper */, true, true /* isUserSentimentNegative */, false /* isNoisy */, - IMPORTANCE_DEFAULT, NotificationInfo.ACTION_NONE); + IMPORTANCE_DEFAULT); mNotificationInfo.handleCloseControls(true /* save */, false /* force */); @@ -731,8 +729,7 @@ public class NotificationInfoTest extends SysuiTestCase { null /* onSettingsClick */, null /* onAppSettingsClick */, true /* provisioned */, false /* isNonblockable */, true /* isForBlockingHelper */, - true /* isUserSentimentNegative */, false /* isNoisy */, IMPORTANCE_DEFAULT, - NotificationInfo.ACTION_NONE); + true /* isUserSentimentNegative */, false /* isNoisy */, IMPORTANCE_DEFAULT); mNotificationInfo.findViewById(R.id.block).performClick(); mTestableLooper.processAllMessages(); @@ -755,7 +752,7 @@ public class NotificationInfoTest extends SysuiTestCase { true /* isForBlockingHelper */, true, false /* isUserSentimentNegative */, - false /* isNoisy */, IMPORTANCE_DEFAULT, NotificationInfo.ACTION_NONE); + false /* isNoisy */, IMPORTANCE_DEFAULT); NotificationGuts guts = mock(NotificationGuts.class); doCallRealMethod().when(guts).closeControls(anyInt(), anyInt(), anyBoolean(), anyBoolean()); mNotificationInfo.setGutsParent(guts); @@ -770,7 +767,7 @@ public class NotificationInfoTest extends SysuiTestCase { mNotificationChannel.setImportance(IMPORTANCE_LOW); mNotificationInfo.bindNotification(mMockPackageManager, mMockINotificationManager, TEST_PACKAGE_NAME, mNotificationChannel, 1, mSbn, null, null, null, true, true, - false, IMPORTANCE_DEFAULT, NotificationInfo.ACTION_NONE); + false, IMPORTANCE_DEFAULT); mNotificationInfo.findViewById(R.id.block).performClick(); waitForUndoButton(); @@ -784,7 +781,7 @@ public class NotificationInfoTest extends SysuiTestCase { mNotificationChannel.setImportance(IMPORTANCE_LOW); mNotificationInfo.bindNotification(mMockPackageManager, mMockINotificationManager, TEST_PACKAGE_NAME, mNotificationChannel, 1, mSbn, null, null, null, true, false, - false, IMPORTANCE_DEFAULT, NotificationInfo.ACTION_NONE); + false, IMPORTANCE_DEFAULT); mNotificationInfo.findViewById(R.id.block).performClick(); waitForUndoButton(); @@ -817,7 +814,7 @@ public class NotificationInfoTest extends SysuiTestCase { false /* isNonblockable */, true /* isForBlockingHelper */, true /* isUserSentimentNegative */, - false/* isNoisy */, IMPORTANCE_DEFAULT, NotificationInfo.ACTION_NONE); + false/* isNoisy */, IMPORTANCE_DEFAULT); mNotificationInfo.findViewById(R.id.block).performClick(); waitForUndoButton(); @@ -839,7 +836,7 @@ public class NotificationInfoTest extends SysuiTestCase { mNotificationChannel.setImportance(IMPORTANCE_LOW); mNotificationInfo.bindNotification(mMockPackageManager, mMockINotificationManager, TEST_PACKAGE_NAME, mNotificationChannel, 1, mSbn, null, null, null, true, true, - false, IMPORTANCE_DEFAULT, NotificationInfo.ACTION_NONE); + false, IMPORTANCE_DEFAULT); mNotificationInfo.findViewById(R.id.minimize).performClick(); waitForUndoButton(); @@ -854,7 +851,7 @@ public class NotificationInfoTest extends SysuiTestCase { mSbn.getNotification().flags = Notification.FLAG_FOREGROUND_SERVICE; mNotificationInfo.bindNotification(mMockPackageManager, mMockINotificationManager, TEST_PACKAGE_NAME, mNotificationChannel, 1, mSbn, null, null, null, true, false, - false, IMPORTANCE_DEFAULT, NotificationInfo.ACTION_NONE); + false, IMPORTANCE_DEFAULT); mNotificationInfo.findViewById(R.id.minimize).performClick(); waitForUndoButton(); @@ -875,7 +872,7 @@ public class NotificationInfoTest extends SysuiTestCase { mNotificationChannel.setImportance(IMPORTANCE_LOW); mNotificationInfo.bindNotification(mMockPackageManager, mMockINotificationManager, TEST_PACKAGE_NAME, mNotificationChannel, 1, mSbn, null, null, null, true, false, - false, IMPORTANCE_DEFAULT, NotificationInfo.ACTION_NONE); + false, IMPORTANCE_DEFAULT); mNotificationInfo.handleCloseControls(true, false); @@ -893,7 +890,7 @@ public class NotificationInfoTest extends SysuiTestCase { mNotificationChannel.setImportance(IMPORTANCE_LOW); mNotificationInfo.bindNotification(mMockPackageManager, mMockINotificationManager, TEST_PACKAGE_NAME, mNotificationChannel, 1, mSbn, null, null, null, true, false, - false, IMPORTANCE_DEFAULT, NotificationInfo.ACTION_NONE); + false, IMPORTANCE_DEFAULT); mNotificationInfo.findViewById(R.id.block).performClick(); waitForUndoButton(); @@ -915,7 +912,7 @@ public class NotificationInfoTest extends SysuiTestCase { mNotificationChannel.setImportance(IMPORTANCE_LOW); mNotificationInfo.bindNotification(mMockPackageManager, mMockINotificationManager, TEST_PACKAGE_NAME, mNotificationChannel, 1, mSbn, null, null, null, true, false, - false, IMPORTANCE_DEFAULT, NotificationInfo.ACTION_NONE); + false, IMPORTANCE_DEFAULT); mNotificationInfo.findViewById(R.id.minimize).performClick(); waitForUndoButton(); @@ -937,7 +934,7 @@ public class NotificationInfoTest extends SysuiTestCase { mNotificationChannel.setImportance(IMPORTANCE_DEFAULT); mNotificationInfo.bindNotification(mMockPackageManager, mMockINotificationManager, TEST_PACKAGE_NAME, mNotificationChannel, 1, mSbn, null, null, null, true, false, - true, IMPORTANCE_DEFAULT, NotificationInfo.ACTION_NONE); + true, IMPORTANCE_DEFAULT); mNotificationInfo.findViewById(R.id.toggle_silent).performClick(); waitForUndoButton(); @@ -958,7 +955,7 @@ public class NotificationInfoTest extends SysuiTestCase { mNotificationChannel.setImportance(IMPORTANCE_LOW); mNotificationInfo.bindNotification(mMockPackageManager, mMockINotificationManager, TEST_PACKAGE_NAME, mNotificationChannel, 1, mSbn, null, null, null, true, false, - false, IMPORTANCE_DEFAULT, NotificationInfo.ACTION_NONE); + false, IMPORTANCE_DEFAULT); mNotificationInfo.findViewById(R.id.toggle_silent).performClick(); waitForUndoButton(); @@ -980,7 +977,7 @@ public class NotificationInfoTest extends SysuiTestCase { mNotificationChannel.setImportance(IMPORTANCE_UNSPECIFIED); mNotificationInfo.bindNotification(mMockPackageManager, mMockINotificationManager, TEST_PACKAGE_NAME, mNotificationChannel, 1, mSbn, null, null, null, true, false, - true, IMPORTANCE_DEFAULT, NotificationInfo.ACTION_NONE); + true, IMPORTANCE_DEFAULT); mNotificationInfo.findViewById(R.id.toggle_silent).performClick(); waitForUndoButton(); @@ -1002,7 +999,7 @@ public class NotificationInfoTest extends SysuiTestCase { mNotificationChannel.setImportance(IMPORTANCE_UNSPECIFIED); mNotificationInfo.bindNotification(mMockPackageManager, mMockINotificationManager, TEST_PACKAGE_NAME, mNotificationChannel, 1, mSbn, null, null, null, true, false, - false, IMPORTANCE_LOW, NotificationInfo.ACTION_NONE); + false, IMPORTANCE_LOW); mNotificationInfo.findViewById(R.id.toggle_silent).performClick(); waitForUndoButton(); @@ -1023,7 +1020,7 @@ public class NotificationInfoTest extends SysuiTestCase { mNotificationChannel.setImportance(IMPORTANCE_LOW); mNotificationInfo.bindNotification(mMockPackageManager, mMockINotificationManager, TEST_PACKAGE_NAME, mNotificationChannel, 1, mSbn, null, null, null, true, true, - false, IMPORTANCE_DEFAULT, NotificationInfo.ACTION_NONE); + false, IMPORTANCE_DEFAULT); mNotificationInfo.findViewById(R.id.minimize).performClick(); waitForUndoButton(); @@ -1039,7 +1036,7 @@ public class NotificationInfoTest extends SysuiTestCase { mNotificationChannel.setImportance(IMPORTANCE_LOW); mNotificationInfo.bindNotification(mMockPackageManager, mMockINotificationManager, TEST_PACKAGE_NAME, mNotificationChannel, 1, mSbn, null, null, null, true, true, - false, IMPORTANCE_DEFAULT, NotificationInfo.ACTION_NONE); + false, IMPORTANCE_DEFAULT); mNotificationInfo.findViewById(R.id.block).performClick(); waitForUndoButton(); @@ -1056,7 +1053,7 @@ public class NotificationInfoTest extends SysuiTestCase { mNotificationInfo.bindNotification(mMockPackageManager, mMockINotificationManager, TEST_PACKAGE_NAME, mNotificationChannel, 1, mSbn, (Runnable saveImportance, StatusBarNotification sbn) -> { - }, null, null, true, true, false, IMPORTANCE_DEFAULT, NotificationInfo.ACTION_NONE); + }, null, null, true, true, false, IMPORTANCE_DEFAULT); mNotificationInfo.findViewById(R.id.block).performClick(); mTestableLooper.processAllMessages(); @@ -1074,8 +1071,8 @@ public class NotificationInfoTest extends SysuiTestCase { TEST_PACKAGE_NAME, mNotificationChannel, 1, mSbn, (Runnable saveImportance, StatusBarNotification sbn) -> { saveImportance.run(); - }, null, null, true, false, false, IMPORTANCE_DEFAULT, - NotificationInfo.ACTION_NONE); + }, null, null, true, false, false, IMPORTANCE_DEFAULT + ); mNotificationInfo.findViewById(R.id.block).performClick(); mTestableLooper.processAllMessages(); @@ -1111,7 +1108,7 @@ public class NotificationInfoTest extends SysuiTestCase { TEST_PACKAGE_NAME, mNotificationChannel, 1, sbn, null, null, (View v, Intent intent) -> { latch.countDown(); - }, true, false, false, IMPORTANCE_DEFAULT, NotificationInfo.ACTION_NONE); + }, true, false, false, IMPORTANCE_DEFAULT); final TextView settingsLink = mNotificationInfo.findViewById(R.id.app_settings); assertEquals(View.VISIBLE, settingsLink.getVisibility()); settingsLink.performClick(); @@ -1139,7 +1136,7 @@ public class NotificationInfoTest extends SysuiTestCase { TEST_PACKAGE_NAME, mNotificationChannel, MULTIPLE_CHANNEL_COUNT, sbn, null, null, (View v, Intent intent) -> { latch.countDown(); - }, true, false, false, IMPORTANCE_DEFAULT, NotificationInfo.ACTION_NONE); + }, true, false, false, IMPORTANCE_DEFAULT); final TextView settingsLink = mNotificationInfo.findViewById(R.id.app_settings); assertEquals(View.VISIBLE, settingsLink.getVisibility()); settingsLink.performClick(); @@ -1158,7 +1155,7 @@ public class NotificationInfoTest extends SysuiTestCase { mNotificationInfo.bindNotification(mMockPackageManager, mMockINotificationManager, TEST_PACKAGE_NAME, mNotificationChannel, MULTIPLE_CHANNEL_COUNT, sbn, null, null, - null, true, false, false, IMPORTANCE_DEFAULT, NotificationInfo.ACTION_NONE); + null, true, false, false, IMPORTANCE_DEFAULT); final TextView settingsLink = mNotificationInfo.findViewById(R.id.app_settings); assertEquals(GONE, settingsLink.getVisibility()); } @@ -1179,7 +1176,7 @@ public class NotificationInfoTest extends SysuiTestCase { mNotificationInfo.bindNotification(mMockPackageManager, mMockINotificationManager, TEST_PACKAGE_NAME, mNotificationChannel, 1, sbn, null, null, null, true, false, - false, IMPORTANCE_DEFAULT, NotificationInfo.ACTION_NONE); + false, IMPORTANCE_DEFAULT); final TextView settingsLink = mNotificationInfo.findViewById(R.id.app_settings); assertEquals(GONE, settingsLink.getVisibility()); } @@ -1202,7 +1199,7 @@ public class NotificationInfoTest extends SysuiTestCase { mNotificationInfo.bindNotification(mMockPackageManager, mMockINotificationManager, TEST_PACKAGE_NAME, mNotificationChannel, 1, sbn, null, null, null, false, true, - true, true, false, IMPORTANCE_DEFAULT, NotificationInfo.ACTION_NONE); + true, true, false, IMPORTANCE_DEFAULT); final TextView settingsLink = mNotificationInfo.findViewById(R.id.app_settings); assertEquals(GONE, settingsLink.getVisibility()); } @@ -1219,7 +1216,7 @@ public class NotificationInfoTest extends SysuiTestCase { mNotificationChannel.setImportance(IMPORTANCE_LOW); mNotificationInfo.bindNotification(mMockPackageManager, mMockINotificationManager, TEST_PACKAGE_NAME, mNotificationChannel, 1, mSbn, null, null, null, true, true, - false, IMPORTANCE_DEFAULT, NotificationInfo.ACTION_NONE); + false, IMPORTANCE_DEFAULT); mNotificationInfo.findViewById(R.id.minimize).performClick(); waitForUndoButton(); @@ -1232,7 +1229,7 @@ public class NotificationInfoTest extends SysuiTestCase { mNotificationChannel.setImportance(IMPORTANCE_LOW); mNotificationInfo.bindNotification(mMockPackageManager, mMockINotificationManager, TEST_PACKAGE_NAME, mNotificationChannel, 1, mSbn, null, null, null, true, true, - false, IMPORTANCE_DEFAULT, NotificationInfo.ACTION_NONE); + false, IMPORTANCE_DEFAULT); mNotificationInfo.findViewById(R.id.block).performClick(); waitForUndoButton(); @@ -1245,7 +1242,7 @@ public class NotificationInfoTest extends SysuiTestCase { mNotificationChannel.setImportance(IMPORTANCE_DEFAULT); mNotificationInfo.bindNotification(mMockPackageManager, mMockINotificationManager, TEST_PACKAGE_NAME, mNotificationChannel, 1, mSbn, null, null, null, true, true, - true, IMPORTANCE_DEFAULT, NotificationInfo.ACTION_NONE); + true, IMPORTANCE_DEFAULT); mNotificationInfo.findViewById(R.id.toggle_silent).performClick(); waitForUndoButton(); @@ -1259,7 +1256,7 @@ public class NotificationInfoTest extends SysuiTestCase { mNotificationChannel.setImportance(IMPORTANCE_LOW); mNotificationInfo.bindNotification(mMockPackageManager, mMockINotificationManager, TEST_PACKAGE_NAME, mNotificationChannel, 1, mSbn, null, null, null, true, true, - true, IMPORTANCE_DEFAULT, NotificationInfo.ACTION_NONE); + true, IMPORTANCE_DEFAULT); mNotificationInfo.findViewById(R.id.toggle_silent).performClick(); waitForUndoButton(); @@ -1273,7 +1270,7 @@ public class NotificationInfoTest extends SysuiTestCase { mNotificationChannel.setImportance(IMPORTANCE_LOW); mNotificationInfo.bindNotification(mMockPackageManager, mMockINotificationManager, TEST_PACKAGE_NAME, mNotificationChannel, 1, mSbn, null, null, null, true, true, - false, IMPORTANCE_DEFAULT, NotificationInfo.ACTION_NONE); + false, IMPORTANCE_DEFAULT); mNotificationInfo.findViewById(R.id.block).performClick(); waitForUndoButton(); @@ -1285,7 +1282,7 @@ public class NotificationInfoTest extends SysuiTestCase { mNotificationChannel.setImportance(IMPORTANCE_LOW); mNotificationInfo.bindNotification(mMockPackageManager, mMockINotificationManager, TEST_PACKAGE_NAME, mNotificationChannel, 1, mSbn, null, null, null, true, true, - false, IMPORTANCE_DEFAULT, NotificationInfo.ACTION_NONE); + false, IMPORTANCE_DEFAULT); mNotificationInfo.findViewById(R.id.block).performClick(); waitForUndoButton(); @@ -1293,60 +1290,4 @@ public class NotificationInfoTest extends SysuiTestCase { waitForStopButton(); assertEquals(VISIBLE, mNotificationInfo.findViewById(R.id.header).getVisibility()); } - - @Test - public void testBindNotificationWithInitialBlockAction() throws Exception { - mNotificationInfo.bindNotification(mMockPackageManager, mMockINotificationManager, - TEST_PACKAGE_NAME, mNotificationChannel, 1, mSbn, null, null, null, true, false, - false, IMPORTANCE_DEFAULT, NotificationInfo.ACTION_BLOCK); - waitForUndoButton(); - mNotificationInfo.handleCloseControls(true, false); - - mTestableLooper.processAllMessages(); - ArgumentCaptor<NotificationChannel> updated = - ArgumentCaptor.forClass(NotificationChannel.class); - verify(mMockINotificationManager, times(1)).updateNotificationChannelForPackage( - anyString(), eq(TEST_UID), updated.capture()); - assertTrue((updated.getValue().getUserLockedFields() - & USER_LOCKED_IMPORTANCE) != 0); - assertEquals(IMPORTANCE_NONE, updated.getValue().getImportance()); - } - - @Test - public void testBindNotificationWithInitialSilenceAction() throws Exception { - mNotificationChannel.setImportance(IMPORTANCE_DEFAULT); - mNotificationInfo.bindNotification(mMockPackageManager, mMockINotificationManager, - TEST_PACKAGE_NAME, mNotificationChannel, 1, mSbn, null, null, null, true, false, - true, IMPORTANCE_DEFAULT, NotificationInfo.ACTION_TOGGLE_SILENT); - waitForUndoButton(); - mNotificationInfo.handleCloseControls(true, false); - - mTestableLooper.processAllMessages(); - ArgumentCaptor<NotificationChannel> updated = - ArgumentCaptor.forClass(NotificationChannel.class); - verify(mMockINotificationManager, times(1)).updateNotificationChannelForPackage( - anyString(), eq(TEST_UID), updated.capture()); - assertTrue((updated.getValue().getUserLockedFields() - & USER_LOCKED_IMPORTANCE) != 0); - assertEquals(IMPORTANCE_LOW, updated.getValue().getImportance()); - } - - @Test - public void testBindNotificationWithInitialUnSilenceAction() throws Exception { - mNotificationChannel.setImportance(IMPORTANCE_LOW); - mNotificationInfo.bindNotification(mMockPackageManager, mMockINotificationManager, - TEST_PACKAGE_NAME, mNotificationChannel, 1, mSbn, null, null, null, true, false, - true, IMPORTANCE_LOW, NotificationInfo.ACTION_TOGGLE_SILENT); - waitForUndoButton(); - mNotificationInfo.handleCloseControls(true, false); - - mTestableLooper.processAllMessages(); - ArgumentCaptor<NotificationChannel> updated = - ArgumentCaptor.forClass(NotificationChannel.class); - verify(mMockINotificationManager, times(1)).updateNotificationChannelForPackage( - anyString(), eq(TEST_UID), updated.capture()); - assertTrue((updated.getValue().getUserLockedFields() - & USER_LOCKED_IMPORTANCE) != 0); - assertEquals(IMPORTANCE_HIGH, updated.getValue().getImportance()); - } } diff --git a/services/core/java/com/android/server/Watchdog.java b/services/core/java/com/android/server/Watchdog.java index d1b56e9afa4e..e80e9e1b594e 100644 --- a/services/core/java/com/android/server/Watchdog.java +++ b/services/core/java/com/android/server/Watchdog.java @@ -105,6 +105,7 @@ public class Watchdog extends Thread { "android.hardware.camera.provider@2.4::ICameraProvider", "android.hardware.graphics.allocator@2.0::IAllocator", "android.hardware.graphics.composer@2.1::IComposer", + "android.hardware.health@2.0::IHealth", "android.hardware.media.omx@1.0::IOmx", "android.hardware.media.omx@1.0::IOmxStore", "android.hardware.sensors@1.0::ISensors", diff --git a/services/core/java/com/android/server/am/TEST_MAPPING b/services/core/java/com/android/server/am/TEST_MAPPING index b817669ce70c..48b414581d45 100644 --- a/services/core/java/com/android/server/am/TEST_MAPPING +++ b/services/core/java/com/android/server/am/TEST_MAPPING @@ -1,17 +1,6 @@ { "presubmit": [ { - "name": "CtsActivityManagerDeviceTestCases", - "options": [ - { - "include-annotation": "android.platform.test.annotations.Presubmit" - }, - { - "exclude-annotation": "android.support.test.filters.FlakyTest" - } - ] - }, - { "name": "CtsActivityManagerDeviceSdk25TestCases", "options": [ { diff --git a/services/core/java/com/android/server/audio/AudioService.java b/services/core/java/com/android/server/audio/AudioService.java index 67d27c94f41b..6cde4adb9f11 100644 --- a/services/core/java/com/android/server/audio/AudioService.java +++ b/services/core/java/com/android/server/audio/AudioService.java @@ -649,6 +649,9 @@ public class AudioService extends IAudioService.Stub private String mEnabledSurroundFormats; private boolean mSurroundModeChanged; + @GuardedBy("mSettingsLock") + private int mAssistantUid; + // Intent "extra" data keys. public static final String CONNECT_INTENT_KEY_PORT_NAME = "portName"; public static final String CONNECT_INTENT_KEY_STATE = "state"; @@ -1079,6 +1082,10 @@ public class AudioService extends IAudioService.Stub AudioSystem.setForceUse(AudioSystem.FOR_DOCK, forDock); sendEncodedSurroundMode(mContentResolver, "onAudioServerDied"); sendEnabledSurroundFormats(mContentResolver, true); + updateAssistantUId(true); + } + synchronized (mAccessibilityServiceUidsLock) { + AudioSystem.setA11yServicesUids(mAccessibilityServiceUids); } synchronized (mHdmiClientLock) { if (mHdmiManager != null && mHdmiTvClient != null) { @@ -1404,6 +1411,39 @@ public class AudioService extends IAudioService.Stub } } + @GuardedBy("mSettingsLock") + private void updateAssistantUId(boolean forceUpdate) { + int assistantUid = 0; + + // Consider assistants in the following order of priority: + // 1) voice interaction service + // 2) assistant + String assistantName = Settings.Secure.getStringForUser( + mContentResolver, + Settings.Secure.VOICE_INTERACTION_SERVICE, UserHandle.USER_CURRENT); + if (TextUtils.isEmpty(assistantName)) { + assistantName = Settings.Secure.getStringForUser( + mContentResolver, + Settings.Secure.ASSISTANT, UserHandle.USER_CURRENT); + } + if (!TextUtils.isEmpty(assistantName)) { + String packageName = ComponentName.unflattenFromString(assistantName).getPackageName(); + if (!TextUtils.isEmpty(packageName)) { + try { + assistantUid = mContext.getPackageManager().getPackageUid(packageName, 0); + } catch (PackageManager.NameNotFoundException e) { + Log.e(TAG, + "updateAssistantUId() could not find UID for package: " + packageName); + } + } + } + + if (assistantUid != mAssistantUid || forceUpdate) { + AudioSystem.setAssistantUid(assistantUid); + mAssistantUid = assistantUid; + } + } + private void readPersistedSettings() { final ContentResolver cr = mContentResolver; @@ -1447,6 +1487,7 @@ public class AudioService extends IAudioService.Stub readDockAudioSettings(cr); sendEncodedSurroundMode(cr, "readPersistedSettings"); sendEnabledSurroundFormats(cr, true); + updateAssistantUId(true); } mMuteAffectedStreams = System.getIntForUser(cr, @@ -5811,6 +5852,9 @@ public class AudioService extends IAudioService.Stub mContentResolver, Settings.Global.ENCODED_SURROUND_OUTPUT_ENABLED_FORMATS); mContentResolver.registerContentObserver(Settings.Global.getUriFor( Settings.Global.ENCODED_SURROUND_OUTPUT_ENABLED_FORMATS), false, this); + + mContentResolver.registerContentObserver(Settings.Secure.getUriFor( + Settings.Secure.VOICE_INTERACTION_SERVICE), false, this); } @Override @@ -5832,6 +5876,7 @@ public class AudioService extends IAudioService.Stub updateMasterMono(mContentResolver); updateEncodedSurroundOutput(); sendEnabledSurroundFormats(mContentResolver, mSurroundModeChanged); + updateAssistantUId(false); } } @@ -7658,6 +7703,7 @@ public class AudioService extends IAudioService.Stub mAccessibilityServiceUids = uids.toArray(); } } + AudioSystem.setA11yServicesUids(mAccessibilityServiceUids); } } } diff --git a/services/core/java/com/android/server/net/NetworkPolicyLogger.java b/services/core/java/com/android/server/net/NetworkPolicyLogger.java index 4f4b6bfdb358..4bd8f450c76b 100644 --- a/services/core/java/com/android/server/net/NetworkPolicyLogger.java +++ b/services/core/java/com/android/server/net/NetworkPolicyLogger.java @@ -64,6 +64,7 @@ public class NetworkPolicyLogger { private static final int EVENT_UID_FIREWALL_RULE_CHANGED = 11; private static final int EVENT_FIREWALL_CHAIN_ENABLED = 12; private static final int EVENT_UPDATE_METERED_RESTRICTED_PKGS = 13; + private static final int EVENT_APP_IDLE_WL_CHANGED = 14; static final int NTWK_BLOCKED_POWER = 0; static final int NTWK_ALLOWED_NON_METERED = 1; @@ -145,6 +146,13 @@ public class NetworkPolicyLogger { } } + void appIdleWlChanged(int uid, boolean isWhitelisted) { + synchronized (mLock) { + if (LOGD) Slog.d(TAG, getAppIdleWlChangedLog(uid, isWhitelisted)); + mEventsBuffer.appIdleWlChanged(uid, isWhitelisted); + } + } + void paroleStateChanged(boolean paroleOn) { synchronized (mLock) { if (LOGD) Slog.d(TAG, getParoleStateChanged(paroleOn)); @@ -259,6 +267,10 @@ public class NetworkPolicyLogger { return "App idle state of uid " + uid + ": " + idle; } + private static String getAppIdleWlChangedLog(int uid, boolean isWhitelisted) { + return "App idle whitelist state of uid " + uid + ": " + isWhitelisted; + } + private static String getParoleStateChanged(boolean paroleOn) { return "Parole state: " + paroleOn; } @@ -409,6 +421,17 @@ public class NetworkPolicyLogger { data.timeStamp = System.currentTimeMillis(); } + public void appIdleWlChanged(int uid, boolean isWhitelisted) { + final Data data = getNextSlot(); + if (data == null) return; + + data.reset(); + data.type = EVENT_APP_IDLE_WL_CHANGED; + data.ifield1 = uid; + data.bfield1 = isWhitelisted; + data.timeStamp = System.currentTimeMillis(); + } + public void paroleStateChanged(boolean paroleOn) { final Data data = getNextSlot(); if (data == null) return; @@ -487,6 +510,8 @@ public class NetworkPolicyLogger { return getDeviceIdleModeEnabled(data.bfield1); case EVENT_APP_IDLE_STATE_CHANGED: return getAppIdleChangedLog(data.ifield1, data.bfield1); + case EVENT_APP_IDLE_WL_CHANGED: + return getAppIdleWlChangedLog(data.ifield1, data.bfield1); case EVENT_PAROLE_STATE_CHANGED: return getParoleStateChanged(data.bfield1); case EVENT_TEMP_POWER_SAVE_WL_CHANGED: diff --git a/services/core/java/com/android/server/net/NetworkPolicyManagerInternal.java b/services/core/java/com/android/server/net/NetworkPolicyManagerInternal.java index 099671d81a3e..7f650ee7e138 100644 --- a/services/core/java/com/android/server/net/NetworkPolicyManagerInternal.java +++ b/services/core/java/com/android/server/net/NetworkPolicyManagerInternal.java @@ -105,6 +105,12 @@ public abstract class NetworkPolicyManagerInternal { public abstract void onAdminDataAvailable(); /** + * Control if a UID should be whitelisted even if it's in app idle mode. Other restrictions may + * still be in effect. + */ + public abstract void setAppIdleWhitelist(int uid, boolean shouldWhitelist); + + /** * Sets a list of packages which are restricted by admin from accessing metered data. * * @param packageNames the list of restricted packages. diff --git a/services/core/java/com/android/server/net/NetworkPolicyManagerService.java b/services/core/java/com/android/server/net/NetworkPolicyManagerService.java index d7996422870c..7750c3781067 100644 --- a/services/core/java/com/android/server/net/NetworkPolicyManagerService.java +++ b/services/core/java/com/android/server/net/NetworkPolicyManagerService.java @@ -464,6 +464,10 @@ public class NetworkPolicyManagerService extends INetworkPolicyManager.Stub { @GuardedBy("mUidRulesFirstLock") final SparseBooleanArray mFirewallChainStates = new SparseBooleanArray(); + // "Power save mode" is the concept used in the DeviceIdleController that includes various + // features including Doze and Battery Saver. It include Battery Saver, but "power save mode" + // and "battery saver" are not equivalent. + /** * UIDs that have been white-listed to always be able to have network access * in power save mode, except device idle (doze) still applies. @@ -484,6 +488,13 @@ public class NetworkPolicyManagerService extends INetworkPolicyManager.Stub { private final SparseBooleanArray mPowerSaveTempWhitelistAppIds = new SparseBooleanArray(); /** + * UIDs that have been white-listed temporarily to be able to have network access despite being + * idle. Other power saving restrictions still apply. + */ + @GuardedBy("mUidRulesFirstLock") + private final SparseBooleanArray mAppIdleTempWhitelistAppIds = new SparseBooleanArray(); + + /** * UIDs that have been initially white-listed by system to avoid restricted background. */ @GuardedBy("mUidRulesFirstLock") @@ -3372,6 +3383,20 @@ public class NetworkPolicyManagerService extends INetworkPolicyManager.Stub { fout.decreaseIndent(); } + size = mAppIdleTempWhitelistAppIds.size(); + if (size > 0) { + fout.println("App idle whitelist app ids:"); + fout.increaseIndent(); + for (int i = 0; i < size; i++) { + fout.print("UID="); + fout.print(mAppIdleTempWhitelistAppIds.keyAt(i)); + fout.print(": "); + fout.print(mAppIdleTempWhitelistAppIds.valueAt(i)); + fout.println(); + } + fout.decreaseIndent(); + } + size = mDefaultRestrictBackgroundWhitelistUids.size(); if (size > 0) { fout.println("Default restrict background whitelist uids:"); @@ -3640,12 +3665,15 @@ public class NetworkPolicyManagerService extends INetworkPolicyManager.Stub { } /** + * Returns whether a uid is whitelisted from power saving restrictions (eg: Battery Saver, Doze + * mode, and app idle). + * * @param deviceIdleMode if true then we don't consider * {@link #mPowerSaveWhitelistExceptIdleAppIds} for checking if the {@param uid} is * whitelisted. */ @GuardedBy("mUidRulesFirstLock") - private boolean isWhitelistedBatterySaverUL(int uid, boolean deviceIdleMode) { + private boolean isWhitelistedFromPowerSaveUL(int uid, boolean deviceIdleMode) { final int appId = UserHandle.getAppId(uid); boolean isWhitelisted = mPowerSaveTempWhitelistAppIds.get(appId) || mPowerSaveWhitelistAppIds.get(appId); @@ -3660,7 +3688,7 @@ public class NetworkPolicyManagerService extends INetworkPolicyManager.Stub { @GuardedBy("mUidRulesFirstLock") private void updateRulesForWhitelistedPowerSaveUL(int uid, boolean enabled, int chain) { if (enabled) { - final boolean isWhitelisted = isWhitelistedBatterySaverUL(uid, + final boolean isWhitelisted = isWhitelistedFromPowerSaveUL(uid, chain == FIREWALL_CHAIN_DOZABLE); if (isWhitelisted || isUidForegroundOnRestrictPowerUL(uid)) { setUidFirewallRule(chain, uid, FIREWALL_RULE_ALLOW); @@ -3712,8 +3740,10 @@ public class NetworkPolicyManagerService extends INetworkPolicyManager.Stub { if (!mPowerSaveTempWhitelistAppIds.get(appId) && isUidIdle(uid) && !isUidForegroundOnRestrictPowerUL(uid)) { setUidFirewallRule(FIREWALL_CHAIN_STANDBY, uid, FIREWALL_RULE_DENY); + if (LOGD) Log.d(TAG, "updateRuleForAppIdleUL DENY " + uid); } else { setUidFirewallRule(FIREWALL_CHAIN_STANDBY, uid, FIREWALL_RULE_DEFAULT); + if (LOGD) Log.d(TAG, "updateRuleForAppIdleUL " + uid + " to DEFAULT"); } } finally { Trace.traceEnd(Trace.TRACE_TAG_NETWORK); @@ -3896,7 +3926,59 @@ public class NetworkPolicyManagerService extends INetworkPolicyManager.Stub { return UserHandle.isApp(uid) && hasInternetPermissions(uid); } - private boolean isUidIdle(int uid) { + /** + * Set whether or not an app should be whitelisted for network access while in app idle. Other + * power saving restrictions may still apply. + */ + @VisibleForTesting + public void setAppIdleWhitelist(int uid, boolean shouldWhitelist) { + synchronized (mUidRulesFirstLock) { + if (mAppIdleTempWhitelistAppIds.get(uid) == shouldWhitelist) { + // No change. + return; + } + + final long token = Binder.clearCallingIdentity(); + try { + mLogger.appIdleWlChanged(uid, shouldWhitelist); + if (shouldWhitelist) { + mAppIdleTempWhitelistAppIds.put(uid, true); + } else { + mAppIdleTempWhitelistAppIds.delete(uid); + } + updateRuleForAppIdleUL(uid); + updateRulesForPowerRestrictionsUL(uid); + } finally { + Binder.restoreCallingIdentity(token); + } + } + } + + /** Return the list of UIDs currently in the app idle whitelist. */ + @VisibleForTesting + public int[] getAppIdleWhitelist() { + mContext.enforceCallingOrSelfPermission(MANAGE_NETWORK_POLICY, TAG); + + synchronized (mUidRulesFirstLock) { + final int len = mAppIdleTempWhitelistAppIds.size(); + int[] uids = new int[len]; + for (int i = 0; i < len; ++i) { + uids[i] = mAppIdleTempWhitelistAppIds.keyAt(i); + } + return uids; + } + } + + /** Returns if the UID is currently considered idle. */ + @VisibleForTesting + public boolean isUidIdle(int uid) { + synchronized (mUidRulesFirstLock) { + if (mAppIdleTempWhitelistAppIds.get(uid)) { + // UID is temporarily whitelisted. + return false; + } + } + final String[] packages = mContext.getPackageManager().getPackagesForUid(uid); final int userId = UserHandle.getUserId(uid); @@ -3940,6 +4022,7 @@ public class NetworkPolicyManagerService extends INetworkPolicyManager.Stub { mPowerSaveWhitelistExceptIdleAppIds.delete(uid); mPowerSaveWhitelistAppIds.delete(uid); mPowerSaveTempWhitelistAppIds.delete(uid); + mAppIdleTempWhitelistAppIds.delete(uid); // ...then update iptables asynchronously. mHandler.obtainMessage(MSG_RESET_FIREWALL_RULES_BY_UID, uid, 0).sendToTarget(); @@ -3984,7 +4067,7 @@ public class NetworkPolicyManagerService extends INetworkPolicyManager.Stub { * <li>@{code bw_happy_box}: UIDs added to this chain have access (whitelist), unless they're * also blacklisted. * <li>@{code bw_data_saver}: when enabled (through {@link #setRestrictBackground(boolean)}), - * no UIDs other those whitelisted will have access. + * no UIDs other than those whitelisted will have access. * <ul> * * <p>The @{code bw_penalty_box} and @{code bw_happy_box} are primarily managed through the @@ -4194,7 +4277,7 @@ public class NetworkPolicyManagerService extends INetworkPolicyManager.Stub { final boolean restrictMode = isIdle || mRestrictPower || mDeviceIdleMode; final boolean isForeground = isUidForegroundOnRestrictPowerUL(uid); - final boolean isWhitelisted = isWhitelistedBatterySaverUL(uid, mDeviceIdleMode); + final boolean isWhitelisted = isWhitelistedFromPowerSaveUL(uid, mDeviceIdleMode); final int oldRule = oldUidRules & MASK_ALL_NETWORKS; int newRule = RULE_NONE; @@ -5023,6 +5106,11 @@ public class NetworkPolicyManagerService extends INetworkPolicyManager.Stub { } @Override + public void setAppIdleWhitelist(int uid, boolean shouldWhitelist) { + NetworkPolicyManagerService.this.setAppIdleWhitelist(uid, shouldWhitelist); + } + + @Override public void setMeteredRestrictedPackages(Set<String> packageNames, int userId) { setMeteredRestrictedPackagesInternal(packageNames, userId); } diff --git a/services/core/java/com/android/server/net/NetworkPolicyManagerShellCommand.java b/services/core/java/com/android/server/net/NetworkPolicyManagerShellCommand.java index 56d41c5a72aa..156c01dbcbb3 100644 --- a/services/core/java/com/android/server/net/NetworkPolicyManagerShellCommand.java +++ b/services/core/java/com/android/server/net/NetworkPolicyManagerShellCommand.java @@ -78,6 +78,8 @@ class NetworkPolicyManagerShellCommand extends ShellCommand { pw.println(" Adds a UID to the whitelist for restrict background usage."); pw.println(" add restrict-background-blacklist UID"); pw.println(" Adds a UID to the blacklist for restrict background usage."); + pw.println(" add app-idle-whitelist UID"); + pw.println(" Adds a UID to the temporary app idle whitelist."); pw.println(" get restrict-background"); pw.println(" Gets the global restrict background usage status."); pw.println(" list wifi-networks [true|false]"); @@ -92,6 +94,8 @@ class NetworkPolicyManagerShellCommand extends ShellCommand { pw.println(" Removes a UID from the whitelist for restrict background usage."); pw.println(" remove restrict-background-blacklist UID"); pw.println(" Removes a UID from the blacklist for restrict background usage."); + pw.println(" remove app-idle-whitelist UID"); + pw.println(" Removes a UID from the temporary app idle whitelist."); pw.println(" set metered-network ID [undefined|true|false]"); pw.println(" Toggles whether the given wi-fi network is metered."); pw.println(" set restrict-background BOOLEAN"); @@ -142,6 +146,8 @@ class NetworkPolicyManagerShellCommand extends ShellCommand { return -1; } switch(type) { + case "app-idle-whitelist": + return listAppIdleWhitelist(); case "wifi-networks": return listWifiNetworks(); case "restrict-background-whitelist": @@ -165,6 +171,8 @@ class NetworkPolicyManagerShellCommand extends ShellCommand { return addRestrictBackgroundWhitelist(); case "restrict-background-blacklist": return addRestrictBackgroundBlacklist(); + case "app-idle-whitelist": + return addAppIdleWhitelist(); } pw.println("Error: unknown add type '" + type + "'"); return -1; @@ -182,14 +190,20 @@ class NetworkPolicyManagerShellCommand extends ShellCommand { return removeRestrictBackgroundWhitelist(); case "restrict-background-blacklist": return removeRestrictBackgroundBlacklist(); + case "app-idle-whitelist": + return removeAppIdleWhitelist(); } pw.println("Error: unknown remove type '" + type + "'"); return -1; } private int listUidPolicies(String msg, int policy) throws RemoteException { - final PrintWriter pw = getOutPrintWriter(); final int[] uids = mInterface.getUidsWithPolicy(policy); + return listUidList(msg, uids); + } + + private int listUidList(String msg, int[] uids) { + final PrintWriter pw = getOutPrintWriter(); pw.print(msg); pw.print(": "); if (uids.length == 0) { pw.println("none"); @@ -214,6 +228,12 @@ class NetworkPolicyManagerShellCommand extends ShellCommand { POLICY_REJECT_METERED_BACKGROUND); } + private int listAppIdleWhitelist() throws RemoteException { + final PrintWriter pw = getOutPrintWriter(); + final int[] uids = mInterface.getAppIdleWhitelist(); + return listUidList("App Idle whitelisted UIDs", uids); + } + private int getRestrictBackground() throws RemoteException { final PrintWriter pw = getOutPrintWriter(); pw.print("Restrict background status: "); @@ -277,6 +297,23 @@ class NetworkPolicyManagerShellCommand extends ShellCommand { return resetUidPolicy("not blacklisted", POLICY_REJECT_METERED_BACKGROUND); } + private int setAppIdleWhitelist(boolean isWhitelisted) { + final int uid = getUidFromNextArg(); + if (uid < 0) { + return uid; + } + mInterface.setAppIdleWhitelist(uid, isWhitelisted); + return 0; + } + + private int addAppIdleWhitelist() throws RemoteException { + return setAppIdleWhitelist(true); + } + + private int removeAppIdleWhitelist() throws RemoteException { + return setAppIdleWhitelist(false); + } + private int listWifiNetworks() { final PrintWriter pw = getOutPrintWriter(); final String arg = getNextArg(); diff --git a/services/core/java/com/android/server/pm/PackageManagerService.java b/services/core/java/com/android/server/pm/PackageManagerService.java index 14682f325940..c708b0af321f 100644 --- a/services/core/java/com/android/server/pm/PackageManagerService.java +++ b/services/core/java/com/android/server/pm/PackageManagerService.java @@ -53,7 +53,6 @@ import static android.content.pm.PackageManager.INSTALL_FAILED_INVALID_APK; import static android.content.pm.PackageManager.INSTALL_FAILED_INVALID_INSTALL_LOCATION; import static android.content.pm.PackageManager.INSTALL_FAILED_MISSING_SHARED_LIBRARY; import static android.content.pm.PackageManager.INSTALL_FAILED_PACKAGE_CHANGED; -import static android.content.pm.PackageManager.INSTALL_FAILED_REPLACE_COULDNT_DELETE; import static android.content.pm.PackageManager.INSTALL_FAILED_SHARED_USER_INCOMPATIBLE; import static android.content.pm.PackageManager.INSTALL_FAILED_TEST_ONLY; import static android.content.pm.PackageManager.INSTALL_FAILED_UPDATE_INCOMPATIBLE; @@ -15191,8 +15190,11 @@ public class PackageManagerService extends IPackageManager.Stub } } private static class ReconcileFailure extends PackageManagerException { - public ReconcileFailure(String message) { - super("Invalid reconcile request: " + message); + ReconcileFailure(String message) { + super("Reconcile failed: " + message); + } + ReconcileFailure(int reason, String message) { + super(reason, "Reconcile failed: " + message); } } @@ -15211,10 +15213,12 @@ public class PackageManagerService extends IPackageManager.Stub @PackageManager.InstallFlags public final int installFlags; public final InstallArgs installArgs; + public final DeletePackageAction deletePackageAction; private ReconciledPackage(InstallArgs installArgs, PackageSetting pkgSetting, UserHandle installForUser, PackageInstalledInfo installResult, int installFlags, - String volumeUuid, PrepareResult prepareResult, ScanResult scanResult) { + String volumeUuid, PrepareResult prepareResult, ScanResult scanResult, + DeletePackageAction deletePackageAction) { this.installArgs = installArgs; this.pkgSetting = pkgSetting; this.installForUser = installForUser; @@ -15223,6 +15227,7 @@ public class PackageManagerService extends IPackageManager.Stub this.volumeUuid = volumeUuid; this.prepareResult = prepareResult; this.scanResult = scanResult; + this.deletePackageAction = deletePackageAction; } } @@ -15235,14 +15240,29 @@ public class PackageManagerService extends IPackageManager.Stub final ScanResult scanResult = request.scannedPackages.get(installPackageName); final InstallArgs installArgs = request.installArgs.get(installPackageName); final PackageInstalledInfo res = request.installResults.get(installPackageName); + final PrepareResult prepareResult = request.preparedPackages.get(installPackageName); if (scanResult == null || installArgs == null || res == null) { throw new ReconcileFailure( "inputs not balanced; missing argument for " + installPackageName); } + final DeletePackageAction deletePackageAction; + if (prepareResult.replace) { + deletePackageAction = mayDeletePackageLocked(res.removedInfo, + prepareResult.originalPs, prepareResult.disabledPs, + prepareResult.childPackageSettings); + if (deletePackageAction == null) { + throw new ReconcileFailure( + PackageManager.INSTALL_FAILED_REPLACE_COULDNT_DELETE, + "May not delete " + installPackageName + " to replace"); + } + } else { + deletePackageAction = null; + } result.put(installPackageName, new ReconciledPackage(installArgs, scanResult.pkgSetting, installArgs.getUser(), res, installArgs.installFlags, installArgs.volumeUuid, - request.preparedPackages.get(installPackageName), scanResult)); + request.preparedPackages.get(installPackageName), scanResult, + deletePackageAction)); } return result; } @@ -15314,28 +15334,28 @@ public class PackageManagerService extends IPackageManager.Stub final boolean killApp = (scanRequest.scanFlags & SCAN_DONT_KILL_APP) == 0; final int deleteFlags = PackageManager.DELETE_KEEP_DATA | (killApp ? 0 : PackageManager.DELETE_DONT_KILL_APP); - // First delete the existing package while retaining the data directory - if (!deletePackageLIF(packageName, null, true, request.mAllUsers, deleteFlags, - res.removedInfo, true, pkg)) { - // If the existing package wasn't successfully deleted - res.setError(INSTALL_FAILED_REPLACE_COULDNT_DELETE, - "replaceNonSystemPackageLI"); - return false; - } else { - // Successfully deleted the old package; proceed with replace. + try { + executeDeletePackageLIF(reconciledPkg.deletePackageAction, packageName, + null, true, request.mAllUsers, deleteFlags, true, pkg); + } catch (SystemDeleteException e) { + if (Build.IS_ENG) { + throw new RuntimeException("Unexpected failure", e); + // ignore; not possible for non-system app + } + } + // Successfully deleted the old package; proceed with replace. - // If deleted package lived in a container, give users a chance to - // relinquish resources before killing. - if (oldPackage.isForwardLocked() || isExternal(oldPackage)) { - if (DEBUG_INSTALL) { - Slog.i(TAG, "upgrading pkg " + oldPackage - + " is ASEC-hosted -> UNAVAILABLE"); - } - final int[] uidArray = new int[]{oldPackage.applicationInfo.uid}; - final ArrayList<String> pkgList = new ArrayList<>(1); - pkgList.add(oldPackage.applicationInfo.packageName); - sendResourcesChangedBroadcast(false, true, pkgList, uidArray, null); + // If deleted package lived in a container, give users a chance to + // relinquish resources before killing. + if (oldPackage.isForwardLocked() || isExternal(oldPackage)) { + if (DEBUG_INSTALL) { + Slog.i(TAG, "upgrading pkg " + oldPackage + + " is ASEC-hosted -> UNAVAILABLE"); } + final int[] uidArray = new int[]{oldPackage.applicationInfo.uid}; + final ArrayList<String> pkgList = new ArrayList<>(1); + pkgList.add(oldPackage.applicationInfo.packageName); + sendResourcesChangedBroadcast(false, true, pkgList, uidArray, null); } // Update the in-memory copy of the previous code paths. @@ -15744,13 +15764,16 @@ public class PackageManagerService extends IPackageManager.Stub @Nullable public final String renamedPackage; public final PackageFreezer freezer; + public final PackageSetting originalPs; + public final PackageSetting disabledPs; + public final PackageSetting[] childPackageSettings; private PrepareResult(int installReason, String volumeUuid, String installerPackageName, UserHandle user, boolean replace, int scanFlags, int parseFlags, PackageParser.Package existingPackage, PackageParser.Package packageToScan, boolean clearCodeCache, boolean system, - String renamedPackage, - PackageFreezer freezer) { + String renamedPackage, PackageFreezer freezer, PackageSetting originalPs, + PackageSetting disabledPs, PackageSetting[] childPackageSettings) { this.installReason = installReason; this.volumeUuid = volumeUuid; this.installerPackageName = installerPackageName; @@ -15764,6 +15787,9 @@ public class PackageManagerService extends IPackageManager.Stub this.system = system; this.renamedPackage = renamedPackage; this.freezer = freezer; + this.originalPs = originalPs; + this.disabledPs = disabledPs; + this.childPackageSettings = childPackageSettings; } } @@ -16267,7 +16293,9 @@ public class PackageManagerService extends IPackageManager.Stub String targetVolumeUuid = volumeUuid; int targetScanFlags = scanFlags; int targetParseFlags = parseFlags; - + final PackageSetting ps; + final PackageSetting disabledPs; + final PackageSetting[] childPackages; if (replace) { targetVolumeUuid = null; if (pkg.applicationInfo.isStaticSharedLibrary()) { @@ -16287,7 +16315,6 @@ public class PackageManagerService extends IPackageManager.Stub final boolean isInstantApp = (scanFlags & SCAN_AS_INSTANT_APP) != 0; final PackageParser.Package oldPackage; - final PackageSetting ps; final String pkgName11 = pkg.packageName; final int[] allUsers; final int[] installedUsers; @@ -16314,6 +16341,7 @@ public class PackageManagerService extends IPackageManager.Stub } ps = mSettings.mPackages.get(pkgName11); + disabledPs = mSettings.getDisabledSystemPkgLPr(ps); // verify signatures are valid final KeySetManagerService ksms = mSettings.mKeySetManagerService; @@ -16410,49 +16438,52 @@ public class PackageManagerService extends IPackageManager.Stub res.removedInfo.installReasons.put(userId, ps.getInstallReason(userId)); } - final int childCount = (oldPackage.childPackages != null) - ? oldPackage.childPackages.size() : 0; - for (int i = 0; i < childCount; i++) { - boolean childPackageUpdated = false; - PackageParser.Package childPkg = oldPackage.childPackages.get(i); - final PackageSetting childPs = mSettings.getPackageLPr(childPkg.packageName); - if (res.addedChildPackages != null) { - PackageInstalledInfo childRes = res.addedChildPackages.get( - childPkg.packageName); - if (childRes != null) { - childRes.removedInfo.uid = childPkg.applicationInfo.uid; - childRes.removedInfo.removedPackage = childPkg.packageName; - if (childPs != null) { - childRes.removedInfo.installerPackageName = - childPs.installerPackageName; + childPackages = mSettings.getChildSettingsLPr(ps); + if (childPackages != null) { + for (PackageSetting childPs : childPackages) { + boolean childPackageUpdated = false; + PackageParser.Package childPkg = (childPs == null) ? null : childPs.pkg; + if (res.addedChildPackages != null) { + PackageInstalledInfo childRes = res.addedChildPackages.get( + childPkg.packageName); + if (childRes != null) { + childRes.removedInfo.uid = childPkg.applicationInfo.uid; + childRes.removedInfo.removedPackage = childPkg.packageName; + if (childPs != null) { + childRes.removedInfo.installerPackageName = + childPs.installerPackageName; + } + childRes.removedInfo.isUpdate = true; + childRes.removedInfo.installReasons = + res.removedInfo.installReasons; + childPackageUpdated = true; } - childRes.removedInfo.isUpdate = true; - childRes.removedInfo.installReasons = res.removedInfo.installReasons; - childPackageUpdated = true; } - } - if (!childPackageUpdated) { - PackageRemovedInfo childRemovedRes = new PackageRemovedInfo(this); - childRemovedRes.removedPackage = childPkg.packageName; - if (childPs != null) { - childRemovedRes.installerPackageName = childPs.installerPackageName; - } - childRemovedRes.isUpdate = false; - childRemovedRes.dataRemoved = true; - synchronized (mPackages) { + if (!childPackageUpdated) { + PackageRemovedInfo childRemovedRes = new PackageRemovedInfo(this); + childRemovedRes.removedPackage = childPkg.packageName; if (childPs != null) { - childRemovedRes.origUsers = childPs.queryInstalledUsers(allUsers, - true); + childRemovedRes.installerPackageName = childPs.installerPackageName; } + childRemovedRes.isUpdate = false; + childRemovedRes.dataRemoved = true; + synchronized (mPackages) { + if (childPs != null) { + childRemovedRes.origUsers = childPs.queryInstalledUsers( + allUsers, + true); + } + } + if (res.removedInfo.removedChildPackages == null) { + res.removedInfo.removedChildPackages = new ArrayMap<>(); + } + res.removedInfo.removedChildPackages.put(childPkg.packageName, + childRemovedRes); } - if (res.removedInfo.removedChildPackages == null) { - res.removedInfo.removedChildPackages = new ArrayMap<>(); - } - res.removedInfo.removedChildPackages.put(childPkg.packageName, - childRemovedRes); } } + sysPkg = (isSystemApp(oldPackage)); if (sysPkg) { // Set the system/privileged/oem/vendor/product flags as needed @@ -16495,6 +16526,9 @@ public class PackageManagerService extends IPackageManager.Stub } } else { // new package install + ps = null; + childPackages = null; + disabledPs = null; replace = false; existingPackage = null; // Remember this for later, in case we need to rollback this install @@ -16525,9 +16559,11 @@ public class PackageManagerService extends IPackageManager.Stub } // we're passing the freezer back to be closed in a later phase of install shouldCloseFreezerBeforeReturn = false; + return new PrepareResult(args.installReason, targetVolumeUuid, installerPackageName, args.user, replace, targetScanFlags, targetParseFlags, existingPackage, pkg, - replace /* clearCodeCache */, sysPkg, renamedPackage, freezer); + replace /* clearCodeCache */, sysPkg, renamedPackage, freezer, + ps, disabledPs, childPackages); } finally { if (shouldCloseFreezerBeforeReturn) { freezer.close(); @@ -17477,34 +17513,20 @@ public class PackageManagerService extends IPackageManager.Stub /* * Tries to delete system package. */ - private boolean deleteSystemPackageLIF(PackageParser.Package deletedPkg, - PackageSetting deletedPs, int[] allUserHandles, int flags, PackageRemovedInfo outInfo, - boolean writeSettings) { - if (deletedPs.parentPackageName != null) { - Slog.w(TAG, "Attempt to delete child system package " + deletedPkg.packageName); - return false; - } - + private void deleteSystemPackageLIF(DeletePackageAction action, + PackageParser.Package deletedPkg, PackageSetting deletedPs, int[] allUserHandles, + int flags, PackageRemovedInfo outInfo, boolean writeSettings) + throws SystemDeleteException { final boolean applyUserRestrictions = (allUserHandles != null) && (outInfo.origUsers != null); - final PackageSetting disabledPs; // Confirm if the system package has been updated // An updated system app can be deleted. This will also have to restore // the system pkg from system partition // reader - synchronized (mPackages) { - disabledPs = mSettings.getDisabledSystemPkgLPr(deletedPs.name); - } - + final PackageSetting disabledPs = action.disabledPs; if (DEBUG_REMOVE) Slog.d(TAG, "deleteSystemPackageLI: newPs=" + deletedPkg.packageName + " disabledPs=" + disabledPs); - - if (disabledPs == null) { - Slog.w(TAG, "Attempt to delete unknown system package "+ deletedPkg.packageName); - return false; - } else if (DEBUG_REMOVE) { - Slog.d(TAG, "Deleting system pkg from data partition"); - } + Slog.d(TAG, "Deleting system pkg from data partition"); if (DEBUG_REMOVE) { if (applyUserRestrictions) { @@ -17542,11 +17564,8 @@ public class PackageManagerService extends IPackageManager.Stub flags |= PackageManager.DELETE_KEEP_DATA; } - boolean ret = deleteInstalledPackageLIF(deletedPs, true, flags, allUserHandles, + deleteInstalledPackageLIF(deletedPs, true, flags, allUserHandles, outInfo, writeSettings, disabledPs.pkg); - if (!ret) { - return false; - } // writer synchronized (mPackages) { @@ -17563,25 +17582,25 @@ public class PackageManagerService extends IPackageManager.Stub // Install the system package if (DEBUG_REMOVE) Slog.d(TAG, "Re-installing system package: " + disabledPs); try { - installPackageFromSystemLIF(disabledPs.codePathString, false, allUserHandles, + installPackageFromSystemLIF(disabledPs.codePathString, allUserHandles, outInfo.origUsers, deletedPs.getPermissionsState(), writeSettings); } catch (PackageManagerException e) { Slog.w(TAG, "Failed to restore system package:" + deletedPkg.packageName + ": " + e.getMessage()); - return false; + // TODO(patb): can we avoid this; throw would come from scan... + throw new SystemDeleteException(e); } finally { if (disabledPs.pkg.isStub) { mSettings.disableSystemPackageLPw(disabledPs.name, true /*replaced*/); } } - return true; } /** * Installs a package that's already on the system partition. */ private PackageParser.Package installPackageFromSystemLIF(@NonNull String codePathString, - boolean isPrivileged, @Nullable int[] allUserHandles, @Nullable int[] origUserHandles, + @Nullable int[] allUserHandles, @Nullable int[] origUserHandles, @Nullable PermissionsState origPermissionState, boolean writeSettings) throws PackageManagerException { @ParseFlags int parseFlags = @@ -17589,7 +17608,7 @@ public class PackageManagerService extends IPackageManager.Stub | PackageParser.PARSE_MUST_BE_APK | PackageParser.PARSE_IS_SYSTEM_DIR; @ScanFlags int scanFlags = SCAN_AS_SYSTEM; - if (isPrivileged || locationIsPrivileged(codePathString)) { + if (locationIsPrivileged(codePathString)) { scanFlags |= SCAN_AS_PRIVILEGED; } if (locationIsOem(codePathString)) { @@ -17665,7 +17684,7 @@ public class PackageManagerService extends IPackageManager.Stub return pkg; } - private boolean deleteInstalledPackageLIF(PackageSetting ps, + private void deleteInstalledPackageLIF(PackageSetting ps, boolean deleteCodeAndResources, int flags, int[] allUserHandles, PackageRemovedInfo outInfo, boolean writeSettings, PackageParser.Package replacingPackage) { @@ -17680,9 +17699,6 @@ public class PackageManagerService extends IPackageManager.Stub for (int i = 0; i < childCount; i++) { String childPackageName = ps.childPackageNames.get(i); PackageSetting childPs = mSettings.mPackages.get(childPackageName); - if (childPs == null) { - return false; - } PackageRemovedInfo childInfo = outInfo.removedChildPackages.get( childPackageName); if (childInfo != null) { @@ -17723,8 +17739,6 @@ public class PackageManagerService extends IPackageManager.Stub if (DEBUG_SD_INSTALL) Slog.i(TAG, "args=" + outInfo.args); } } - - return true; } @Override @@ -17780,26 +17794,59 @@ public class PackageManagerService extends IPackageManager.Stub private static class DeletePackageAction { public final PackageSetting deletingPs; + public final PackageSetting disabledPs; + public final PackageRemovedInfo outInfo; - private DeletePackageAction(PackageSetting deletingPs) { + private DeletePackageAction(PackageSetting deletingPs, PackageSetting disabledPs, + PackageRemovedInfo outInfo) { this.deletingPs = deletingPs; + this.disabledPs = disabledPs; + this.outInfo = outInfo; } } /** - * @return a {@link DeletePackageAction} if the provided package may be deleted, {@code null} - * otherwise. + * @return a {@link DeletePackageAction} if the provided package and related state may be + * deleted, {@code null} otherwise. */ @Nullable - private DeletePackageAction mayDeletePackageLIF(@NonNull String packageName) { - synchronized (mPackages) { - final PackageSetting ps; - ps = mSettings.mPackages.get(packageName); - if (ps == null) { + @GuardedBy("mPackages") + private static DeletePackageAction mayDeletePackageLocked( + PackageRemovedInfo outInfo, PackageSetting ps, @Nullable PackageSetting disabledPs, + @Nullable PackageSetting[] children) { + if (ps == null) { + return null; + } + if (isSystemApp(ps)) { + if (ps.parentPackageName == null) { + Slog.w(TAG, "Attempt to delete child system package " + ps.pkg.packageName); return null; } - return new DeletePackageAction(ps); + + // Confirm if the system package has been updated + // An updated system app can be deleted. This will also have to restore + // the system pkg from system partition + // reader + if (disabledPs == null) { + Slog.w(TAG, + "Attempt to delete unknown system package " + ps.pkg.packageName); + return null; + } + } + final int parentReferenceCount = + (ps.childPackageNames != null) ? ps.childPackageNames.size() : 0; + final int childCount = children != null ? children.length : 0; + if (childCount != parentReferenceCount) { + return null; + } + if (childCount != 0 && outInfo != null && outInfo.removedChildPackages != null) { + for (PackageSetting child : children) { + if (child == null || !ps.childPackageNames.contains(child.name)) { + return null; + } + } } + return new DeletePackageAction(ps, disabledPs, outInfo); } /* @@ -17809,22 +17856,43 @@ public class PackageManagerService extends IPackageManager.Stub boolean deleteCodeAndResources, int[] allUserHandles, int flags, PackageRemovedInfo outInfo, boolean writeSettings, PackageParser.Package replacingPackage) { - final DeletePackageAction action = mayDeletePackageLIF(packageName); + final DeletePackageAction action; + synchronized (mPackages) { + final PackageSetting ps = mSettings.mPackages.get(packageName); + final PackageSetting disabledPs = mSettings.getDisabledSystemPkgLPr(ps); + PackageSetting[] children = mSettings.getChildSettingsLPr(ps); + action = mayDeletePackageLocked(outInfo, ps, disabledPs, children); + } if (null == action) { return false; } if (DEBUG_REMOVE) Slog.d(TAG, "deletePackageLI: " + packageName + " user " + user); - return executeDeletePackageLIF(action, packageName, user, deleteCodeAndResources, - allUserHandles, flags, outInfo, writeSettings, replacingPackage); + try { + executeDeletePackageLIF(action, packageName, user, deleteCodeAndResources, + allUserHandles, flags, writeSettings, replacingPackage); + } catch (SystemDeleteException e) { + return false; + } + return true; + } + + private static class SystemDeleteException extends Exception { + public final PackageManagerException reason; + + private SystemDeleteException(PackageManagerException reason) { + this.reason = reason; + } } - private boolean executeDeletePackageLIF(DeletePackageAction action, + /** Deletes a package. Only throws when install of a disabled package fails. */ + private void executeDeletePackageLIF(DeletePackageAction action, String packageName, UserHandle user, boolean deleteCodeAndResources, - int[] allUserHandles, int flags, PackageRemovedInfo outInfo, - boolean writeSettings, PackageParser.Package replacingPackage) { + int[] allUserHandles, int flags, boolean writeSettings, + PackageParser.Package replacingPackage) throws SystemDeleteException { final PackageSetting ps = action.deletingPs; + final PackageRemovedInfo outInfo = action.outInfo; final boolean systemApp = isSystemApp(ps); synchronized (mPackages) { @@ -17840,7 +17908,7 @@ public class PackageManagerService extends IPackageManager.Stub clearPackageStateForUserLIF(ps, removedUserId, outInfo); markPackageUninstalledForUserLPw(ps, user); scheduleWritePackageRestrictionsLocked(user); - return true; + return; } } @@ -17869,7 +17937,7 @@ public class PackageManagerService extends IPackageManager.Stub if (DEBUG_REMOVE) Slog.d(TAG, "Still installed by other users"); clearPackageStateForUserLIF(ps, user.getIdentifier(), outInfo); scheduleWritePackageRestrictionsLocked(user); - return true; + return; } else { // We need to set it back to 'installed' so the uninstall // broadcasts will be sent correctly. @@ -17885,7 +17953,7 @@ public class PackageManagerService extends IPackageManager.Stub if (DEBUG_REMOVE) Slog.d(TAG, "Deleting system app"); clearPackageStateForUserLIF(ps, user.getIdentifier(), outInfo); scheduleWritePackageRestrictionsLocked(user); - return true; + return; } } @@ -17910,15 +17978,15 @@ public class PackageManagerService extends IPackageManager.Stub } // TODO(b/109941548): break reasons for ret = false out into mayDelete method - final boolean ret; if (systemApp) { if (DEBUG_REMOVE) Slog.d(TAG, "Removing system package: " + ps.name); // When an updated system application is deleted we delete the existing resources // as well and fall back to existing code in system partition - ret = deleteSystemPackageLIF(ps.pkg, ps, allUserHandles, flags, outInfo, writeSettings); + deleteSystemPackageLIF( + action, ps.pkg, ps, allUserHandles, flags, outInfo, writeSettings); } else { if (DEBUG_REMOVE) Slog.d(TAG, "Removing non-system package: " + ps.name); - ret = deleteInstalledPackageLIF(ps, deleteCodeAndResources, flags, allUserHandles, + deleteInstalledPackageLIF(ps, deleteCodeAndResources, flags, allUserHandles, outInfo, writeSettings, replacingPackage); } @@ -17967,8 +18035,6 @@ public class PackageManagerService extends IPackageManager.Stub } } } - - return ret; } @GuardedBy("mPackages") @@ -19694,7 +19760,7 @@ public class PackageManagerService extends IPackageManager.Stub enableSystemPackageLPw(deletedPkg); } installPackageFromSystemLIF(deletedPkg.codePath, - false /*isPrivileged*/, null /*allUserHandles*/, + /*isPrivileged*/ null /*allUserHandles*/, null /*origUserHandles*/, null /*origPermissionsState*/, true /*writeSettings*/); } catch (PackageManagerException pme) { diff --git a/services/core/java/com/android/server/pm/Settings.java b/services/core/java/com/android/server/pm/Settings.java index 9e5a4c6a9888..c524dba01ba6 100644 --- a/services/core/java/com/android/server/pm/Settings.java +++ b/services/core/java/com/android/server/pm/Settings.java @@ -4255,11 +4255,48 @@ public final class Settings { return false; } + /** + * Returns the disabled {@link PackageSetting} for the provided package name if one exists, + * {@code null} otherwise. + */ + @Nullable public PackageSetting getDisabledSystemPkgLPr(String name) { PackageSetting ps = mDisabledSysPackages.get(name); return ps; } + /** + * Returns the disabled {@link PackageSetting} for the provided enabled {@link PackageSetting} + * if one exists, {@code null} otherwise. + */ + @Nullable + public PackageSetting getDisabledSystemPkgLPr(PackageSetting enabledPackageSetting) { + if (enabledPackageSetting == null) { + return null; + } + return getDisabledSystemPkgLPr(enabledPackageSetting.name); + } + + /** + * Fetches an array of the child {@link PackageSetting}s for all child package names referenced + * by the provided parent {@link PackageSetting} or {@code null} if no children are referenced. + * + * Note: Any child packages not found will be null in the returned array. + */ + @Nullable + public PackageSetting[] getChildSettingsLPr(PackageSetting parentPackageSetting) { + if (parentPackageSetting == null || !parentPackageSetting.hasChildPackages()) { + return null; + } + final int childCount = parentPackageSetting.childPackageNames.size(); + PackageSetting[] children = + new PackageSetting[childCount]; + for (int i = 0; i < childCount; i++) { + children[i] = mPackages.get(parentPackageSetting.childPackageNames.get(i)); + } + return children; + } + boolean isEnabledAndMatchLPr(ComponentInfo componentInfo, int flags, int userId) { final PackageSetting ps = mPackages.get(componentInfo.packageName); if (ps == null) return false; diff --git a/services/core/java/com/android/server/wm/ActivityDisplay.java b/services/core/java/com/android/server/wm/ActivityDisplay.java index ed366453028e..33584d4a1710 100644 --- a/services/core/java/com/android/server/wm/ActivityDisplay.java +++ b/services/core/java/com/android/server/wm/ActivityDisplay.java @@ -39,8 +39,6 @@ import static com.android.server.am.ActivityDisplayProto.ID; import static com.android.server.am.ActivityDisplayProto.RESUMED_ACTIVITY; import static com.android.server.am.ActivityDisplayProto.STACKS; import static com.android.server.wm.ActivityStack.ActivityState.RESUMED; -import static com.android.server.wm.ActivityStackSupervisor.FindTaskResult; -import static com.android.server.wm.ActivityStackSupervisor.TAG_STATES; import static com.android.server.wm.ActivityStackSupervisor.TAG_TASKS; import static com.android.server.wm.ActivityTaskManagerDebugConfig.DEBUG_STACK; import static com.android.server.wm.ActivityTaskManagerDebugConfig.DEBUG_STATES; @@ -48,6 +46,8 @@ import static com.android.server.wm.ActivityTaskManagerDebugConfig.DEBUG_TASKS; import static com.android.server.wm.ActivityTaskManagerDebugConfig.POSTFIX_STACK; import static com.android.server.wm.ActivityTaskManagerDebugConfig.TAG_ATM; import static com.android.server.wm.ActivityTaskManagerDebugConfig.TAG_WITH_CLASS_NAME; +import static com.android.server.wm.RootActivityContainer.FindTaskResult; +import static com.android.server.wm.RootActivityContainer.TAG_STATES; import android.annotation.Nullable; import android.app.ActivityOptions; @@ -84,7 +84,8 @@ class ActivityDisplay extends ConfigurationContainer<ActivityStack> */ private static int sNextFreeStackId = 0; - private ActivityStackSupervisor mSupervisor; + private ActivityTaskManagerService mService; + private RootActivityContainer mRootActivityContainer; /** Actual Display this object tracks. */ int mDisplayId; Display mDisplay; @@ -141,8 +142,9 @@ class ActivityDisplay extends ConfigurationContainer<ActivityStack> private final FindTaskResult mTmpFindTaskResult = new FindTaskResult(); - ActivityDisplay(ActivityStackSupervisor supervisor, Display display) { - mSupervisor = supervisor; + ActivityDisplay(RootActivityContainer root, Display display) { + mRootActivityContainer = root; + mService = root.mService; mDisplayId = display.getDisplayId(); mDisplay = display; mWindowContainerController = createWindowContainerController(); @@ -168,7 +170,7 @@ class ActivityDisplay extends ConfigurationContainer<ActivityStack> if (displayId != DEFAULT_DISPLAY) { final int displayState = mDisplay.getState(); if (displayState == Display.STATE_OFF && mOffToken == null) { - mOffToken = mSupervisor.mService.acquireSleepToken("Display-off", displayId); + mOffToken = mService.acquireSleepToken("Display-off", displayId); } else if (displayState == Display.STATE_ON && mOffToken != null) { mOffToken.release(); mOffToken = null; @@ -189,7 +191,7 @@ class ActivityDisplay extends ConfigurationContainer<ActivityStack> + " to displayId=" + mDisplayId + " position=" + position); addStackReferenceIfNeeded(stack); positionChildAt(stack, position); - mSupervisor.mService.updateSleepIfNeededLocked(); + mService.updateSleepIfNeededLocked(); } void removeChild(ActivityStack stack) { @@ -201,7 +203,7 @@ class ActivityDisplay extends ConfigurationContainer<ActivityStack> } removeStackReferenceIfNeeded(stack); releaseSelfIfNeeded(); - mSupervisor.mService.updateSleepIfNeededLocked(); + mService.updateSleepIfNeededLocked(); onStackOrderChanged(); } @@ -252,7 +254,7 @@ class ActivityDisplay extends ConfigurationContainer<ActivityStack> final ActivityStack currentFocusedStack = getFocusedStack(); if (currentFocusedStack != prevFocusedStack) { mLastFocusedStack = prevFocusedStack; - EventLogTags.writeAmFocusedStack(mSupervisor.mCurrentUser, mDisplayId, + EventLogTags.writeAmFocusedStack(mRootActivityContainer.mCurrentUser, mDisplayId, currentFocusedStack == null ? -1 : currentFocusedStack.getStackId(), mLastFocusedStack == null ? -1 : mLastFocusedStack.getStackId(), updateLastFocusedStackReason); @@ -409,10 +411,10 @@ class ActivityDisplay extends ConfigurationContainer<ActivityStack> } } - final ActivityTaskManagerService service = mSupervisor.mService; - if (!isWindowingModeSupported(windowingMode, service.mSupportsMultiWindow, - service.mSupportsSplitScreenMultiWindow, service.mSupportsFreeformWindowManagement, - service.mSupportsPictureInPicture, activityType)) { + if (!isWindowingModeSupported(windowingMode, mService.mSupportsMultiWindow, + mService.mSupportsSplitScreenMultiWindow, + mService.mSupportsFreeformWindowManagement, mService.mSupportsPictureInPicture, + activityType)) { throw new IllegalArgumentException("Can't create stack for unsupported windowingMode=" + windowingMode); } @@ -425,10 +427,12 @@ class ActivityDisplay extends ConfigurationContainer<ActivityStack> <T extends ActivityStack> T createStackUnchecked(int windowingMode, int activityType, int stackId, boolean onTop) { if (windowingMode == WINDOWING_MODE_PINNED) { - return (T) new PinnedActivityStack(this, stackId, mSupervisor, onTop); + return (T) new PinnedActivityStack(this, stackId, + mRootActivityContainer.mStackSupervisor, onTop); } - return (T) new ActivityStack( - this, stackId, mSupervisor, windowingMode, activityType, onTop); + return (T) new ActivityStack(this, stackId, + mRootActivityContainer.mStackSupervisor, windowingMode, activityType, + onTop); } /** @@ -543,7 +547,7 @@ class ActivityDisplay extends ConfigurationContainer<ActivityStack> for (int stackNdx = mStacks.size() - 1; stackNdx >= 0; --stackNdx) { final ActivityStack stack = mStacks.get(stackNdx); // TODO(b/111541062): Check if resumed activity on this display instead - if (!mSupervisor.isTopDisplayFocusedStack(stack) + if (!mRootActivityContainer.isTopDisplayFocusedStack(stack) && stack.getResumedActivity() != null) { if (DEBUG_STATES) Slog.d(TAG_STATES, "pauseBackStacks: stack=" + stack + " mResumedActivity=" + stack.getResumedActivity()); @@ -608,7 +612,7 @@ class ActivityDisplay extends ConfigurationContainer<ActivityStack> if (stack.getWindowingMode() != windowingMode) { continue; } - mSupervisor.removeStack(stack); + mRootActivityContainer.mStackSupervisor.removeStack(stack); } } } @@ -623,7 +627,7 @@ class ActivityDisplay extends ConfigurationContainer<ActivityStack> for (int i = mStacks.size() - 1; i >= 0; --i) { final ActivityStack stack = mStacks.get(i); if (stack.getActivityType() == activityType) { - mSupervisor.removeStack(stack); + mRootActivityContainer.mStackSupervisor.removeStack(stack); } } } @@ -685,7 +689,7 @@ class ActivityDisplay extends ConfigurationContainer<ActivityStack> } private void onSplitScreenModeDismissed() { - mSupervisor.mWindowManager.deferSurfaceLayout(); + mRootActivityContainer.mWindowManager.deferSurfaceLayout(); try { // Adjust the windowing mode of any stack in secondary split-screen to fullscreen. for (int i = mStacks.size() - 1; i >= 0; --i) { @@ -709,12 +713,12 @@ class ActivityDisplay extends ConfigurationContainer<ActivityStack> mHomeStack.moveToFront("onSplitScreenModeDismissed"); topFullscreenStack.moveToFront("onSplitScreenModeDismissed"); } - mSupervisor.mWindowManager.continueSurfaceLayout(); + mRootActivityContainer.mWindowManager.continueSurfaceLayout(); } } private void onSplitScreenModeActivated() { - mSupervisor.mWindowManager.deferSurfaceLayout(); + mRootActivityContainer.mWindowManager.deferSurfaceLayout(); try { // Adjust the windowing mode of any affected by split-screen to split-screen secondary. for (int i = mStacks.size() - 1; i >= 0; --i) { @@ -729,7 +733,7 @@ class ActivityDisplay extends ConfigurationContainer<ActivityStack> false /* creating */); } } finally { - mSupervisor.mWindowManager.continueSurfaceLayout(); + mRootActivityContainer.mWindowManager.continueSurfaceLayout(); } } @@ -824,11 +828,10 @@ class ActivityDisplay extends ConfigurationContainer<ActivityStack> int validateWindowingMode(int windowingMode, @Nullable ActivityRecord r, @Nullable TaskRecord task, int activityType) { // Make sure the windowing mode we are trying to use makes sense for what is supported. - final ActivityTaskManagerService service = mSupervisor.mService; - boolean supportsMultiWindow = service.mSupportsMultiWindow; - boolean supportsSplitScreen = service.mSupportsSplitScreenMultiWindow; - boolean supportsFreeform = service.mSupportsFreeformWindowManagement; - boolean supportsPip = service.mSupportsPictureInPicture; + boolean supportsMultiWindow = mService.mSupportsMultiWindow; + boolean supportsSplitScreen = mService.mSupportsSplitScreenMultiWindow; + boolean supportsFreeform = mService.mSupportsFreeformWindowManagement; + boolean supportsPip = mService.mSupportsPictureInPicture; if (supportsMultiWindow) { if (task != null) { supportsMultiWindow = task.isResizeable(); @@ -932,7 +935,7 @@ class ActivityDisplay extends ConfigurationContainer<ActivityStack> // 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 - && mSupervisor.getKeyguardController().isKeyguardLocked() + && mRootActivityContainer.mStackSupervisor.getKeyguardController().isKeyguardLocked() && !topRunning.canShowWhenLocked()) { return null; } @@ -1010,7 +1013,7 @@ class ActivityDisplay extends ConfigurationContainer<ActivityStack> @Override protected ConfigurationContainer getParent() { - return mSupervisor; + return mRootActivityContainer; } boolean isPrivate() { @@ -1043,8 +1046,8 @@ class ActivityDisplay extends ConfigurationContainer<ActivityStack> // released (no more ActivityStack). But, we cannot release it at that moment or the // related WindowContainer and WindowContainerController will also be removed. So, we // set display as removed after reparenting stack finished. - final ActivityDisplay toDisplay = mSupervisor.getDefaultDisplay(); - mSupervisor.beginDeferResume(); + final ActivityDisplay toDisplay = mRootActivityContainer.getDefaultDisplay(); + mRootActivityContainer.mStackSupervisor.beginDeferResume(); try { int numStacks = mStacks.size(); // Keep the order from bottom to top. @@ -1070,7 +1073,7 @@ class ActivityDisplay extends ConfigurationContainer<ActivityStack> numStacks = mStacks.size(); } } finally { - mSupervisor.endDeferResume(); + mRootActivityContainer.mStackSupervisor.endDeferResume(); } mRemoved = true; @@ -1082,9 +1085,9 @@ class ActivityDisplay extends ConfigurationContainer<ActivityStack> releaseSelfIfNeeded(); if (!mAllSleepTokens.isEmpty()) { - mSupervisor.mSleepTokens.removeAll(mAllSleepTokens); + mRootActivityContainer.mSleepTokens.removeAll(mAllSleepTokens); mAllSleepTokens.clear(); - mSupervisor.mService.updateSleepIfNeededLocked(); + mService.updateSleepIfNeededLocked(); } } @@ -1092,8 +1095,9 @@ class ActivityDisplay extends ConfigurationContainer<ActivityStack> if (mStacks.isEmpty() && mRemoved) { mWindowContainerController.removeContainer(); mWindowContainerController = null; - mSupervisor.removeChild(this); - mSupervisor.getKeyguardController().onDisplayRemoved(mDisplayId); + mRootActivityContainer.removeChild(this); + mRootActivityContainer.mStackSupervisor + .getKeyguardController().onDisplayRemoved(mDisplayId); } } @@ -1122,7 +1126,7 @@ class ActivityDisplay extends ConfigurationContainer<ActivityStack> boolean shouldSleep() { return (mStacks.isEmpty() || !mAllSleepTokens.isEmpty()) - && (mSupervisor.mService.mRunningVoice == null); + && (mService.mRunningVoice == null); } void setFocusedApp(ActivityRecord r, boolean moveFocusNow) { @@ -1213,7 +1217,7 @@ class ActivityDisplay extends ConfigurationContainer<ActivityStack> @Nullable ActivityRecord getHomeActivity() { - return getHomeActivityForUser(mSupervisor.mCurrentUser); + return getHomeActivityForUser(mRootActivityContainer.mCurrentUser); } @Nullable diff --git a/services/core/java/com/android/server/wm/ActivityMetricsLogger.java b/services/core/java/com/android/server/wm/ActivityMetricsLogger.java index 1c08d039207b..0c0c818e0baa 100644 --- a/services/core/java/com/android/server/wm/ActivityMetricsLogger.java +++ b/services/core/java/com/android/server/wm/ActivityMetricsLogger.java @@ -277,7 +277,8 @@ class ActivityMetricsLogger { mLastLogTimeSecs = now; mWindowState = WINDOW_STATE_INVALID; - ActivityStack stack = mSupervisor.getTopDisplayFocusedStack(); + ActivityStack stack = + mSupervisor.mRootActivityContainer.getTopDisplayFocusedStack(); if (stack == null) { return; } @@ -289,7 +290,7 @@ class ActivityMetricsLogger { @WindowingMode int windowingMode = stack.getWindowingMode(); if (windowingMode == WINDOWING_MODE_PINNED) { - stack = mSupervisor.findStackBehind(stack); + stack = mSupervisor.mRootActivityContainer.findStackBehind(stack); windowingMode = stack.getWindowingMode(); } switch (windowingMode) { diff --git a/services/core/java/com/android/server/wm/ActivityRecord.java b/services/core/java/com/android/server/wm/ActivityRecord.java index 5e92b9e4d46a..eec22d5d1ee9 100644 --- a/services/core/java/com/android/server/wm/ActivityRecord.java +++ b/services/core/java/com/android/server/wm/ActivityRecord.java @@ -18,7 +18,17 @@ package com.android.server.wm; import static android.app.ActivityManager.LOCK_TASK_MODE_NONE; import static android.app.ActivityManager.TaskDescription.ATTR_TASKDESCRIPTION_PREFIX; +import static android.app.ActivityOptions.ANIM_CLIP_REVEAL; +import static android.app.ActivityOptions.ANIM_CUSTOM; +import static android.app.ActivityOptions.ANIM_NONE; +import static android.app.ActivityOptions.ANIM_OPEN_CROSS_PROFILE_APPS; +import static android.app.ActivityOptions.ANIM_REMOTE_ANIMATION; +import static android.app.ActivityOptions.ANIM_SCALE_UP; import static android.app.ActivityOptions.ANIM_SCENE_TRANSITION; +import static android.app.ActivityOptions.ANIM_THUMBNAIL_ASPECT_SCALE_DOWN; +import static android.app.ActivityOptions.ANIM_THUMBNAIL_ASPECT_SCALE_UP; +import static android.app.ActivityOptions.ANIM_THUMBNAIL_SCALE_DOWN; +import static android.app.ActivityOptions.ANIM_THUMBNAIL_SCALE_UP; import static android.app.ActivityTaskManager.INVALID_STACK_ID; import static android.app.ActivityTaskManager.INVALID_TASK_ID; import static android.app.AppOpsManager.MODE_ALLOWED; @@ -63,6 +73,7 @@ import static android.content.pm.ActivityInfo.RESIZE_MODE_FORCE_RESIZEABLE; import static android.content.pm.ActivityInfo.RESIZE_MODE_RESIZEABLE; import static android.content.pm.ActivityInfo.RESIZE_MODE_RESIZEABLE_VIA_SDK_VERSION; import static android.content.pm.ActivityInfo.RESIZE_MODE_UNRESIZEABLE; +import static android.content.pm.ActivityInfo.SCREEN_ORIENTATION_UNSPECIFIED; import static android.content.pm.ActivityInfo.isFixedOrientationLandscape; import static android.content.pm.ActivityInfo.isFixedOrientationPortrait; import static android.content.res.Configuration.EMPTY; @@ -111,12 +122,18 @@ import static com.android.server.wm.ActivityTaskManagerDebugConfig.TAG_ATM; import static com.android.server.wm.ActivityTaskManagerDebugConfig.TAG_WITH_CLASS_NAME; import static com.android.server.wm.ActivityTaskManagerService.RELAUNCH_REASON_FREE_RESIZE; import static com.android.server.wm.ActivityTaskManagerService.RELAUNCH_REASON_NONE; -import static com.android.server.wm.ActivityTaskManagerService.RELAUNCH_REASON_WINDOWING_MODE_RESIZE; +import static com.android.server.wm.ActivityTaskManagerService + .RELAUNCH_REASON_WINDOWING_MODE_RESIZE; import static com.android.server.wm.IdentifierProto.HASH_CODE; import static com.android.server.wm.IdentifierProto.TITLE; import static com.android.server.wm.IdentifierProto.USER_ID; import static com.android.server.wm.TaskPersister.DEBUG; import static com.android.server.wm.TaskPersister.IMAGE_EXTENSION; +import static com.android.server.wm.WindowManagerDebugConfig.DEBUG_ADD_REMOVE; +import static com.android.server.wm.WindowManagerDebugConfig.DEBUG_ORIENTATION; +import static com.android.server.wm.WindowManagerDebugConfig.DEBUG_STARTING_WINDOW; +import static com.android.server.wm.WindowManagerDebugConfig.DEBUG_TOKEN_MOVEMENT; +import static com.android.server.wm.WindowManagerDebugConfig.TAG_WM; import static org.xmlpull.v1.XmlPullParser.END_DOCUMENT; import static org.xmlpull.v1.XmlPullParser.END_TAG; @@ -147,6 +164,7 @@ import android.content.pm.ApplicationInfo; import android.content.res.CompatibilityInfo; import android.content.res.Configuration; import android.graphics.Bitmap; +import android.graphics.GraphicBuffer; import android.graphics.Rect; import android.os.Binder; import android.os.Build; @@ -167,11 +185,14 @@ import android.util.MergedConfiguration; import android.util.Slog; import android.util.TimeUtils; import android.util.proto.ProtoOutputStream; +import android.view.AppTransitionAnimationSpec; +import android.view.IAppTransitionAnimationSpecsFuture; import android.view.IApplicationToken; import android.view.RemoteAnimationDefinition; import android.view.WindowManager.LayoutParams; import com.android.internal.R; +import com.android.internal.annotations.VisibleForTesting; import com.android.internal.app.ResolverActivity; import com.android.internal.content.ReferrerIntent; import com.android.internal.util.XmlUtils; @@ -200,7 +221,7 @@ import java.util.Objects; /** * An entry in the history stack, representing an activity. */ -final class ActivityRecord extends ConfigurationContainer implements AppWindowContainerListener { +final class ActivityRecord extends ConfigurationContainer { private static final String TAG = TAG_WITH_CLASS_NAME ? "ActivityRecord" : TAG_ATM; private static final String TAG_CONFIGURATION = TAG + POSTFIX_CONFIGURATION; private static final String TAG_SAVED_STATE = TAG + POSTFIX_SAVED_STATE; @@ -225,7 +246,9 @@ final class ActivityRecord extends ConfigurationContainer implements AppWindowCo final ActivityTaskManagerService service; // owner final IApplicationToken.Stub appToken; // window manager token - AppWindowContainerController mWindowContainerController; + // TODO: Remove after unification + AppWindowToken mAppWindowToken; + final ActivityInfo info; // all about me // TODO: This is duplicated state already contained in info.applicationInfo - remove ApplicationInfo appInfo; // information about activity's app @@ -322,6 +345,7 @@ final class ActivityRecord extends ConfigurationContainer implements AppWindowCo private boolean inHistory; // are we in the history stack? final ActivityStackSupervisor mStackSupervisor; + final RootActivityContainer mRootActivityContainer; static final int STARTING_WINDOW_NOT_SHOWN = 0; static final int STARTING_WINDOW_SHOWN = 1; @@ -769,10 +793,16 @@ final class ActivityRecord extends ConfigurationContainer implements AppWindowCo } /** - * See {@link AppWindowContainerController#setWillCloseOrEnterPip(boolean)} + * Notifies AWT that this app is waiting to pause in order to determine if it will enter PIP. + * This information helps AWT know that the app is in the process of pausing before it gets the + * signal on the WM side. */ void setWillCloseOrEnterPip(boolean willCloseOrEnterPip) { - getWindowContainerController().setWillCloseOrEnterPip(willCloseOrEnterPip); + if (mAppWindowToken == null) { + return; + } + + mAppWindowToken.setWillCloseOrEnterPip(willCloseOrEnterPip); } static class Token extends IApplicationToken.Stub { @@ -844,6 +874,7 @@ final class ActivityRecord extends ConfigurationContainer implements AppWindowCo boolean _rootVoiceInteraction, ActivityStackSupervisor supervisor, ActivityOptions options, ActivityRecord sourceRecord) { service = _service; + mRootActivityContainer = _service.mRootActivityContainer; appToken = new Token(this, _intent); info = aInfo; launchedFromPid = _launchedFromPid; @@ -991,13 +1022,9 @@ final class ActivityRecord extends ConfigurationContainer implements AppWindowCo return hasProcess() && app.hasThread(); } - AppWindowContainerController getWindowContainerController() { - return mWindowContainerController; - } - - void createWindowContainer() { - if (mWindowContainerController != null) { - throw new IllegalArgumentException("Window container=" + mWindowContainerController + void createAppWindowToken() { + if (mAppWindowToken != null) { + throw new IllegalArgumentException("App Window Token=" + mAppWindowToken + " already created for r=" + this); } @@ -1010,12 +1037,31 @@ final class ActivityRecord extends ConfigurationContainer implements AppWindowCo // Make sure override configuration is up-to-date before using to create window controller. updateOverrideConfiguration(); - mWindowContainerController = new AppWindowContainerController(taskController, appToken, - realActivity, this, Integer.MAX_VALUE /* add on top */, info.screenOrientation, - fullscreen, (info.flags & FLAG_SHOW_FOR_ALL_USERS) != 0, info.configChanges, - task.voiceSession != null, mLaunchTaskBehind, isAlwaysFocusable(), - appInfo.targetSdkVersion, mRotationAnimationHint, - ActivityTaskManagerService.getInputDispatchingTimeoutLocked(this) * 1000000L); + // TODO: remove after unification + mAppWindowToken = service.mWindowManager.mRoot.getAppWindowToken(appToken.asBinder()); + if (mAppWindowToken != null) { + // TODO: Should this throw an exception instead? + Slog.w(TAG, "Attempted to add existing app token: " + appToken); + } else { + final Task container = taskController.mContainer; + if (container == null) { + throw new IllegalArgumentException("AppWindowContainerController: invalid " + + " controller=" + taskController); + } + mAppWindowToken = createAppWindow(service.mWindowManager, appToken, + task.voiceSession != null, container.getDisplayContent(), + ActivityTaskManagerService.getInputDispatchingTimeoutLocked(this) + * 1000000L, fullscreen, + (info.flags & FLAG_SHOW_FOR_ALL_USERS) != 0, appInfo.targetSdkVersion, + info.screenOrientation, mRotationAnimationHint, info.configChanges, + mLaunchTaskBehind, isAlwaysFocusable()); + if (DEBUG_TOKEN_MOVEMENT || DEBUG_ADD_REMOVE) { + Slog.v(TAG, "addAppToken: " + + mAppWindowToken + " controller=" + taskController + " at " + + Integer.MAX_VALUE); + } + container.addChild(mAppWindowToken, Integer.MAX_VALUE /* add on top */); + } task.addActivityToTop(this); @@ -1026,17 +1072,49 @@ final class ActivityRecord extends ConfigurationContainer implements AppWindowCo mLastReportedPictureInPictureMode = inPinnedWindowingMode(); } + boolean addStartingWindow(String pkg, int theme, CompatibilityInfo compatInfo, + CharSequence nonLocalizedLabel, int labelRes, int icon, int logo, int windowFlags, + IBinder transferFrom, boolean newTask, boolean taskSwitch, boolean processRunning, + boolean allowTaskSnapshot, boolean activityCreated, boolean fromRecents) { + if (DEBUG_STARTING_WINDOW) { + Slog.v(TAG, "setAppStartingWindow: token=" + appToken + + " pkg=" + pkg + " transferFrom=" + transferFrom + " newTask=" + newTask + + " taskSwitch=" + taskSwitch + " processRunning=" + processRunning + + " allowTaskSnapshot=" + allowTaskSnapshot); + } + if (mAppWindowToken == null) { + Slog.w(TAG_WM, "Attempted to set icon of non-existing app token: " + appToken); + return false; + } + return mAppWindowToken.addStartingWindow(pkg, theme, compatInfo, nonLocalizedLabel, + labelRes, icon, logo, windowFlags, transferFrom, newTask, taskSwitch, + processRunning, allowTaskSnapshot, activityCreated, fromRecents); + } + + // TODO: Remove after unification + @VisibleForTesting + AppWindowToken createAppWindow(WindowManagerService service, IApplicationToken token, + boolean voiceInteraction, DisplayContent dc, long inputDispatchingTimeoutNanos, + boolean fullscreen, boolean showForAllUsers, int targetSdk, int orientation, + int rotationAnimationHint, int configChanges, boolean launchTaskBehind, + boolean alwaysFocusable) { + return new AppWindowToken(service, token, realActivity, voiceInteraction, dc, + inputDispatchingTimeoutNanos, fullscreen, showForAllUsers, targetSdk, orientation, + rotationAnimationHint, configChanges, launchTaskBehind, alwaysFocusable, + this); + } + void removeWindowContainer() { - // Do not try to remove a window container if we have already removed it. - if (mWindowContainerController == null) { + final DisplayContent dc = service.mWindowManager.mRoot.getDisplayContent( + getDisplayId()); + if (dc == null) { + Slog.w(TAG, "removeWindowContainer: Attempted to remove token: " + + appToken + " from non-existing displayId=" + getDisplayId()); return; } - // Resume key dispatching if it is currently paused before we remove the container. resumeKeyDispatchingLocked(); - - mWindowContainerController.removeContainer(getDisplayId()); - mWindowContainerController = null; + dc.removeAppToken(appToken.asBinder()); } /** @@ -1044,6 +1122,10 @@ final class ActivityRecord extends ConfigurationContainer implements AppWindowCo * should ensure that the {@param newTask} is not already the parent of this activity. */ void reparent(TaskRecord newTask, int position, String reason) { + if (mAppWindowToken == null) { + Slog.w(TAG, "reparent: Attempted to reparent non-existing app token: " + appToken); + return; + } final TaskRecord prevTask = task; if (prevTask == newTask) { throw new IllegalArgumentException(reason + ": task=" + newTask @@ -1059,8 +1141,7 @@ final class ActivityRecord extends ConfigurationContainer implements AppWindowCo + " r=" + this + " (" + prevTask.getStackId() + ")"); } - // Must reparent first in window manager - mWindowContainerController.reparent(newTask.getWindowContainerController(), position); + mAppWindowToken.reparent(newTask.getWindowContainerController(), position); // Reparenting prevents informing the parent stack of activity removal in the case that // the new stack has the same parent. we must manually signal here if this is not the case. @@ -1200,7 +1281,7 @@ final class ActivityRecord extends ConfigurationContainer implements AppWindowCo } boolean isFocusable() { - return mStackSupervisor.isFocusable(this, isAlwaysFocusable()); + return mRootActivityContainer.isFocusable(this, isAlwaysFocusable()); } boolean isResizeable() { @@ -1353,7 +1434,7 @@ final class ActivityRecord extends ConfigurationContainer implements AppWindowCo return false; } - if (mStackSupervisor.getTopResumedActivity() == this) { + if (mRootActivityContainer.getTopResumedActivity() == this) { if (DEBUG_FOCUS) { Slog.d(TAG_FOCUS, "moveActivityStackToFront: already on top, activity=" + this); } @@ -1366,7 +1447,7 @@ final class ActivityRecord extends ConfigurationContainer implements AppWindowCo stack.moveToFront(reason, task); // Report top activity change to tracking services and WM - if (mStackSupervisor.getTopResumedActivity() == this) { + if (mRootActivityContainer.getTopResumedActivity() == this) { // TODO(b/111361570): Support multiple focused apps in WM service.setResumedActivityUncheckLocked(this, reason); } @@ -1490,7 +1571,7 @@ final class ActivityRecord extends ConfigurationContainer implements AppWindowCo void applyOptionsLocked() { if (pendingOptions != null && pendingOptions.getAnimationType() != ANIM_SCENE_TRANSITION) { - mWindowContainerController.applyOptionsLocked(pendingOptions, intent); + applyOptionsLocked(pendingOptions, intent); if (task == null) { clearOptionsLocked(false /* withAbort */); } else { @@ -1500,6 +1581,104 @@ final class ActivityRecord extends ConfigurationContainer implements AppWindowCo } } + /** + * Apply override app transition base on options & animation type. + */ + void applyOptionsLocked(ActivityOptions pendingOptions, Intent intent) { + final int animationType = pendingOptions.getAnimationType(); + final DisplayContent displayContent = mAppWindowToken.getDisplayContent(); + switch (animationType) { + case ANIM_CUSTOM: + displayContent.mAppTransition.overridePendingAppTransition( + pendingOptions.getPackageName(), + pendingOptions.getCustomEnterResId(), + pendingOptions.getCustomExitResId(), + pendingOptions.getOnAnimationStartListener()); + break; + case ANIM_CLIP_REVEAL: + displayContent.mAppTransition.overridePendingAppTransitionClipReveal( + pendingOptions.getStartX(), pendingOptions.getStartY(), + pendingOptions.getWidth(), pendingOptions.getHeight()); + if (intent.getSourceBounds() == null) { + intent.setSourceBounds(new Rect(pendingOptions.getStartX(), + pendingOptions.getStartY(), + pendingOptions.getStartX() + pendingOptions.getWidth(), + pendingOptions.getStartY() + pendingOptions.getHeight())); + } + break; + case ANIM_SCALE_UP: + displayContent.mAppTransition.overridePendingAppTransitionScaleUp( + pendingOptions.getStartX(), pendingOptions.getStartY(), + pendingOptions.getWidth(), pendingOptions.getHeight()); + if (intent.getSourceBounds() == null) { + intent.setSourceBounds(new Rect(pendingOptions.getStartX(), + pendingOptions.getStartY(), + pendingOptions.getStartX() + pendingOptions.getWidth(), + pendingOptions.getStartY() + pendingOptions.getHeight())); + } + break; + case ANIM_THUMBNAIL_SCALE_UP: + case ANIM_THUMBNAIL_SCALE_DOWN: + final boolean scaleUp = (animationType == ANIM_THUMBNAIL_SCALE_UP); + final GraphicBuffer buffer = pendingOptions.getThumbnail(); + displayContent.mAppTransition.overridePendingAppTransitionThumb(buffer, + pendingOptions.getStartX(), pendingOptions.getStartY(), + pendingOptions.getOnAnimationStartListener(), + scaleUp); + if (intent.getSourceBounds() == null && buffer != null) { + intent.setSourceBounds(new Rect(pendingOptions.getStartX(), + pendingOptions.getStartY(), + pendingOptions.getStartX() + buffer.getWidth(), + pendingOptions.getStartY() + buffer.getHeight())); + } + break; + case ANIM_THUMBNAIL_ASPECT_SCALE_UP: + case ANIM_THUMBNAIL_ASPECT_SCALE_DOWN: + final AppTransitionAnimationSpec[] specs = pendingOptions.getAnimSpecs(); + final IAppTransitionAnimationSpecsFuture specsFuture = + pendingOptions.getSpecsFuture(); + if (specsFuture != null) { + // TODO(multidisplay): Shouldn't be really used anymore from next CL. + displayContent.mAppTransition.overridePendingAppTransitionMultiThumbFuture( + specsFuture, pendingOptions.getOnAnimationStartListener(), + animationType == ANIM_THUMBNAIL_ASPECT_SCALE_UP); + } else if (animationType == ANIM_THUMBNAIL_ASPECT_SCALE_DOWN + && specs != null) { + displayContent.mAppTransition.overridePendingAppTransitionMultiThumb( + specs, pendingOptions.getOnAnimationStartListener(), + pendingOptions.getAnimationFinishedListener(), false); + } else { + displayContent.mAppTransition.overridePendingAppTransitionAspectScaledThumb( + pendingOptions.getThumbnail(), + pendingOptions.getStartX(), pendingOptions.getStartY(), + pendingOptions.getWidth(), pendingOptions.getHeight(), + pendingOptions.getOnAnimationStartListener(), + (animationType == ANIM_THUMBNAIL_ASPECT_SCALE_UP)); + if (intent.getSourceBounds() == null) { + intent.setSourceBounds(new Rect(pendingOptions.getStartX(), + pendingOptions.getStartY(), + pendingOptions.getStartX() + pendingOptions.getWidth(), + pendingOptions.getStartY() + pendingOptions.getHeight())); + } + } + break; + case ANIM_OPEN_CROSS_PROFILE_APPS: + displayContent.mAppTransition + .overridePendingAppTransitionStartCrossProfileApps(); + break; + case ANIM_REMOTE_ANIMATION: + // TODO(multidisplay): Will pass displayId and adjust dependencies from next CL. + displayContent.mAppTransition.overridePendingAppTransitionRemote( + pendingOptions.getRemoteAnimationAdapter()); + break; + case ANIM_NONE: + break; + default: + Slog.e(TAG_WM, "applyOptionsLocked: Unknown animationType=" + animationType); + break; + } + } + ActivityOptions getOptionsForTargetActivityLocked() { return pendingOptions != null ? pendingOptions.forTargetActivity() : null; } @@ -1532,8 +1711,11 @@ final class ActivityRecord extends ConfigurationContainer implements AppWindowCo if (!keysPaused) { keysPaused = true; - if (mWindowContainerController != null) { - mWindowContainerController.pauseKeyDispatching(); + // TODO: remove the check after unification with AppWindowToken. The DC check is not + // needed after no mock mAppWindowToken in tests. + if (mAppWindowToken != null && mAppWindowToken.getDisplayContent() != null) { + mAppWindowToken.getDisplayContent().getInputMonitor().pauseDispatchingLw( + mAppWindowToken); } } } @@ -1542,8 +1724,11 @@ final class ActivityRecord extends ConfigurationContainer implements AppWindowCo if (keysPaused) { keysPaused = false; - if (mWindowContainerController != null) { - mWindowContainerController.resumeKeyDispatching(); + // TODO: remove the check after unification with AppWindowToken. The DC check is not + // needed after no mock mAppWindowToken in tests. + if (mAppWindowToken != null && mAppWindowToken.getDisplayContent() != null) { + mAppWindowToken.getDisplayContent().getInputMonitor().resumeDispatchingLw( + mAppWindowToken); } } } @@ -1565,11 +1750,16 @@ final class ActivityRecord extends ConfigurationContainer implements AppWindowCo } void setVisibility(boolean visible) { - mWindowContainerController.setVisibility(visible, mDeferHidingClient); + if (mAppWindowToken == null) { + Slog.w(TAG_WM, "Attempted to set visibility of non-existing app token: " + + appToken); + return; + } + mAppWindowToken.setVisibility(visible, mDeferHidingClient); mStackSupervisor.getActivityMetricsLogger().notifyVisibilityChanged(this); } - // TODO: Look into merging with #setVisibility() + // TODO: Look into merging with #commitVisibility() void setVisible(boolean newVisible) { visible = newVisible; mDeferHidingClient = !visible && mDeferHidingClient; @@ -1599,7 +1789,12 @@ final class ActivityRecord extends ConfigurationContainer implements AppWindowCo // an indication that the Surface will eventually be destroyed. // This however isn't necessarily true if we are going to sleep. if (state == STOPPING && !isSleeping()) { - mWindowContainerController.notifyAppStopping(); + if (mAppWindowToken == null) { + Slog.w(TAG_WM, "Attempted to notify stopping on non-existing app token: " + + appToken); + return; + } + mAppWindowToken.detachChildren(); } } @@ -1637,7 +1832,12 @@ final class ActivityRecord extends ConfigurationContainer implements AppWindowCo } void notifyAppResumed(boolean wasStopped) { - mWindowContainerController.notifyAppResumed(wasStopped); + if (mAppWindowToken == null) { + Slog.w(TAG_WM, "Attempted to notify resumed of non-existing app token: " + + appToken); + return; + } + mAppWindowToken.notifyAppResumed(wasStopped); } void notifyUnknownVisibilityLaunched() { @@ -1645,7 +1845,10 @@ final class ActivityRecord extends ConfigurationContainer implements AppWindowCo // No display activities never add a window, so there is no point in waiting them for // relayout. if (!noDisplay) { - mWindowContainerController.notifyUnknownVisibilityLaunched(); + if (mAppWindowToken != null) { + mAppWindowToken.getDisplayContent().mUnknownAppVisibilityController + .notifyLaunched(mAppWindowToken); + } } } @@ -1857,16 +2060,18 @@ final class ActivityRecord extends ConfigurationContainer implements AppWindowCo stopped = true; setState(STOPPED, "activityStoppedLocked"); - mWindowContainerController.notifyAppStopped(); + if (mAppWindowToken != null) { + mAppWindowToken.notifyAppStopped(); + } if (finishing) { clearOptionsLocked(); } else { if (deferRelaunchUntilPaused) { stack.destroyActivityLocked(this, true /* removeFromApp */, "stop-config"); - mStackSupervisor.resumeFocusedStacksTopActivitiesLocked(); + mRootActivityContainer.resumeFocusedStacksTopActivities(); } else { - mStackSupervisor.updatePreviousProcessLocked(this); + mRootActivityContainer.updatePreviousProcess(this); } } } @@ -1918,14 +2123,33 @@ final class ActivityRecord extends ConfigurationContainer implements AppWindowCo public void startFreezingScreenLocked(WindowProcessController app, int configChanges) { if (mayFreezeScreenLocked(app)) { - mWindowContainerController.startFreezingScreen(configChanges); + if (mAppWindowToken == null) { + Slog.w(TAG_WM, + "Attempted to freeze screen with non-existing app token: " + appToken); + return; + } + + if (configChanges == 0 && mAppWindowToken.okToDisplay()) { + if (DEBUG_ORIENTATION) Slog.v(TAG_WM, "Skipping set freeze of " + appToken); + return; + } + + mAppWindowToken.startFreezingScreen(); } } public void stopFreezingScreenLocked(boolean force) { if (force || frozenBeforeDestroy) { frozenBeforeDestroy = false; - mWindowContainerController.stopFreezingScreen(force); + if (mAppWindowToken == null) { + return; + } + if (DEBUG_ORIENTATION) { + Slog.v(TAG_WM, "Clear freezing of " + appToken + ": hidden=" + + mAppWindowToken.isHidden() + " freezing=" + + mAppWindowToken.isFreezingScreen()); + } + mAppWindowToken.stopFreezingScreen(true, force); } } @@ -1937,7 +2161,10 @@ final class ActivityRecord extends ConfigurationContainer implements AppWindowCo info.windowsFullyDrawnDelayMs); } } - @Override + + /** + * Called when the starting window for this container is drawn. + */ public void onStartingWindowDrawn(long timestamp) { synchronized (service.mGlobalLock) { mStackSupervisor.getActivityMetricsLogger().notifyStartingWindowDrawn( @@ -1945,7 +2172,7 @@ final class ActivityRecord extends ConfigurationContainer implements AppWindowCo } } - @Override + /** Called when the windows associated app window container are drawn. */ public void onWindowsDrawn(boolean drawn, long timestamp) { synchronized (service.mGlobalLock) { mDrawn = drawn; @@ -1965,7 +2192,7 @@ final class ActivityRecord extends ConfigurationContainer implements AppWindowCo } } - @Override + /** Called when the windows associated app window container are visible. */ public void onWindowsVisible() { synchronized (service.mGlobalLock) { mStackSupervisor.reportActivityVisibleLocked(this); @@ -1999,7 +2226,7 @@ final class ActivityRecord extends ConfigurationContainer implements AppWindowCo } } - @Override + /** Called when the windows associated app window container are no longer visible. */ public void onWindowsGone() { synchronized (service.mGlobalLock) { if (DEBUG_SWITCH) Log.v(TAG_SWITCH, "windowsGone(): " + this); @@ -2007,7 +2234,14 @@ final class ActivityRecord extends ConfigurationContainer implements AppWindowCo } } - @Override + /** + * Called when the key dispatching to a window associated with the app window container + * timed-out. + * + * @param reason The reason for the key dispatching time out. + * @param windowPid The pid of the window key dispatching timed out on. + * @return True if input dispatching should be aborted. + */ public boolean keyDispatchingTimedOut(String reason, int windowPid) { ActivityRecord anrActivity; WindowProcessController anrApp; @@ -2036,7 +2270,7 @@ final class ActivityRecord extends ConfigurationContainer implements AppWindowCo // another activity to start or has stopped, then the key dispatching // timeout should not be caused by this. if (mStackSupervisor.mActivitiesWaitingForVisibleActivity.contains(this) || stopped) { - final ActivityStack stack = mStackSupervisor.getTopDisplayFocusedStack(); + final ActivityStack stack = mRootActivityContainer.getTopDisplayFocusedStack(); // Try to use the one which is closest to top. ActivityRecord r = stack.getResumedActivity(); if (r == null) { @@ -2183,7 +2417,7 @@ final class ActivityRecord extends ConfigurationContainer implements AppWindowCo void showStartingWindow(ActivityRecord prev, boolean newTask, boolean taskSwitch, boolean fromRecents) { - if (mWindowContainerController == null) { + if (mAppWindowToken == null) { return; } if (mTaskOverlay) { @@ -2198,7 +2432,7 @@ final class ActivityRecord extends ConfigurationContainer implements AppWindowCo final CompatibilityInfo compatInfo = service.compatibilityInfoForPackageLocked(info.applicationInfo); - final boolean shown = mWindowContainerController.addStartingWindow(packageName, theme, + final boolean shown = addStartingWindow(packageName, theme, compatInfo, nonLocalizedLabel, labelRes, icon, logo, windowFlags, prev != null ? prev.appToken : null, newTask, taskSwitch, isProcessRunning(), allowTaskSnapshot(), @@ -2213,34 +2447,62 @@ final class ActivityRecord extends ConfigurationContainer implements AppWindowCo if (mStartingWindowState == STARTING_WINDOW_SHOWN && behindFullscreenActivity) { if (DEBUG_VISIBILITY) Slog.w(TAG_VISIBILITY, "Found orphaned starting window " + this); mStartingWindowState = STARTING_WINDOW_REMOVED; - mWindowContainerController.removeStartingWindow(); + mAppWindowToken.removeStartingWindow(); } } int getRequestedOrientation() { - return mWindowContainerController.getOrientation(); + return getOrientation(); } void setRequestedOrientation(int requestedOrientation) { final int displayId = getDisplayId(); final Configuration displayConfig = - mStackSupervisor.getDisplayOverrideConfiguration(displayId); + mRootActivityContainer.getDisplayOverrideConfiguration(displayId); - final Configuration config = mWindowContainerController.setOrientation(requestedOrientation, + final Configuration config = setOrientation(requestedOrientation, displayId, displayConfig, mayFreezeScreenLocked(app)); if (config != null) { frozenBeforeDestroy = true; if (!service.updateDisplayOverrideConfigurationLocked(config, this, false /* deferResume */, displayId)) { - mStackSupervisor.resumeFocusedStacksTopActivitiesLocked(); + mRootActivityContainer.resumeFocusedStacksTopActivities(); } } service.getTaskChangeNotificationController().notifyActivityRequestedOrientationChanged( task.taskId, requestedOrientation); } + Configuration setOrientation(int requestedOrientation, int displayId, + Configuration displayConfig, boolean freezeScreenIfNeeded) { + if (mAppWindowToken == null) { + Slog.w(TAG_WM, + "Attempted to set orientation of non-existing app token: " + appToken); + return null; + } + + mAppWindowToken.setOrientation(requestedOrientation); + + final IBinder binder = freezeScreenIfNeeded ? appToken.asBinder() : null; + return service.mWindowManager.updateOrientationFromAppTokens(displayConfig, binder, + displayId); + } + + int getOrientation() { + if (mAppWindowToken == null) { + return SCREEN_ORIENTATION_UNSPECIFIED; + } + + return mAppWindowToken.getOrientationIgnoreVisibility(); + } + void setDisablePreviewScreenshots(boolean disable) { - mWindowContainerController.setDisablePreviewScreenshots(disable); + if (mAppWindowToken == null) { + Slog.w(TAG_WM, "Attempted to set disable screenshots of non-existing app" + + " token: " + appToken); + return; + } + mAppWindowToken.setDisablePreviewScreenshots(disable); } /** @@ -2288,8 +2550,8 @@ final class ActivityRecord extends ConfigurationContainer implements AppWindowCo /** Returns true if the configuration is compatible with this activity. */ boolean isConfigurationCompatible(Configuration config) { - final int orientation = mWindowContainerController != null - ? mWindowContainerController.getOrientation() : info.screenOrientation; + final int orientation = mAppWindowToken != null + ? getOrientation() : info.screenOrientation; if (isFixedOrientationPortrait(orientation) && config.orientation != ORIENTATION_PORTRAIT) { return false; @@ -2867,7 +3129,7 @@ final class ActivityRecord extends ConfigurationContainer implements AppWindowCo void setShowWhenLocked(boolean showWhenLocked) { mShowWhenLocked = showWhenLocked; - mStackSupervisor.ensureActivitiesVisibleLocked(null, 0 /* configChanges */, + mRootActivityContainer.ensureActivitiesVisible(null, 0 /* configChanges */, false /* preserveWindows */); } @@ -2905,7 +3167,7 @@ final class ActivityRecord extends ConfigurationContainer implements AppWindowCo } boolean isTopRunningActivity() { - return mStackSupervisor.topRunningActivityLocked() == this; + return mRootActivityContainer.topRunningActivity() == this; } /** @@ -2918,7 +3180,12 @@ final class ActivityRecord extends ConfigurationContainer implements AppWindowCo } void registerRemoteAnimations(RemoteAnimationDefinition definition) { - mWindowContainerController.registerRemoteAnimations(definition); + if (mAppWindowToken == null) { + Slog.w(TAG_WM, "Attempted to register remote animations with non-existing app" + + " token: " + appToken); + return; + } + mAppWindowToken.registerRemoteAnimations(definition); } @Override diff --git a/services/core/java/com/android/server/wm/ActivityStack.java b/services/core/java/com/android/server/wm/ActivityStack.java index bd3e43c380b6..9fbeaf8cd7bf 100644 --- a/services/core/java/com/android/server/wm/ActivityStack.java +++ b/services/core/java/com/android/server/wm/ActivityStack.java @@ -63,7 +63,6 @@ import static com.android.server.wm.ActivityStack.ActivityState.PAUSING; import static com.android.server.wm.ActivityStack.ActivityState.RESUMED; import static com.android.server.wm.ActivityStack.ActivityState.STOPPED; import static com.android.server.wm.ActivityStack.ActivityState.STOPPING; -import static com.android.server.wm.ActivityStackSupervisor.FindTaskResult; import static com.android.server.wm.ActivityStackSupervisor.PAUSE_IMMEDIATELY; import static com.android.server.wm.ActivityStackSupervisor.PRESERVE_WINDOWS; import static com.android.server.wm.ActivityStackSupervisor.REMOVE_FROM_RECENTS; @@ -103,6 +102,7 @@ import static com.android.server.wm.ActivityTaskManagerDebugConfig.TAG_WITH_CLAS import static com.android.server.wm.ActivityTaskManagerService.H.FIRST_ACTIVITY_STACK_MSG; import static com.android.server.wm.ActivityTaskManagerService.RELAUNCH_REASON_FREE_RESIZE; import static com.android.server.wm.ActivityTaskManagerService.RELAUNCH_REASON_WINDOWING_MODE_RESIZE; +import static com.android.server.wm.RootActivityContainer.FindTaskResult; import static java.lang.Integer.MAX_VALUE; @@ -267,7 +267,7 @@ class ActivityStack<T extends StackWindowController> extends ConfigurationContai mStackSupervisor.resizeDockedStackLocked( getOverrideBounds(), mTmpRect2, mTmpRect2, null, null, PRESERVE_WINDOWS); } - mStackSupervisor.updateUIDsPresentOnDisplay(); + mRootActivityContainer.updateUIDsPresentOnDisplay(); } enum ActivityState { @@ -390,6 +390,7 @@ class ActivityStack<T extends StackWindowController> extends ConfigurationContai /** Run all ActivityStacks through this */ protected final ActivityStackSupervisor mStackSupervisor; + protected final RootActivityContainer mRootActivityContainer; private boolean mTopActivityOccludesKeyguard; private ActivityRecord mTopDismissingKeyguardActivity; @@ -489,6 +490,7 @@ class ActivityStack<T extends StackWindowController> extends ConfigurationContai int windowingMode, int activityType, boolean onTop) { mStackSupervisor = supervisor; mService = supervisor.mService; + mRootActivityContainer = mService.mRootActivityContainer; mHandler = new ActivityStackHandler(supervisor.mLooper); mWindowManager = mService.mWindowManager; mStackId = stackId; @@ -508,7 +510,7 @@ class ActivityStack<T extends StackWindowController> extends ConfigurationContai T createStackWindowController(int displayId, boolean onTop, Rect outBounds) { return (T) new StackWindowController(mStackId, this, displayId, onTop, outBounds, - mStackSupervisor.mWindowManager); + mRootActivityContainer.mWindowManager); } T getWindowContainerController() { @@ -532,7 +534,7 @@ class ActivityStack<T extends StackWindowController> extends ConfigurationContai if (DEBUG_STACK) Slog.v(TAG_STACK, "set resumed activity to:" + record + " reason:" + reason); setResumedActivity(record, reason + " - onActivityStateChanged"); - if (record == mStackSupervisor.getTopResumedActivity()) { + if (record == mRootActivityContainer.getTopResumedActivity()) { // TODO(b/111361570): Support multiple focused apps in WM mService.setResumedActivityUncheckLocked(record, reason); } @@ -622,7 +624,7 @@ class ActivityStack<T extends StackWindowController> extends ConfigurationContai display.onStackWindowingModeChanged(this); } if (hasNewOverrideBounds) { - mStackSupervisor.resizeStackLocked(this, mTmpRect2, null, null, PRESERVE_WINDOWS, + mRootActivityContainer.resizeStack(this, mTmpRect2, null, null, PRESERVE_WINDOWS, true /* allowResizeInDockedMode */, true /* deferResume */); } if (prevIsAlwaysOnTop != isAlwaysOnTop()) { @@ -819,8 +821,8 @@ class ActivityStack<T extends StackWindowController> extends ConfigurationContai } if (!deferEnsuringVisibility) { - mStackSupervisor.ensureActivitiesVisibleLocked(null, 0, PRESERVE_WINDOWS); - mStackSupervisor.resumeFocusedStacksTopActivitiesLocked(); + mRootActivityContainer.ensureActivitiesVisible(null, 0, PRESERVE_WINDOWS); + mRootActivityContainer.resumeFocusedStacksTopActivities(); } } @@ -854,10 +856,10 @@ class ActivityStack<T extends StackWindowController> extends ConfigurationContai /** Resume next focusable stack after reparenting to another display. */ void postReparent() { adjustFocusToNextFocusableStack("reparent", true /* allowFocusSelf */); - mStackSupervisor.resumeFocusedStacksTopActivitiesLocked(); + mRootActivityContainer.resumeFocusedStacksTopActivities(); // Update visibility of activities before notifying WM. This way it won't try to resize // windows that are no longer visible. - mStackSupervisor.ensureActivitiesVisibleLocked(null /* starting */, 0 /* configChanges */, + mRootActivityContainer.ensureActivitiesVisible(null /* starting */, 0 /* configChanges */, !PRESERVE_WINDOWS); } @@ -882,7 +884,7 @@ class ActivityStack<T extends StackWindowController> extends ConfigurationContai } ActivityDisplay getDisplay() { - return mStackSupervisor.getActivityDisplay(mDisplayId); + return mRootActivityContainer.getActivityDisplay(mDisplayId); } /** @@ -1034,7 +1036,7 @@ class ActivityStack<T extends StackWindowController> extends ConfigurationContai } /** - * This is a simplified version of topRunningActivityLocked that provides a number of + * This is a simplified version of topRunningActivity that provides a number of * optional skip-over modes. It is intended for use with the ActivityController hook only. * * @param token If non-null, any history records matching this token will be skipped. @@ -1236,7 +1238,7 @@ class ActivityStack<T extends StackWindowController> extends ConfigurationContai boolean isFocusable() { final ActivityRecord r = topRunningActivityLocked(); - return mStackSupervisor.isFocusable(this, r != null && r.isFocusable()); + return mRootActivityContainer.isFocusable(this, r != null && r.isFocusable()); } boolean isFocusableAndVisible() { @@ -1398,7 +1400,7 @@ class ActivityStack<T extends StackWindowController> extends ConfigurationContai final TaskRecord task = mTaskHistory.get(i); if (task.okToShowLocked()) { - if (DEBUG_TASKS) Slog.d(TAG_TASKS, "switchUserLocked: stack=" + getStackId() + + if (DEBUG_TASKS) Slog.d(TAG_TASKS, "switchUser: stack=" + getStackId() + " moving " + task + " to top"); mTaskHistory.remove(i); mTaskHistory.add(task); @@ -1587,7 +1589,7 @@ class ActivityStack<T extends StackWindowController> extends ConfigurationContai if (prev == null) { if (resuming == null) { Slog.wtf(TAG, "Trying to pause when nothing is resumed"); - mStackSupervisor.resumeFocusedStacksTopActivitiesLocked(); + mRootActivityContainer.resumeFocusedStacksTopActivities(); } return false; } @@ -1665,7 +1667,7 @@ class ActivityStack<T extends StackWindowController> extends ConfigurationContai // pause, so just treat it as being paused now. if (DEBUG_PAUSE) Slog.v(TAG_PAUSE, "Activity not running, resuming next."); if (resuming == null) { - mStackSupervisor.resumeFocusedStacksTopActivitiesLocked(); + mRootActivityContainer.resumeFocusedStacksTopActivities(); } return false; } @@ -1704,7 +1706,7 @@ class ActivityStack<T extends StackWindowController> extends ConfigurationContai } } } - mStackSupervisor.ensureActivitiesVisibleLocked(null, 0, !PRESERVE_WINDOWS); + mRootActivityContainer.ensureActivitiesVisible(null, 0, !PRESERVE_WINDOWS); } private void completePauseLocked(boolean resumeNext, ActivityRecord resuming) { @@ -1757,9 +1759,9 @@ class ActivityStack<T extends StackWindowController> extends ConfigurationContai } if (resumeNext) { - final ActivityStack topStack = mStackSupervisor.getTopDisplayFocusedStack(); + final ActivityStack topStack = mRootActivityContainer.getTopDisplayFocusedStack(); if (!topStack.shouldSleepOrShutDownActivities()) { - mStackSupervisor.resumeFocusedStacksTopActivitiesLocked(topStack, prev, null); + mRootActivityContainer.resumeFocusedStacksTopActivities(topStack, prev, null); } else { checkReadyForSleep(); ActivityRecord top = topStack.topRunningActivityLocked(); @@ -1768,7 +1770,7 @@ class ActivityStack<T extends StackWindowController> extends ConfigurationContai // something. Also if the top activity on the stack is not the just paused // activity, we need to go ahead and resume it to ensure we complete an // in-flight app switch. - mStackSupervisor.resumeFocusedStacksTopActivitiesLocked(); + mRootActivityContainer.resumeFocusedStacksTopActivities(); } } } @@ -1799,7 +1801,7 @@ class ActivityStack<T extends StackWindowController> extends ConfigurationContai mStackSupervisor.mAppVisibilitiesChangedSinceLastPause = false; } - mStackSupervisor.ensureActivitiesVisibleLocked(resuming, 0, !PRESERVE_WINDOWS); + mRootActivityContainer.ensureActivitiesVisible(resuming, 0, !PRESERVE_WINDOWS); } private void addToStopping(ActivityRecord r, boolean scheduleIdle, boolean idleDelayed) { @@ -2011,7 +2013,7 @@ class ActivityStack<T extends StackWindowController> extends ConfigurationContai /** * Ensure visibility with an option to also update the configuration of visible activities. * @see #ensureActivitiesVisibleLocked(ActivityRecord, int, boolean) - * @see ActivityStackSupervisor#ensureActivitiesVisibleLocked(ActivityRecord, int, boolean) + * @see RootActivityContainer#ensureActivitiesVisible(ActivityRecord, int, boolean) */ // TODO: Should be re-worked based on the fact that each task as a stack in most cases. final void ensureActivitiesVisibleLocked(ActivityRecord starting, int configChanges, @@ -2032,7 +2034,7 @@ class ActivityStack<T extends StackWindowController> extends ConfigurationContai boolean aboveTop = top != null; final boolean stackShouldBeVisible = shouldBeVisible(starting); boolean behindFullscreenActivity = !stackShouldBeVisible; - boolean resumeNextActivity = mStackSupervisor.isTopDisplayFocusedStack(this) + boolean resumeNextActivity = mRootActivityContainer.isTopDisplayFocusedStack(this) && (isInStackLocked(starting) == null); final boolean isTopNotPinnedStack = isAttached() && getDisplay().isTopNotPinnedStack(this); @@ -2443,7 +2445,7 @@ class ActivityStack<T extends StackWindowController> extends ConfigurationContai * * NOTE: It is not safe to call this method directly as it can cause an activity in a * non-focused stack to be resumed. - * Use {@link ActivityStackSupervisor#resumeFocusedStacksTopActivitiesLocked} to resume the + * Use {@link RootActivityContainer#resumeFocusedStacksTopActivities} to resume the * right activity for the current system state. */ @GuardedBy("mService") @@ -2513,7 +2515,7 @@ class ActivityStack<T extends StackWindowController> extends ConfigurationContai return false; } - mStackSupervisor.cancelInitializingActivities(); + mRootActivityContainer.cancelInitializingActivities(); // Remember how we'll process this pause/resume situation, and ensure // that the state is reset however we wind up proceeding. @@ -2536,7 +2538,6 @@ class ActivityStack<T extends StackWindowController> extends ConfigurationContai executeAppTransition(options); if (DEBUG_STATES) Slog.d(TAG_STATES, "resumeTopActivityLocked: Top activity resumed " + next); - if (DEBUG_STACK) mStackSupervisor.validateTopActivitiesLocked(); return false; } @@ -2544,7 +2545,7 @@ class ActivityStack<T extends StackWindowController> extends ConfigurationContai // activity is paused, well that is the state we want. if (shouldSleepOrShutDownActivities() && mLastPausedActivity == next - && mStackSupervisor.allPausedActivitiesComplete()) { + && mRootActivityContainer.allPausedActivitiesComplete()) { // If the current top activity may be able to occlude keyguard but the occluded state // has not been set, update visibility and check again if we should continue to resume. boolean nothingToResume = true; @@ -2565,7 +2566,6 @@ class ActivityStack<T extends StackWindowController> extends ConfigurationContai executeAppTransition(options); if (DEBUG_STATES) Slog.d(TAG_STATES, "resumeTopActivityLocked: Going to sleep and all paused"); - if (DEBUG_STACK) mStackSupervisor.validateTopActivitiesLocked(); return false; } } @@ -2576,7 +2576,6 @@ class ActivityStack<T extends StackWindowController> extends ConfigurationContai if (!mService.mAmInternal.hasStartedUserState(next.userId)) { Slog.w(TAG, "Skipping resume of top activity " + next + ": user " + next.userId + " is stopped"); - if (DEBUG_STACK) mStackSupervisor.validateTopActivitiesLocked(); return false; } @@ -2590,10 +2589,9 @@ class ActivityStack<T extends StackWindowController> extends ConfigurationContai if (DEBUG_SWITCH) Slog.v(TAG_SWITCH, "Resuming " + next); // If we are currently pausing an activity, then don't do anything until that is done. - if (!mStackSupervisor.allPausedActivitiesComplete()) { + if (!mRootActivityContainer.allPausedActivitiesComplete()) { if (DEBUG_SWITCH || DEBUG_PAUSE || DEBUG_STATES) Slog.v(TAG_PAUSE, "resumeTopActivityLocked: Skip resume: some activity pausing."); - if (DEBUG_STACK) mStackSupervisor.validateTopActivitiesLocked(); return false; } @@ -2640,7 +2638,6 @@ class ActivityStack<T extends StackWindowController> extends ConfigurationContai next.app.updateProcessInfo(false /* updateServiceConnectionActivities */, true /* updateLru */, true /* activityChange */, false /* updateOomAdj */); } - if (DEBUG_STACK) mStackSupervisor.validateTopActivitiesLocked(); if (lastResumed != null) { lastResumed.setWillCloseOrEnterPip(true); } @@ -2655,7 +2652,6 @@ class ActivityStack<T extends StackWindowController> extends ConfigurationContai executeAppTransition(options); if (DEBUG_STATES) Slog.d(TAG_STATES, "resumeTopActivityLocked: Top activity resumed (dontWaitForPause) " + next); - if (DEBUG_STACK) mStackSupervisor.validateTopActivitiesLocked(); return true; } @@ -2673,7 +2669,7 @@ class ActivityStack<T extends StackWindowController> extends ConfigurationContai if (prev != null && prev != next) { if (!mStackSupervisor.mActivitiesWaitingForVisibleActivity.contains(prev) - && next != null && !next.nowVisible) { + && !next.nowVisible) { mStackSupervisor.mActivitiesWaitingForVisibleActivity.add(prev); if (DEBUG_SWITCH) Slog.v(TAG_SWITCH, "Resuming top, waiting visible to hide: " + prev); @@ -2814,7 +2810,7 @@ class ActivityStack<T extends StackWindowController> extends ConfigurationContai // result of invisible window resize. // TODO: Remove this once visibilities are set correctly immediately when // starting an activity. - notUpdated = !mStackSupervisor.ensureVisibilityAndConfig(next, mDisplayId, + notUpdated = !mRootActivityContainer.ensureVisibilityAndConfig(next, mDisplayId, true /* markFrozenIfConfigChanged */, false /* deferResume */); } @@ -2836,7 +2832,6 @@ class ActivityStack<T extends StackWindowController> extends ConfigurationContai next.setVisibility(true); } next.completeResumeLocked(); - if (DEBUG_STACK) mStackSupervisor.validateTopActivitiesLocked(); return true; } @@ -2899,7 +2894,6 @@ class ActivityStack<T extends StackWindowController> extends ConfigurationContai false /* taskSwitch */); } mStackSupervisor.startSpecificActivityLocked(next, true, false); - if (DEBUG_STACK) mStackSupervisor.validateTopActivitiesLocked(); return true; } @@ -2913,7 +2907,6 @@ class ActivityStack<T extends StackWindowController> extends ConfigurationContai Slog.w(TAG, "Exception thrown during resume of " + next, e); requestFinishActivityLocked(next.appToken, Activity.RESULT_CANCELED, null, "resume-exception", true); - if (DEBUG_STACK) mStackSupervisor.validateTopActivitiesLocked(); return true; } } else { @@ -2931,7 +2924,6 @@ class ActivityStack<T extends StackWindowController> extends ConfigurationContai mStackSupervisor.startSpecificActivityLocked(next, true, true); } - if (DEBUG_STACK) mStackSupervisor.validateTopActivitiesLocked(); return true; } @@ -2942,7 +2934,7 @@ class ActivityStack<T extends StackWindowController> extends ConfigurationContai // Try to move focus to the next visible stack with a running activity if this // stack is not covering the entire screen or is on a secondary display (with no home // stack). - return mStackSupervisor.resumeFocusedStacksTopActivitiesLocked(nextFocusedStack, prev, + return mRootActivityContainer.resumeFocusedStacksTopActivities(nextFocusedStack, prev, null /* targetOptions */); } @@ -2950,8 +2942,7 @@ class ActivityStack<T extends StackWindowController> extends ConfigurationContai ActivityOptions.abort(options); if (DEBUG_STATES) Slog.d(TAG_STATES, "resumeTopActivityInNextFocusableStack: " + reason + ", go home"); - if (DEBUG_STACK) mStackSupervisor.validateTopActivitiesLocked(); - return mStackSupervisor.resumeHomeActivity(prev, reason, mDisplayId); + return mRootActivityContainer.resumeHomeActivity(prev, reason, mDisplayId); } /** Returns the position the input task should be placed in this stack. */ @@ -3043,7 +3034,7 @@ class ActivityStack<T extends StackWindowController> extends ConfigurationContai if (!startIt) { if (DEBUG_ADD_REMOVE) Slog.i(TAG, "Adding activity " + r + " to task " + task, new RuntimeException("here").fillInStackTrace()); - r.createWindowContainer(); + r.createAppWindowToken(); ActivityOptions.abort(options); return; } @@ -3073,9 +3064,10 @@ class ActivityStack<T extends StackWindowController> extends ConfigurationContai // TODO: Need to investigate if it is okay for the controller to already be created by the // time we get to this point. I think it is, but need to double check. // Use test in b/34179495 to trace the call path. - if (r.getWindowContainerController() == null) { - r.createWindowContainer(); + if (r.mAppWindowToken == null) { + r.createAppWindowToken(); } + task.setFrontOfTask(); if (!isHomeOrRecentsStack() || numActivities() > 0) { @@ -3536,7 +3528,7 @@ class ActivityStack<T extends StackWindowController> extends ConfigurationContai } private void adjustFocusedActivityStack(ActivityRecord r, String reason) { - if (!mStackSupervisor.isTopDisplayFocusedStack(this) || + if (!mRootActivityContainer.isTopDisplayFocusedStack(this) || ((mResumedActivity != r) && (mResumedActivity != null))) { return; } @@ -3545,7 +3537,7 @@ class ActivityStack<T extends StackWindowController> extends ConfigurationContai final String myReason = reason + " adjustFocus"; if (next == r) { - final ActivityRecord top = mStackSupervisor.topRunningActivityLocked(); + final ActivityRecord top = mRootActivityContainer.topRunningActivity(); if (top != null) { top.moveFocusableActivityToTop(myReason); } @@ -3569,7 +3561,7 @@ class ActivityStack<T extends StackWindowController> extends ConfigurationContai final ActivityStack nextFocusableStack = adjustFocusToNextFocusableStack(myReason); if (nextFocusableStack != null) { final ActivityRecord top = nextFocusableStack.topRunningActivityLocked(); - if (top != null && top == mStackSupervisor.getTopResumedActivity()) { + if (top != null && top == mRootActivityContainer.getTopResumedActivity()) { // TODO(b/111361570): Remove this and update focused app per-display in // WindowManager every time an activity becomes resumed in // ActivityTaskManagerService#setResumedActivityUncheckLocked(). @@ -3597,7 +3589,7 @@ class ActivityStack<T extends StackWindowController> extends ConfigurationContai */ private ActivityStack adjustFocusToNextFocusableStack(String reason, boolean allowFocusSelf) { final ActivityStack stack = - mStackSupervisor.getNextFocusableStackLocked(this, !allowFocusSelf); + mRootActivityContainer.getNextFocusableStack(this, !allowFocusSelf); final String myReason = reason + " adjustFocusToNextFocusableStack"; if (stack == null) { return null; @@ -4018,11 +4010,11 @@ class ActivityStack<T extends StackWindowController> extends ConfigurationContai // stack, need to make something visible in its place. Also if the display does not // have running activity, the configuration may need to be updated for restoring // original orientation of the display. - mStackSupervisor.ensureVisibilityAndConfig(next, mDisplayId, + mRootActivityContainer.ensureVisibilityAndConfig(next, mDisplayId, false /* markFrozenIfConfigChanged */, true /* deferResume */); } if (activityRemoved) { - mStackSupervisor.resumeFocusedStacksTopActivitiesLocked(); + mRootActivityContainer.resumeFocusedStacksTopActivities(); } if (DEBUG_CONTAINERS) Slog.d(TAG_CONTAINERS, "destroyActivityLocked: finishCurrentActivityLocked r=" + r + @@ -4035,7 +4027,7 @@ class ActivityStack<T extends StackWindowController> extends ConfigurationContai if (DEBUG_ALL) Slog.v(TAG, "Enqueueing pending finish: " + r); mStackSupervisor.mFinishingActivities.add(r); r.resumeKeyDispatchingLocked(); - mStackSupervisor.resumeFocusedStacksTopActivitiesLocked(); + mRootActivityContainer.resumeFocusedStacksTopActivities(); return r; } @@ -4377,7 +4369,7 @@ class ActivityStack<T extends StackWindowController> extends ConfigurationContai } } if (activityRemoved) { - mStackSupervisor.resumeFocusedStacksTopActivitiesLocked(); + mRootActivityContainer.resumeFocusedStacksTopActivities(); } } @@ -4568,7 +4560,7 @@ class ActivityStack<T extends StackWindowController> extends ConfigurationContai } } - mStackSupervisor.resumeFocusedStacksTopActivitiesLocked(); + mRootActivityContainer.resumeFocusedStacksTopActivities(); } private void removeHistoryRecordsForAppLocked(ArrayList<ActivityRecord> list, @@ -4712,7 +4704,7 @@ class ActivityStack<T extends StackWindowController> extends ConfigurationContai task.mLastTimeMoved *= -1; } } - mStackSupervisor.invalidateTaskLayers(); + mRootActivityContainer.invalidateTaskLayers(); } final void moveTaskToFrontLocked(TaskRecord tr, boolean noAnimation, ActivityOptions options, @@ -4788,7 +4780,7 @@ class ActivityStack<T extends StackWindowController> extends ConfigurationContai topActivity.supportsEnterPipOnTaskSwitch = true; } - mStackSupervisor.resumeFocusedStacksTopActivitiesLocked(); + mRootActivityContainer.resumeFocusedStacksTopActivities(); EventLog.writeEvent(EventLogTags.AM_TASK_TO_FRONT, tr.userId, tr.taskId); mService.getTaskChangeNotificationController().notifyTaskMovedToFront(tr.taskId); @@ -4860,7 +4852,7 @@ class ActivityStack<T extends StackWindowController> extends ConfigurationContai return true; } - mStackSupervisor.resumeFocusedStacksTopActivitiesLocked(); + mRootActivityContainer.resumeFocusedStacksTopActivities(); return true; } @@ -4907,7 +4899,7 @@ class ActivityStack<T extends StackWindowController> extends ConfigurationContai if (updatedConfig) { // Ensure the resumed state of the focus activity if we updated the configuration of // any activity. - mStackSupervisor.resumeFocusedStacksTopActivitiesLocked(); + mRootActivityContainer.resumeFocusedStacksTopActivities(); } } @@ -5099,7 +5091,7 @@ class ActivityStack<T extends StackWindowController> extends ConfigurationContai */ void getRunningTasks(List<TaskRecord> tasksOut, @ActivityType int ignoreActivityType, @WindowingMode int ignoreWindowingMode, int callingUid, boolean allowed) { - boolean focusedStack = mStackSupervisor.getTopDisplayFocusedStack() == this; + boolean focusedStack = mRootActivityContainer.getTopDisplayFocusedStack() == this; boolean topTask = true; for (int taskNdx = mTaskHistory.size() - 1; taskNdx >= 0; --taskNdx) { final TaskRecord task = mTaskHistory.get(taskNdx); @@ -5164,7 +5156,7 @@ class ActivityStack<T extends StackWindowController> extends ConfigurationContai return removeHistoryRecordsForAppLocked(app); } - void handleAppCrashLocked(WindowProcessController app) { + void handleAppCrash(WindowProcessController app) { for (int taskNdx = mTaskHistory.size() - 1; taskNdx >= 0; --taskNdx) { final ArrayList<ActivityRecord> activities = mTaskHistory.get(taskNdx).mActivities; for (int activityNdx = activities.size() - 1; activityNdx >= 0; --activityNdx) { @@ -5311,7 +5303,7 @@ class ActivityStack<T extends StackWindowController> extends ConfigurationContai // We only need to adjust focused stack if this stack is in focus and we are not in the // process of moving the task to the top of the stack that will be focused. if (mode != REMOVE_TASK_MODE_MOVING_TO_TOP - && mStackSupervisor.isTopDisplayFocusedStack(this)) { + && mRootActivityContainer.isTopDisplayFocusedStack(this)) { String myReason = reason + " leftTaskHistoryEmpty"; if (!inMultiWindowMode() || adjustFocusToNextFocusableStack(myReason) == null) { getDisplay().moveHomeStackToFront(myReason); @@ -5417,7 +5409,7 @@ class ActivityStack<T extends StackWindowController> extends ConfigurationContai // The task might have already been running and its visibility needs to be synchronized with // the visibility of the stack / windows. ensureActivitiesVisibleLocked(null, 0, !PRESERVE_WINDOWS); - mStackSupervisor.resumeFocusedStacksTopActivitiesLocked(); + mRootActivityContainer.resumeFocusedStacksTopActivities(); } private ActivityStack preAddTask(TaskRecord task, String reason, boolean toTop) { @@ -5484,7 +5476,7 @@ class ActivityStack<T extends StackWindowController> extends ConfigurationContai moveToFront(reason); // If the original state is resumed, there is no state change to update focused app. // So here makes sure the activity focus is set if it is the top. - if (origState == RESUMED && r == mStackSupervisor.getTopResumedActivity()) { + if (origState == RESUMED && r == mRootActivityContainer.getTopResumedActivity()) { // TODO(b/111361570): Support multiple focused apps in WM mService.setResumedActivityUncheckLocked(r, reason); } diff --git a/services/core/java/com/android/server/wm/ActivityStackSupervisor.java b/services/core/java/com/android/server/wm/ActivityStackSupervisor.java index c517bd70cbf5..3162ee37276e 100644 --- a/services/core/java/com/android/server/wm/ActivityStackSupervisor.java +++ b/services/core/java/com/android/server/wm/ActivityStackSupervisor.java @@ -25,28 +25,18 @@ import static android.app.ActivityManager.START_FLAG_DEBUG; import static android.app.ActivityManager.START_FLAG_NATIVE_DEBUGGING; import static android.app.ActivityManager.START_FLAG_TRACK_ALLOCATION; import static android.app.ActivityManager.START_TASK_TO_FRONT; -import static android.app.ActivityTaskManager.INVALID_STACK_ID; -import static android.app.ActivityTaskManager.INVALID_TASK_ID; import static android.app.ITaskStackListener.FORCED_RESIZEABLE_REASON_SECONDARY_DISPLAY; import static android.app.ITaskStackListener.FORCED_RESIZEABLE_REASON_SPLIT_SCREEN; import static android.app.WaitResult.INVALID_DELAY; -import static android.app.WindowConfiguration.ACTIVITY_TYPE_ASSISTANT; 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.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_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.activityTypeToString; -import static android.app.WindowConfiguration.windowingModeToString; -import static android.content.Intent.FLAG_ACTIVITY_NEW_TASK; -import static android.content.pm.ActivityInfo.LAUNCH_SINGLE_INSTANCE; -import static android.content.pm.ActivityInfo.LAUNCH_SINGLE_TASK; import static android.content.pm.PackageManager.NOTIFY_PACKAGE_USE_ACTIVITY; import static android.content.pm.PackageManager.PERMISSION_DENIED; import static android.content.pm.PackageManager.PERMISSION_GRANTED; @@ -59,26 +49,13 @@ import static android.view.Display.INVALID_DISPLAY; import static android.view.Display.TYPE_VIRTUAL; import static android.view.WindowManager.TRANSIT_DOCK_TASK_FROM_RECENTS; -import static com.android.server.am.ActivityStackSupervisorProto.CONFIGURATION_CONTAINER; -import static com.android.server.am.ActivityStackSupervisorProto.DISPLAYS; -import static com.android.server.am.ActivityStackSupervisorProto.FOCUSED_STACK_ID; -import static com.android.server.am.ActivityStackSupervisorProto.IS_HOME_RECENTS_COMPONENT; -import static com.android.server.am.ActivityStackSupervisorProto.KEYGUARD_CONTROLLER; -import static com.android.server.am.ActivityStackSupervisorProto.PENDING_ACTIVITIES; -import static com.android.server.am.ActivityStackSupervisorProto.RESUMED_ACTIVITY; -import static com.android.server.wm.ActivityStack.ActivityState.DESTROYED; -import static com.android.server.wm.ActivityStack.ActivityState.INITIALIZING; import static com.android.server.wm.ActivityStack.ActivityState.PAUSED; import static com.android.server.wm.ActivityStack.ActivityState.PAUSING; -import static com.android.server.wm.ActivityStack.ActivityState.RESUMED; -import static com.android.server.wm.ActivityStack.ActivityState.STOPPED; -import static com.android.server.wm.ActivityStack.ActivityState.STOPPING; import static com.android.server.wm.ActivityStack.REMOVE_TASK_MODE_MOVING; import static com.android.server.wm.ActivityTaskManagerDebugConfig.DEBUG_ALL; import static com.android.server.wm.ActivityTaskManagerDebugConfig.DEBUG_IDLE; import static com.android.server.wm.ActivityTaskManagerDebugConfig.DEBUG_PAUSE; import static com.android.server.wm.ActivityTaskManagerDebugConfig.DEBUG_RECENTS; -import static com.android.server.wm.ActivityTaskManagerDebugConfig.DEBUG_RELEASE; import static com.android.server.wm.ActivityTaskManagerDebugConfig.DEBUG_STACK; import static com.android.server.wm.ActivityTaskManagerDebugConfig.DEBUG_STATES; import static com.android.server.wm.ActivityTaskManagerDebugConfig.DEBUG_SWITCH; @@ -86,9 +63,7 @@ import static com.android.server.wm.ActivityTaskManagerDebugConfig.DEBUG_TASKS; import static com.android.server.wm.ActivityTaskManagerDebugConfig.POSTFIX_IDLE; import static com.android.server.wm.ActivityTaskManagerDebugConfig.POSTFIX_PAUSE; import static com.android.server.wm.ActivityTaskManagerDebugConfig.POSTFIX_RECENTS; -import static com.android.server.wm.ActivityTaskManagerDebugConfig.POSTFIX_RELEASE; import static com.android.server.wm.ActivityTaskManagerDebugConfig.POSTFIX_STACK; -import static com.android.server.wm.ActivityTaskManagerDebugConfig.POSTFIX_STATES; import static com.android.server.wm.ActivityTaskManagerDebugConfig.POSTFIX_SWITCH; import static com.android.server.wm.ActivityTaskManagerDebugConfig.POSTFIX_TASKS; import static com.android.server.wm.ActivityTaskManagerDebugConfig.TAG_ATM; @@ -96,6 +71,10 @@ import static com.android.server.wm.ActivityTaskManagerDebugConfig.TAG_WITH_CLAS import static com.android.server.wm.ActivityTaskManagerService.ANIMATE; import static com.android.server.wm.ActivityTaskManagerService.H.FIRST_SUPERVISOR_STACK_MSG; import static com.android.server.wm.ActivityTaskManagerService.RELAUNCH_REASON_NONE; +import static com.android.server.wm.RootActivityContainer.MATCH_TASK_IN_STACKS_ONLY; +import static com.android.server.wm.RootActivityContainer.MATCH_TASK_IN_STACKS_OR_RECENT_TASKS; +import static com.android.server.wm.RootActivityContainer.MATCH_TASK_IN_STACKS_OR_RECENT_TASKS_AND_RESTORE; +import static com.android.server.wm.RootActivityContainer.TAG_STATES; import static com.android.server.wm.TaskRecord.LOCK_TASK_AUTH_LAUNCHABLE; import static com.android.server.wm.TaskRecord.LOCK_TASK_AUTH_LAUNCHABLE_PRIV; import static com.android.server.wm.TaskRecord.LOCK_TASK_AUTH_WHITELISTED; @@ -103,20 +82,11 @@ import static com.android.server.wm.TaskRecord.REPARENT_KEEP_STACK_AT_FRONT; import static com.android.server.wm.TaskRecord.REPARENT_LEAVE_STACK_IN_PLACE; import static com.android.server.wm.TaskRecord.REPARENT_MOVE_STACK_TO_FRONT; -import static java.lang.Integer.MAX_VALUE; - import android.Manifest; -import android.annotation.IntDef; -import android.annotation.NonNull; -import android.annotation.Nullable; -import android.annotation.UserIdInt; import android.app.Activity; import android.app.ActivityManager; -import android.app.ActivityManager.RunningTaskInfo; -import android.app.ActivityManager.StackInfo; import android.app.ActivityManagerInternal; import android.app.ActivityOptions; -import android.app.AppGlobals; import android.app.AppOpsManager; import android.app.ProfilerInfo; import android.app.ResultInfo; @@ -139,17 +109,11 @@ import android.content.pm.PackageManager; import android.content.pm.ResolveInfo; import android.content.pm.UserInfo; import android.content.res.Configuration; -import android.content.res.Resources; import android.graphics.Rect; -import android.hardware.display.DisplayManager; -import android.hardware.display.DisplayManager.DisplayListener; -import android.hardware.display.DisplayManagerInternal; -import android.hardware.power.V1_0.PowerHint; import android.os.Binder; import android.os.Build; import android.os.Bundle; import android.os.Debug; -import android.os.FactoryTest; import android.os.Handler; import android.os.IBinder; import android.os.Looper; @@ -163,20 +127,13 @@ import android.os.UserHandle; import android.os.UserManager; import android.os.WorkSource; import android.provider.MediaStore; -import android.service.voice.IVoiceInteractionSession; import android.util.ArrayMap; import android.util.ArraySet; -import android.util.DisplayMetrics; import android.util.EventLog; -import android.util.IntArray; import android.util.MergedConfiguration; import android.util.Slog; import android.util.SparseArray; import android.util.SparseIntArray; -import android.util.TimeUtils; -import android.util.proto.ProtoOutputStream; -import android.view.Display; -import android.view.DisplayInfo; import com.android.internal.annotations.GuardedBy; import com.android.internal.annotations.VisibleForTesting; @@ -185,34 +142,28 @@ import com.android.internal.os.TransferPipe; import com.android.internal.os.logging.MetricsLoggerWrapper; import com.android.internal.util.ArrayUtils; import com.android.internal.util.function.pooled.PooledLambda; -import com.android.server.LocalServices; import com.android.server.am.ActivityManagerService; -import com.android.server.am.AppTimeTracker; import com.android.server.am.EventLogTags; import com.android.server.am.UserState; -import com.android.server.wm.ActivityStack.ActivityState; -import com.android.server.wm.ActivityTaskManagerInternal.SleepToken; import java.io.FileDescriptor; import java.io.IOException; import java.io.PrintWriter; -import java.lang.annotation.Retention; -import java.lang.annotation.RetentionPolicy; import java.util.ArrayList; -import java.util.Iterator; import java.util.List; -import java.util.Set; -public class ActivityStackSupervisor extends ConfigurationContainer implements DisplayListener, - RecentTasks.Callbacks, RootWindowContainerListener { +// TODO: This class has become a dumping ground. Let's +// - Move things relating to the hierarchy to RootWindowContainer +// - Move things relating to activity life cycles to maybe a new class called ActivityLifeCycler +// - Move interface things to ActivityTaskManagerService. +// - All other little things to other files. +public class ActivityStackSupervisor implements RecentTasks.Callbacks { private static final String TAG = TAG_WITH_CLASS_NAME ? "ActivityStackSupervisor" : TAG_ATM; private static final String TAG_IDLE = TAG + POSTFIX_IDLE; private static final String TAG_PAUSE = TAG + POSTFIX_PAUSE; private static final String TAG_RECENTS = TAG + POSTFIX_RECENTS; - private static final String TAG_RELEASE = TAG + POSTFIX_RELEASE; private static final String TAG_STACK = TAG + POSTFIX_STACK; private static final String TAG_SWITCH = TAG + POSTFIX_SWITCH; - static final String TAG_STATES = TAG + POSTFIX_STATES; static final String TAG_TASKS = TAG + POSTFIX_TASKS; /** How long we wait until giving up on the last activity telling us it is idle. */ @@ -233,12 +184,6 @@ public class ActivityStackSupervisor extends ConfigurationContainer implements D static final int REPORT_MULTI_WINDOW_MODE_CHANGED_MSG = FIRST_SUPERVISOR_STACK_MSG + 14; static final int REPORT_PIP_MODE_CHANGED_MSG = FIRST_SUPERVISOR_STACK_MSG + 15; - private static final String VIRTUAL_DISPLAY_BASE_NAME = "ActivityViewVirtualDisplay"; - - // Used to indicate if an object (e.g. stack) that we are trying to get - // should be created if it doesn't exist already. - static final boolean CREATE_IF_NEEDED = true; - // Used to indicate that windows of activities should be preserved during the resize. static final boolean PRESERVE_WINDOWS = true; @@ -270,25 +215,6 @@ public class ActivityStackSupervisor extends ConfigurationContainer implements D private Rect mPendingTempOtherTaskBounds; private Rect mPendingTempOtherTaskInsetBounds; - /** - * The modes which affect which tasks are returned when calling - * {@link ActivityStackSupervisor#anyTaskForIdLocked(int)}. - */ - @Retention(RetentionPolicy.SOURCE) - @IntDef({ - MATCH_TASK_IN_STACKS_ONLY, - MATCH_TASK_IN_STACKS_OR_RECENT_TASKS, - MATCH_TASK_IN_STACKS_OR_RECENT_TASKS_AND_RESTORE - }) - public @interface AnyTaskForIdMatchTaskMode {} - // Match only tasks in the current stacks - static final int MATCH_TASK_IN_STACKS_ONLY = 0; - // Match either tasks in the current stacks, or in the recent tasks if not found in the stacks - static final int MATCH_TASK_IN_STACKS_OR_RECENT_TASKS = 1; - // Match either tasks in the current stacks, or in the recent tasks, restoring it to the - // provided stack id - static final int MATCH_TASK_IN_STACKS_OR_RECENT_TASKS_AND_RESTORE = 2; - // Activity actions an app cannot start if it uses a permission which is not granted. private static final ArrayMap<String, String> ACTION_TO_RUNTIME_PERMISSION = new ArrayMap<>(); @@ -316,19 +242,19 @@ public class ActivityStackSupervisor extends ConfigurationContainer implements D private static final int MAX_TASK_IDS_PER_USER = UserHandle.PER_USER_RANGE; final ActivityTaskManagerService mService; + RootActivityContainer mRootActivityContainer; /** The historial list of recent tasks including inactive tasks */ RecentTasks mRecentTasks; /** Helper class to abstract out logic for fetching the set of currently running tasks */ - private RunningTasks mRunningTasks; + RunningTasks mRunningTasks; final ActivityStackSupervisorHandler mHandler; final Looper mLooper; /** Short cut */ WindowManagerService mWindowManager; - DisplayManager mDisplayManager; /** Common synchronization logic used to save things to disks. */ PersisterQueue mPersisterQueue; @@ -341,9 +267,6 @@ public class ActivityStackSupervisor extends ConfigurationContainer implements D */ private final SparseIntArray mCurTaskIdForUser = new SparseIntArray(20); - /** The current user */ - int mCurrentUser; - /** List of activities that are waiting for a new activity to become visible before completing * whatever operation they are supposed to do. */ // TODO: Remove mActivitiesWaitingForVisibleActivity list and just remove activity from @@ -392,9 +315,6 @@ public class ActivityStackSupervisor extends ConfigurationContainer implements D * is being brought in front of us. */ boolean mUserLeaving = false; - /** Set when a power hint has started, but not ended. */ - private boolean mPowerHintSent; - /** * We don't want to allow the device to go to sleep while in the process * of launching an activity. This is primarily to allow alarm intent @@ -410,29 +330,6 @@ public class ActivityStackSupervisor extends ConfigurationContainer implements D */ PowerManager.WakeLock mGoingToSleep; - /** - * A list of tokens that cause the top activity to be put to sleep. - * They are used by components that may hide and block interaction with underlying - * activities. - */ - final ArrayList<SleepToken> mSleepTokens = new ArrayList<>(); - - /** Stack id of the front stack when user switched, indexed by userId. */ - SparseIntArray mUserStackInFront = new SparseIntArray(2); - - /** Reference to default display so we can quickly look it up. */ - private ActivityDisplay mDefaultDisplay; - - /** - * List of displays which contain activities, sorted by z-order. - * The last entry in the list is the topmost. - */ - private final ArrayList<ActivityDisplay> mActivityDisplays = new ArrayList<>(); - - private final SparseArray<IntArray> mDisplayAccessUIDs = new SparseArray<>(); - - private DisplayManagerInternal mDisplayManagerInternal; - /** Used to keep resumeTopActivityUncheckedLocked() from being entered recursively */ boolean inResumeTopActivity; @@ -443,50 +340,8 @@ public class ActivityStackSupervisor extends ConfigurationContainer implements D private final Rect tempRect = new Rect(); private final ActivityOptions mTmpOptions = ActivityOptions.makeBasic(); - // The default minimal size that will be used if the activity doesn't specify its minimal size. - // It will be calculated when the default display gets added. - int mDefaultMinSizeOfResizeableTaskDp = -1; - - // Whether tasks have moved and we need to rank the tasks before next OOM scoring - private boolean mTaskLayersChanged = true; - private ActivityMetricsLogger mActivityMetricsLogger; - private final ArrayList<ActivityRecord> mTmpActivityList = new ArrayList<>(); - - @Override - protected int getChildCount() { - return mActivityDisplays.size(); - } - - @Override - protected ActivityDisplay getChildAt(int index) { - return mActivityDisplays.get(index); - } - - @Override - protected ConfigurationContainer getParent() { - return null; - } - - Configuration getDisplayOverrideConfiguration(int displayId) { - final ActivityDisplay activityDisplay = getActivityDisplayOrCreateLocked(displayId); - if (activityDisplay == null) { - throw new IllegalArgumentException("No display found with id: " + displayId); - } - - return activityDisplay.getOverrideConfiguration(); - } - - void setDisplayOverrideConfiguration(Configuration overrideConfiguration, int displayId) { - final ActivityDisplay activityDisplay = getActivityDisplayOrCreateLocked(displayId); - if (activityDisplay == null) { - throw new IllegalArgumentException("No display found with id: " + displayId); - } - - activityDisplay.onOverrideConfigurationChanged(overrideConfiguration); - } - /** Check if placing task or activity on specified display is allowed. */ boolean canPlaceEntityOnDisplay(int displayId, int callingPid, int callingUid, ActivityInfo activityInfo) { @@ -508,44 +363,6 @@ public class ActivityStackSupervisor extends ConfigurationContainer implements D } /** - * Check if configuration of specified display matches current global config. - * Used to check if we can put a non-resizeable activity on a secondary display and it will get - * the same config as on the default display. - * @param displayId Id of the display to check. - * @return {@code true} if configuration matches. - */ - private boolean displayConfigMatchesGlobal(int displayId) { - if (displayId == DEFAULT_DISPLAY) { - return true; - } - if (displayId == INVALID_DISPLAY) { - return false; - } - final ActivityDisplay targetDisplay = getActivityDisplayOrCreateLocked(displayId); - if (targetDisplay == null) { - throw new IllegalArgumentException("No display found with id: " + displayId); - } - return getConfiguration().equals(targetDisplay.getConfiguration()); - } - - static class FindTaskResult { - ActivityRecord mRecord; - boolean mIdealMatch; - - void clear() { - mRecord = null; - mIdealMatch = false; - } - - void setTo(FindTaskResult result) { - mRecord = result.mRecord; - mIdealMatch = result.mIdealMatch; - } - } - - private final FindTaskResult mTmpFindTaskResult = new FindTaskResult(); - - /** * Used to keep track whether app visibilities got changed since the last pause. Useful to * determine whether to invoke the task stack change listener after pausing. */ @@ -565,11 +382,6 @@ public class ActivityStackSupervisor extends ConfigurationContainer implements D */ private boolean mAllowDockedStackResize = true; - /** - * Is dock currently minimized. - */ - boolean mIsDockMinimized; - private KeyguardController mKeyguardController; private PowerManager mPowerManager; @@ -577,8 +389,6 @@ public class ActivityStackSupervisor extends ConfigurationContainer implements D private boolean mInitialized; - private RootWindowContainerController mWindowContainerController; - /** * Description of a request to start a new activity, which has been held * due to app switches being disabled. @@ -617,11 +427,6 @@ public class ActivityStackSupervisor extends ConfigurationContainer implements D mHandler = new ActivityStackSupervisorHandler(looper); } - @VisibleForTesting - void setWindowContainerController(RootWindowContainerController controller) { - mWindowContainerController = controller; - } - public void initialize() { if (mInitialized) { return; @@ -676,321 +481,16 @@ public class ActivityStackSupervisor extends ConfigurationContainer implements D void setWindowManager(WindowManagerService wm) { mWindowManager = wm; getKeyguardController().setWindowManager(wm); - setWindowContainerController(new RootWindowContainerController(this)); - - mDisplayManager = mService.mContext.getSystemService(DisplayManager.class); - mDisplayManager.registerDisplayListener(this, mHandler); - mDisplayManagerInternal = LocalServices.getService(DisplayManagerInternal.class); - - final Display[] displays = mDisplayManager.getDisplays(); - for (int displayNdx = 0; displayNdx < displays.length; ++displayNdx) { - final Display display = displays[displayNdx]; - final ActivityDisplay activityDisplay = new ActivityDisplay(this, display); - if (activityDisplay.mDisplayId == DEFAULT_DISPLAY) { - mDefaultDisplay = activityDisplay; - } - addChild(activityDisplay, ActivityDisplay.POSITION_TOP); - } - calculateDefaultMinimalSizeOfResizeableTasks(); - - final ActivityDisplay defaultDisplay = getDefaultDisplay(); - - defaultDisplay.getOrCreateStack(WINDOWING_MODE_FULLSCREEN, ACTIVITY_TYPE_HOME, ON_TOP); - positionChildAt(defaultDisplay, ActivityDisplay.POSITION_TOP); - } - - /** Change the z-order of the given display. */ - private void positionChildAt(ActivityDisplay display, int position) { - if (position >= mActivityDisplays.size()) { - position = mActivityDisplays.size() - 1; - } else if (position < 0) { - position = 0; - } - - if (mActivityDisplays.isEmpty()) { - mActivityDisplays.add(display); - } else if (mActivityDisplays.get(position) != display) { - mActivityDisplays.remove(display); - mActivityDisplays.add(position, display); - } - } - - @Override - public void onChildPositionChanged(DisplayWindowController childController, int position) { - // Assume AM lock is held from positionChildAt of controller in each hierarchy. - final ActivityDisplay display = getActivityDisplay(childController.getDisplayId()); - if (display != null) { - positionChildAt(display, position); - } - } - - ActivityStack getTopDisplayFocusedStack() { - for (int i = mActivityDisplays.size() - 1; i >= 0; --i) { - final ActivityStack focusedStack = mActivityDisplays.get(i).getFocusedStack(); - if (focusedStack != null) { - return focusedStack; - } - } - return null; - } - - ActivityRecord getTopResumedActivity() { - final ActivityStack focusedStack = getTopDisplayFocusedStack(); - if (focusedStack == null) { - return null; - } - final ActivityRecord resumedActivity = focusedStack.getResumedActivity(); - if (resumedActivity != null && resumedActivity.app != null) { - return resumedActivity; - } - // The top focused stack might not have a resumed activity yet - look on all displays in - // focus order. - for (int i = mActivityDisplays.size() - 1; i >= 0; --i) { - final ActivityDisplay display = mActivityDisplays.get(i); - final ActivityRecord resumedActivityOnDisplay = display.getResumedActivity(); - if (resumedActivityOnDisplay != null) { - return resumedActivityOnDisplay; - } - } - return null; - } - - boolean isFocusable(ConfigurationContainer container, boolean alwaysFocusable) { - if (container.inSplitScreenPrimaryWindowingMode() && mIsDockMinimized) { - return false; - } - - return container.getWindowConfiguration().canReceiveKeys() || alwaysFocusable; - } - - boolean isTopDisplayFocusedStack(ActivityStack stack) { - return stack != null && stack == getTopDisplayFocusedStack(); } void moveRecentsStackToFront(String reason) { - final ActivityStack recentsStack = getDefaultDisplay().getStack( + final ActivityStack recentsStack = mRootActivityContainer.getDefaultDisplay().getStack( WINDOWING_MODE_UNDEFINED, ACTIVITY_TYPE_RECENTS); if (recentsStack != null) { recentsStack.moveToFront(reason); } } - boolean resumeHomeActivity(ActivityRecord prev, String reason, int displayId) { - if (!mService.isBooting() && !mService.isBooted()) { - // Not ready yet! - return false; - } - - if (displayId == INVALID_DISPLAY) { - displayId = DEFAULT_DISPLAY; - } - - final ActivityRecord r = getActivityDisplay(displayId).getHomeActivity(); - final String myReason = reason + " resumeHomeActivity"; - - // Only resume home activity if isn't finishing. - if (r != null && !r.finishing) { - r.moveFocusableActivityToTop(myReason); - return resumeFocusedStacksTopActivitiesLocked(r.getStack(), prev, null); - } - return startHomeOnDisplay(mCurrentUser, myReason, displayId); - } - - /** - * Check if home activity start should be allowed on a display. - * @param homeInfo {@code ActivityInfo} of the home activity that is going to be launched. - * @param displayId The id of the target display. - * @param allowInstrumenting Whether launching home should be allowed if being instrumented. - * @return {@code true} if allow to launch, {@code false} otherwise. - */ - boolean canStartHomeOnDisplay(ActivityInfo homeInfo, int displayId, - boolean allowInstrumenting) { - if (mService.mFactoryTest == FactoryTest.FACTORY_TEST_LOW_LEVEL - && mService.mTopAction == null) { - // We are running in factory test mode, but unable to find the factory test app, so - // just sit around displaying the error message and don't try to start anything. - return false; - } - - final WindowProcessController app = - mService.getProcessController(homeInfo.processName, homeInfo.applicationInfo.uid); - if (!allowInstrumenting && app != null && app.isInstrumenting()) { - // Don't do this if the home app is currently being instrumented. - return false; - } - - if (displayId == DEFAULT_DISPLAY || (displayId != INVALID_DISPLAY - && displayId == mService.mVr2dDisplayId)) { - // No restrictions to default display or vr 2d display. - return true; - } - - final ActivityDisplay display = getActivityDisplay(displayId); - if (display == null || display.isRemoved() || !display.supportsSystemDecorations()) { - // Can't launch home on display that doesn't support system decorations. - return false; - } - - final boolean supportMultipleInstance = homeInfo.launchMode != LAUNCH_SINGLE_TASK - && homeInfo.launchMode != LAUNCH_SINGLE_INSTANCE - && homeInfo.applicationInfo.targetSdkVersion >= Build.VERSION_CODES.Q; - if (!supportMultipleInstance) { - // Can't launch home on other displays if it requested to be single instance. Also we - // don't allow home applications that target before Q to have multiple home activity - // instances because they may not be expected to have multiple home scenario and - // haven't explicitly request for single instance. - return false; - } - - return true; - } - - TaskRecord anyTaskForIdLocked(int id) { - return anyTaskForIdLocked(id, MATCH_TASK_IN_STACKS_OR_RECENT_TASKS_AND_RESTORE); - } - - TaskRecord anyTaskForIdLocked(int id, @AnyTaskForIdMatchTaskMode int matchMode) { - return anyTaskForIdLocked(id, matchMode, null, !ON_TOP); - } - - /** - * Returns a {@link TaskRecord} for the input id if available. {@code null} otherwise. - * @param id Id of the task we would like returned. - * @param matchMode The mode to match the given task id in. - * @param aOptions The activity options to use for restoration. Can be null. - * @param onTop If the stack for the task should be the topmost on the display. - */ - TaskRecord anyTaskForIdLocked(int id, @AnyTaskForIdMatchTaskMode int matchMode, - @Nullable ActivityOptions aOptions, boolean onTop) { - // If options are set, ensure that we are attempting to actually restore a task - if (matchMode != MATCH_TASK_IN_STACKS_OR_RECENT_TASKS_AND_RESTORE && aOptions != null) { - throw new IllegalArgumentException("Should not specify activity options for non-restore" - + " lookup"); - } - - int numDisplays = mActivityDisplays.size(); - for (int displayNdx = 0; displayNdx < numDisplays; ++displayNdx) { - final ActivityDisplay display = mActivityDisplays.get(displayNdx); - for (int stackNdx = display.getChildCount() - 1; stackNdx >= 0; --stackNdx) { - final ActivityStack stack = display.getChildAt(stackNdx); - final TaskRecord task = stack.taskForIdLocked(id); - if (task == null) { - continue; - } - if (aOptions != null) { - // Resolve the stack the task should be placed in now based on options - // and reparent if needed. - final ActivityStack launchStack = getLaunchStack(null, aOptions, task, onTop); - if (launchStack != null && stack != launchStack) { - final int reparentMode = onTop - ? REPARENT_MOVE_STACK_TO_FRONT : REPARENT_LEAVE_STACK_IN_PLACE; - task.reparent(launchStack, onTop, reparentMode, ANIMATE, DEFER_RESUME, - "anyTaskForIdLocked"); - } - } - return task; - } - } - - // If we are matching stack tasks only, return now - if (matchMode == MATCH_TASK_IN_STACKS_ONLY) { - return null; - } - - // Otherwise, check the recent tasks and return if we find it there and we are not restoring - // the task from recents - if (DEBUG_RECENTS) Slog.v(TAG_RECENTS, "Looking for task id=" + id + " in recents"); - final TaskRecord task = mRecentTasks.getTask(id); - - if (task == null) { - if (DEBUG_RECENTS) { - Slog.d(TAG_RECENTS, "\tDidn't find task id=" + id + " in recents"); - } - - return null; - } - - if (matchMode == MATCH_TASK_IN_STACKS_OR_RECENT_TASKS) { - return task; - } - - // Implicitly, this case is MATCH_TASK_IN_STACKS_OR_RECENT_TASKS_AND_RESTORE - if (!restoreRecentTaskLocked(task, aOptions, onTop)) { - if (DEBUG_RECENTS) Slog.w(TAG_RECENTS, - "Couldn't restore task id=" + id + " found in recents"); - return null; - } - if (DEBUG_RECENTS) Slog.w(TAG_RECENTS, "Restored task id=" + id + " from in recents"); - return task; - } - - ActivityRecord isInAnyStackLocked(IBinder token) { - int numDisplays = mActivityDisplays.size(); - for (int displayNdx = 0; displayNdx < numDisplays; ++displayNdx) { - final ActivityDisplay display = mActivityDisplays.get(displayNdx); - for (int stackNdx = display.getChildCount() - 1; stackNdx >= 0; --stackNdx) { - final ActivityStack stack = display.getChildAt(stackNdx); - final ActivityRecord r = stack.isInStackLocked(token); - if (r != null) { - return r; - } - } - } - return null; - } - - /** - * Detects whether we should show a lock screen in front of this task for a locked user. - * <p> - * We'll do this if either of the following holds: - * <ul> - * <li>The top activity explicitly belongs to {@param userId}.</li> - * <li>The top activity returns a result to an activity belonging to {@param userId}.</li> - * </ul> - * - * @return {@code true} if the top activity looks like it belongs to {@param userId}. - */ - private boolean taskTopActivityIsUser(TaskRecord task, @UserIdInt int userId) { - // To handle the case that work app is in the task but just is not the top one. - final ActivityRecord activityRecord = task.getTopActivity(); - final ActivityRecord resultTo = (activityRecord != null ? activityRecord.resultTo : null); - - return (activityRecord != null && activityRecord.userId == userId) - || (resultTo != null && resultTo.userId == userId); - } - - /** - * Find all visible task stacks containing {@param userId} and intercept them with an activity - * to block out the contents and possibly start a credential-confirming intent. - * - * @param userId user handle for the locked managed profile. - */ - void lockAllProfileTasks(@UserIdInt int userId) { - mWindowManager.deferSurfaceLayout(); - try { - for (int displayNdx = mActivityDisplays.size() - 1; displayNdx >= 0; --displayNdx) { - final ActivityDisplay display = mActivityDisplays.get(displayNdx); - for (int stackNdx = display.getChildCount() - 1; stackNdx >= 0; --stackNdx) { - final ActivityStack stack = display.getChildAt(stackNdx); - final List<TaskRecord> tasks = stack.getAllTasks(); - for (int taskNdx = tasks.size() - 1; taskNdx >= 0; taskNdx--) { - final TaskRecord task = tasks.get(taskNdx); - - // Check the task for a top activity belonging to userId, or returning a - // result to an activity belonging to userId. Example case: a document - // picker for personal files, opened by a work app, should still get locked. - if (taskTopActivityIsUser(task, userId)) { - mService.getTaskChangeNotificationController().notifyTaskProfileLocked( - task.taskId, userId); - } - } - } - } - } finally { - mWindowManager.continueSurfaceLayout(); - } - } - void setNextTaskIdForUserLocked(int taskId, int userId) { final int currentTaskId = mCurTaskIdForUser.get(userId, -1); if (taskId > currentTaskId) { @@ -1014,7 +514,7 @@ public class ActivityStackSupervisor extends ConfigurationContainer implements D // was 10, user 0 could only have taskIds 0 to 9, user 1: 10 to 19, user 2: 20 to 29, so on. int candidateTaskId = nextTaskIdForUser(currentTaskId, userId); while (mRecentTasks.containsTaskId(candidateTaskId, userId) - || anyTaskForIdLocked( + || mRootActivityContainer.anyTaskForId( candidateTaskId, MATCH_TASK_IN_STACKS_OR_RECENT_TASKS) != null) { candidateTaskId = nextTaskIdForUser(candidateTaskId, userId); if (candidateTaskId == currentTaskId) { @@ -1029,142 +529,6 @@ public class ActivityStackSupervisor extends ConfigurationContainer implements D return candidateTaskId; } - boolean attachApplicationLocked(WindowProcessController app) throws RemoteException { - final String processName = app.mName; - boolean didSomething = false; - for (int displayNdx = mActivityDisplays.size() - 1; displayNdx >= 0; --displayNdx) { - final ActivityDisplay display = mActivityDisplays.get(displayNdx); - final ActivityStack stack = display.getFocusedStack(); - if (stack != null) { - stack.getAllRunningVisibleActivitiesLocked(mTmpActivityList); - final ActivityRecord top = stack.topRunningActivityLocked(); - final int size = mTmpActivityList.size(); - for (int i = 0; i < size; i++) { - final ActivityRecord activity = mTmpActivityList.get(i); - if (activity.app == null && app.mUid == activity.info.applicationInfo.uid - && processName.equals(activity.processName)) { - try { - if (realStartActivityLocked(activity, app, - top == activity /* andResume */, true /* checkConfig */)) { - didSomething = true; - } - } catch (RemoteException e) { - Slog.w(TAG, "Exception in new application when starting activity " - + top.intent.getComponent().flattenToShortString(), e); - throw e; - } - } - } - } - } - if (!didSomething) { - ensureActivitiesVisibleLocked(null, 0, !PRESERVE_WINDOWS); - } - return didSomething; - } - - boolean allResumedActivitiesIdle() { - for (int displayNdx = mActivityDisplays.size() - 1; displayNdx >= 0; --displayNdx) { - // TODO(b/117135575): Check resumed activities on all visible stacks. - final ActivityDisplay display = mActivityDisplays.get(displayNdx); - if (display.isSleeping()) { - // No resumed activities while display is sleeping. - continue; - } - - // If the focused stack is not null or not empty, there should have some activities - // resuming or resumed. Make sure these activities are idle. - final ActivityStack stack = display.getFocusedStack(); - if (stack == null || stack.numActivities() == 0) { - continue; - } - final ActivityRecord resumedActivity = stack.getResumedActivity(); - if (resumedActivity == null || !resumedActivity.idle) { - if (DEBUG_STATES) { - Slog.d(TAG_STATES, "allResumedActivitiesIdle: stack=" - + stack.mStackId + " " + resumedActivity + " not idle"); - } - return false; - } - } - // Send launch end powerhint when idle - sendPowerHintForLaunchEndIfNeeded(); - return true; - } - - private boolean allResumedActivitiesVisible() { - boolean foundResumed = false; - for (int displayNdx = mActivityDisplays.size() - 1; displayNdx >= 0; --displayNdx) { - final ActivityDisplay display = mActivityDisplays.get(displayNdx); - for (int stackNdx = display.getChildCount() - 1; stackNdx >= 0; --stackNdx) { - final ActivityStack stack = display.getChildAt(stackNdx); - final ActivityRecord r = stack.getResumedActivity(); - if (r != null) { - if (!r.nowVisible || mActivitiesWaitingForVisibleActivity.contains(r)) { - return false; - } - foundResumed = true; - } - } - } - return foundResumed; - } - - private void executeAppTransitionForAllDisplay() { - for (int displayNdx = mActivityDisplays.size() - 1; displayNdx >= 0; --displayNdx) { - final ActivityDisplay display = mActivityDisplays.get(displayNdx); - display.getWindowContainerController().executeAppTransition(); - } - } - - /** - * Pause all activities in either all of the stacks or just the back stacks. - * @param userLeaving Passed to pauseActivity() to indicate whether to call onUserLeaving(). - * @param resuming The resuming activity. - * @param dontWait The resuming activity isn't going to wait for all activities to be paused - * before resuming. - * @return true if any activity was paused as a result of this call. - */ - boolean pauseBackStacks(boolean userLeaving, ActivityRecord resuming, boolean dontWait) { - boolean someActivityPaused = false; - for (int displayNdx = mActivityDisplays.size() - 1; displayNdx >= 0; --displayNdx) { - someActivityPaused |= mActivityDisplays.get(displayNdx) - .pauseBackStacks(userLeaving, resuming, dontWait); - } - return someActivityPaused; - } - - boolean allPausedActivitiesComplete() { - boolean pausing = true; - for (int displayNdx = mActivityDisplays.size() - 1; displayNdx >= 0; --displayNdx) { - final ActivityDisplay display = mActivityDisplays.get(displayNdx); - for (int stackNdx = display.getChildCount() - 1; stackNdx >= 0; --stackNdx) { - final ActivityStack stack = display.getChildAt(stackNdx); - final ActivityRecord r = stack.mPausingActivity; - if (r != null && !r.isState(PAUSED, STOPPED, STOPPING)) { - if (DEBUG_STATES) { - Slog.d(TAG_STATES, - "allPausedActivitiesComplete: r=" + r + " state=" + r.getState()); - pausing = false; - } else { - return false; - } - } - } - } - return pausing; - } - - void cancelInitializingActivities() { - for (int displayNdx = mActivityDisplays.size() - 1; displayNdx >= 0; --displayNdx) { - final ActivityDisplay display = mActivityDisplays.get(displayNdx); - for (int stackNdx = display.getChildCount() - 1; stackNdx >= 0; --stackNdx) { - final ActivityStack stack = display.getChildAt(stackNdx); - stack.cancelInitializingActivities(); - } - } - } - void waitActivityVisible(ComponentName name, WaitResult result, long startTimeMs) { final WaitInfo waitInfo = new WaitInfo(name, result, startTimeMs); mWaitingForActivityVisible.add(waitInfo); @@ -1255,24 +619,6 @@ public class ActivityStackSupervisor extends ConfigurationContainer implements D } } - ActivityRecord topRunningActivityLocked() { - for (int i = mActivityDisplays.size() - 1; i >= 0; --i) { - final ActivityRecord topActivity = mActivityDisplays.get(i).topRunningActivity(); - if (topActivity != null) { - return topActivity; - } - } - return null; - } - - @VisibleForTesting - void getRunningTasks(int maxNum, List<RunningTaskInfo> list, - @ActivityType int ignoreActivityType, @WindowingMode int ignoreWindowingMode, - int callingUid, boolean allowed) { - mRunningTasks.getTasks(maxNum, list, ignoreActivityType, ignoreWindowingMode, - mActivityDisplays, callingUid, allowed); - } - ActivityInfo resolveActivity(Intent intent, ResolveInfo rInfo, int startFlags, ProfilerInfo profilerInfo) { final ActivityInfo aInfo = rInfo != null ? rInfo.activityInfo : null; @@ -1352,10 +698,10 @@ public class ActivityStackSupervisor extends ConfigurationContainer implements D return resolveActivity(intent, rInfo, startFlags, profilerInfo); } - private boolean realStartActivityLocked(ActivityRecord r, WindowProcessController proc, + boolean realStartActivityLocked(ActivityRecord r, WindowProcessController proc, boolean andResume, boolean checkConfig) throws RemoteException { - if (!allPausedActivitiesComplete()) { + if (!mRootActivityContainer.allPausedActivitiesComplete()) { // While there are activities pausing we skipping starting any new activities until // pauses are complete. NOTE: that we also do this for activities that are starting in // the paused state because they will first be resumed then paused on the client side. @@ -1390,7 +736,7 @@ public class ActivityStackSupervisor extends ConfigurationContainer implements D // Deferring resume here because we're going to launch new activity shortly. // We don't want to perform a redundant launch of the same record while ensuring // configurations and trying to resume top activity of focused stack. - ensureVisibilityAndConfig(r, r.getDisplayId(), + mRootActivityContainer.ensureVisibilityAndConfig(r, r.getDisplayId(), false /* markFrozenIfConfigChanged */, true /* deferResume */); } @@ -1560,7 +906,7 @@ public class ActivityStackSupervisor extends ConfigurationContainer implements D // launching the initial activity (that is, home), so that it can have // a chance to initialize itself while in the background, making the // switch back to it faster and look better. - if (isTopDisplayFocusedStack(stack)) { + if (mRootActivityContainer.isTopDisplayFocusedStack(stack)) { mService.getActivityStartController().startSetupActivity(); } @@ -1573,47 +919,6 @@ public class ActivityStackSupervisor extends ConfigurationContainer implements D return true; } - /** - * Ensure all activities visibility, update orientation and configuration. - * - * @param starting The currently starting activity or {@code null} if there is none. - * @param displayId The id of the display where operation is executed. - * @param markFrozenIfConfigChanged Whether to set {@link ActivityRecord#frozenBeforeDestroy} to - * {@code true} if config changed. - * @param deferResume Whether to defer resume while updating config. - * @return 'true' if starting activity was kept or wasn't provided, 'false' if it was relaunched - * because of configuration update. - */ - boolean ensureVisibilityAndConfig(ActivityRecord starting, int displayId, - boolean markFrozenIfConfigChanged, boolean deferResume) { - // First ensure visibility without updating the config just yet. We need this to know what - // activities are affecting configuration now. - // Passing null here for 'starting' param value, so that visibility of actual starting - // activity will be properly updated. - ensureActivitiesVisibleLocked(null /* starting */, 0 /* configChanges */, - false /* preserveWindows */, false /* notifyClients */); - - if (displayId == INVALID_DISPLAY) { - // The caller didn't provide a valid display id, skip updating config. - return true; - } - - // Force-update the orientation from the WindowManager, since we need the true configuration - // to send to the client now. - final Configuration config = mWindowManager.updateOrientationFromAppTokens( - getDisplayOverrideConfiguration(displayId), - starting != null && starting.mayFreezeScreenLocked(starting.app) - ? starting.appToken : null, - displayId, true /* forceUpdate */); - if (starting != null && markFrozenIfConfigChanged && config != null) { - starting.frozenBeforeDestroy = true; - } - - // Update the configuration of the activities on the display. - return mService.updateDisplayOverrideConfigurationLocked(config, starting, deferResume, - displayId); - } - private void logIfTransactionTooLarge(Intent intent, Bundle icicle) { int extrasSize = 0; if (intent != null) { @@ -1669,47 +974,6 @@ public class ActivityStackSupervisor extends ConfigurationContainer implements D mService.mH.sendMessage(msg); } - void sendPowerHintForLaunchStartIfNeeded(boolean forceSend, ActivityRecord targetActivity) { - boolean sendHint = forceSend; - - if (!sendHint) { - // Send power hint if we don't know what we're launching yet - sendHint = targetActivity == null || targetActivity.app == null; - } - - if (!sendHint) { // targetActivity != null - // Send power hint when the activity's process is different than the current resumed - // activity on all displays, or if there are no resumed activities in the system. - boolean noResumedActivities = true; - boolean allFocusedProcessesDiffer = true; - for (int displayNdx = 0; displayNdx < mActivityDisplays.size(); ++displayNdx) { - final ActivityDisplay activityDisplay = mActivityDisplays.get(displayNdx); - final ActivityRecord resumedActivity = activityDisplay.getResumedActivity(); - final WindowProcessController resumedActivityProcess = - resumedActivity == null ? null : resumedActivity.app; - - noResumedActivities &= resumedActivityProcess == null; - if (resumedActivityProcess != null) { - allFocusedProcessesDiffer &= !resumedActivityProcess.equals(targetActivity.app); - } - } - sendHint = noResumedActivities || allFocusedProcessesDiffer; - } - - if (sendHint && mService.mPowerManagerInternal != null) { - mService.mPowerManagerInternal.powerHint(PowerHint.LAUNCH, 1); - mPowerHintSent = true; - } - } - - void sendPowerHintForLaunchEndIfNeeded() { - // Trigger launch power hint if activity is launched - if (mPowerHintSent && mService.mPowerManagerInternal != null) { - mService.mPowerManagerInternal.powerHint(PowerHint.LAUNCH, 0); - mPowerHintSent = false; - } - } - boolean checkStartAnyActivityPermission(Intent intent, ActivityInfo aInfo, String resultWho, int requestCode, int callingPid, int callingUid, String callingPackage, boolean ignoreTargetSecurity, boolean launchingInTask, @@ -1788,7 +1052,8 @@ public class ActivityStackSupervisor extends ConfigurationContainer implements D return true; } - final ActivityDisplay activityDisplay = getActivityDisplayOrCreateLocked(launchDisplayId); + final ActivityDisplay activityDisplay = + mRootActivityContainer.getActivityDisplayOrCreate(launchDisplayId); if (activityDisplay == null || activityDisplay.isRemoved()) { Slog.w(TAG, "Launch on display check: display not found"); return false; @@ -1850,21 +1115,6 @@ public class ActivityStackSupervisor extends ConfigurationContainer implements D return false; } - /** Update lists of UIDs that are present on displays and have access to them. */ - void updateUIDsPresentOnDisplay() { - mDisplayAccessUIDs.clear(); - for (int displayNdx = mActivityDisplays.size() - 1; displayNdx >= 0; --displayNdx) { - final ActivityDisplay activityDisplay = mActivityDisplays.get(displayNdx); - // Only bother calculating the whitelist for private displays - if (activityDisplay.isPrivate()) { - mDisplayAccessUIDs.append( - activityDisplay.mDisplayId, activityDisplay.getPresentUIDs()); - } - } - // Store updated lists in DisplayManager. Callers from outside of AM should get them there. - mDisplayManagerInternal.setDisplayAccessUIDs(mDisplayAccessUIDs); - } - UserInfo getUserInfo(int userId) { final long identity = Binder.clearCallingIdentity(); try { @@ -2016,7 +1266,8 @@ public class ActivityStackSupervisor extends ConfigurationContainer implements D // Check if able to finish booting when device is booting and all resumed activities // are idle. - if ((mService.isBooting() && allResumedActivitiesIdle()) || fromTimeout) { + if ((mService.isBooting() && mRootActivityContainer.allResumedActivitiesIdle()) + || fromTimeout) { booting = checkFinishBootingLocked(); } @@ -2025,7 +1276,7 @@ public class ActivityStackSupervisor extends ConfigurationContainer implements D r.mRelaunchReason = RELAUNCH_REASON_NONE; } - if (allResumedActivitiesIdle()) { + if (mRootActivityContainer.allResumedActivitiesIdle()) { if (r != null) { mService.scheduleAppGcsLocked(); } @@ -2038,7 +1289,7 @@ public class ActivityStackSupervisor extends ConfigurationContainer implements D } mLaunchingActivity.release(); } - ensureActivitiesVisibleLocked(null, 0, !PRESERVE_WINDOWS); + mRootActivityContainer.ensureActivitiesVisible(null, 0, !PRESERVE_WINDOWS); } // Atomically retrieve all of the other things to do. @@ -2094,186 +1345,13 @@ public class ActivityStackSupervisor extends ConfigurationContainer implements D //mWindowManager.dump(); if (activityRemoved) { - resumeFocusedStacksTopActivitiesLocked(); + mRootActivityContainer.resumeFocusedStacksTopActivities(); } return r; } - boolean handleAppDiedLocked(WindowProcessController app) { - boolean hasVisibleActivities = false; - for (int displayNdx = mActivityDisplays.size() - 1; displayNdx >= 0; --displayNdx) { - final ActivityDisplay display = mActivityDisplays.get(displayNdx); - for (int stackNdx = display.getChildCount() - 1; stackNdx >= 0; --stackNdx) { - final ActivityStack stack = display.getChildAt(stackNdx); - hasVisibleActivities |= stack.handleAppDiedLocked(app); - } - } - return hasVisibleActivities; - } - - void closeSystemDialogsLocked() { - for (int displayNdx = mActivityDisplays.size() - 1; displayNdx >= 0; --displayNdx) { - final ActivityDisplay display = mActivityDisplays.get(displayNdx); - for (int stackNdx = display.getChildCount() - 1; stackNdx >= 0; --stackNdx) { - final ActivityStack stack = display.getChildAt(stackNdx); - stack.closeSystemDialogsLocked(); - } - } - } - - void removeUserLocked(int userId) { - mUserStackInFront.delete(userId); - } - - /** - * Update the last used stack id for non-current user (current user's last - * used stack is the focused stack) - */ - void updateUserStackLocked(int userId, ActivityStack stack) { - if (userId != mCurrentUser) { - mUserStackInFront.put(userId, stack != null ? stack.getStackId() - : getDefaultDisplay().getHomeStack().mStackId); - } - } - - /** - * @return true if some activity was finished (or would have finished if doit were true). - */ - boolean finishDisabledPackageActivitiesLocked(String packageName, Set<String> filterByClasses, - boolean doit, boolean evenPersistent, int userId) { - boolean didSomething = false; - for (int displayNdx = mActivityDisplays.size() - 1; displayNdx >= 0; --displayNdx) { - final ActivityDisplay display = mActivityDisplays.get(displayNdx); - for (int stackNdx = display.getChildCount() - 1; stackNdx >= 0; --stackNdx) { - final ActivityStack stack = display.getChildAt(stackNdx); - if (stack.finishDisabledPackageActivitiesLocked( - packageName, filterByClasses, doit, evenPersistent, userId)) { - didSomething = true; - } - } - } - return didSomething; - } - - void updatePreviousProcessLocked(ActivityRecord r) { - // Now that this process has stopped, we may want to consider - // it to be the previous app to try to keep around in case - // the user wants to return to it. - - // First, found out what is currently the foreground app, so that - // we don't blow away the previous app if this activity is being - // hosted by the process that is actually still the foreground. - WindowProcessController fgApp = null; - for (int displayNdx = mActivityDisplays.size() - 1; displayNdx >= 0; --displayNdx) { - final ActivityDisplay display = mActivityDisplays.get(displayNdx); - for (int stackNdx = display.getChildCount() - 1; stackNdx >= 0; --stackNdx) { - final ActivityStack stack = display.getChildAt(stackNdx); - if (isTopDisplayFocusedStack(stack)) { - final ActivityRecord resumedActivity = stack.getResumedActivity(); - if (resumedActivity != null) { - fgApp = resumedActivity.app; - } else if (stack.mPausingActivity != null) { - fgApp = stack.mPausingActivity.app; - } - break; - } - } - } - - // Now set this one as the previous process, only if that really - // makes sense to. - if (r.hasProcess() && fgApp != null && r.app != fgApp - && r.lastVisibleTime > mService.mPreviousProcessVisibleTime - && r.app != mService.mHomeProcess) { - mService.mPreviousProcess = r.app; - mService.mPreviousProcessVisibleTime = r.lastVisibleTime; - } - } - - boolean resumeFocusedStacksTopActivitiesLocked() { - return resumeFocusedStacksTopActivitiesLocked(null, null, null); - } - - boolean resumeFocusedStacksTopActivitiesLocked( - ActivityStack targetStack, ActivityRecord target, ActivityOptions targetOptions) { - - if (!readyToResume()) { - return false; - } - - if (targetStack != null && (targetStack.isTopStackOnDisplay() - || getTopDisplayFocusedStack() == targetStack)) { - return targetStack.resumeTopActivityUncheckedLocked(target, targetOptions); - } - - // Resume all top activities in focused stacks on all displays. - for (int displayNdx = mActivityDisplays.size() - 1; displayNdx >= 0; --displayNdx) { - final ActivityDisplay display = mActivityDisplays.get(displayNdx); - final ActivityStack focusedStack = display.getFocusedStack(); - if (focusedStack == null) { - continue; - } - final ActivityRecord r = focusedStack.topRunningActivityLocked(); - if (r == null || !r.isState(RESUMED)) { - focusedStack.resumeTopActivityUncheckedLocked(null, null); - } else if (r.isState(RESUMED)) { - // Kick off any lingering app transitions form the MoveTaskToFront operation. - focusedStack.executeAppTransition(targetOptions); - } - } - - return false; - } - - void updateActivityApplicationInfoLocked(ApplicationInfo aInfo) { - for (int displayNdx = mActivityDisplays.size() - 1; displayNdx >= 0; --displayNdx) { - final ActivityDisplay display = mActivityDisplays.get(displayNdx); - for (int stackNdx = display.getChildCount() - 1; stackNdx >= 0; --stackNdx) { - final ActivityStack stack = display.getChildAt(stackNdx); - stack.updateActivityApplicationInfoLocked(aInfo); - } - } - } - - /** - * Finish the topmost activities in all stacks that belong to the crashed app. - * @param app The app that crashed. - * @param reason Reason to perform this action. - * @return The task id that was finished in this stack, or INVALID_TASK_ID if none was finished. - */ - int finishTopCrashedActivitiesLocked(WindowProcessController app, String reason) { - TaskRecord finishedTask = null; - ActivityStack focusedStack = getTopDisplayFocusedStack(); - for (int displayNdx = mActivityDisplays.size() - 1; displayNdx >= 0; --displayNdx) { - final ActivityDisplay display = mActivityDisplays.get(displayNdx); - // It is possible that request to finish activity might also remove its task and stack, - // so we need to be careful with indexes in the loop and check child count every time. - for (int stackNdx = 0; stackNdx < display.getChildCount(); ++stackNdx) { - final ActivityStack stack = display.getChildAt(stackNdx); - final TaskRecord t = stack.finishTopCrashedActivityLocked(app, reason); - if (stack == focusedStack || finishedTask == null) { - finishedTask = t; - } - } - } - return finishedTask != null ? finishedTask.taskId : INVALID_TASK_ID; - } - - void finishVoiceTask(IVoiceInteractionSession session) { - for (int displayNdx = mActivityDisplays.size() - 1; displayNdx >= 0; --displayNdx) { - final ActivityDisplay display = mActivityDisplays.get(displayNdx); - final int numStacks = display.getChildCount(); - for (int stackNdx = 0; stackNdx < numStacks; ++stackNdx) { - final ActivityStack stack = display.getChildAt(stackNdx); - stack.finishVoiceTask(session); - } - } - } - - /** - * This doesn't just find a task, it also moves the task to front. - */ + /** This doesn't just find a task, it also moves the task to front. */ void findTaskToMoveToFront(TaskRecord task, int flags, ActivityOptions options, String reason, boolean forceNonResizeable) { ActivityStack currentStack = task.getStack(); @@ -2293,7 +1371,8 @@ public class ActivityStackSupervisor extends ConfigurationContainer implements D final Rect bounds = options.getLaunchBounds(); task.updateOverrideConfiguration(bounds); - ActivityStack stack = getLaunchStack(null, options, task, ON_TOP); + ActivityStack stack = + mRootActivityContainer.getLaunchStack(null, options, task, ON_TOP); if (stack != currentStack) { moveHomeStackToFrontIfNeeded(flags, stack.getDisplay(), reason); @@ -2305,7 +1384,7 @@ public class ActivityStackSupervisor extends ConfigurationContainer implements D // still need moveTaskToFrontLocked() below for any transition settings. } if (stack.resizeStackWithLaunchBounds()) { - resizeStackLocked(stack, bounds, null /* tempTaskBounds */, + mRootActivityContainer.resizeStack(stack, bounds, null /* tempTaskBounds */, null /* tempTaskInsetBounds */, !PRESERVE_WINDOWS, true /* allowResizeInDockedMode */, !DEFER_RESUME); } else { @@ -2358,388 +1437,22 @@ public class ActivityStackSupervisor extends ConfigurationContainer implements D return mLaunchParamsController; } - protected <T extends ActivityStack> T getStack(int stackId) { - for (int i = mActivityDisplays.size() - 1; i >= 0; --i) { - final T stack = mActivityDisplays.get(i).getStack(stackId); - if (stack != null) { - return stack; - } - } - return null; - } - - /** @see ActivityDisplay#getStack(int, int) */ - private <T extends ActivityStack> T getStack(int windowingMode, int activityType) { - for (int i = mActivityDisplays.size() - 1; i >= 0; --i) { - final T stack = mActivityDisplays.get(i).getStack(windowingMode, activityType); - if (stack != null) { - return stack; - } - } - return null; - } - - int resolveActivityType(@Nullable ActivityRecord r, @Nullable ActivityOptions options, - @Nullable TaskRecord task) { - // Preference is given to the activity type for the activity then the task since the type - // once set shouldn't change. - int activityType = r != null ? r.getActivityType() : ACTIVITY_TYPE_UNDEFINED; - if (activityType == ACTIVITY_TYPE_UNDEFINED && task != null) { - activityType = task.getActivityType(); - } - if (activityType != ACTIVITY_TYPE_UNDEFINED) { - return activityType; - } - if (options != null) { - activityType = options.getLaunchActivityType(); - } - return activityType != ACTIVITY_TYPE_UNDEFINED ? activityType : ACTIVITY_TYPE_STANDARD; - } - - <T extends ActivityStack> T getLaunchStack(@Nullable ActivityRecord r, - @Nullable ActivityOptions options, @Nullable TaskRecord candidateTask, boolean onTop) { - return getLaunchStack(r, options, candidateTask, onTop, null /* launchParams */); + private void deferUpdateRecentsHomeStackBounds() { + mRootActivityContainer.deferUpdateBounds(ACTIVITY_TYPE_RECENTS); + mRootActivityContainer.deferUpdateBounds(ACTIVITY_TYPE_HOME); } - /** - * Returns the right stack to use for launching factoring in all the input parameters. - * - * @param r The activity we are trying to launch. Can be null. - * @param options The activity options used to the launch. Can be null. - * @param candidateTask The possible task the activity might be launched in. Can be null. - * @params launchParams The resolved launch params to use. - * - * @return The stack to use for the launch or INVALID_STACK_ID. - */ - <T extends ActivityStack> T getLaunchStack(@Nullable ActivityRecord r, - @Nullable ActivityOptions options, @Nullable TaskRecord candidateTask, boolean onTop, - @Nullable LaunchParamsController.LaunchParams launchParams) { - int taskId = INVALID_TASK_ID; - int displayId = INVALID_DISPLAY; - //Rect bounds = null; - - // We give preference to the launch preference in activity options. - if (options != null) { - taskId = options.getLaunchTaskId(); - displayId = options.getLaunchDisplayId(); - } - - // First preference for stack goes to the task Id set in the activity options. Use the stack - // associated with that if possible. - if (taskId != INVALID_TASK_ID) { - // Temporarily set the task id to invalid in case in re-entry. - options.setLaunchTaskId(INVALID_TASK_ID); - final TaskRecord task = anyTaskForIdLocked(taskId, - MATCH_TASK_IN_STACKS_OR_RECENT_TASKS_AND_RESTORE, options, onTop); - options.setLaunchTaskId(taskId); - if (task != null) { - return task.getStack(); - } - } - - final int activityType = resolveActivityType(r, options, candidateTask); - T stack; - - // Next preference for stack goes to the display Id set the candidate display. - if (launchParams != null && launchParams.mPreferredDisplayId != INVALID_DISPLAY) { - displayId = launchParams.mPreferredDisplayId; - } - if (displayId != INVALID_DISPLAY && canLaunchOnDisplay(r, displayId)) { - if (r != null) { - stack = (T) getValidLaunchStackOnDisplay(displayId, r, candidateTask, options, - launchParams); - if (stack != null) { - return stack; - } - } - final ActivityDisplay display = getActivityDisplayOrCreateLocked(displayId); - if (display != null) { - stack = display.getOrCreateStack(r, options, candidateTask, activityType, onTop); - if (stack != null) { - return stack; - } - } - } - - // Give preference to the stack and display of the input task and activity if they match the - // mode we want to launch into. - stack = null; - ActivityDisplay display = null; - if (candidateTask != null) { - stack = candidateTask.getStack(); - } - if (stack == null && r != null) { - stack = r.getStack(); - } - if (stack != null) { - display = stack.getDisplay(); - if (display != null && canLaunchOnDisplay(r, display.mDisplayId)) { - int windowingMode = launchParams != null ? launchParams.mWindowingMode - : WindowConfiguration.WINDOWING_MODE_UNDEFINED; - if (windowingMode == WindowConfiguration.WINDOWING_MODE_UNDEFINED) { - windowingMode = display.resolveWindowingMode(r, options, candidateTask, - activityType); - } - if (stack.isCompatible(windowingMode, activityType)) { - return stack; - } - if (windowingMode == WINDOWING_MODE_FULLSCREEN_OR_SPLIT_SCREEN_SECONDARY - && display.getSplitScreenPrimaryStack() == stack - && candidateTask == stack.topTask()) { - // This is a special case when we try to launch an activity that is currently on - // top of split-screen primary stack, but is targeting split-screen secondary. - // In this case we don't want to move it to another stack. - // TODO(b/78788972): Remove after differentiating between preferred and required - // launch options. - return stack; - } - } - } - - if (display == null || !canLaunchOnDisplay(r, display.mDisplayId)) { - display = getDefaultDisplay(); - } - - return display.getOrCreateStack(r, options, candidateTask, activityType, onTop); - } - - /** @return true if activity record is null or can be launched on provided display. */ - private boolean canLaunchOnDisplay(ActivityRecord r, int displayId) { - if (r == null) { - return true; - } - return r.canBeLaunchedOnDisplay(displayId); - } - - /** - * Get a topmost stack on the display, that is a valid launch stack for specified activity. - * If there is no such stack, new dynamic stack can be created. - * @param displayId Target display. - * @param r Activity that should be launched there. - * @param candidateTask The possible task the activity might be put in. - * @return Existing stack if there is a valid one, new dynamic stack if it is valid or null. - */ - private ActivityStack getValidLaunchStackOnDisplay(int displayId, @NonNull ActivityRecord r, - @Nullable TaskRecord candidateTask, @Nullable ActivityOptions options, - @Nullable LaunchParamsController.LaunchParams launchParams) { - final ActivityDisplay activityDisplay = getActivityDisplayOrCreateLocked(displayId); - if (activityDisplay == null) { - throw new IllegalArgumentException( - "Display with displayId=" + displayId + " not found."); - } - - if (!r.canBeLaunchedOnDisplay(displayId)) { - return null; - } - - // If {@code r} is already in target display and its task is the same as the candidate task, - // the intention should be getting a launch stack for the reusable activity, so we can use - // the existing stack. - if (r.getDisplayId() == displayId && r.getTask() == candidateTask) { - return candidateTask.getStack(); - } - - // Return the topmost valid stack on the display. - for (int i = activityDisplay.getChildCount() - 1; i >= 0; --i) { - final ActivityStack stack = activityDisplay.getChildAt(i); - if (isValidLaunchStack(stack, displayId, r)) { - return stack; - } - } - - // If there is no valid stack on the external display - check if new dynamic stack will do. - if (displayId != DEFAULT_DISPLAY) { - final int windowingMode; - if (launchParams != null) { - // When launch params is not null, we always defer to its windowing mode. Sometimes - // it could be unspecified, which indicates it should inherit windowing mode from - // display. - windowingMode = launchParams.mWindowingMode; - } else { - windowingMode = options != null ? options.getLaunchWindowingMode() - : r.getWindowingMode(); - } - final int activityType = - options != null && options.getLaunchActivityType() != ACTIVITY_TYPE_UNDEFINED - ? options.getLaunchActivityType() : r.getActivityType(); - return activityDisplay.createStack(windowingMode, activityType, true /*onTop*/); - } - - Slog.w(TAG, "getValidLaunchStackOnDisplay: can't launch on displayId " + displayId); - return null; - } - - ActivityStack getValidLaunchStackOnDisplay(int displayId, @NonNull ActivityRecord r, - @Nullable ActivityOptions options, - @Nullable LaunchParamsController.LaunchParams launchParams) { - return getValidLaunchStackOnDisplay(displayId, r, null /* candidateTask */, options, - launchParams); - } - - // TODO: Can probably be consolidated into getLaunchStack()... - private boolean isValidLaunchStack(ActivityStack stack, int displayId, ActivityRecord r) { - switch (stack.getActivityType()) { - case ACTIVITY_TYPE_HOME: return r.isActivityTypeHome(); - case ACTIVITY_TYPE_RECENTS: return r.isActivityTypeRecents(); - case ACTIVITY_TYPE_ASSISTANT: return r.isActivityTypeAssistant(); - } - // There is a 1-to-1 relationship between stack and task when not in - // primary split-windowing mode. - if (stack.getWindowingMode() != WINDOWING_MODE_SPLIT_SCREEN_PRIMARY) { - return false; - } else { - return r.supportsSplitScreenWindowingMode(); - } - } - - /** - * Get next focusable stack in the system. This will search through the stack on the same - * display as the current focused stack, looking for a focusable and visible stack, different - * from the target stack. If no valid candidates will be found, it will then go through all - * displays and stacks in last-focused order. - * - * @param currentFocus The stack that previously had focus. - * @param ignoreCurrent If we should ignore {@param currentFocus} when searching for next - * candidate. - * @return Next focusable {@link ActivityStack}, {@code null} if not found. - */ - ActivityStack getNextFocusableStackLocked(@NonNull ActivityStack currentFocus, - boolean ignoreCurrent) { - // First look for next focusable stack on the same display - final ActivityDisplay preferredDisplay = currentFocus.getDisplay(); - final ActivityStack preferredFocusableStack = preferredDisplay.getNextFocusableStack( - currentFocus, ignoreCurrent); - if (preferredFocusableStack != null) { - return preferredFocusableStack; - } - if (preferredDisplay.supportsSystemDecorations()) { - // Stop looking for focusable stack on other displays because the preferred display - // supports system decorations. Home activity would be launched on the same display if - // no focusable stack found. - return null; - } - - // Now look through all displays - for (int i = mActivityDisplays.size() - 1; i >= 0; --i) { - final ActivityDisplay display = mActivityDisplays.get(i); - if (display == preferredDisplay) { - // We've already checked this one - continue; - } - final ActivityStack nextFocusableStack = display.getNextFocusableStack(currentFocus, - ignoreCurrent); - if (nextFocusableStack != null) { - return nextFocusableStack; - } - } - - return null; - } - - /** - * Get next valid stack for launching provided activity in the system. This will search across - * displays and stacks in last-focused order for a focusable and visible stack, except those - * that are on a currently focused display. - * - * @param r The activity that is being launched. - * @param currentFocus The display that previously had focus and thus needs to be ignored when - * searching for the next candidate. - * @return Next valid {@link ActivityStack}, null if not found. - */ - ActivityStack getNextValidLaunchStackLocked(@NonNull ActivityRecord r, int currentFocus) { - for (int i = mActivityDisplays.size() - 1; i >= 0; --i) { - final ActivityDisplay display = mActivityDisplays.get(i); - if (display.mDisplayId == currentFocus) { - continue; - } - final ActivityStack stack = getValidLaunchStackOnDisplay(display.mDisplayId, r, - null /* options */, null /* launchParams */); - if (stack != null) { - return stack; - } - } - return null; - } - - ActivityRecord getDefaultDisplayHomeActivity() { - return getDefaultDisplayHomeActivityForUser(mCurrentUser); - } - - ActivityRecord getDefaultDisplayHomeActivityForUser(int userId) { - return getActivityDisplay(DEFAULT_DISPLAY).getHomeActivityForUser(userId); - } - - void resizeStackLocked(ActivityStack stack, Rect bounds, Rect tempTaskBounds, - Rect tempTaskInsetBounds, boolean preserveWindows, boolean allowResizeInDockedMode, - boolean deferResume) { - - if (stack.inSplitScreenPrimaryWindowingMode()) { - resizeDockedStackLocked(bounds, tempTaskBounds, tempTaskInsetBounds, null, null, - preserveWindows, deferResume); - return; - } - - final boolean splitScreenActive = getDefaultDisplay().hasSplitScreenPrimaryStack(); - if (!allowResizeInDockedMode - && !stack.getWindowConfiguration().tasksAreFloating() && splitScreenActive) { - // If the docked stack exists, don't resize non-floating stacks independently of the - // size computed from the docked stack size (otherwise they will be out of sync) - return; - } - - Trace.traceBegin(TRACE_TAG_ACTIVITY_MANAGER, "am.resizeStack_" + stack.mStackId); - mWindowManager.deferSurfaceLayout(); - try { - if (stack.affectedBySplitScreenResize()) { - if (bounds == null && stack.inSplitScreenWindowingMode()) { - // null bounds = fullscreen windowing mode...at least for now. - stack.setWindowingMode(WINDOWING_MODE_FULLSCREEN); - } else if (splitScreenActive) { - // If we are in split-screen mode and this stack support split-screen, then - // it should be split-screen secondary mode. i.e. adjacent to the docked stack. - stack.setWindowingMode(WINDOWING_MODE_SPLIT_SCREEN_SECONDARY); - } - } - stack.resize(bounds, tempTaskBounds, tempTaskInsetBounds); - if (!deferResume) { - stack.ensureVisibleActivitiesConfigurationLocked( - stack.topRunningActivityLocked(), preserveWindows); - } - } finally { - mWindowManager.continueSurfaceLayout(); - Trace.traceEnd(TRACE_TAG_ACTIVITY_MANAGER); - } - } - - void deferUpdateRecentsHomeStackBounds() { - deferUpdateBounds(ACTIVITY_TYPE_RECENTS); - deferUpdateBounds(ACTIVITY_TYPE_HOME); - } - - void deferUpdateBounds(int activityType) { - final ActivityStack stack = getStack(WINDOWING_MODE_UNDEFINED, activityType); - if (stack != null) { - stack.deferUpdateBounds(); - } - } - - void continueUpdateRecentsHomeStackBounds() { - continueUpdateBounds(ACTIVITY_TYPE_RECENTS); - continueUpdateBounds(ACTIVITY_TYPE_HOME); - } - - void continueUpdateBounds(int activityType) { - final ActivityStack stack = getStack(WINDOWING_MODE_UNDEFINED, activityType); - if (stack != null) { - stack.continueUpdateBounds(); - } + private void continueUpdateRecentsHomeStackBounds() { + mRootActivityContainer.continueUpdateBounds(ACTIVITY_TYPE_RECENTS); + mRootActivityContainer.continueUpdateBounds(ACTIVITY_TYPE_HOME); } void notifyAppTransitionDone() { continueUpdateRecentsHomeStackBounds(); for (int i = mResizingTasksDuringAnimation.size() - 1; i >= 0; i--) { final int taskId = mResizingTasksDuringAnimation.valueAt(i); - final TaskRecord task = anyTaskForIdLocked(taskId, MATCH_TASK_IN_STACKS_ONLY); + final TaskRecord task = + mRootActivityContainer.anyTaskForId(taskId, MATCH_TASK_IN_STACKS_ONLY); if (task != null) { task.setTaskDockedResizing(false); } @@ -2758,7 +1471,8 @@ public class ActivityStackSupervisor extends ConfigurationContainer implements D try { final int windowingMode = fromStack.getWindowingMode(); final boolean inPinnedWindowingMode = windowingMode == WINDOWING_MODE_PINNED; - final ActivityDisplay toDisplay = getActivityDisplay(toDisplayId); + final ActivityDisplay toDisplay = + mRootActivityContainer.getActivityDisplay(toDisplayId); if (windowingMode == WINDOWING_MODE_SPLIT_SCREEN_PRIMARY) { // Tell the display we are exiting split-screen mode. @@ -2815,8 +1529,8 @@ public class ActivityStackSupervisor extends ConfigurationContainer implements D } } - ensureActivitiesVisibleLocked(null, 0, PRESERVE_WINDOWS); - resumeFocusedStacksTopActivitiesLocked(); + mRootActivityContainer.ensureActivitiesVisible(null, 0, PRESERVE_WINDOWS); + mRootActivityContainer.resumeFocusedStacksTopActivities(); } finally { mAllowDockedStackResize = true; mWindowManager.continueSurfaceLayout(); @@ -2862,7 +1576,7 @@ public class ActivityStackSupervisor extends ConfigurationContainer implements D false /* deferResume */); } - private void resizeDockedStackLocked(Rect dockedBounds, Rect tempDockedTaskBounds, + void resizeDockedStackLocked(Rect dockedBounds, Rect tempDockedTaskBounds, Rect tempDockedTaskInsetBounds, Rect tempOtherTaskBounds, Rect tempOtherTaskInsetBounds, boolean preserveWindows, boolean deferResume) { @@ -2871,7 +1585,8 @@ public class ActivityStackSupervisor extends ConfigurationContainer implements D return; } - final ActivityStack stack = getDefaultDisplay().getSplitScreenPrimaryStack(); + final ActivityStack stack = + mRootActivityContainer.getDefaultDisplay().getSplitScreenPrimaryStack(); if (stack == null) { Slog.w(TAG, "resizeDockedStackLocked: docked stack not found"); return; @@ -2910,7 +1625,7 @@ public class ActivityStackSupervisor extends ConfigurationContainer implements D // static stacks need to be adjusted so they don't overlap with the docked stack. // We get the bounds to use from window manager which has been adjusted for any // screen controls and is also the same for all stacks. - final ActivityDisplay display = getDefaultDisplay(); + final ActivityDisplay display = mRootActivityContainer.getDefaultDisplay(); final Rect otherTaskRect = new Rect(); for (int i = display.getChildCount() - 1; i >= 0; --i) { final ActivityStack current = display.getChildAt(i); @@ -2930,7 +1645,8 @@ public class ActivityStackSupervisor extends ConfigurationContainer implements D tempRect /* outStackBounds */, otherTaskRect /* outTempTaskBounds */); - resizeStackLocked(current, !tempRect.isEmpty() ? tempRect : null, + mRootActivityContainer.resizeStack(current, + !tempRect.isEmpty() ? tempRect : null, !otherTaskRect.isEmpty() ? otherTaskRect : tempOtherTaskBounds, tempOtherTaskInsetBounds, preserveWindows, true /* allowResizeInDockedMode */, deferResume); @@ -2948,7 +1664,8 @@ public class ActivityStackSupervisor extends ConfigurationContainer implements D void resizePinnedStackLocked(Rect pinnedBounds, Rect tempPinnedTaskBounds) { // TODO(multi-display): Pinned stack display should be passed in. - final PinnedActivityStack stack = getDefaultDisplay().getPinnedStack(); + final PinnedActivityStack stack = + mRootActivityContainer.getDefaultDisplay().getPinnedStack(); if (stack == null) { Slog.w(TAG, "resizePinnedStackLocked: pinned stack not found"); return; @@ -3029,22 +1746,6 @@ public class ActivityStackSupervisor extends ConfigurationContainer implements D } /** - * 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) { - for (int i = mActivityDisplays.size() - 1; i >= 0; --i) { - mActivityDisplays.get(i).removeStacksInWindowingModes(windowingModes); - } - } - - void removeStacksWithActivityTypes(int... activityTypes) { - for (int i = mActivityDisplays.size() - 1; i >= 0; --i) { - mActivityDisplays.get(i).removeStacksWithActivityTypes(activityTypes); - } - } - - /** * See {@link #removeTaskByIdLocked(int, boolean, boolean, boolean)} */ boolean removeTaskByIdLocked(int taskId, boolean killProcess, boolean removeFromRecents, @@ -3065,7 +1766,8 @@ public class ActivityStackSupervisor extends ConfigurationContainer implements D */ boolean removeTaskByIdLocked(int taskId, boolean killProcess, boolean removeFromRecents, boolean pauseImmediately, String reason) { - final TaskRecord tr = anyTaskForIdLocked(taskId, MATCH_TASK_IN_STACKS_OR_RECENT_TASKS); + final TaskRecord tr = + mRootActivityContainer.anyTaskForId(taskId, MATCH_TASK_IN_STACKS_OR_RECENT_TASKS); if (tr != null) { tr.removeTaskActivitiesLocked(pauseImmediately, reason); cleanUpRemovedTaskLocked(tr, killProcess, removeFromRecents); @@ -3154,7 +1856,8 @@ public class ActivityStackSupervisor extends ConfigurationContainer implements D * @return true if the task has been restored successfully. */ boolean restoreRecentTaskLocked(TaskRecord task, ActivityOptions aOptions, boolean onTop) { - final ActivityStack stack = getLaunchStack(null, aOptions, task, onTop); + final ActivityStack stack = + mRootActivityContainer.getLaunchStack(null, aOptions, task, onTop); final ActivityStack currentStack = task.getStack(); if (currentStack != null) { // Task has already been restored once. See if we need to do anything more @@ -3174,7 +1877,7 @@ public class ActivityStackSupervisor extends ConfigurationContainer implements D "Added restored task=" + task + " to stack=" + stack); final ArrayList<ActivityRecord> activities = task.mActivities; for (int activityNdx = activities.size() - 1; activityNdx >= 0; --activityNdx) { - activities.get(activityNdx).createWindowContainer(); + activities.get(activityNdx).createAppWindowToken(); } return true; } @@ -3196,39 +1899,6 @@ public class ActivityStackSupervisor extends ConfigurationContainer implements D } /** - * Move stack with all its existing content to specified display. - * @param stackId Id of stack to move. - * @param displayId Id of display to move stack to. - * @param onTop Indicates whether container should be place on top or on bottom. - */ - void moveStackToDisplayLocked(int stackId, int displayId, boolean onTop) { - final ActivityDisplay activityDisplay = getActivityDisplayOrCreateLocked(displayId); - if (activityDisplay == null) { - throw new IllegalArgumentException("moveStackToDisplayLocked: Unknown displayId=" - + displayId); - } - final ActivityStack stack = getStack(stackId); - if (stack == null) { - throw new IllegalArgumentException("moveStackToDisplayLocked: Unknown stackId=" - + stackId); - } - - final ActivityDisplay currentDisplay = stack.getDisplay(); - if (currentDisplay == null) { - throw new IllegalStateException("moveStackToDisplayLocked: Stack with stack=" + stack - + " is not attached to any display."); - } - - if (currentDisplay.mDisplayId == displayId) { - throw new IllegalArgumentException("Trying to move stack=" + stack - + " to its current displayId=" + displayId); - } - - stack.reparent(activityDisplay, onTop, false /* displayRemoved */); - // TODO(multi-display): resize stacks properly if moved from split-screen. - } - - /** * Returns the reparent target stack, creating the stack if necessary. This call also enforces * the various checks on tasks that are going to be reparented from one stack to another. */ @@ -3280,159 +1950,6 @@ public class ActivityStackSupervisor extends ConfigurationContainer implements D return stack; } - boolean moveTopStackActivityToPinnedStackLocked(int stackId, Rect destBounds) { - final ActivityStack stack = getStack(stackId); - if (stack == null) { - throw new IllegalArgumentException( - "moveTopStackActivityToPinnedStackLocked: Unknown stackId=" + stackId); - } - - final ActivityRecord r = stack.topRunningActivityLocked(); - if (r == null) { - Slog.w(TAG, "moveTopStackActivityToPinnedStackLocked: No top running activity" - + " in stack=" + stack); - return false; - } - - if (!mService.mForceResizableActivities && !r.supportsPictureInPicture()) { - Slog.w(TAG, - "moveTopStackActivityToPinnedStackLocked: Picture-In-Picture not supported for " - + " r=" + r); - return false; - } - - moveActivityToPinnedStackLocked(r, null /* sourceBounds */, 0f /* aspectRatio */, - "moveTopActivityToPinnedStack"); - return true; - } - - void moveActivityToPinnedStackLocked(ActivityRecord r, Rect sourceHintBounds, float aspectRatio, - String reason) { - - mWindowManager.deferSurfaceLayout(); - - final ActivityDisplay display = r.getStack().getDisplay(); - PinnedActivityStack stack = display.getPinnedStack(); - - // This will clear the pinned stack by moving an existing task to the full screen stack, - // ensuring only one task is present. - if (stack != null) { - moveTasksToFullscreenStackLocked(stack, !ON_TOP); - } - - // Need to make sure the pinned stack exist so we can resize it below... - stack = display.getOrCreateStack(WINDOWING_MODE_PINNED, r.getActivityType(), ON_TOP); - - // Calculate the target bounds here before the task is reparented back into pinned windowing - // mode (which will reset the saved bounds) - final Rect destBounds = stack.getDefaultPictureInPictureBounds(aspectRatio); - - try { - final TaskRecord task = r.getTask(); - // Resize the pinned stack to match the current size of the task the activity we are - // going to be moving is currently contained in. We do this to have the right starting - // animation bounds for the pinned stack to the desired bounds the caller wants. - resizeStackLocked(stack, task.getOverrideBounds(), null /* tempTaskBounds */, - null /* tempTaskInsetBounds */, !PRESERVE_WINDOWS, - true /* allowResizeInDockedMode */, !DEFER_RESUME); - - if (task.mActivities.size() == 1) { - // Defer resume until below, and do not schedule PiP changes until we animate below - task.reparent(stack, ON_TOP, REPARENT_MOVE_STACK_TO_FRONT, !ANIMATE, DEFER_RESUME, - false /* schedulePictureInPictureModeChange */, reason); - } else { - // There are multiple activities in the task and moving the top activity should - // reveal/leave the other activities in their original task. - - // Currently, we don't support reparenting activities across tasks in two different - // stacks, so instead, just create a new task in the same stack, reparent the - // activity into that task, and then reparent the whole task to the new stack. This - // ensures that all the necessary work to migrate states in the old and new stacks - // is also done. - final TaskRecord newTask = task.getStack().createTaskRecord( - getNextTaskIdForUserLocked(r.userId), r.info, r.intent, null, null, true); - r.reparent(newTask, MAX_VALUE, "moveActivityToStack"); - - // Defer resume until below, and do not schedule PiP changes until we animate below - newTask.reparent(stack, ON_TOP, REPARENT_MOVE_STACK_TO_FRONT, !ANIMATE, - DEFER_RESUME, false /* schedulePictureInPictureModeChange */, reason); - } - - // Reset the state that indicates it can enter PiP while pausing after we've moved it - // to the pinned stack - r.supportsEnterPipOnTaskSwitch = false; - } finally { - mWindowManager.continueSurfaceLayout(); - } - - stack.animateResizePinnedStack(sourceHintBounds, destBounds, -1 /* animationDuration */, - true /* fromFullscreen */); - - // Update the visibility of all activities after the they have been reparented to the new - // stack. This MUST run after the animation above is scheduled to ensure that the windows - // drawn signal is scheduled after the bounds animation start call on the bounds animator - // thread. - ensureActivitiesVisibleLocked(null, 0, !PRESERVE_WINDOWS); - resumeFocusedStacksTopActivitiesLocked(); - - mService.getTaskChangeNotificationController().notifyActivityPinned(r); - } - - ActivityRecord findTaskLocked(ActivityRecord r, int preferredDisplayId) { - if (DEBUG_TASKS) Slog.d(TAG_TASKS, "Looking for task of " + r); - mTmpFindTaskResult.clear(); - - // Looking up task on preferred display first - final ActivityDisplay preferredDisplay = getActivityDisplay(preferredDisplayId); - if (preferredDisplay != null) { - preferredDisplay.findTaskLocked(r, true /* isPreferredDisplay */, mTmpFindTaskResult); - if (mTmpFindTaskResult.mIdealMatch) { - return mTmpFindTaskResult.mRecord; - } - } - - for (int displayNdx = mActivityDisplays.size() - 1; displayNdx >= 0; --displayNdx) { - final ActivityDisplay display = mActivityDisplays.get(displayNdx); - if (display.mDisplayId == preferredDisplayId) { - continue; - } - - display.findTaskLocked(r, false /* isPreferredDisplay */, mTmpFindTaskResult); - if (mTmpFindTaskResult.mIdealMatch) { - return mTmpFindTaskResult.mRecord; - } - } - - if (DEBUG_TASKS && mTmpFindTaskResult.mRecord == null) Slog.d(TAG_TASKS, "No task found"); - return mTmpFindTaskResult.mRecord; - } - - ActivityRecord findActivityLocked(Intent intent, ActivityInfo info, - boolean compareIntentFilters) { - for (int displayNdx = mActivityDisplays.size() - 1; displayNdx >= 0; --displayNdx) { - final ActivityDisplay display = mActivityDisplays.get(displayNdx); - for (int stackNdx = display.getChildCount() - 1; stackNdx >= 0; --stackNdx) { - final ActivityStack stack = display.getChildAt(stackNdx); - final ActivityRecord ar = stack.findActivityLocked( - intent, info, compareIntentFilters); - if (ar != null) { - return ar; - } - } - } - return null; - } - - boolean hasAwakeDisplay() { - for (int displayNdx = mActivityDisplays.size() - 1; displayNdx >= 0; --displayNdx) { - final ActivityDisplay display = mActivityDisplays.get(displayNdx); - if (!display.shouldSleep()) { - return true; - } - } - return false; - } - void goingToSleepLocked() { scheduleSleepTimeout(); if (!mGoingToSleep.isHeld()) { @@ -3446,24 +1963,19 @@ public class ActivityStackSupervisor extends ConfigurationContainer implements D } } - applySleepTokensLocked(false /* applyToStacks */); + mRootActivityContainer.applySleepTokens(false /* applyToStacks */); checkReadyForSleepLocked(true /* allowDelay */); } - void prepareForShutdownLocked() { - for (int i = 0; i < mActivityDisplays.size(); i++) { - createSleepTokenLocked("shutdown", mActivityDisplays.get(i).mDisplayId); - } - } - boolean shutdownLocked(int timeout) { goingToSleepLocked(); boolean timedout = false; final long endTime = System.currentTimeMillis() + timeout; while (true) { - if (!putStacksToSleepLocked(true /* allowDelay */, true /* shuttingDown */)) { + if (!mRootActivityContainer.putStacksToSleep( + true /* allowDelay */, true /* shuttingDown */)) { long timeRemaining = endTime - System.currentTimeMillis(); if (timeRemaining > 0) { try { @@ -3493,51 +2005,6 @@ public class ActivityStackSupervisor extends ConfigurationContainer implements D } } - void applySleepTokensLocked(boolean applyToStacks) { - for (int displayNdx = mActivityDisplays.size() - 1; displayNdx >= 0; --displayNdx) { - // Set the sleeping state of the display. - final ActivityDisplay display = mActivityDisplays.get(displayNdx); - final boolean displayShouldSleep = display.shouldSleep(); - if (displayShouldSleep == display.isSleeping()) { - continue; - } - display.setIsSleeping(displayShouldSleep); - - if (!applyToStacks) { - continue; - } - - // Set the sleeping state of the stacks on the display. - for (int stackNdx = display.getChildCount() - 1; stackNdx >= 0; --stackNdx) { - final ActivityStack stack = display.getChildAt(stackNdx); - if (displayShouldSleep) { - stack.goToSleepIfPossible(false /* shuttingDown */); - } else { - stack.awakeFromSleepingLocked(); - if (stack.isFocusedStackOnDisplay() && !getKeyguardController() - .isKeyguardOrAodShowing(display.mDisplayId)) { - // If the keyguard is unlocked - resume immediately. - // It is possible that the display will not be awake at the time we - // process the keyguard going away, which can happen before the sleep token - // is released. As a result, it is important we resume the activity here. - resumeFocusedStacksTopActivitiesLocked(); - } - } - } - - if (displayShouldSleep || mGoingToSleepActivities.isEmpty()) { - continue; - } - // The display is awake now, so clean up the going to sleep list. - for (Iterator<ActivityRecord> it = mGoingToSleepActivities.iterator(); it.hasNext(); ) { - final ActivityRecord r = it.next(); - if (r.getDisplayId() == display.mDisplayId) { - it.remove(); - } - } - } - } - void activitySleptLocked(ActivityRecord r) { mGoingToSleepActivities.remove(r); final ActivityStack s = r.getStack(); @@ -3554,12 +2021,13 @@ public class ActivityStackSupervisor extends ConfigurationContainer implements D return; } - if (!putStacksToSleepLocked(allowDelay, false /* shuttingDown */)) { + if (!mRootActivityContainer.putStacksToSleep( + allowDelay, false /* shuttingDown */)) { return; } // Send launch end powerhint before going sleep - sendPowerHintForLaunchEndIfNeeded(); + mRootActivityContainer.sendPowerHintForLaunchEndIfNeeded(); removeSleepTimeouts(); @@ -3571,52 +2039,24 @@ public class ActivityStackSupervisor extends ConfigurationContainer implements D } } - // Tries to put all activity stacks to sleep. Returns true if all stacks were - // successfully put to sleep. - private boolean putStacksToSleepLocked(boolean allowDelay, boolean shuttingDown) { - boolean allSleep = true; - for (int displayNdx = mActivityDisplays.size() - 1; displayNdx >= 0; --displayNdx) { - final ActivityDisplay display = mActivityDisplays.get(displayNdx); - for (int stackNdx = display.getChildCount() - 1; stackNdx >= 0; --stackNdx) { - final ActivityStack stack = display.getChildAt(stackNdx); - if (allowDelay) { - allSleep &= stack.goToSleepIfPossible(shuttingDown); - } else { - stack.goToSleep(); - } - } - } - return allSleep; - } - boolean reportResumedActivityLocked(ActivityRecord r) { // A resumed activity cannot be stopping. remove from list mStoppingActivities.remove(r); final ActivityStack stack = r.getStack(); - if (isTopDisplayFocusedStack(stack)) { + if (mRootActivityContainer.isTopDisplayFocusedStack(stack)) { mService.updateUsageStats(r, true); } if (stack.getDisplay().allResumedActivitiesComplete()) { - ensureActivitiesVisibleLocked(null, 0, !PRESERVE_WINDOWS); + mRootActivityContainer.ensureActivitiesVisible(null, 0, !PRESERVE_WINDOWS); // Make sure activity & window visibility should be identical // for all displays in this stage. - executeAppTransitionForAllDisplay(); + mRootActivityContainer.executeAppTransitionForAllDisplay(); return true; } return false; } - void handleAppCrashLocked(WindowProcessController app) { - for (int displayNdx = mActivityDisplays.size() - 1; displayNdx >= 0; --displayNdx) { - final ActivityDisplay display = mActivityDisplays.get(displayNdx); - for (int stackNdx = display.getChildCount() - 1; stackNdx >= 0; --stackNdx) { - final ActivityStack stack = display.getChildAt(stackNdx); - stack.handleAppCrashLocked(app); - } - } - } - // Called when WindowManager has finished animating the launchingBehind activity to the back. private void handleLaunchTaskBehindCompleteLocked(ActivityRecord r) { final TaskRecord task = r.getTask(); @@ -3639,157 +2079,9 @@ public class ActivityStackSupervisor extends ConfigurationContainer implements D mHandler.obtainMessage(LAUNCH_TASK_BEHIND_COMPLETE, token).sendToTarget(); } - /** - * Make sure that all activities that need to be visible in the system actually are and update - * their configuration. - */ - void ensureActivitiesVisibleLocked(ActivityRecord starting, int configChanges, - boolean preserveWindows) { - ensureActivitiesVisibleLocked(starting, configChanges, preserveWindows, - true /* notifyClients */); - } - - /** - * @see #ensureActivitiesVisibleLocked(ActivityRecord, int, boolean) - */ - void ensureActivitiesVisibleLocked(ActivityRecord starting, int configChanges, - boolean preserveWindows, boolean notifyClients) { - getKeyguardController().beginActivityVisibilityUpdate(); - try { - // First the front stacks. In case any are not fullscreen and are in front of home. - for (int displayNdx = mActivityDisplays.size() - 1; displayNdx >= 0; --displayNdx) { - final ActivityDisplay display = mActivityDisplays.get(displayNdx); - for (int stackNdx = display.getChildCount() - 1; stackNdx >= 0; --stackNdx) { - final ActivityStack stack = display.getChildAt(stackNdx); - stack.ensureActivitiesVisibleLocked(starting, configChanges, preserveWindows, - notifyClients); - } - } - } finally { - getKeyguardController().endActivityVisibilityUpdate(); - } - } - - void addStartingWindowsForVisibleActivities(boolean taskSwitch) { - for (int displayNdx = mActivityDisplays.size() - 1; displayNdx >= 0; --displayNdx) { - final ActivityDisplay display = mActivityDisplays.get(displayNdx); - for (int stackNdx = display.getChildCount() - 1; stackNdx >= 0; --stackNdx) { - final ActivityStack stack = display.getChildAt(stackNdx); - stack.addStartingWindowsForVisibleActivities(taskSwitch); - } - } - } - - void invalidateTaskLayers() { - mTaskLayersChanged = true; - } - - void rankTaskLayersIfNeeded() { - if (!mTaskLayersChanged) { - return; - } - mTaskLayersChanged = false; - for (int displayNdx = 0; displayNdx < mActivityDisplays.size(); displayNdx++) { - final ActivityDisplay display = mActivityDisplays.get(displayNdx); - int baseLayer = 0; - for (int stackNdx = display.getChildCount() - 1; stackNdx >= 0; --stackNdx) { - final ActivityStack stack = display.getChildAt(stackNdx); - baseLayer += stack.rankTaskLayers(baseLayer); - } - } - } - - void clearOtherAppTimeTrackers(AppTimeTracker except) { - for (int displayNdx = mActivityDisplays.size() - 1; displayNdx >= 0; --displayNdx) { - final ActivityDisplay display = mActivityDisplays.get(displayNdx); - for (int stackNdx = display.getChildCount() - 1; stackNdx >= 0; --stackNdx) { - final ActivityStack stack = display.getChildAt(stackNdx); - stack.clearOtherAppTimeTrackers(except); - } - } - } - - void scheduleDestroyAllActivities(WindowProcessController app, String reason) { - for (int displayNdx = mActivityDisplays.size() - 1; displayNdx >= 0; --displayNdx) { - final ActivityDisplay display = mActivityDisplays.get(displayNdx); - for (int stackNdx = display.getChildCount() - 1; stackNdx >= 0; --stackNdx) { - final ActivityStack stack = display.getChildAt(stackNdx); - stack.scheduleDestroyActivities(app, reason); - } - } - } - - void releaseSomeActivitiesLocked(WindowProcessController app, String reason) { - // Tasks is non-null only if two or more tasks are found. - ArraySet<TaskRecord> tasks = app.getReleaseSomeActivitiesTasks(); - if (tasks == null) { - if (DEBUG_RELEASE) Slog.d(TAG_RELEASE, "Didn't find two or more tasks to release"); - return; - } - // If we have activities in multiple tasks that are in a position to be destroyed, - // let's iterate through the tasks and release the oldest one. - final int numDisplays = mActivityDisplays.size(); - for (int displayNdx = 0; displayNdx < numDisplays; ++displayNdx) { - final ActivityDisplay display = mActivityDisplays.get(displayNdx); - final int stackCount = display.getChildCount(); - // Step through all stacks starting from behind, to hit the oldest things first. - for (int stackNdx = 0; stackNdx < stackCount; stackNdx++) { - final ActivityStack stack = display.getChildAt(stackNdx); - // Try to release activities in this stack; if we manage to, we are done. - if (stack.releaseSomeActivitiesLocked(app, tasks, reason) > 0) { - return; - } - } - } - } - - boolean switchUserLocked(int userId, UserState uss) { - final int focusStackId = getTopDisplayFocusedStack().getStackId(); - // We dismiss the docked stack whenever we switch users. - final ActivityStack dockedStack = getDefaultDisplay().getSplitScreenPrimaryStack(); - if (dockedStack != null) { - moveTasksToFullscreenStackLocked(dockedStack, dockedStack.isFocusedStackOnDisplay()); - } - // 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 - // appropriate. - removeStacksInWindowingModes(WINDOWING_MODE_PINNED); - - mUserStackInFront.put(mCurrentUser, focusStackId); - final int restoreStackId = - mUserStackInFront.get(userId, getDefaultDisplay().getHomeStack().mStackId); - mCurrentUser = userId; - - mStartingUsers.add(uss); - for (int displayNdx = mActivityDisplays.size() - 1; displayNdx >= 0; --displayNdx) { - final ActivityDisplay display = mActivityDisplays.get(displayNdx); - for (int stackNdx = display.getChildCount() - 1; stackNdx >= 0; --stackNdx) { - final ActivityStack stack = display.getChildAt(stackNdx); - stack.switchUserLocked(userId); - TaskRecord task = stack.topTask(); - if (task != null) { - stack.positionChildWindowContainerAtTop(task); - } - } - } - - ActivityStack stack = getStack(restoreStackId); - if (stack == null) { - stack = getDefaultDisplay().getHomeStack(); - } - final boolean homeInFront = stack.isActivityTypeHome(); - if (stack.isOnHomeDisplay()) { - stack.moveToFront("switchUserOnHomeDisplay"); - } else { - // Stack was moved to another display while user was swapped out. - resumeHomeActivity(null, "switchUserOnOtherDisplay", DEFAULT_DISPLAY); - } - return homeInFront; - } - /** Checks whether the userid is a profile of the current user. */ boolean isCurrentProfileLocked(int userId) { - if (userId == mCurrentUser) return true; + if (userId == mRootActivityContainer.mCurrentUser) return true; return mService.mAmInternal.isCurrentProfile(userId); } @@ -3814,7 +2106,7 @@ public class ActivityStackSupervisor extends ConfigurationContainer implements D boolean remove, boolean processPausingActivities) { ArrayList<ActivityRecord> stops = null; - final boolean nowVisible = allResumedActivitiesVisible(); + final boolean nowVisible = mRootActivityContainer.allResumedActivitiesVisible(); for (int activityNdx = mStoppingActivities.size() - 1; activityNdx >= 0; --activityNdx) { ActivityRecord s = mStoppingActivities.get(activityNdx); boolean waitingVisible = mActivitiesWaitingForVisibleActivity.contains(s); @@ -3864,134 +2156,26 @@ public class ActivityStackSupervisor extends ConfigurationContainer implements D return stops; } - void validateTopActivitiesLocked() { - for (int displayNdx = mActivityDisplays.size() - 1; displayNdx >= 0; --displayNdx) { - final ActivityDisplay display = mActivityDisplays.get(displayNdx); - for (int stackNdx = display.getChildCount() - 1; stackNdx >= 0; --stackNdx) { - final ActivityStack stack = display.getChildAt(stackNdx); - final ActivityRecord r = stack.topRunningActivityLocked(); - final ActivityState state = r == null ? DESTROYED : r.getState(); - if (isTopDisplayFocusedStack(stack)) { - if (r == null) Slog.e(TAG, - "validateTop...: null top activity, stack=" + stack); - else { - final ActivityRecord pausing = stack.mPausingActivity; - if (pausing != null && pausing == r) Slog.e(TAG, - "validateTop...: top stack has pausing activity r=" + r - + " state=" + state); - if (state != INITIALIZING && state != RESUMED) Slog.e(TAG, - "validateTop...: activity in front not resumed r=" + r - + " state=" + state); - } - } else { - final ActivityRecord resumed = stack.getResumedActivity(); - if (resumed != null && resumed == r) Slog.e(TAG, - "validateTop...: back stack has resumed activity r=" + r - + " state=" + state); - if (r != null && (state == INITIALIZING || state == RESUMED)) Slog.e(TAG, - "validateTop...: activity in back resumed r=" + r + " state=" + state); - } - } - } - } - - public void dumpDisplays(PrintWriter pw) { - for (int i = mActivityDisplays.size() - 1; i >= 0; --i) { - final ActivityDisplay display = mActivityDisplays.get(i); - pw.print("[id:" + display.mDisplayId + " stacks:"); - display.dumpStacks(pw); - pw.print("]"); - } - } - public void dump(PrintWriter pw, String prefix) { pw.println(); pw.println("ActivityStackSupervisor state:"); - pw.print(prefix); - pw.println("topDisplayFocusedStack=" + getTopDisplayFocusedStack()); + mRootActivityContainer.dump(pw, prefix); pw.print(prefix); pw.println("mCurTaskIdForUser=" + mCurTaskIdForUser); - pw.print(prefix); pw.println("mUserStackInFront=" + mUserStackInFront); - for (int i = mActivityDisplays.size() - 1; i >= 0; --i) { - final ActivityDisplay display = mActivityDisplays.get(i); - display.dump(pw, prefix); - } + pw.println(prefix + "mUserStackInFront=" + mRootActivityContainer.mUserStackInFront); if (!mWaitingForActivityVisible.isEmpty()) { - pw.print(prefix); pw.println("mWaitingForActivityVisible="); + pw.println(prefix + "mWaitingForActivityVisible="); for (int i = 0; i < mWaitingForActivityVisible.size(); ++i) { - pw.print(prefix); pw.print(prefix); mWaitingForActivityVisible.get(i).dump(pw, prefix); + pw.print(prefix + prefix); mWaitingForActivityVisible.get(i).dump(pw, prefix); } } pw.print(prefix); pw.print("isHomeRecentsComponent="); - pw.print(mRecentTasks.isRecentsComponentHomeActivity(mCurrentUser)); + pw.print(mRecentTasks.isRecentsComponentHomeActivity(mRootActivityContainer.mCurrentUser)); getKeyguardController().dump(pw, prefix); mService.getLockTaskController().dump(pw, prefix); } - public void writeToProto(ProtoOutputStream proto, long fieldId) { - final long token = proto.start(fieldId); - super.writeToProto(proto, CONFIGURATION_CONTAINER, false /* trim */); - for (int displayNdx = 0; displayNdx < mActivityDisplays.size(); ++displayNdx) { - final ActivityDisplay activityDisplay = mActivityDisplays.get(displayNdx); - activityDisplay.writeToProto(proto, DISPLAYS); - } - getKeyguardController().writeToProto(proto, KEYGUARD_CONTROLLER); - // TODO(b/111541062): Update tests to look for resumed activities on all displays - final ActivityStack focusedStack = getTopDisplayFocusedStack(); - if (focusedStack != null) { - proto.write(FOCUSED_STACK_ID, focusedStack.mStackId); - final ActivityRecord focusedActivity = focusedStack.getDisplay().getResumedActivity(); - if (focusedActivity != null) { - focusedActivity.writeIdentifierToProto(proto, RESUMED_ACTIVITY); - } - } else { - proto.write(FOCUSED_STACK_ID, INVALID_STACK_ID); - } - proto.write(IS_HOME_RECENTS_COMPONENT, - mRecentTasks.isRecentsComponentHomeActivity(mCurrentUser)); - mService.getActivityStartController().writeToProto(proto, PENDING_ACTIVITIES); - proto.end(token); - } - - /** - * Dump all connected displays' configurations. - * @param prefix Prefix to apply to each line of the dump. - */ - void dumpDisplayConfigs(PrintWriter pw, String prefix) { - pw.print(prefix); pw.println("Display override configurations:"); - final int displayCount = mActivityDisplays.size(); - for (int i = 0; i < displayCount; i++) { - final ActivityDisplay activityDisplay = mActivityDisplays.get(i); - pw.print(prefix); pw.print(" "); pw.print(activityDisplay.mDisplayId); pw.print(": "); - pw.println(activityDisplay.getOverrideConfiguration()); - } - } - - /** - * Dumps the activities matching the given {@param name} in the either the focused stack - * or all visible stacks if {@param dumpVisibleStacks} is true. - */ - ArrayList<ActivityRecord> getDumpActivitiesLocked(String name, boolean dumpVisibleStacksOnly, - boolean dumpFocusedStackOnly) { - if (dumpFocusedStackOnly) { - return getTopDisplayFocusedStack().getDumpActivitiesLocked(name); - } else { - ArrayList<ActivityRecord> activities = new ArrayList<>(); - int numDisplays = mActivityDisplays.size(); - for (int displayNdx = 0; displayNdx < numDisplays; ++displayNdx) { - final ActivityDisplay display = mActivityDisplays.get(displayNdx); - for (int stackNdx = display.getChildCount() - 1; stackNdx >= 0; --stackNdx) { - final ActivityStack stack = display.getChildAt(stackNdx); - if (!dumpVisibleStacksOnly || stack.shouldBeVisible(null)) { - activities.addAll(stack.getDumpActivitiesLocked(name)); - } - } - } - return activities; - } - } - static boolean printThisActivity(PrintWriter pw, ActivityRecord activity, String dumpPackage, boolean needSep, String prefix) { if (activity != null) { @@ -4007,73 +2191,6 @@ public class ActivityStackSupervisor extends ConfigurationContainer implements D return false; } - boolean dumpActivitiesLocked(FileDescriptor fd, PrintWriter pw, boolean dumpAll, - boolean dumpClient, String dumpPackage) { - boolean printed = false; - boolean needSep = false; - for (int displayNdx = mActivityDisplays.size() - 1; displayNdx >= 0; --displayNdx) { - ActivityDisplay activityDisplay = mActivityDisplays.get(displayNdx); - pw.print("Display #"); pw.print(activityDisplay.mDisplayId); - pw.println(" (activities from top to bottom):"); - final ActivityDisplay display = mActivityDisplays.get(displayNdx); - for (int stackNdx = display.getChildCount() - 1; stackNdx >= 0; --stackNdx) { - final ActivityStack stack = display.getChildAt(stackNdx); - pw.println(); - pw.println(" Stack #" + stack.mStackId - + ": type=" + activityTypeToString(stack.getActivityType()) - + " mode=" + windowingModeToString(stack.getWindowingMode())); - pw.println(" isSleeping=" + stack.shouldSleepActivities()); - pw.println(" mBounds=" + stack.getOverrideBounds()); - - printed |= stack.dumpActivitiesLocked(fd, pw, dumpAll, dumpClient, dumpPackage, - needSep); - - printed |= dumpHistoryList(fd, pw, stack.mLRUActivities, " ", "Run", false, - !dumpAll, false, dumpPackage, true, - " Running activities (most recent first):", null); - - needSep = printed; - boolean pr = printThisActivity(pw, stack.mPausingActivity, dumpPackage, needSep, - " mPausingActivity: "); - if (pr) { - printed = true; - needSep = false; - } - pr = printThisActivity(pw, stack.getResumedActivity(), dumpPackage, needSep, - " mResumedActivity: "); - if (pr) { - printed = true; - needSep = false; - } - if (dumpAll) { - pr = printThisActivity(pw, stack.mLastPausedActivity, dumpPackage, needSep, - " mLastPausedActivity: "); - if (pr) { - printed = true; - needSep = true; - } - printed |= printThisActivity(pw, stack.mLastNoHistoryActivity, dumpPackage, - needSep, " mLastNoHistoryActivity: "); - } - needSep = printed; - } - printThisActivity(pw, activityDisplay.getResumedActivity(), dumpPackage, needSep, - " ResumedActivity:"); - } - - printed |= dumpHistoryList(fd, pw, mFinishingActivities, " ", "Fin", false, !dumpAll, - false, dumpPackage, true, " Activities waiting to finish:", null); - printed |= dumpHistoryList(fd, pw, mStoppingActivities, " ", "Stop", false, !dumpAll, - false, dumpPackage, true, " Activities waiting to stop:", null); - printed |= dumpHistoryList(fd, pw, mActivitiesWaitingForVisibleActivity, " ", "Wait", - false, !dumpAll, false, dumpPackage, true, - " Activities waiting for another to become visible:", null); - printed |= dumpHistoryList(fd, pw, mGoingToSleepActivities, " ", "Sleep", false, !dumpAll, - false, dumpPackage, true, " Activities waiting to sleep:", null); - - return printed; - } - static boolean dumpHistoryList(FileDescriptor fd, PrintWriter pw, List<ActivityRecord> list, String prefix, String label, boolean complete, boolean brief, boolean client, String dumpPackage, boolean needNL, String header, TaskRecord lastTask) { @@ -4183,294 +2300,6 @@ public class ActivityStackSupervisor extends ConfigurationContainer implements D mHandler.sendEmptyMessageDelayed(SLEEP_TIMEOUT_MSG, SLEEP_TIMEOUT); } - @Override - public void onDisplayAdded(int displayId) { - if (DEBUG_STACK) Slog.v(TAG, "Display added displayId=" + displayId); - synchronized (mService.mGlobalLock) { - getActivityDisplayOrCreateLocked(displayId); - // Do not start home before booting, or it may accidentally finish booting before it - // starts. Instead, we expect home activities to be launched when the system is ready - // (ActivityManagerService#systemReady). - if (mService.isBooted() || mService.isBooting()) { - startHomeOnDisplay(mCurrentUser, "displayAdded", displayId); - } - } - } - - @Override - public void onDisplayRemoved(int displayId) { - if (DEBUG_STACK) Slog.v(TAG, "Display removed displayId=" + displayId); - if (displayId == DEFAULT_DISPLAY) { - throw new IllegalArgumentException("Can't remove the primary display."); - } - - synchronized (mService.mGlobalLock) { - final ActivityDisplay activityDisplay = getActivityDisplay(displayId); - if (activityDisplay == null) { - return; - } - - activityDisplay.remove(); - } - } - - @Override - public void onDisplayChanged(int displayId) { - if (DEBUG_STACK) Slog.v(TAG, "Display changed displayId=" + displayId); - synchronized (mService.mGlobalLock) { - final ActivityDisplay activityDisplay = getActivityDisplay(displayId); - if (activityDisplay != null) { - activityDisplay.onDisplayChanged(); - } - } - } - - /** Check if display with specified id is added to the list. */ - boolean isDisplayAdded(int displayId) { - return getActivityDisplayOrCreateLocked(displayId) != null; - } - - // TODO: Look into consolidating with getActivityDisplayOrCreateLocked() - ActivityDisplay getActivityDisplay(int displayId) { - for (int i = mActivityDisplays.size() - 1; i >= 0; --i) { - final ActivityDisplay activityDisplay = mActivityDisplays.get(i); - if (activityDisplay.mDisplayId == displayId) { - return activityDisplay; - } - } - return null; - } - - // TODO(multi-display): Look at all callpoints to make sure they make sense in multi-display. - ActivityDisplay getDefaultDisplay() { - return mDefaultDisplay; - } - - /** - * Get an existing instance of {@link ActivityDisplay} or create new if there is a - * corresponding record in display manager. - */ - // TODO: Look into consolidating with getActivityDisplay() - ActivityDisplay getActivityDisplayOrCreateLocked(int displayId) { - ActivityDisplay activityDisplay = getActivityDisplay(displayId); - if (activityDisplay != null) { - return activityDisplay; - } - if (mDisplayManager == null) { - // The system isn't fully initialized yet. - return null; - } - final Display display = mDisplayManager.getDisplay(displayId); - if (display == null) { - // The display is not registered in DisplayManager. - return null; - } - // The display hasn't been added to ActivityManager yet, create a new record now. - activityDisplay = new ActivityDisplay(this, display); - addChild(activityDisplay, ActivityDisplay.POSITION_BOTTOM); - return activityDisplay; - } - - /** - * Get an existing instance of {@link ActivityDisplay} that has the given uniqueId. Unique ID is - * defined in {@link DisplayInfo#uniqueId}. - * - * @param uniqueId the unique ID of the display - * @return the {@link ActivityDisplay} or {@code null} if nothing is found. - */ - ActivityDisplay getActivityDisplay(String uniqueId) { - for (int i = mActivityDisplays.size() - 1; i >= 0; --i) { - final ActivityDisplay display = mActivityDisplays.get(i); - final boolean isValid = display.mDisplay.isValid(); - if (isValid && display.mDisplay.getUniqueId().equals(uniqueId)) { - return display; - } - } - - return null; - } - - boolean startHomeOnAllDisplays(int userId, String reason) { - boolean homeStarted = false; - for (int i = mActivityDisplays.size() - 1; i >= 0; i--) { - final int displayId = mActivityDisplays.get(i).mDisplayId; - homeStarted |= startHomeOnDisplay(userId, reason, displayId); - } - return homeStarted; - } - - /** - * This starts home activity on displays that can have system decorations and only if the - * home activity can have multiple instances. - */ - boolean startHomeOnDisplay(int userId, String reason, int displayId) { - final Intent homeIntent = mService.getHomeIntent(); - final ActivityInfo aInfo = resolveHomeActivity(userId, homeIntent); - if (aInfo == null) { - return false; - } - - if (!canStartHomeOnDisplay(aInfo, displayId, false /* allowInstrumenting */)) { - return false; - } - - // Update the reason for ANR debugging to verify if the user activity is the one that - // actually launched. - final String myReason = reason + ":" + userId + ":" + UserHandle.getUserId( - aInfo.applicationInfo.uid); - mService.getActivityStartController().startHomeActivity(homeIntent, aInfo, myReason, - displayId); - return true; - } - - /** - * This resolves the home activity info and updates the home component of the given intent. - * @return the home activity info if any. - */ - private ActivityInfo resolveHomeActivity(int userId, Intent homeIntent) { - final int flags = ActivityManagerService.STOCK_PM_FLAGS; - final ComponentName comp = homeIntent.getComponent(); - ActivityInfo aInfo = null; - try { - if (comp != null) { - // Factory test. - aInfo = AppGlobals.getPackageManager().getActivityInfo(comp, flags, userId); - } else { - final String resolvedType = - homeIntent.resolveTypeIfNeeded(mService.mContext.getContentResolver()); - final ResolveInfo info = AppGlobals.getPackageManager() - .resolveIntent(homeIntent, resolvedType, flags, userId); - if (info != null) { - aInfo = info.activityInfo; - } - } - } catch (RemoteException e) { - // ignore - } - - if (aInfo == null) { - Slog.wtf(TAG, "No home screen found for " + homeIntent, new Throwable()); - return null; - } - - homeIntent.setComponent(new ComponentName(aInfo.applicationInfo.packageName, aInfo.name)); - aInfo = new ActivityInfo(aInfo); - aInfo.applicationInfo = mService.getAppInfoForUser(aInfo.applicationInfo, userId); - homeIntent.setFlags(homeIntent.getFlags() | FLAG_ACTIVITY_NEW_TASK); - return aInfo; - } - - @VisibleForTesting - void addChild(ActivityDisplay activityDisplay, int position) { - positionChildAt(activityDisplay, position); - mWindowContainerController.positionChildAt( - activityDisplay.getWindowContainerController(), position); - } - - void removeChild(ActivityDisplay activityDisplay) { - // The caller must tell the controller of {@link ActivityDisplay} to release its container - // {@link DisplayContent}. That is done in {@link ActivityDisplay#releaseSelfIfNeeded}). - mActivityDisplays.remove(activityDisplay); - } - - private void calculateDefaultMinimalSizeOfResizeableTasks() { - final Resources res = mService.mContext.getResources(); - final float minimalSize = res.getDimension( - com.android.internal.R.dimen.default_minimal_size_resizable_task); - final DisplayMetrics dm = res.getDisplayMetrics(); - - mDefaultMinSizeOfResizeableTaskDp = (int) (minimalSize / dm.density); - } - - SleepToken createSleepTokenLocked(String tag, int displayId) { - final ActivityDisplay display = getActivityDisplay(displayId); - if (display == null) { - throw new IllegalArgumentException("Invalid display: " + displayId); - } - - final SleepTokenImpl token = new SleepTokenImpl(tag, displayId); - mSleepTokens.add(token); - display.mAllSleepTokens.add(token); - return token; - } - - private void removeSleepTokenLocked(SleepTokenImpl token) { - mSleepTokens.remove(token); - - final ActivityDisplay display = getActivityDisplay(token.mDisplayId); - if (display != null) { - display.mAllSleepTokens.remove(token); - if (display.mAllSleepTokens.isEmpty()) { - mService.updateSleepIfNeededLocked(); - } - } - } - - private StackInfo getStackInfo(ActivityStack stack) { - final int displayId = stack.mDisplayId; - final ActivityDisplay display = getActivityDisplay(displayId); - StackInfo info = new StackInfo(); - stack.getWindowContainerBounds(info.bounds); - info.displayId = displayId; - info.stackId = stack.mStackId; - info.userId = stack.mCurrentUser; - info.visible = stack.shouldBeVisible(null); - // A stack might be not attached to a display. - info.position = display != null ? display.getIndexOf(stack) : 0; - info.configuration.setTo(stack.getConfiguration()); - - ArrayList<TaskRecord> tasks = stack.getAllTasks(); - final int numTasks = tasks.size(); - int[] taskIds = new int[numTasks]; - String[] taskNames = new String[numTasks]; - Rect[] taskBounds = new Rect[numTasks]; - int[] taskUserIds = new int[numTasks]; - for (int i = 0; i < numTasks; ++i) { - final TaskRecord task = tasks.get(i); - taskIds[i] = task.taskId; - taskNames[i] = task.origActivity != null ? task.origActivity.flattenToString() - : task.realActivity != null ? task.realActivity.flattenToString() - : task.getTopActivity() != null ? task.getTopActivity().packageName - : "unknown"; - taskBounds[i] = new Rect(); - task.getWindowContainerBounds(taskBounds[i]); - taskUserIds[i] = task.userId; - } - info.taskIds = taskIds; - info.taskNames = taskNames; - info.taskBounds = taskBounds; - info.taskUserIds = taskUserIds; - - final ActivityRecord top = stack.topRunningActivityLocked(); - info.topActivity = top != null ? top.intent.getComponent() : null; - return info; - } - - StackInfo getStackInfo(int stackId) { - ActivityStack stack = getStack(stackId); - if (stack != null) { - return getStackInfo(stack); - } - return null; - } - - StackInfo getStackInfo(int windowingMode, int activityType) { - final ActivityStack stack = getStack(windowingMode, activityType); - return (stack != null) ? getStackInfo(stack) : null; - } - - ArrayList<StackInfo> getAllStackInfosLocked() { - ArrayList<StackInfo> list = new ArrayList<>(); - for (int displayNdx = 0; displayNdx < mActivityDisplays.size(); ++displayNdx) { - final ActivityDisplay display = mActivityDisplays.get(displayNdx); - for (int stackNdx = display.getChildCount() - 1; stackNdx >= 0; --stackNdx) { - final ActivityStack stack = display.getChildAt(stackNdx); - list.add(getStackInfo(stack)); - } - } - return list; - } - void handleNonResizableTaskIfNeeded(TaskRecord task, int preferredWindowingMode, int preferredDisplayId, ActivityStack actualStack) { handleNonResizableTaskIfNeeded(task, preferredWindowingMode, preferredDisplayId, @@ -4616,21 +2445,6 @@ public class ActivityStackSupervisor extends ConfigurationContainer implements D } } - void setDockedStackMinimized(boolean minimized) { - // Get currently focused stack before setting mIsDockMinimized. We do this because if - // split-screen is active, primary stack will not be focusable (see #isFocusable) while - // still occluding other stacks. This will cause getTopDisplayFocusedStack() to return null. - final ActivityStack current = getTopDisplayFocusedStack(); - mIsDockMinimized = minimized; - if (mIsDockMinimized) { - if (current.inSplitScreenPrimaryWindowingMode()) { - // The primary split-screen stack can't be focused while it is minimize, so move - // focus to something else. - current.adjustFocusToNextFocusableStack("setDockedStackMinimized"); - } - } - } - void wakeUp(String reason) { mPowerManager.wakeUp(SystemClock.uptimeMillis(), "android.server.am:TURN_ON:" + reason); } @@ -4649,10 +2463,8 @@ public class ActivityStackSupervisor extends ConfigurationContainer implements D mDeferResumeCount--; } - /** - * @return True if resume can be called. - */ - private boolean readyToResume() { + /** @return True if resume can be called. */ + boolean readyToResume() { return mDeferResumeCount == 0; } @@ -4704,7 +2516,7 @@ public class ActivityStackSupervisor extends ConfigurationContainer implements D } break; case RESUME_TOP_ACTIVITY_MSG: { synchronized (mService.mGlobalLock) { - resumeFocusedStacksTopActivitiesLocked(); + mRootActivityContainer.resumeFocusedStacksTopActivities(); } } break; case SLEEP_TIMEOUT_MSG: { @@ -4740,19 +2552,6 @@ public class ActivityStackSupervisor extends ConfigurationContainer implements D } } - ActivityStack findStackBehind(ActivityStack stack) { - final ActivityDisplay display = getActivityDisplay(stack.mDisplayId); - if (display != null) { - for (int i = display.getChildCount() - 1; i >= 0; i--) { - if (display.getChildAt(i) == stack && i > 0) { - return display.getChildAt(i - 1); - } - } - } - throw new IllegalStateException("Failed to find a stack behind stack=" + stack - + " in=" + display); - } - /** * Puts a task into resizing mode during the next app transition. * @@ -4797,8 +2596,8 @@ public class ActivityStackSupervisor extends ConfigurationContainer implements D mWindowManager.prepareAppTransition(TRANSIT_DOCK_TASK_FROM_RECENTS, false); } - task = anyTaskForIdLocked(taskId, MATCH_TASK_IN_STACKS_OR_RECENT_TASKS_AND_RESTORE, - activityOptions, ON_TOP); + task = mRootActivityContainer.anyTaskForId(taskId, + MATCH_TASK_IN_STACKS_OR_RECENT_TASKS_AND_RESTORE, activityOptions, ON_TOP); if (task == null) { continueUpdateRecentsHomeStackBounds(); mWindowManager.executeAppTransition(); @@ -4811,7 +2610,8 @@ public class ActivityStackSupervisor extends ConfigurationContainer implements D // from whatever is started from the recents activity, so move the home stack // forward. // TODO (b/115289124): Multi-display supports for recents. - getDefaultDisplay().moveHomeStackToFront("startActivityFromRecents"); + mRootActivityContainer.getDefaultDisplay().moveHomeStackToFront( + "startActivityFromRecents"); } // If the user must confirm credentials (e.g. when first launching a work app and the @@ -4820,7 +2620,8 @@ public class ActivityStackSupervisor extends ConfigurationContainer implements D && task.getRootActivity() != null) { final ActivityRecord targetActivity = task.getTopActivity(); - sendPowerHintForLaunchStartIfNeeded(true /* forceSend */, targetActivity); + mRootActivityContainer.sendPowerHintForLaunchStartIfNeeded( + true /* forceSend */, targetActivity); mActivityMetricsLogger.notifyActivityLaunching(task.intent); try { mService.moveTaskToFrontLocked(task.taskId, 0, options, @@ -4873,35 +2674,6 @@ public class ActivityStackSupervisor extends ConfigurationContainer implements D } /** - * @return a list of activities which are the top ones in each visible stack. The first - * entry will be the focused activity. - */ - List<IBinder> getTopVisibleActivities() { - final ArrayList<IBinder> topActivityTokens = new ArrayList<>(); - final ActivityStack topFocusedStack = getTopDisplayFocusedStack(); - // Traverse all displays. - for (int i = mActivityDisplays.size() - 1; i >= 0; i--) { - final ActivityDisplay display = mActivityDisplays.get(i); - // Traverse all stacks on a display. - for (int j = display.getChildCount() - 1; j >= 0; --j) { - final ActivityStack stack = display.getChildAt(j); - // Get top activity from a visible stack and add it to the list. - if (stack.shouldBeVisible(null /* starting */)) { - final ActivityRecord top = stack.getTopActivity(); - if (top != null) { - if (stack == topFocusedStack) { - topActivityTokens.add(0, top.appToken); - } else { - topActivityTokens.add(top.appToken); - } - } - } - } - } - return topActivityTokens; - } - - /** * Internal container to store a match qualifier alongside a WaitResult. */ static class WaitInfo { @@ -4939,30 +2711,4 @@ public class ActivityStackSupervisor extends ConfigurationContainer implements D mResult.dump(pw, prefix); } } - - private final class SleepTokenImpl extends SleepToken { - private final String mTag; - private final long mAcquireTime; - private final int mDisplayId; - - public SleepTokenImpl(String tag, int displayId) { - mTag = tag; - mDisplayId = displayId; - mAcquireTime = SystemClock.uptimeMillis(); - } - - @Override - public void release() { - synchronized (mService.mGlobalLock) { - removeSleepTokenLocked(this); - } - } - - @Override - public String toString() { - return "{\"" + mTag + "\", display " + mDisplayId - + ", acquire at " + TimeUtils.formatUptime(mAcquireTime) + "}"; - } - } - } diff --git a/services/core/java/com/android/server/wm/ActivityStartInterceptor.java b/services/core/java/com/android/server/wm/ActivityStartInterceptor.java index ee5a43ce0edb..54a63a168588 100644 --- a/services/core/java/com/android/server/wm/ActivityStartInterceptor.java +++ b/services/core/java/com/android/server/wm/ActivityStartInterceptor.java @@ -70,6 +70,7 @@ class ActivityStartInterceptor { private final ActivityTaskManagerService mService; private final ActivityStackSupervisor mSupervisor; + private final RootActivityContainer mRootActivityContainer; private final Context mServiceContext; // UserManager cannot be final as it's not ready when this class is instantiated during boot @@ -102,14 +103,15 @@ class ActivityStartInterceptor { ActivityStartInterceptor( ActivityTaskManagerService service, ActivityStackSupervisor supervisor) { - this(service, supervisor, service.mContext); + this(service, supervisor, service.mRootActivityContainer, service.mContext); } @VisibleForTesting ActivityStartInterceptor(ActivityTaskManagerService service, ActivityStackSupervisor supervisor, - Context context) { + RootActivityContainer root, Context context) { mService = service; mSupervisor = supervisor; + mRootActivityContainer = root; mServiceContext = context; } @@ -279,7 +281,7 @@ class ActivityStartInterceptor { mActivityOptions = ActivityOptions.makeBasic(); } - ActivityRecord homeActivityRecord = mSupervisor.getDefaultDisplayHomeActivity(); + ActivityRecord homeActivityRecord = mRootActivityContainer.getDefaultDisplayHomeActivity(); if (homeActivityRecord != null && homeActivityRecord.getTask() != null) { // Showing credential confirmation activity in home task to avoid stopping multi-windowed // mode after showing the full-screen credential confirmation activity. diff --git a/services/core/java/com/android/server/wm/ActivityStarter.java b/services/core/java/com/android/server/wm/ActivityStarter.java index 90f3ff84a027..d22623eeb14d 100644 --- a/services/core/java/com/android/server/wm/ActivityStarter.java +++ b/services/core/java/com/android/server/wm/ActivityStarter.java @@ -137,6 +137,7 @@ class ActivityStarter { private static final int INVALID_LAUNCH_MODE = -1; private final ActivityTaskManagerService mService; + private final RootActivityContainer mRootActivityContainer; private final ActivityStackSupervisor mSupervisor; private final ActivityStartInterceptor mInterceptor; private final ActivityStartController mController; @@ -421,6 +422,7 @@ class ActivityStarter { ActivityStackSupervisor supervisor, ActivityStartInterceptor interceptor) { mController = controller; mService = service; + mRootActivityContainer = service.mRootActivityContainer; mSupervisor = supervisor; mInterceptor = interceptor; reset(true); @@ -617,7 +619,7 @@ class ActivityStarter { ActivityRecord sourceRecord = null; ActivityRecord resultRecord = null; if (resultTo != null) { - sourceRecord = mSupervisor.isInAnyStackLocked(resultTo); + sourceRecord = mRootActivityContainer.isInAnyStack(resultTo); if (DEBUG_RESULTS) Slog.v(TAG_RESULTS, "Will send result to " + resultTo + " " + sourceRecord); if (sourceRecord != null) { @@ -811,7 +813,8 @@ class ActivityStarter { null /*profilerInfo*/); if (DEBUG_PERMISSIONS_REVIEW) { - final ActivityStack focusedStack = mSupervisor.getTopDisplayFocusedStack(); + final ActivityStack focusedStack = + mRootActivityContainer.getTopDisplayFocusedStack(); Slog.i(TAG, "START u" + userId + " {" + intent.toShortString(true, true, true, false) + "} from uid " + callingUid + " on display " + (focusedStack == null ? DEFAULT_DISPLAY : focusedStack.mDisplayId)); @@ -847,7 +850,7 @@ class ActivityStarter { r.appTimeTracker = sourceRecord.appTimeTracker; } - final ActivityStack stack = mSupervisor.getTopDisplayFocusedStack(); + final ActivityStack stack = mRootActivityContainer.getTopDisplayFocusedStack(); // If we are starting an activity that is not from the same uid as the currently resumed // one, check whether app switches are allowed. @@ -1063,7 +1066,7 @@ class ActivityStarter { ActivityInfo aInfo = mSupervisor.resolveActivity(intent, rInfo, startFlags, profilerInfo); synchronized (mService.mGlobalLock) { - final ActivityStack stack = mSupervisor.getTopDisplayFocusedStack(); + final ActivityStack stack = mRootActivityContainer.getTopDisplayFocusedStack(); stack.mConfigWillChange = globalConfig != null && mService.getGlobalConfiguration().diff(globalConfig) != 0; if (DEBUG_CONFIGURATION) Slog.v(TAG_CONFIGURATION, @@ -1249,7 +1252,8 @@ class ActivityStarter { final ActivityRecord currentTop = startedActivityStack.topRunningActivityLocked(); if (currentTop != null && currentTop.shouldUpdateConfigForDisplayChanged()) { - mSupervisor.ensureVisibilityAndConfig(currentTop, currentTop.getDisplayId(), + mRootActivityContainer.ensureVisibilityAndConfig( + currentTop, currentTop.getDisplayId(), true /* markFrozenIfConfigChanged */, false /* deferResume */); } } @@ -1284,7 +1288,7 @@ class ActivityStarter { // Do not start home activity if it cannot be launched on preferred display. We are not // doing this in ActivityStackSupervisor#canPlaceEntityOnDisplay because it might // fallback to launch on other displays. - if (r.isActivityTypeHome() && !mSupervisor.canStartHomeOnDisplay(r.info, + if (r.isActivityTypeHome() && !mRootActivityContainer.canStartHomeOnDisplay(r.info, mPreferredDisplayId, true /* allowInstrumenting */)) { Slog.w(TAG, "Cannot launch home on display " + mPreferredDisplayId); return START_CANCELED; @@ -1361,7 +1365,8 @@ class ActivityStarter { } } - mSupervisor.sendPowerHintForLaunchStartIfNeeded(false /* forceSend */, reusedActivity); + mRootActivityContainer.sendPowerHintForLaunchStartIfNeeded + (false /* forceSend */, reusedActivity); reusedActivity = setTargetStackAndMoveToFrontIfNeeded(reusedActivity); @@ -1413,7 +1418,7 @@ class ActivityStarter { // If the activity being launched is the same as the one currently at the top, then // we need to check if it should only be launched once. - final ActivityStack topStack = mSupervisor.getTopDisplayFocusedStack(); + final ActivityStack topStack = mRootActivityContainer.getTopDisplayFocusedStack(); final ActivityRecord topFocused = topStack.getTopActivity(); final ActivityRecord top = topStack.topRunningNonDelayedActivityLocked(mNotTop); final boolean dontStart = top != null && mStartActivity.resultTo == null @@ -1430,7 +1435,7 @@ class ActivityStarter { // For paranoia, make sure we have correctly resumed the top activity. topStack.mLastPausedActivity = null; if (mDoResume) { - mSupervisor.resumeFocusedStacksTopActivitiesLocked(); + mRootActivityContainer.resumeFocusedStacksTopActivities(); } ActivityOptions.abort(mOptions); if ((mStartFlags & START_FLAG_ONLY_IF_NEEDED) != 0) { @@ -1485,7 +1490,8 @@ class ActivityStarter { EventLogTags.AM_CREATE_ACTIVITY, mStartActivity, mStartActivity.getTask()); mTargetStack.mLastPausedActivity = null; - mSupervisor.sendPowerHintForLaunchStartIfNeeded(false /* forceSend */, mStartActivity); + mRootActivityContainer.sendPowerHintForLaunchStartIfNeeded( + false /* forceSend */, mStartActivity); mTargetStack.startActivityLocked(mStartActivity, topFocused, newTask, mKeepCurTransition, mOptions); @@ -1512,16 +1518,16 @@ class ActivityStarter { // task stack to be focusable, then ensure that we now update the focused stack // accordingly. if (mTargetStack.isFocusable() - && !mSupervisor.isTopDisplayFocusedStack(mTargetStack)) { + && !mRootActivityContainer.isTopDisplayFocusedStack(mTargetStack)) { mTargetStack.moveToFront("startActivityUnchecked"); } - mSupervisor.resumeFocusedStacksTopActivitiesLocked(mTargetStack, mStartActivity, - mOptions); + mRootActivityContainer.resumeFocusedStacksTopActivities( + mTargetStack, mStartActivity, mOptions); } } else if (mStartActivity != null) { mSupervisor.mRecentTasks.add(mStartActivity.getTask()); } - mSupervisor.updateUserStackLocked(mStartActivity.userId, mTargetStack); + mRootActivityContainer.updateUserStack(mStartActivity.userId, mTargetStack); mSupervisor.handleNonResizableTaskIfNeeded(mStartActivity.getTask(), preferredWindowingMode, mPreferredDisplayId, mTargetStack); @@ -1642,7 +1648,7 @@ class ActivityStarter { if (mOptions.getLaunchTaskId() != -1 && mOptions.getTaskOverlay()) { r.mTaskOverlay = true; if (!mOptions.canTaskOverlayResume()) { - final TaskRecord task = mSupervisor.anyTaskForIdLocked( + final TaskRecord task = mRootActivityContainer.anyTaskForId( mOptions.getLaunchTaskId()); final ActivityRecord top = task != null ? task.getTopActivity() : null; if (top != null && !top.isState(RESUMED)) { @@ -1678,7 +1684,7 @@ class ActivityStarter { if ((startFlags & START_FLAG_ONLY_IF_NEEDED) != 0) { ActivityRecord checkedCaller = sourceRecord; if (checkedCaller == null) { - checkedCaller = mSupervisor.getTopDisplayFocusedStack() + checkedCaller = mRootActivityContainer.getTopDisplayFocusedStack() .topRunningNonDelayedActivityLocked(mNotTop); } if (!checkedCaller.realActivity.equals(r.realActivity)) { @@ -1840,22 +1846,23 @@ class ActivityStarter { putIntoExistingTask &= mInTask == null && mStartActivity.resultTo == null; ActivityRecord intentActivity = null; if (mOptions != null && mOptions.getLaunchTaskId() != -1) { - final TaskRecord task = mSupervisor.anyTaskForIdLocked(mOptions.getLaunchTaskId()); + final TaskRecord task = mRootActivityContainer.anyTaskForId(mOptions.getLaunchTaskId()); intentActivity = task != null ? task.getTopActivity() : null; } else if (putIntoExistingTask) { if (LAUNCH_SINGLE_INSTANCE == mLaunchMode) { // There can be one and only one instance of single instance activity in the // history, and it is always in its own unique task, so we do a special search. - intentActivity = mSupervisor.findActivityLocked(mIntent, mStartActivity.info, + intentActivity = mRootActivityContainer.findActivity(mIntent, mStartActivity.info, mStartActivity.isActivityTypeHome()); } else if ((mLaunchFlags & FLAG_ACTIVITY_LAUNCH_ADJACENT) != 0) { // For the launch adjacent case we only want to put the activity in an existing // task if the activity already exists in the history. - intentActivity = mSupervisor.findActivityLocked(mIntent, mStartActivity.info, + intentActivity = mRootActivityContainer.findActivity(mIntent, mStartActivity.info, !(LAUNCH_SINGLE_TASK == mLaunchMode)); } else { // Otherwise find the best task to put the activity in. - intentActivity = mSupervisor.findTaskLocked(mStartActivity, mPreferredDisplayId); + intentActivity = + mRootActivityContainer.findTask(mStartActivity, mPreferredDisplayId); } } @@ -2067,11 +2074,11 @@ class ActivityStarter { private void resumeTargetStackIfNeeded() { if (mDoResume) { - mSupervisor.resumeFocusedStacksTopActivitiesLocked(mTargetStack, null, mOptions); + mRootActivityContainer.resumeFocusedStacksTopActivities(mTargetStack, null, mOptions); } else { ActivityOptions.abort(mOptions); } - mSupervisor.updateUserStackLocked(mStartActivity.userId, mTargetStack); + mRootActivityContainer.updateUserStack(mStartActivity.userId, mTargetStack); } private int setTaskFromReuseOrCreateNewTask(TaskRecord taskToAffiliate) { @@ -2145,13 +2152,13 @@ class ActivityStarter { // be not suitable. Let's check other displays. if (mTargetStack == null && targetDisplayId != sourceStack.mDisplayId) { // Can't use target display, lets find a stack on the source display. - mTargetStack = mSupervisor.getValidLaunchStackOnDisplay( + mTargetStack = mRootActivityContainer.getValidLaunchStackOnDisplay( sourceStack.mDisplayId, mStartActivity, mOptions, mLaunchParams); } if (mTargetStack == null) { // There are no suitable stacks on the target and source display(s). Look on all // displays. - mTargetStack = mSupervisor.getNextValidLaunchStackLocked( + mTargetStack = mRootActivityContainer.getNextValidLaunchStack( mStartActivity, -1 /* currentFocus */); } } @@ -2182,7 +2189,7 @@ class ActivityStarter { // For paranoia, make sure we have correctly resumed the top activity. mTargetStack.mLastPausedActivity = null; if (mDoResume) { - mSupervisor.resumeFocusedStacksTopActivitiesLocked(); + mRootActivityContainer.resumeFocusedStacksTopActivities(); } ActivityOptions.abort(mOptions); return START_DELIVERED_TO_TOP; @@ -2200,7 +2207,7 @@ class ActivityStarter { deliverNewIntent(top); mTargetStack.mLastPausedActivity = null; if (mDoResume) { - mSupervisor.resumeFocusedStacksTopActivitiesLocked(); + mRootActivityContainer.resumeFocusedStacksTopActivities(); } return START_DELIVERED_TO_TOP; } @@ -2254,7 +2261,8 @@ class ActivityStarter { if (!mLaunchParams.mBounds.isEmpty()) { // TODO: Shouldn't we already know what stack to use by the time we get here? - ActivityStack stack = mSupervisor.getLaunchStack(null, null, mInTask, ON_TOP); + ActivityStack stack = mRootActivityContainer.getLaunchStack( + null, null, mInTask, ON_TOP); if (stack != mInTask.getStack()) { mInTask.reparent(stack, ON_TOP, REPARENT_KEEP_STACK_AT_FRONT, !ANIMATE, DEFER_RESUME, "inTaskToFront"); @@ -2348,7 +2356,7 @@ class ActivityStarter { } final ActivityStack currentStack = task != null ? task.getStack() : null; - final ActivityStack focusedStack = mSupervisor.getTopDisplayFocusedStack(); + final ActivityStack focusedStack = mRootActivityContainer.getTopDisplayFocusedStack(); if (currentStack != null) { if (focusedStack != currentStack) { if (DEBUG_FOCUS || DEBUG_STACK) Slog.d(TAG_FOCUS, @@ -2369,18 +2377,18 @@ class ActivityStarter { if (mPreferredDisplayId != DEFAULT_DISPLAY) { // Try to put the activity in a stack on a secondary display. - stack = mSupervisor.getValidLaunchStackOnDisplay(mPreferredDisplayId, r, aOptions, - mLaunchParams); + stack = mRootActivityContainer.getValidLaunchStackOnDisplay( + mPreferredDisplayId, r, aOptions, mLaunchParams); if (stack == null) { // If source display is not suitable - look for topmost valid stack in the system. if (DEBUG_FOCUS || DEBUG_STACK) Slog.d(TAG_FOCUS, "computeStackFocus: Can't launch on mPreferredDisplayId=" + mPreferredDisplayId + ", looking on all displays."); - stack = mSupervisor.getNextValidLaunchStackLocked(r, mPreferredDisplayId); + stack = mRootActivityContainer.getNextValidLaunchStack(r, mPreferredDisplayId); } } if (stack == null) { - stack = mSupervisor.getLaunchStack(r, aOptions, task, ON_TOP); + stack = mRootActivityContainer.getLaunchStack(r, aOptions, task, ON_TOP); } if (DEBUG_FOCUS || DEBUG_STACK) Slog.d(TAG_FOCUS, "computeStackFocus: New stack r=" + r + " stackId=" + stack.mStackId); @@ -2390,7 +2398,7 @@ class ActivityStarter { /** Check if provided activity record can launch in currently focused stack. */ // TODO: This method can probably be consolidated into getLaunchStack() below. private boolean canLaunchIntoFocusedStack(ActivityRecord r, boolean newTask) { - final ActivityStack focusedStack = mSupervisor.getTopDisplayFocusedStack(); + final ActivityStack focusedStack = mRootActivityContainer.getTopDisplayFocusedStack(); final boolean canUseFocusedStack; if (focusedStack.isActivityTypeAssistant()) { canUseFocusedStack = r.isActivityTypeAssistant(); @@ -2436,14 +2444,14 @@ class ActivityStarter { // full resolution. mLaunchParams.mPreferredDisplayId = mPreferredDisplayId != DEFAULT_DISPLAY ? mPreferredDisplayId : INVALID_DISPLAY; - final ActivityStack stack = mSupervisor.getLaunchStack(r, aOptions, task, ON_TOP, - mLaunchParams); + final ActivityStack stack = + mRootActivityContainer.getLaunchStack(r, aOptions, task, ON_TOP, mLaunchParams); mLaunchParams.mPreferredDisplayId = mPreferredDisplayId; return stack; } // Otherwise handle adjacent launch. - final ActivityStack focusedStack = mSupervisor.getTopDisplayFocusedStack(); + final ActivityStack focusedStack = mRootActivityContainer.getTopDisplayFocusedStack(); // The parent activity doesn't want to launch the activity on top of itself, but // instead tries to put it onto other side in side-by-side mode. final ActivityStack parentStack = task != null ? task.getStack(): focusedStack; @@ -2461,7 +2469,8 @@ class ActivityStarter { if (parentStack != null && parentStack.inSplitScreenPrimaryWindowingMode()) { // If parent was in docked stack, the natural place to launch another activity // will be fullscreen, so it can appear alongside the docked window. - final int activityType = mSupervisor.resolveActivityType(r, mOptions, task); + final int activityType = + mRootActivityContainer.resolveActivityType(r, mOptions, task); return parentStack.getDisplay().getOrCreateStack( WINDOWING_MODE_SPLIT_SCREEN_SECONDARY, activityType, ON_TOP); } else { @@ -2469,10 +2478,10 @@ class ActivityStarter { // and if yes, we will launch into that stack. If not, we just put the new // activity into parent's stack, because we can't find a better place. final ActivityStack dockedStack = - mSupervisor.getDefaultDisplay().getSplitScreenPrimaryStack(); + mRootActivityContainer.getDefaultDisplay().getSplitScreenPrimaryStack(); if (dockedStack != null && !dockedStack.shouldBeVisible(r)) { // There is a docked stack, but it isn't visible, so we can't launch into that. - return mSupervisor.getLaunchStack(r, aOptions, task, ON_TOP); + return mRootActivityContainer.getLaunchStack(r, aOptions, task, ON_TOP); } else { return dockedStack; } @@ -2660,7 +2669,7 @@ class ActivityStarter { prefix = prefix + " "; pw.print(prefix); pw.print("mCurrentUser="); - pw.println(mSupervisor.mCurrentUser); + pw.println(mRootActivityContainer.mCurrentUser); pw.print(prefix); pw.print("mLastStartReason="); pw.println(mLastStartReason); diff --git a/services/core/java/com/android/server/wm/ActivityTaskManagerService.java b/services/core/java/com/android/server/wm/ActivityTaskManagerService.java index d0e3fb47730e..d480fb732df3 100644 --- a/services/core/java/com/android/server/wm/ActivityTaskManagerService.java +++ b/services/core/java/com/android/server/wm/ActivityTaskManagerService.java @@ -91,8 +91,6 @@ import static com.android.server.am.ActivityManagerServiceDumpProcessesProto.Scr .PACKAGE; import static com.android.server.wm.ActivityStack.REMOVE_TASK_MODE_DESTROYING; import static com.android.server.wm.ActivityStackSupervisor.DEFER_RESUME; -import static com.android.server.wm.ActivityStackSupervisor.MATCH_TASK_IN_STACKS_ONLY; -import static com.android.server.wm.ActivityStackSupervisor.MATCH_TASK_IN_STACKS_OR_RECENT_TASKS; import static com.android.server.wm.ActivityStackSupervisor.ON_TOP; import static com.android.server.wm.ActivityStackSupervisor.PRESERVE_WINDOWS; import static com.android.server.wm.ActivityStackSupervisor.REMOVE_FROM_RECENTS; @@ -122,6 +120,8 @@ import static com.android.server.wm.ActivityTaskManagerService.H.REPORT_TIME_TRA import static com.android.server.wm.ActivityTaskManagerService.UiHandler.DISMISS_DIALOG_UI_MSG; import static com.android.server.wm.RecentsAnimationController.REORDER_KEEP_IN_PLACE; import static com.android.server.wm.RecentsAnimationController.REORDER_MOVE_TO_ORIGINAL_POSITION; +import static com.android.server.wm.RootActivityContainer.MATCH_TASK_IN_STACKS_ONLY; +import static com.android.server.wm.RootActivityContainer.MATCH_TASK_IN_STACKS_OR_RECENT_TASKS; import static com.android.server.wm.TaskRecord.LOCK_TASK_AUTH_DONT_LOCK; import static com.android.server.wm.TaskRecord.REPARENT_KEEP_STACK_AT_FRONT; import static com.android.server.wm.TaskRecord.REPARENT_LEAVE_STACK_IN_PLACE; @@ -352,6 +352,7 @@ public class ActivityTaskManagerService extends IActivityTaskManager.Stub { /* Global service lock used by the package the owns this service. */ final WindowManagerGlobalLock mGlobalLock = new WindowManagerGlobalLock(); ActivityStackSupervisor mStackSupervisor; + RootActivityContainer mRootActivityContainer; WindowManagerService mWindowManager; private UserManagerService mUserManager; private AppOpsService mAppOpsService; @@ -766,7 +767,8 @@ public class ActivityTaskManagerService extends IActivityTaskManager.Stub { mTempConfig.setLocales(LocaleList.getDefault()); mConfigurationSeq = mTempConfig.seq = 1; mStackSupervisor = createStackSupervisor(); - mStackSupervisor.onConfigurationChanged(mTempConfig); + mRootActivityContainer = new RootActivityContainer(this); + mRootActivityContainer.onConfigurationChanged(mTempConfig); mTaskChangeNotificationController = new TaskChangeNotificationController(mGlobalLock, mStackSupervisor, mH); @@ -801,6 +803,7 @@ public class ActivityTaskManagerService extends IActivityTaskManager.Stub { mWindowManager = wm; mLockTaskController.setWindowManager(wm); mStackSupervisor.setWindowManager(wm); + mRootActivityContainer.setWindowManager(wm); } } @@ -1255,7 +1258,7 @@ public class ActivityTaskManagerService extends IActivityTaskManager.Stub { sourceToken = resultTo; } - sourceRecord = mStackSupervisor.isInAnyStackLocked(sourceToken); + sourceRecord = mRootActivityContainer.isInAnyStack(sourceToken); if (sourceRecord == null) { throw new SecurityException("Called with bad activity token: " + sourceToken); } @@ -1799,7 +1802,7 @@ public class ActivityTaskManagerService extends IActivityTaskManager.Stub { } final boolean translucentChanged = r.changeWindowTranslucency(true); if (translucentChanged) { - mStackSupervisor.ensureActivitiesVisibleLocked(null, 0, !PRESERVE_WINDOWS); + mRootActivityContainer.ensureActivitiesVisible(null, 0, !PRESERVE_WINDOWS); } mWindowManager.setAppFullscreen(token, true); return translucentChanged; @@ -1829,7 +1832,7 @@ public class ActivityTaskManagerService extends IActivityTaskManager.Stub { if (translucentChanged) { r.getStack().convertActivityToTranslucent(r); } - mStackSupervisor.ensureActivitiesVisibleLocked(null, 0, !PRESERVE_WINDOWS); + mRootActivityContainer.ensureActivitiesVisible(null, 0, !PRESERVE_WINDOWS); mWindowManager.setAppFullscreen(token, false); return translucentChanged; } @@ -1842,7 +1845,7 @@ public class ActivityTaskManagerService extends IActivityTaskManager.Stub { public void notifyActivityDrawn(IBinder token) { if (DEBUG_VISIBILITY) Slog.d(TAG_VISIBILITY, "notifyActivityDrawn: token=" + token); synchronized (mGlobalLock) { - ActivityRecord r = mStackSupervisor.isInAnyStackLocked(token); + ActivityRecord r = mRootActivityContainer.isInAnyStack(token); if (r != null) { r.getStack().notifyActivityDrawnLocked(r); } @@ -1879,7 +1882,7 @@ public class ActivityTaskManagerService extends IActivityTaskManager.Stub { synchronized (mGlobalLock) { ActivityStack focusedStack = getTopDisplayFocusedStack(); if (focusedStack != null) { - return mStackSupervisor.getStackInfo(focusedStack.mStackId); + return mRootActivityContainer.getStackInfo(focusedStack.mStackId); } return null; } @@ -1895,14 +1898,14 @@ public class ActivityTaskManagerService extends IActivityTaskManager.Stub { final long callingId = Binder.clearCallingIdentity(); try { synchronized (mGlobalLock) { - final ActivityStack stack = mStackSupervisor.getStack(stackId); + final ActivityStack stack = mRootActivityContainer.getStack(stackId); if (stack == null) { Slog.w(TAG, "setFocusedStack: No stack with id=" + stackId); return; } final ActivityRecord r = stack.topRunningActivityLocked(); if (r != null && r.moveFocusableActivityToTop("setFocusedStack")) { - mStackSupervisor.resumeFocusedStacksTopActivitiesLocked(); + mRootActivityContainer.resumeFocusedStacksTopActivities(); } } } finally { @@ -1917,14 +1920,14 @@ public class ActivityTaskManagerService extends IActivityTaskManager.Stub { final long callingId = Binder.clearCallingIdentity(); try { synchronized (mGlobalLock) { - final TaskRecord task = mStackSupervisor.anyTaskForIdLocked(taskId, + final TaskRecord task = mRootActivityContainer.anyTaskForId(taskId, MATCH_TASK_IN_STACKS_ONLY); if (task == null) { return; } final ActivityRecord r = task.topRunningActivityLocked(); if (r != null && r.moveFocusableActivityToTop("setFocusedTask")) { - mStackSupervisor.resumeFocusedStacksTopActivitiesLocked(); + mRootActivityContainer.resumeFocusedStacksTopActivities(); } } } finally { @@ -2009,7 +2012,7 @@ public class ActivityTaskManagerService extends IActivityTaskManager.Stub { final long origId = Binder.clearCallingIdentity(); try { int taskId = ActivityRecord.getTaskForActivityLocked(token, !nonRoot); - final TaskRecord task = mStackSupervisor.anyTaskForIdLocked(taskId); + final TaskRecord task = mRootActivityContainer.anyTaskForId(taskId); if (task != null) { return ActivityRecord.getStackLocked(token).moveTaskToBackLocked(taskId); } @@ -2027,7 +2030,7 @@ public class ActivityTaskManagerService extends IActivityTaskManager.Stub { Rect rect = new Rect(); try { synchronized (mGlobalLock) { - final TaskRecord task = mStackSupervisor.anyTaskForIdLocked(taskId, + final TaskRecord task = mRootActivityContainer.anyTaskForId(taskId, MATCH_TASK_IN_STACKS_OR_RECENT_TASKS); if (task == null) { Slog.w(TAG, "getTaskBounds: taskId=" + taskId + " not found"); @@ -2058,7 +2061,7 @@ public class ActivityTaskManagerService extends IActivityTaskManager.Stub { synchronized (mGlobalLock) { enforceCallerIsRecentsOrHasPermission( MANAGE_ACTIVITY_STACKS, "getTaskDescription()"); - final TaskRecord tr = mStackSupervisor.anyTaskForIdLocked(id, + final TaskRecord tr = mRootActivityContainer.anyTaskForId(id, MATCH_TASK_IN_STACKS_OR_RECENT_TASKS); if (tr != null) { return tr.lastTaskDescription; @@ -2078,7 +2081,7 @@ public class ActivityTaskManagerService extends IActivityTaskManager.Stub { synchronized (mGlobalLock) { final long ident = Binder.clearCallingIdentity(); try { - final TaskRecord task = mStackSupervisor.anyTaskForIdLocked(taskId, + final TaskRecord task = mRootActivityContainer.anyTaskForId(taskId, MATCH_TASK_IN_STACKS_ONLY); if (task == null) { Slog.w(TAG, "setTaskWindowingMode: No task for id=" + taskId); @@ -2167,7 +2170,7 @@ public class ActivityTaskManagerService extends IActivityTaskManager.Stub { } final long origId = Binder.clearCallingIdentity(); try { - final TaskRecord task = mStackSupervisor.anyTaskForIdLocked(taskId); + final TaskRecord task = mRootActivityContainer.anyTaskForId(taskId); if (task == null) { Slog.d(TAG, "Could not find task for id: "+ taskId); SafeActivityOptions.abort(options); @@ -2284,7 +2287,7 @@ public class ActivityTaskManagerService extends IActivityTaskManager.Stub { final boolean allowed = isGetTasksAllowed("getTasks", Binder.getCallingPid(), callingUid); - mStackSupervisor.getRunningTasks(maxNum, list, ignoreActivityType, + mRootActivityContainer.getRunningTasks(maxNum, list, ignoreActivityType, ignoreWindowingMode, callingUid, allowed); } @@ -2320,7 +2323,7 @@ public class ActivityTaskManagerService extends IActivityTaskManager.Stub { synchronized (mGlobalLock) { final long ident = Binder.clearCallingIdentity(); try { - final TaskRecord task = mStackSupervisor.anyTaskForIdLocked(taskId); + final TaskRecord task = mRootActivityContainer.anyTaskForId(taskId); if (task == null) { Slog.w(TAG, "moveTaskToStack: No task for id=" + taskId); return; @@ -2329,7 +2332,7 @@ public class ActivityTaskManagerService extends IActivityTaskManager.Stub { if (DEBUG_STACK) Slog.d(TAG_STACK, "moveTaskToStack: moving task=" + taskId + " to stackId=" + stackId + " toTop=" + toTop); - final ActivityStack stack = mStackSupervisor.getStack(stackId); + final ActivityStack stack = mRootActivityContainer.getStack(stackId); if (stack == null) { throw new IllegalStateException( "moveTaskToStack: No stack for stackId=" + stackId); @@ -2359,7 +2362,7 @@ public class ActivityTaskManagerService extends IActivityTaskManager.Stub { try { synchronized (mGlobalLock) { if (animate) { - final PinnedActivityStack stack = mStackSupervisor.getStack(stackId); + final PinnedActivityStack stack = mRootActivityContainer.getStack(stackId); if (stack == null) { Slog.w(TAG, "resizeStack: stackId " + stackId + " not found."); return; @@ -2371,12 +2374,12 @@ public class ActivityTaskManagerService extends IActivityTaskManager.Stub { stack.animateResizePinnedStack(null /* sourceHintBounds */, destBounds, animationDuration, false /* fromFullscreen */); } else { - final ActivityStack stack = mStackSupervisor.getStack(stackId); + final ActivityStack stack = mRootActivityContainer.getStack(stackId); if (stack == null) { Slog.w(TAG, "resizeStack: stackId " + stackId + " not found."); return; } - mStackSupervisor.resizeStackLocked(stack, destBounds, + mRootActivityContainer.resizeStack(stack, destBounds, null /* tempTaskBounds */, null /* tempTaskInsetBounds */, preserveWindows, allowResizeInDockedMode, !DEFER_RESUME); } @@ -2410,7 +2413,7 @@ public class ActivityTaskManagerService extends IActivityTaskManager.Stub { synchronized (mGlobalLock) { final long ident = Binder.clearCallingIdentity(); try { - final TaskRecord task = mStackSupervisor.anyTaskForIdLocked(taskId, + final TaskRecord task = mRootActivityContainer.anyTaskForId(taskId, MATCH_TASK_IN_STACKS_ONLY); if (task == null) { Slog.w(TAG, "setTaskWindowingModeSplitScreenPrimary: No task for id=" + taskId); @@ -2452,7 +2455,7 @@ public class ActivityTaskManagerService extends IActivityTaskManager.Stub { synchronized (mGlobalLock) { final long ident = Binder.clearCallingIdentity(); try { - mStackSupervisor.removeStacksInWindowingModes(windowingModes); + mRootActivityContainer.removeStacksInWindowingModes(windowingModes); } finally { Binder.restoreCallingIdentity(ident); } @@ -2467,7 +2470,7 @@ public class ActivityTaskManagerService extends IActivityTaskManager.Stub { synchronized (mGlobalLock) { final long ident = Binder.clearCallingIdentity(); try { - mStackSupervisor.removeStacksWithActivityTypes(activityTypes); + mRootActivityContainer.removeStacksWithActivityTypes(activityTypes); } finally { Binder.restoreCallingIdentity(ident); } @@ -2498,7 +2501,7 @@ public class ActivityTaskManagerService extends IActivityTaskManager.Stub { long ident = Binder.clearCallingIdentity(); try { synchronized (mGlobalLock) { - return mStackSupervisor.getAllStackInfosLocked(); + return mRootActivityContainer.getAllStackInfos(); } } finally { Binder.restoreCallingIdentity(ident); @@ -2511,7 +2514,7 @@ public class ActivityTaskManagerService extends IActivityTaskManager.Stub { long ident = Binder.clearCallingIdentity(); try { synchronized (mGlobalLock) { - return mStackSupervisor.getStackInfo(windowingMode, activityType); + return mRootActivityContainer.getStackInfo(windowingMode, activityType); } } finally { Binder.restoreCallingIdentity(ident); @@ -2553,7 +2556,7 @@ public class ActivityTaskManagerService extends IActivityTaskManager.Stub { long ident = Binder.clearCallingIdentity(); try { synchronized (mGlobalLock) { - final TaskRecord task = mStackSupervisor.anyTaskForIdLocked(taskId, + final TaskRecord task = mRootActivityContainer.anyTaskForId(taskId, MATCH_TASK_IN_STACKS_ONLY); if (task == null) { return; @@ -2595,7 +2598,7 @@ public class ActivityTaskManagerService extends IActivityTaskManager.Stub { return; } - final ActivityStack stack = mStackSupervisor.getTopDisplayFocusedStack(); + final ActivityStack stack = mRootActivityContainer.getTopDisplayFocusedStack(); if (stack == null || task != stack.topTask()) { throw new IllegalArgumentException("Invalid task, not in foreground"); } @@ -2610,7 +2613,7 @@ public class ActivityTaskManagerService extends IActivityTaskManager.Stub { long ident = Binder.clearCallingIdentity(); try { // When a task is locked, dismiss the pinned stack if it exists - mStackSupervisor.removeStacksInWindowingModes(WINDOWING_MODE_PINNED); + mRootActivityContainer.removeStacksInWindowingModes(WINDOWING_MODE_PINNED); getLockTaskController().startLockTaskMode(task, isSystemCaller, callingUid); } finally { @@ -2712,7 +2715,7 @@ public class ActivityTaskManagerService extends IActivityTaskManager.Stub { try { // TODO: VI Consider treating local voice interactions and voice tasks // differently here - mStackSupervisor.finishVoiceTask(session); + mRootActivityContainer.finishVoiceTask(session); } finally { Binder.restoreCallingIdentity(origId); } @@ -2902,7 +2905,7 @@ public class ActivityTaskManagerService extends IActivityTaskManager.Stub { @Override public void setTaskResizeable(int taskId, int resizeableMode) { synchronized (mGlobalLock) { - final TaskRecord task = mStackSupervisor.anyTaskForIdLocked( + final TaskRecord task = mRootActivityContainer.anyTaskForId( taskId, MATCH_TASK_IN_STACKS_OR_RECENT_TASKS); if (task == null) { Slog.w(TAG, "setTaskResizeable: taskId=" + taskId + " not found"); @@ -2918,7 +2921,7 @@ public class ActivityTaskManagerService extends IActivityTaskManager.Stub { long ident = Binder.clearCallingIdentity(); try { synchronized (mGlobalLock) { - final TaskRecord task = mStackSupervisor.anyTaskForIdLocked(taskId, + final TaskRecord task = mRootActivityContainer.anyTaskForId(taskId, MATCH_TASK_IN_STACKS_ONLY); if (task == null) { Slog.w(TAG, "resizeTask: taskId=" + taskId + " not found"); @@ -2983,7 +2986,7 @@ public class ActivityTaskManagerService extends IActivityTaskManager.Stub { final long origId = Binder.clearCallingIdentity(); try { final WindowProcessController app = getProcessController(appInt); - mStackSupervisor.releaseSomeActivitiesLocked(app, "low-mem"); + mRootActivityContainer.releaseSomeActivitiesLocked(app, "low-mem"); } finally { Binder.restoreCallingIdentity(origId); } @@ -3077,7 +3080,7 @@ public class ActivityTaskManagerService extends IActivityTaskManager.Stub { synchronized (mGlobalLock) { final long ident = Binder.clearCallingIdentity(); try { - final ActivityStack stack = mStackSupervisor.getStack(stackId); + final ActivityStack stack = mRootActivityContainer.getStack(stackId); if (stack == null) { Slog.w(TAG, "removeStack: No stack with id=" + stackId); return; @@ -3102,7 +3105,7 @@ public class ActivityTaskManagerService extends IActivityTaskManager.Stub { try { if (DEBUG_STACK) Slog.d(TAG_STACK, "moveStackToDisplay: moving stackId=" + stackId + " to displayId=" + displayId); - mStackSupervisor.moveStackToDisplayLocked(stackId, displayId, ON_TOP); + mRootActivityContainer.moveStackToDisplay(stackId, displayId, ON_TOP); } finally { Binder.restoreCallingIdentity(ident); } @@ -3564,13 +3567,13 @@ public class ActivityTaskManagerService extends IActivityTaskManager.Stub { try { if (DEBUG_STACK) Slog.d(TAG_STACK, "positionTaskInStack: positioning task=" + taskId + " in stackId=" + stackId + " at position=" + position); - final TaskRecord task = mStackSupervisor.anyTaskForIdLocked(taskId); + final TaskRecord task = mRootActivityContainer.anyTaskForId(taskId); if (task == null) { throw new IllegalArgumentException("positionTaskInStack: no task for id=" + taskId); } - final ActivityStack stack = mStackSupervisor.getStack(stackId); + final ActivityStack stack = mRootActivityContainer.getStack(stackId); if (stack == null) { throw new IllegalArgumentException("positionTaskInStack: no stack for id=" @@ -3625,7 +3628,7 @@ public class ActivityTaskManagerService extends IActivityTaskManager.Stub { try { synchronized (mGlobalLock) { final ActivityStack stack = - mStackSupervisor.getDefaultDisplay().getSplitScreenPrimaryStack(); + mRootActivityContainer.getDefaultDisplay().getSplitScreenPrimaryStack(); if (stack == null) { Slog.w(TAG, "dismissSplitScreenMode: primary split-screen stack not found."); return; @@ -3635,7 +3638,7 @@ public class ActivityTaskManagerService extends IActivityTaskManager.Stub { // Caller wants the current split-screen primary stack to be the top stack after // it goes fullscreen, so move it to the front. stack.moveToFront("dismissSplitScreenMode"); - } else if (mStackSupervisor.isTopDisplayFocusedStack(stack)) { + } else if (mRootActivityContainer.isTopDisplayFocusedStack(stack)) { // In this case the current split-screen primary stack shouldn't be the top // stack after it goes fullscreen, but it current has focus, so we move the // focus to the top-most split-screen secondary stack next to it. @@ -3666,7 +3669,7 @@ public class ActivityTaskManagerService extends IActivityTaskManager.Stub { try { synchronized (mGlobalLock) { final PinnedActivityStack stack = - mStackSupervisor.getDefaultDisplay().getPinnedStack(); + mRootActivityContainer.getDefaultDisplay().getPinnedStack(); if (stack == null) { Slog.w(TAG, "dismissPip: pinned stack not found."); return; @@ -3708,7 +3711,7 @@ public class ActivityTaskManagerService extends IActivityTaskManager.Stub { synchronized (mGlobalLock) { final long origId = Binder.clearCallingIdentity(); try { - final ActivityStack stack = mStackSupervisor.getStack(fromStackId); + final ActivityStack stack = mRootActivityContainer.getStack(fromStackId); if (stack != null){ if (!stack.isActivityTypeStandardOrUndefined()) { throw new IllegalArgumentException( @@ -3743,7 +3746,7 @@ public class ActivityTaskManagerService extends IActivityTaskManager.Stub { long ident = Binder.clearCallingIdentity(); try { - return mStackSupervisor.moveTopStackActivityToPinnedStackLocked(stackId, bounds); + return mRootActivityContainer.moveTopStackActivityToPinnedStack(stackId); } finally { Binder.restoreCallingIdentity(ident); } @@ -3821,7 +3824,7 @@ public class ActivityTaskManagerService extends IActivityTaskManager.Stub { // Adjust the source bounds by the insets for the transition down final Rect sourceBounds = new Rect( r.pictureInPictureArgs.getSourceRectHint()); - mStackSupervisor.moveActivityToPinnedStackLocked( + mRootActivityContainer.moveActivityToPinnedStack( r, sourceBounds, aspectRatio, "enterPictureInPictureMode"); final PinnedActivityStack stack = r.getStack(); stack.setPictureInPictureAspectRatio(aspectRatio); @@ -4100,7 +4103,7 @@ public class ActivityTaskManagerService extends IActivityTaskManager.Stub { synchronized (mGlobalLock) { // Check if display is initialized in AM. - if (!mStackSupervisor.isDisplayAdded(displayId)) { + if (!mRootActivityContainer.isDisplayAdded(displayId)) { // Call might come when display is not yet added or has already been removed. if (DEBUG_CONFIGURATION) { Slog.w(TAG, "Trying to update display configuration for non-existing displayId=" @@ -4190,7 +4193,7 @@ public class ActivityTaskManagerService extends IActivityTaskManager.Stub { final long ident = Binder.clearCallingIdentity(); try { synchronized (mGlobalLock) { - final TaskRecord task = mStackSupervisor.anyTaskForIdLocked(taskId, + final TaskRecord task = mRootActivityContainer.anyTaskForId(taskId, MATCH_TASK_IN_STACKS_ONLY); if (task == null) { Slog.w(TAG, "cancelTaskWindowTransition: taskId=" + taskId + " not found"); @@ -4210,7 +4213,7 @@ public class ActivityTaskManagerService extends IActivityTaskManager.Stub { try { final TaskRecord task; synchronized (mGlobalLock) { - task = mStackSupervisor.anyTaskForIdLocked(taskId, + task = mRootActivityContainer.anyTaskForId(taskId, MATCH_TASK_IN_STACKS_OR_RECENT_TASKS); if (task == null) { Slog.w(TAG, "getTaskSnapshot: taskId=" + taskId + " not found"); @@ -4430,7 +4433,7 @@ public class ActivityTaskManagerService extends IActivityTaskManager.Stub { if (r.requestedVrComponent != null && r.getDisplayId() != DEFAULT_DISPLAY) { Slog.i(TAG, "Moving " + r.shortComponentName + " from stack " + r.getStackId() + " to main stack for VR"); - final ActivityStack stack = mStackSupervisor.getDefaultDisplay().getOrCreateStack( + final ActivityStack stack = mRootActivityContainer.getDefaultDisplay().getOrCreateStack( WINDOWING_MODE_FULLSCREEN, r.getActivityType(), true /* toTop */); moveTaskToStack(r.getTask().taskId, stack.mStackId, true /* toTop */); } @@ -4444,7 +4447,7 @@ public class ActivityTaskManagerService extends IActivityTaskManager.Stub { if (disableNonVrUi) { // If we are in a VR mode where Picture-in-Picture mode is unsupported, // then remove the pinned stack. - mStackSupervisor.removeStacksInWindowingModes(WINDOWING_MODE_PINNED); + mRootActivityContainer.removeStacksInWindowingModes(WINDOWING_MODE_PINNED); } } }); @@ -4496,7 +4499,7 @@ public class ActivityTaskManagerService extends IActivityTaskManager.Stub { } ActivityStack getTopDisplayFocusedStack() { - return mStackSupervisor.getTopDisplayFocusedStack(); + return mRootActivityContainer.getTopDisplayFocusedStack(); } /** Pokes the task persister. */ @@ -4557,12 +4560,12 @@ public class ActivityTaskManagerService extends IActivityTaskManager.Stub { int opti, boolean dumpAll, boolean dumpClient, String dumpPackage, String header) { pw.println(header); - boolean printedAnything = mStackSupervisor.dumpActivitiesLocked(fd, pw, dumpAll, dumpClient, + boolean printedAnything = mRootActivityContainer.dumpActivities(fd, pw, dumpAll, dumpClient, dumpPackage); boolean needSep = printedAnything; boolean printed = ActivityStackSupervisor.printThisActivity(pw, - mStackSupervisor.getTopResumedActivity(), dumpPackage, needSep, + mRootActivityContainer.getTopResumedActivity(), dumpPackage, needSep, " ResumedActivity: "); if (printed) { printedAnything = true; @@ -4584,7 +4587,7 @@ public class ActivityTaskManagerService extends IActivityTaskManager.Stub { void dumpActivityContainersLocked(PrintWriter pw) { pw.println("ACTIVITY MANAGER STARTER (dumpsys activity containers)"); - mStackSupervisor.dumpChildrenNames(pw, " "); + mRootActivityContainer.dumpChildrenNames(pw, " "); pw.println(" "); } @@ -4608,7 +4611,7 @@ public class ActivityTaskManagerService extends IActivityTaskManager.Stub { ArrayList<ActivityRecord> activities; synchronized (mGlobalLock) { - activities = mStackSupervisor.getDumpActivitiesLocked(name, dumpVisibleStacksOnly, + activities = mRootActivityContainer.getDumpActivities(name, dumpVisibleStacksOnly, dumpFocusedStackOnly); } @@ -4683,7 +4686,7 @@ public class ActivityTaskManagerService extends IActivityTaskManager.Stub { } void writeSleepStateToProto(ProtoOutputStream proto) { - for (ActivityTaskManagerInternal.SleepToken st : mStackSupervisor.mSleepTokens) { + for (ActivityTaskManagerInternal.SleepToken st : mRootActivityContainer.mSleepTokens) { proto.write(ActivityManagerServiceDumpProcessesProto.SleepStatus.SLEEP_TOKENS, st.toString()); } @@ -4728,7 +4731,7 @@ public class ActivityTaskManagerService extends IActivityTaskManager.Stub { * also corresponds to the merged configuration of the default display. */ Configuration getGlobalConfiguration() { - return mStackSupervisor.getConfiguration(); + return mRootActivityContainer.getConfiguration(); } boolean updateConfigurationLocked(Configuration values, ActivityRecord starting, @@ -4860,7 +4863,7 @@ public class ActivityTaskManagerService extends IActivityTaskManager.Stub { mTempConfig.seq = increaseConfigurationSeqLocked(); // Update stored global config and notify everyone about the change. - mStackSupervisor.onConfigurationChanged(mTempConfig); + mRootActivityContainer.onConfigurationChanged(mTempConfig); Slog.i(TAG, "Config changes=" + Integer.toHexString(changes) + " " + mTempConfig); // TODO(multi-display): Update UsageEvents#Event to include displayId. @@ -4907,7 +4910,7 @@ public class ActivityTaskManagerService extends IActivityTaskManager.Stub { // Override configuration of the default display duplicates global config, so we need to // update it also. This will also notify WindowManager about changes. - performDisplayOverrideConfigUpdate(mStackSupervisor.getConfiguration(), deferResume, + performDisplayOverrideConfigUpdate(mRootActivityContainer.getConfiguration(), deferResume, DEFAULT_DISPLAY); return changes; @@ -4961,12 +4964,12 @@ public class ActivityTaskManagerService extends IActivityTaskManager.Stub { private int performDisplayOverrideConfigUpdate(Configuration values, boolean deferResume, int displayId) { - mTempConfig.setTo(mStackSupervisor.getDisplayOverrideConfiguration(displayId)); + mTempConfig.setTo(mRootActivityContainer.getDisplayOverrideConfiguration(displayId)); final int changes = mTempConfig.updateFrom(values); if (changes != 0) { Slog.i(TAG, "Override config changes=" + Integer.toHexString(changes) + " " + mTempConfig + " for displayId=" + displayId); - mStackSupervisor.setDisplayOverrideConfiguration(mTempConfig, displayId); + mRootActivityContainer.setDisplayOverrideConfiguration(mTempConfig, displayId); final boolean isDensityChange = (changes & ActivityInfo.CONFIG_DENSITY) != 0; if (isDensityChange && displayId == DEFAULT_DISPLAY) { @@ -5096,7 +5099,7 @@ public class ActivityTaskManagerService extends IActivityTaskManager.Stub { mCurAppTimeTracker.stop(); mH.obtainMessage( REPORT_TIME_TRACKER_MSG, mCurAppTimeTracker).sendToTarget(); - mStackSupervisor.clearOtherAppTimeTrackers(r.appTimeTracker); + mRootActivityContainer.clearOtherAppTimeTrackers(r.appTimeTracker); mCurAppTimeTracker = null; } if (r.appTimeTracker != null) { @@ -5157,14 +5160,15 @@ public class ActivityTaskManagerService extends IActivityTaskManager.Stub { ActivityTaskManagerInternal.SleepToken acquireSleepToken(String tag, int displayId) { synchronized (mGlobalLock) { - final ActivityTaskManagerInternal.SleepToken token = mStackSupervisor.createSleepTokenLocked(tag, displayId); + final ActivityTaskManagerInternal.SleepToken token = + mRootActivityContainer.createSleepToken(tag, displayId); updateSleepIfNeededLocked(); return token; } } void updateSleepIfNeededLocked() { - final boolean shouldSleep = !mStackSupervisor.hasAwakeDisplay(); + final boolean shouldSleep = !mRootActivityContainer.hasAwakeDisplay(); final boolean wasSleeping = mSleeping; boolean updateOomAdj = false; @@ -5180,7 +5184,7 @@ public class ActivityTaskManagerService extends IActivityTaskManager.Stub { mTopProcessState = ActivityManager.PROCESS_STATE_TOP; mStackSupervisor.comeOutOfSleepIfNeededLocked(); } - mStackSupervisor.applySleepTokensLocked(true /* applyToStacks */); + mRootActivityContainer.applySleepTokens(true /* applyToStacks */); if (wasSleeping) { updateOomAdj = true; } @@ -5356,7 +5360,7 @@ public class ActivityTaskManagerService extends IActivityTaskManager.Stub { // TODO(b/111541062): Update app time tracking to make it aware of multiple resumed activities private void startTimeTrackingFocusedActivityLocked() { - final ActivityRecord resumedActivity = mStackSupervisor.getTopResumedActivity(); + final ActivityRecord resumedActivity = mRootActivityContainer.getTopResumedActivity(); if (!mSleeping && mCurAppTimeTracker != null && resumedActivity != null) { mCurAppTimeTracker.start(resumedActivity.packageName); } @@ -5381,7 +5385,7 @@ public class ActivityTaskManagerService extends IActivityTaskManager.Stub { /** Applies latest configuration and/or visibility updates if needed. */ private boolean ensureConfigAndVisibilityAfterUpdate(ActivityRecord starting, int changes) { boolean kept = true; - final ActivityStack mainStack = mStackSupervisor.getTopDisplayFocusedStack(); + final ActivityStack mainStack = mRootActivityContainer.getTopDisplayFocusedStack(); // mainStack is null during startup. if (mainStack != null) { if (changes != 0 && starting == null) { @@ -5396,7 +5400,7 @@ public class ActivityTaskManagerService extends IActivityTaskManager.Stub { false /* preserveWindow */); // And we need to make sure at this point that all other activities // are made visible with the correct configuration. - mStackSupervisor.ensureActivitiesVisibleLocked(starting, changes, + mRootActivityContainer.ensureActivitiesVisible(starting, changes, !PRESERVE_WINDOWS); } } @@ -5612,8 +5616,8 @@ public class ActivityTaskManagerService extends IActivityTaskManager.Stub { @Override public ComponentName getHomeActivityForUser(int userId) { synchronized (mGlobalLock) { - ActivityRecord homeActivity = - mStackSupervisor.getDefaultDisplayHomeActivityForUser(userId); + final ActivityRecord homeActivity = + mRootActivityContainer.getDefaultDisplayHomeActivityForUser(userId); return homeActivity == null ? null : homeActivity.realActivity; } } @@ -5651,14 +5655,14 @@ public class ActivityTaskManagerService extends IActivityTaskManager.Stub { @Override public List<IBinder> getTopVisibleActivities() { synchronized (mGlobalLock) { - return mStackSupervisor.getTopVisibleActivities(); + return mRootActivityContainer.getTopVisibleActivities(); } } @Override public void notifyDockedStackMinimizedChanged(boolean minimized) { synchronized (mGlobalLock) { - mStackSupervisor.setDockedStackMinimized(minimized); + mRootActivityContainer.setDockedStackMinimized(minimized); } } @@ -5739,7 +5743,7 @@ public class ActivityTaskManagerService extends IActivityTaskManager.Stub { // We might change the visibilities here, so prepare an empty app transition which // might be overridden later if we actually change visibilities. final ActivityDisplay activityDisplay = - mStackSupervisor.getActivityDisplay(displayId); + mRootActivityContainer.getActivityDisplay(displayId); if (activityDisplay == null) { return; } @@ -5748,7 +5752,7 @@ public class ActivityTaskManagerService extends IActivityTaskManager.Stub { if (!wasTransitionSet) { dwc.prepareAppTransition(TRANSIT_NONE, false /* alwaysKeepCurrent */); } - mStackSupervisor.ensureActivitiesVisibleLocked(null, 0, !PRESERVE_WINDOWS); + mRootActivityContainer.ensureActivitiesVisible(null, 0, !PRESERVE_WINDOWS); // If there was a transition set already we don't want to interfere with it as we // might be starting it too early. @@ -5765,7 +5769,7 @@ public class ActivityTaskManagerService extends IActivityTaskManager.Stub { public void notifyKeyguardTrustedChanged() { synchronized (mGlobalLock) { if (mKeyguardController.isKeyguardShowing(DEFAULT_DISPLAY)) { - mStackSupervisor.ensureActivitiesVisibleLocked(null, 0, !PRESERVE_WINDOWS); + mRootActivityContainer.ensureActivitiesVisible(null, 0, !PRESERVE_WINDOWS); } } } @@ -5792,7 +5796,7 @@ public class ActivityTaskManagerService extends IActivityTaskManager.Stub { "setFocusedActivity: No activity record matching token=" + token); } if (r.moveFocusableActivityToTop("setFocusedActivity")) { - mStackSupervisor.resumeFocusedStacksTopActivitiesLocked(); + mRootActivityContainer.resumeFocusedStacksTopActivities(); } } } @@ -5943,7 +5947,7 @@ public class ActivityTaskManagerService extends IActivityTaskManager.Stub { public boolean shuttingDown(boolean booted, int timeout) { synchronized (mGlobalLock) { mShuttingDown = true; - mStackSupervisor.prepareForShutdownLocked(); + mRootActivityContainer.prepareForShutdown(); updateEventDispatchingLocked(booted); notifyTaskPersisterLocked(null, true); return mStackSupervisor.shutdownLocked(timeout); @@ -6050,7 +6054,7 @@ public class ActivityTaskManagerService extends IActivityTaskManager.Stub { @Override public void onPackageReplaced(ApplicationInfo aInfo) { synchronized (mGlobalLock) { - mStackSupervisor.updateActivityApplicationInfoLocked(aInfo); + mRootActivityContainer.updateActivityApplicationInfo(aInfo); } } @@ -6080,7 +6084,7 @@ public class ActivityTaskManagerService extends IActivityTaskManager.Stub { mH.post(() -> { synchronized (mGlobalLock) { final ActivityDisplay activityDisplay = - mStackSupervisor.getActivityDisplay(displayId); + mRootActivityContainer.getActivityDisplay(displayId); if (activityDisplay == null) { // Call might come when display is not yet added or has been removed. if (DEBUG_CONFIGURATION) { @@ -6163,14 +6167,14 @@ public class ActivityTaskManagerService extends IActivityTaskManager.Stub { @Override public boolean startHomeActivity(int userId, String reason) { synchronized (mGlobalLock) { - return mStackSupervisor.startHomeOnDisplay(userId, reason, DEFAULT_DISPLAY); + return mRootActivityContainer.startHomeOnDisplay(userId, reason, DEFAULT_DISPLAY); } } @Override public boolean startHomeOnAllDisplays(int userId, String reason) { synchronized (mGlobalLock) { - return mStackSupervisor.startHomeOnAllDisplays(userId, reason); + return mRootActivityContainer.startHomeOnAllDisplays(userId, reason); } } @@ -6234,7 +6238,7 @@ public class ActivityTaskManagerService extends IActivityTaskManager.Stub { Runnable finishInstrumentationCallback) { synchronized (mGlobalLock) { // Remove this application's activities from active lists. - boolean hasVisibleActivities = mStackSupervisor.handleAppDiedLocked(wpc); + boolean hasVisibleActivities = mRootActivityContainer.handleAppDied(wpc); wpc.clearRecentTasks(); wpc.clearActivities(); @@ -6246,12 +6250,12 @@ public class ActivityTaskManagerService extends IActivityTaskManager.Stub { mWindowManager.deferSurfaceLayout(); try { if (!restarting && hasVisibleActivities - && !mStackSupervisor.resumeFocusedStacksTopActivitiesLocked()) { + && !mRootActivityContainer.resumeFocusedStacksTopActivities()) { // If there was nothing to resume, and we are not already restarting this // process, but there is a visible activity that is hosted by the process... // then make sure all visible activities are running, taking care of // restarting this process. - mStackSupervisor.ensureActivitiesVisibleLocked(null, 0, !PRESERVE_WINDOWS); + mRootActivityContainer.ensureActivitiesVisible(null, 0, !PRESERVE_WINDOWS); } } finally { mWindowManager.continueSurfaceLayout(); @@ -6280,7 +6284,7 @@ public class ActivityTaskManagerService extends IActivityTaskManager.Stub { } mWindowManager.closeSystemDialogs(reason); - mStackSupervisor.closeSystemDialogsLocked(); + mRootActivityContainer.closeSystemDialogs(); } // Call into AM outside the synchronized block. mAmInternal.broadcastCloseSystemDialogs(reason); @@ -6294,9 +6298,9 @@ public class ActivityTaskManagerService extends IActivityTaskManager.Stub { String packageName, Set<String> disabledClasses, int userId, boolean booted) { synchronized (mGlobalLock) { // Clean-up disabled activities. - if (mStackSupervisor.finishDisabledPackageActivitiesLocked( + if (mRootActivityContainer.finishDisabledPackageActivities( packageName, disabledClasses, true, false, userId) && booted) { - mStackSupervisor.resumeFocusedStacksTopActivitiesLocked(); + mRootActivityContainer.resumeFocusedStacksTopActivities(); mStackSupervisor.scheduleIdleLocked(); } @@ -6313,7 +6317,7 @@ public class ActivityTaskManagerService extends IActivityTaskManager.Stub { boolean didSomething = getActivityStartController().clearPendingActivityLaunches(packageName); - didSomething |= mStackSupervisor.finishDisabledPackageActivitiesLocked(packageName, + didSomething |= mRootActivityContainer.finishDisabledPackageActivities(packageName, null, doit, evenPersistent, userId); return didSomething; } @@ -6322,7 +6326,7 @@ public class ActivityTaskManagerService extends IActivityTaskManager.Stub { @Override public void resumeTopActivities(boolean scheduleIdle) { synchronized (mGlobalLock) { - mStackSupervisor.resumeFocusedStacksTopActivitiesLocked(); + mRootActivityContainer.resumeFocusedStacksTopActivities(); if (scheduleIdle) { mStackSupervisor.scheduleIdleLocked(); } @@ -6339,7 +6343,7 @@ public class ActivityTaskManagerService extends IActivityTaskManager.Stub { @Override public boolean attachApplication(WindowProcessController wpc) throws RemoteException { synchronized (mGlobalLock) { - return mStackSupervisor.attachApplicationLocked(wpc); + return mRootActivityContainer.attachApplication(wpc); } } @@ -6361,7 +6365,7 @@ public class ActivityTaskManagerService extends IActivityTaskManager.Stub { // Showing launcher to avoid user entering credential twice. startHomeActivity(currentUserId, "notifyLockedProfile"); } - mStackSupervisor.lockAllProfileTasks(userId); + mRootActivityContainer.lockAllProfileTasks(userId); } } finally { Binder.restoreCallingIdentity(ident); @@ -6382,7 +6386,7 @@ public class ActivityTaskManagerService extends IActivityTaskManager.Stub { ActivityOptions activityOptions = options != null ? new ActivityOptions(options) : ActivityOptions.makeBasic(); final ActivityRecord homeActivity = - mStackSupervisor.getDefaultDisplayHomeActivity(); + mRootActivityContainer.getDefaultDisplayHomeActivity(); if (homeActivity != null) { activityOptions.setLaunchTaskId(homeActivity.getTask().taskId); } @@ -6399,7 +6403,7 @@ public class ActivityTaskManagerService extends IActivityTaskManager.Stub { synchronized (mGlobalLock) { // The output proto of "activity --proto activities" // is ActivityManagerServiceDumpActivitiesProto - mStackSupervisor.writeToProto(proto, + mRootActivityContainer.writeToProto(proto, ActivityManagerServiceDumpActivitiesProto.ACTIVITY_STACK_SUPERVISOR); } } @@ -6494,7 +6498,7 @@ public class ActivityTaskManagerService extends IActivityTaskManager.Stub { } if (dumpPackage == null) { pw.println(" mGlobalConfiguration: " + getGlobalConfiguration()); - mStackSupervisor.dumpDisplayConfigs(pw, " "); + mRootActivityContainer.dumpDisplayConfigs(pw, " "); } if (dumpAll) { if (dumpPackage == null) { @@ -6522,7 +6526,7 @@ public class ActivityTaskManagerService extends IActivityTaskManager.Stub { if (dumpPackage == null) { pw.println(" mWakefulness=" + PowerManagerInternal.wakefulnessToString(wakefulness)); - pw.println(" mSleepTokens=" + mStackSupervisor.mSleepTokens); + pw.println(" mSleepTokens=" + mRootActivityContainer.mSleepTokens); if (mRunningVoice != null) { pw.println(" mRunningVoice=" + mRunningVoice); pw.println(" mVoiceWakeLock" + mVoiceWakeLock); @@ -6649,14 +6653,14 @@ public class ActivityTaskManagerService extends IActivityTaskManager.Stub { @Override public boolean canGcNow() { synchronized (mGlobalLock) { - return isSleeping() || mStackSupervisor.allResumedActivitiesIdle(); + return isSleeping() || mRootActivityContainer.allResumedActivitiesIdle(); } } @Override public WindowProcessController getTopApp() { synchronized (mGlobalLock) { - final ActivityRecord top = mStackSupervisor.getTopResumedActivity(); + final ActivityRecord top = mRootActivityContainer.getTopResumedActivity(); return top != null ? top.app : null; } } @@ -6664,8 +6668,8 @@ public class ActivityTaskManagerService extends IActivityTaskManager.Stub { @Override public void rankTaskLayersIfNeeded() { synchronized (mGlobalLock) { - if (mStackSupervisor != null) { - mStackSupervisor.rankTaskLayersIfNeeded(); + if (mRootActivityContainer != null) { + mRootActivityContainer.rankTaskLayersIfNeeded(); } } } @@ -6673,35 +6677,35 @@ public class ActivityTaskManagerService extends IActivityTaskManager.Stub { @Override public void scheduleDestroyAllActivities(String reason) { synchronized (mGlobalLock) { - mStackSupervisor.scheduleDestroyAllActivities(null, reason); + mRootActivityContainer.scheduleDestroyAllActivities(null, reason); } } @Override public void removeUser(int userId) { synchronized (mGlobalLock) { - mStackSupervisor.removeUserLocked(userId); + mRootActivityContainer.removeUser(userId); } } @Override public boolean switchUser(int userId, UserState userState) { synchronized (mGlobalLock) { - return mStackSupervisor.switchUserLocked(userId, userState); + return mRootActivityContainer.switchUser(userId, userState); } } @Override public void onHandleAppCrash(WindowProcessController wpc) { synchronized (mGlobalLock) { - mStackSupervisor.handleAppCrashLocked(wpc); + mRootActivityContainer.handleAppCrash(wpc); } } @Override public int finishTopCrashedActivities(WindowProcessController crashedApp, String reason) { synchronized (mGlobalLock) { - return mStackSupervisor.finishTopCrashedActivitiesLocked(crashedApp, reason); + return mRootActivityContainer.finishTopCrashedActivities(crashedApp, reason); } } diff --git a/services/core/java/com/android/server/wm/AppTaskImpl.java b/services/core/java/com/android/server/wm/AppTaskImpl.java index 04fef02cd3b4..441c5935a507 100644 --- a/services/core/java/com/android/server/wm/AppTaskImpl.java +++ b/services/core/java/com/android/server/wm/AppTaskImpl.java @@ -16,8 +16,8 @@ package com.android.server.wm; -import static com.android.server.wm.ActivityStackSupervisor.MATCH_TASK_IN_STACKS_OR_RECENT_TASKS; import static com.android.server.wm.ActivityStackSupervisor.REMOVE_FROM_RECENTS; +import static com.android.server.wm.RootActivityContainer.MATCH_TASK_IN_STACKS_OR_RECENT_TASKS; import android.app.ActivityManager; import android.app.IAppTask; @@ -77,7 +77,7 @@ class AppTaskImpl extends IAppTask.Stub { synchronized (mService.mGlobalLock) { long origId = Binder.clearCallingIdentity(); try { - TaskRecord tr = mService.mStackSupervisor.anyTaskForIdLocked(mTaskId, + TaskRecord tr = mService.mRootActivityContainer.anyTaskForId(mTaskId, MATCH_TASK_IN_STACKS_OR_RECENT_TASKS); if (tr == null) { throw new IllegalArgumentException("Unable to find task ID " + mTaskId); @@ -115,7 +115,7 @@ class AppTaskImpl extends IAppTask.Stub { TaskRecord tr; IApplicationThread appThread; synchronized (mService.mGlobalLock) { - tr = mService.mStackSupervisor.anyTaskForIdLocked(mTaskId, + tr = mService.mRootActivityContainer.anyTaskForId(mTaskId, MATCH_TASK_IN_STACKS_OR_RECENT_TASKS); if (tr == null) { throw new IllegalArgumentException("Unable to find task ID " + mTaskId); @@ -143,7 +143,7 @@ class AppTaskImpl extends IAppTask.Stub { synchronized (mService.mGlobalLock) { long origId = Binder.clearCallingIdentity(); try { - TaskRecord tr = mService.mStackSupervisor.anyTaskForIdLocked(mTaskId, + TaskRecord tr = mService.mRootActivityContainer.anyTaskForId(mTaskId, MATCH_TASK_IN_STACKS_OR_RECENT_TASKS); if (tr == null) { throw new IllegalArgumentException("Unable to find task ID " + mTaskId); diff --git a/services/core/java/com/android/server/wm/AppTransitionController.java b/services/core/java/com/android/server/wm/AppTransitionController.java index 32a6f74b5a11..bf00ffb30222 100644 --- a/services/core/java/com/android/server/wm/AppTransitionController.java +++ b/services/core/java/com/android/server/wm/AppTransitionController.java @@ -305,7 +305,7 @@ public class AppTransitionController { AppWindowToken wtoken = openingApps.valueAt(i); if (DEBUG_APP_TRANSITIONS) Slog.v(TAG, "Now opening app" + wtoken); - if (!wtoken.setVisibility(animLp, true, transit, false, voiceInteraction)) { + if (!wtoken.commitVisibility(animLp, true, transit, false, voiceInteraction)) { // This token isn't going to be animating. Add it to the list of tokens to // be notified of app transition complete since the notification will not be // sent be the app window animator. @@ -341,7 +341,7 @@ public class AppTransitionController { if (DEBUG_APP_TRANSITIONS) Slog.v(TAG, "Now closing app " + wtoken); // TODO: Do we need to add to mNoAnimationNotifyOnTransitionFinished like above if not // animating? - wtoken.setVisibility(animLp, false, transit, false, voiceInteraction); + wtoken.commitVisibility(animLp, false, transit, false, voiceInteraction); wtoken.updateReportedVisibilityLocked(); // Force the allDrawn flag, because we want to start // this guy's animations regardless of whether it's @@ -350,9 +350,8 @@ public class AppTransitionController { wtoken.deferClearAllDrawn = false; // Ensure that apps that are mid-starting are also scheduled to have their // starting windows removed after the animation is complete - if (wtoken.startingWindow != null && !wtoken.startingWindow.mAnimatingExit - && wtoken.getController() != null) { - wtoken.getController().removeStartingWindow(); + if (wtoken.startingWindow != null && !wtoken.startingWindow.mAnimatingExit) { + wtoken.removeStartingWindow(); } if (mDisplayContent.mAppTransition.isNextAppTransitionThumbnailDown()) { diff --git a/services/core/java/com/android/server/wm/AppWindowContainerController.java b/services/core/java/com/android/server/wm/AppWindowContainerController.java deleted file mode 100644 index 7fdea10e3e2c..000000000000 --- a/services/core/java/com/android/server/wm/AppWindowContainerController.java +++ /dev/null @@ -1,912 +0,0 @@ -/* - * Copyright (C) 2016 The Android Open Source Project - * - * Licensed under the Apache License, Version 2.0 (the "License"); - * you may not use this file except in compliance with the License. - * You may obtain a copy of the License at - * - * http://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, - * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - * See the License for the specific language governing permissions and - * limitations under the License - */ - -package com.android.server.wm; - -import static android.app.ActivityOptions.ANIM_CLIP_REVEAL; -import static android.app.ActivityOptions.ANIM_CUSTOM; -import static android.app.ActivityOptions.ANIM_NONE; -import static android.app.ActivityOptions.ANIM_OPEN_CROSS_PROFILE_APPS; -import static android.app.ActivityOptions.ANIM_REMOTE_ANIMATION; -import static android.app.ActivityOptions.ANIM_SCALE_UP; -import static android.app.ActivityOptions.ANIM_THUMBNAIL_ASPECT_SCALE_DOWN; -import static android.app.ActivityOptions.ANIM_THUMBNAIL_ASPECT_SCALE_UP; -import static android.app.ActivityOptions.ANIM_THUMBNAIL_SCALE_DOWN; -import static android.app.ActivityOptions.ANIM_THUMBNAIL_SCALE_UP; -import static android.content.pm.ActivityInfo.SCREEN_ORIENTATION_UNSPECIFIED; -import static android.view.WindowManager.LayoutParams.FLAG_SHOW_WALLPAPER; -import static android.view.WindowManager.TRANSIT_DOCK_TASK_FROM_RECENTS; -import static android.view.WindowManager.TRANSIT_UNSET; - -import static com.android.server.wm.WindowManagerDebugConfig.DEBUG_ADD_REMOVE; -import static com.android.server.wm.WindowManagerDebugConfig.DEBUG_APP_TRANSITIONS; -import static com.android.server.wm.WindowManagerDebugConfig.DEBUG_ORIENTATION; -import static com.android.server.wm.WindowManagerDebugConfig.DEBUG_STARTING_WINDOW; -import static com.android.server.wm.WindowManagerDebugConfig.DEBUG_TOKEN_MOVEMENT; -import static com.android.server.wm.WindowManagerDebugConfig.DEBUG_VISIBILITY; -import static com.android.server.wm.WindowManagerDebugConfig.TAG_WM; - -import android.app.ActivityManager.TaskSnapshot; -import android.app.ActivityOptions; -import android.content.ComponentName; -import android.content.Intent; -import android.content.res.CompatibilityInfo; -import android.content.res.Configuration; -import android.graphics.GraphicBuffer; -import android.graphics.Rect; -import android.os.Debug; -import android.os.Handler; -import android.os.IBinder; -import android.os.Looper; -import android.os.Message; -import android.util.Slog; -import android.view.AppTransitionAnimationSpec; -import android.view.IAppTransitionAnimationSpecsFuture; -import android.view.IApplicationToken; -import android.view.RemoteAnimationDefinition; -import android.view.WindowManager; - -import com.android.internal.annotations.VisibleForTesting; -import com.android.server.AttributeCache; -import com.android.server.policy.WindowManagerPolicy.StartingSurface; - -/** - * Controller for the app window token container. This is created by activity manager to link - * activity records to the app window token container they use in window manager. - * - * Test class: {@link AppWindowContainerControllerTests} - */ -public class AppWindowContainerController - extends WindowContainerController<AppWindowToken, AppWindowContainerListener> { - - private static final int STARTING_WINDOW_TYPE_NONE = 0; - private static final int STARTING_WINDOW_TYPE_SNAPSHOT = 1; - private static final int STARTING_WINDOW_TYPE_SPLASH_SCREEN = 2; - - private final IApplicationToken mToken; - private final Handler mHandler; - - private final class H extends Handler { - public static final int NOTIFY_WINDOWS_DRAWN = 1; - public static final int NOTIFY_STARTING_WINDOW_DRAWN = 2; - public static final int NOTIFY_WINDOWS_NOTDRAWN = 3; - - public H(Looper looper) { - super(looper); - } - - @Override - public void handleMessage(Message msg) { - switch (msg.what) { - case NOTIFY_WINDOWS_DRAWN: - if (mListener == null) { - return; - } - if (DEBUG_VISIBILITY) Slog.v(TAG_WM, "Reporting drawn in " - + AppWindowContainerController.this.mToken); - mListener.onWindowsDrawn(true /* drawn */, msg.getWhen()); - break; - case NOTIFY_STARTING_WINDOW_DRAWN: - if (mListener == null) { - return; - } - if (DEBUG_VISIBILITY) Slog.v(TAG_WM, "Reporting starting window drawn in " - + AppWindowContainerController.this.mToken); - mListener.onStartingWindowDrawn(msg.getWhen()); - break; - case NOTIFY_WINDOWS_NOTDRAWN: - if (mListener == null) { - return; - } - if (DEBUG_VISIBILITY) Slog.v(TAG_WM, "Reporting not drawn in " - + AppWindowContainerController.this.mToken); - mListener.onWindowsDrawn(false /* drawn */, msg.getWhen()); - break; - default: - break; - } - } - } - - private final Runnable mOnWindowsVisible = () -> { - if (mListener == null) { - return; - } - if (DEBUG_VISIBILITY) Slog.v(TAG_WM, "Reporting visible in " - + AppWindowContainerController.this.mToken); - mListener.onWindowsVisible(); - }; - - private final Runnable mOnWindowsGone = () -> { - if (mListener == null) { - return; - } - if (DEBUG_VISIBILITY) Slog.v(TAG_WM, "Reporting gone in " - + AppWindowContainerController.this.mToken); - mListener.onWindowsGone(); - }; - - private final Runnable mAddStartingWindow = new Runnable() { - - @Override - public void run() { - final StartingData startingData; - final AppWindowToken container; - - synchronized (mGlobalLock) { - if (mContainer == null) { - if (DEBUG_STARTING_WINDOW) Slog.v(TAG_WM, "mContainer was null while trying to" - + " add starting window"); - return; - } - - // There can only be one adding request, silly caller! - mService.mAnimationHandler.removeCallbacks(this); - - startingData = mContainer.startingData; - container = mContainer; - } - - if (startingData == null) { - // Animation has been canceled... do nothing. - if (DEBUG_STARTING_WINDOW) - Slog.v(TAG_WM, "startingData was nulled out before handling" - + " mAddStartingWindow: " + mContainer); - return; - } - - if (DEBUG_STARTING_WINDOW) Slog.v(TAG_WM, "Add starting " - + AppWindowContainerController.this + ": startingData=" - + container.startingData); - - StartingSurface surface = null; - try { - surface = startingData.createStartingSurface(container); - } catch (Exception e) { - Slog.w(TAG_WM, "Exception when adding starting window", e); - } - if (surface != null) { - boolean abort = false; - synchronized (mGlobalLock) { - // If the window was successfully added, then - // we need to remove it. - if (container.removed || container.startingData == null) { - if (DEBUG_STARTING_WINDOW) Slog.v(TAG_WM, - "Aborted starting " + container - + ": removed=" + container.removed - + " startingData=" + container.startingData); - container.startingWindow = null; - container.startingData = null; - abort = true; - } else { - container.startingSurface = surface; - } - if (DEBUG_STARTING_WINDOW && !abort) Slog.v(TAG_WM, - "Added starting " + mContainer - + ": startingWindow=" - + container.startingWindow + " startingView=" - + container.startingSurface); - } - if (abort) { - surface.remove(); - } - } else if (DEBUG_STARTING_WINDOW) { - Slog.v(TAG_WM, "Surface returned was null: " + mContainer); - } - } - }; - - public AppWindowContainerController(TaskWindowContainerController taskController, - IApplicationToken token, ComponentName activityComponent, - AppWindowContainerListener listener, int index, int requestedOrientation, - boolean fullscreen, boolean showForAllUsers, int configChanges, - boolean voiceInteraction, boolean launchTaskBehind, boolean alwaysFocusable, - int targetSdkVersion, int rotationAnimationHint, long inputDispatchingTimeoutNanos) { - this(taskController, token, activityComponent, listener, index, requestedOrientation, - fullscreen, showForAllUsers, configChanges, voiceInteraction, launchTaskBehind, - alwaysFocusable, targetSdkVersion, rotationAnimationHint, - inputDispatchingTimeoutNanos, WindowManagerService.getInstance()); - } - - public AppWindowContainerController(TaskWindowContainerController taskController, - IApplicationToken token, ComponentName activityComponent, - AppWindowContainerListener listener, int index, int requestedOrientation, - boolean fullscreen, boolean showForAllUsers, int configChanges, - boolean voiceInteraction, boolean launchTaskBehind, boolean alwaysFocusable, - int targetSdkVersion, int rotationAnimationHint, long inputDispatchingTimeoutNanos, - WindowManagerService service) { - super(listener, service); - mHandler = new H(service.mH.getLooper()); - mToken = token; - synchronized (mGlobalLock) { - AppWindowToken atoken = mRoot.getAppWindowToken(mToken.asBinder()); - if (atoken != null) { - // TODO: Should this throw an exception instead? - Slog.w(TAG_WM, "Attempted to add existing app token: " + mToken); - return; - } - - final Task task = taskController.mContainer; - if (task == null) { - throw new IllegalArgumentException("AppWindowContainerController: invalid " - + " controller=" + taskController); - } - - atoken = createAppWindow(mService, token, activityComponent, voiceInteraction, - task.getDisplayContent(), inputDispatchingTimeoutNanos, fullscreen, - showForAllUsers, targetSdkVersion, requestedOrientation, rotationAnimationHint, - configChanges, launchTaskBehind, alwaysFocusable, this); - if (DEBUG_TOKEN_MOVEMENT || DEBUG_ADD_REMOVE) Slog.v(TAG_WM, "addAppToken: " + atoken - + " controller=" + taskController + " at " + index); - task.addChild(atoken, index); - } - } - - @VisibleForTesting - AppWindowToken createAppWindow(WindowManagerService service, IApplicationToken token, - ComponentName component, boolean voiceInteraction, DisplayContent dc, - long inputDispatchingTimeoutNanos, boolean fullscreen, boolean showForAllUsers, - int targetSdk, int orientation, int rotationAnimationHint, int configChanges, - boolean launchTaskBehind, boolean alwaysFocusable, - AppWindowContainerController controller) { - return new AppWindowToken(service, token, component, voiceInteraction, dc, - inputDispatchingTimeoutNanos, fullscreen, showForAllUsers, targetSdk, orientation, - rotationAnimationHint, configChanges, launchTaskBehind, alwaysFocusable, - controller); - } - - public void removeContainer(int displayId) { - synchronized (mGlobalLock) { - final DisplayContent dc = mRoot.getDisplayContent(displayId); - if (dc == null) { - Slog.w(TAG_WM, "removeAppToken: Attempted to remove binder token: " - + mToken + " from non-existing displayId=" + displayId); - return; - } - dc.removeAppToken(mToken.asBinder()); - super.removeContainer(); - } - } - - @Override - public void removeContainer() { - throw new UnsupportedOperationException("Use removeContainer(displayId) instead."); - } - - public void reparent(TaskWindowContainerController taskController, int position) { - synchronized (mGlobalLock) { - if (DEBUG_ADD_REMOVE) Slog.i(TAG_WM, "reparent: moving app token=" + mToken - + " to task=" + taskController + " at " + position); - if (mContainer == null) { - if (DEBUG_ADD_REMOVE) Slog.i(TAG_WM, - "reparent: could not find app token=" + mToken); - return; - } - final Task task = taskController.mContainer; - if (task == null) { - throw new IllegalArgumentException("reparent: could not find task=" - + taskController); - } - mContainer.reparent(task, position); - mContainer.getDisplayContent().layoutAndAssignWindowLayersIfNeeded(); - } - } - - public Configuration setOrientation(int requestedOrientation, int displayId, - Configuration displayConfig, boolean freezeScreenIfNeeded) { - synchronized (mGlobalLock) { - if (mContainer == null) { - Slog.w(TAG_WM, - "Attempted to set orientation of non-existing app token: " + mToken); - return null; - } - - mContainer.setOrientation(requestedOrientation); - - final IBinder binder = freezeScreenIfNeeded ? mToken.asBinder() : null; - return mService.updateOrientationFromAppTokens(displayConfig, binder, displayId); - - } - } - - public int getOrientation() { - synchronized (mGlobalLock) { - if (mContainer == null) { - return SCREEN_ORIENTATION_UNSPECIFIED; - } - - return mContainer.getOrientationIgnoreVisibility(); - } - } - - public void setDisablePreviewScreenshots(boolean disable) { - synchronized (mGlobalLock) { - if (mContainer == null) { - Slog.w(TAG_WM, "Attempted to set disable screenshots of non-existing app" - + " token: " + mToken); - return; - } - mContainer.setDisablePreviewScreenshots(disable); - } - } - - public void setVisibility(boolean visible, boolean deferHidingClient) { - synchronized (mGlobalLock) { - if (mContainer == null) { - Slog.w(TAG_WM, "Attempted to set visibility of non-existing app token: " - + mToken); - return; - } - - final AppWindowToken wtoken = mContainer; - final AppTransition appTransition = mContainer.getDisplayContent().mAppTransition; - - // Don't set visibility to false if we were already not visible. This prevents WM from - // adding the app to the closing app list which doesn't make sense for something that is - // already not visible. However, set visibility to true even if we are already visible. - // This makes sure the app is added to the opening apps list so that the right - // transition can be selected. - // TODO: Probably a good idea to separate the concept of opening/closing apps from the - // concept of setting visibility... - if (!visible && wtoken.hiddenRequested) { - - if (!deferHidingClient && wtoken.mDeferHidingClient) { - // We previously deferred telling the client to hide itself when visibility was - // initially set to false. Now we would like it to hide, so go ahead and set it. - wtoken.mDeferHidingClient = deferHidingClient; - wtoken.setClientHidden(true); - } - return; - } - - if (DEBUG_APP_TRANSITIONS || DEBUG_ORIENTATION) Slog.v(TAG_WM, "setAppVisibility(" - + mToken + ", visible=" + visible + "): " + appTransition - + " hidden=" + wtoken.isHidden() + " hiddenRequested=" - + wtoken.hiddenRequested + " Callers=" + Debug.getCallers(6)); - - final DisplayContent displayContent = mContainer.getDisplayContent(); - displayContent.mOpeningApps.remove(wtoken); - displayContent.mClosingApps.remove(wtoken); - wtoken.waitingToShow = false; - wtoken.hiddenRequested = !visible; - wtoken.mDeferHidingClient = deferHidingClient; - - if (!visible) { - // If the app is dead while it was visible, we kept its dead window on screen. - // Now that the app is going invisible, we can remove it. It will be restarted - // if made visible again. - wtoken.removeDeadWindows(); - } else { - if (!appTransition.isTransitionSet() - && appTransition.isReady()) { - // Add the app mOpeningApps if transition is unset but ready. This means - // we're doing a screen freeze, and the unfreeze will wait for all opening - // apps to be ready. - displayContent.mOpeningApps.add(wtoken); - } - wtoken.startingMoved = false; - // If the token is currently hidden (should be the common case), or has been - // stopped, then we need to set up to wait for its windows to be ready. - if (wtoken.isHidden() || wtoken.mAppStopped) { - wtoken.clearAllDrawn(); - - // If the app was already visible, don't reset the waitingToShow state. - if (wtoken.isHidden()) { - wtoken.waitingToShow = true; - } - } - - // In the case where we are making an app visible but holding off for a transition, - // we still need to tell the client to make its windows visible so they get drawn. - // Otherwise, we will wait on performing the transition until all windows have been - // drawn, they never will be, and we are sad. - wtoken.setClientHidden(false); - - wtoken.requestUpdateWallpaperIfNeeded(); - - if (DEBUG_ADD_REMOVE) Slog.v(TAG_WM, "No longer Stopped: " + wtoken); - wtoken.mAppStopped = false; - - mContainer.transferStartingWindowFromHiddenAboveTokenIfNeeded(); - } - - // If we are preparing an app transition, then delay changing - // the visibility of this token until we execute that transition. - if (wtoken.okToAnimate() && appTransition.isTransitionSet()) { - wtoken.inPendingTransaction = true; - if (visible) { - displayContent.mOpeningApps.add(wtoken); - wtoken.mEnteringAnimation = true; - } else { - displayContent.mClosingApps.add(wtoken); - wtoken.mEnteringAnimation = false; - } - if (appTransition.getAppTransition() - == WindowManager.TRANSIT_TASK_OPEN_BEHIND) { - // We're launchingBehind, add the launching activity to mOpeningApps. - final WindowState win = mContainer.getDisplayContent().findFocusedWindow(); - if (win != null) { - final AppWindowToken focusedToken = win.mAppToken; - if (focusedToken != null) { - if (DEBUG_APP_TRANSITIONS) Slog.d(TAG_WM, "TRANSIT_TASK_OPEN_BEHIND, " - + " adding " + focusedToken + " to mOpeningApps"); - // Force animation to be loaded. - focusedToken.setHidden(true); - displayContent.mOpeningApps.add(focusedToken); - } - } - } - return; - } - - wtoken.setVisibility(null, visible, TRANSIT_UNSET, true, wtoken.mVoiceInteraction); - wtoken.updateReportedVisibilityLocked(); - } - } - - /** - * Notifies that we launched an app that might be visible or not visible depending on what kind - * of Keyguard flags it's going to set on its windows. - */ - public void notifyUnknownVisibilityLaunched() { - synchronized (mGlobalLock) { - if (mContainer != null) { - mContainer.getDisplayContent().mUnknownAppVisibilityController.notifyLaunched( - mContainer); - } - } - } - - public boolean addStartingWindow(String pkg, int theme, CompatibilityInfo compatInfo, - CharSequence nonLocalizedLabel, int labelRes, int icon, int logo, int windowFlags, - IBinder transferFrom, boolean newTask, boolean taskSwitch, boolean processRunning, - boolean allowTaskSnapshot, boolean activityCreated, boolean fromRecents) { - synchronized (mGlobalLock) { - if (DEBUG_STARTING_WINDOW) Slog.v(TAG_WM, "setAppStartingWindow: token=" + mToken - + " pkg=" + pkg + " transferFrom=" + transferFrom + " newTask=" + newTask - + " taskSwitch=" + taskSwitch + " processRunning=" + processRunning - + " allowTaskSnapshot=" + allowTaskSnapshot); - - if (mContainer == null) { - Slog.w(TAG_WM, "Attempted to set icon of non-existing app token: " + mToken); - return false; - } - - // If the display is frozen, we won't do anything until the actual window is - // displayed so there is no reason to put in the starting window. - if (!mContainer.okToDisplay()) { - return false; - } - - if (mContainer.startingData != null) { - return false; - } - - final WindowState mainWin = mContainer.findMainWindow(); - if (mainWin != null && mainWin.mWinAnimator.getShown()) { - // App already has a visible window...why would you want a starting window? - return false; - } - - final TaskSnapshot snapshot = mService.mTaskSnapshotController.getSnapshot( - mContainer.getTask().mTaskId, mContainer.getTask().mUserId, - false /* restoreFromDisk */, false /* reducedResolution */); - final int type = getStartingWindowType(newTask, taskSwitch, processRunning, - allowTaskSnapshot, activityCreated, fromRecents, snapshot); - - if (type == STARTING_WINDOW_TYPE_SNAPSHOT) { - return createSnapshot(snapshot); - } - - // If this is a translucent window, then don't show a starting window -- the current - // effect (a full-screen opaque starting window that fades away to the real contents - // when it is ready) does not work for this. - if (DEBUG_STARTING_WINDOW) Slog.v(TAG_WM, "Checking theme of starting window: 0x" - + Integer.toHexString(theme)); - if (theme != 0) { - AttributeCache.Entry ent = AttributeCache.instance().get(pkg, theme, - com.android.internal.R.styleable.Window, mService.mCurrentUserId); - if (ent == null) { - // Whoops! App doesn't exist. Um. Okay. We'll just pretend like we didn't - // see that. - return false; - } - final boolean windowIsTranslucent = ent.array.getBoolean( - com.android.internal.R.styleable.Window_windowIsTranslucent, false); - final boolean windowIsFloating = ent.array.getBoolean( - com.android.internal.R.styleable.Window_windowIsFloating, false); - final boolean windowShowWallpaper = ent.array.getBoolean( - com.android.internal.R.styleable.Window_windowShowWallpaper, false); - final boolean windowDisableStarting = ent.array.getBoolean( - com.android.internal.R.styleable.Window_windowDisablePreview, false); - if (DEBUG_STARTING_WINDOW) Slog.v(TAG_WM, "Translucent=" + windowIsTranslucent - + " Floating=" + windowIsFloating - + " ShowWallpaper=" + windowShowWallpaper); - if (windowIsTranslucent) { - return false; - } - if (windowIsFloating || windowDisableStarting) { - return false; - } - if (windowShowWallpaper) { - if (mContainer.getDisplayContent().mWallpaperController.getWallpaperTarget() - == null) { - // If this theme is requesting a wallpaper, and the wallpaper - // is not currently visible, then this effectively serves as - // an opaque window and our starting window transition animation - // can still work. We just need to make sure the starting window - // is also showing the wallpaper. - windowFlags |= FLAG_SHOW_WALLPAPER; - } else { - return false; - } - } - } - - if (mContainer.transferStartingWindow(transferFrom)) { - return true; - } - - // There is no existing starting window, and we don't want to create a splash screen, so - // that's it! - if (type != STARTING_WINDOW_TYPE_SPLASH_SCREEN) { - return false; - } - - if (DEBUG_STARTING_WINDOW) Slog.v(TAG_WM, "Creating SplashScreenStartingData"); - mContainer.startingData = new SplashScreenStartingData(mService, pkg, theme, - compatInfo, nonLocalizedLabel, labelRes, icon, logo, windowFlags, - mContainer.getMergedOverrideConfiguration()); - scheduleAddStartingWindow(); - } - return true; - } - - private int getStartingWindowType(boolean newTask, boolean taskSwitch, boolean processRunning, - boolean allowTaskSnapshot, boolean activityCreated, boolean fromRecents, - TaskSnapshot snapshot) { - if (mContainer.getDisplayContent().mAppTransition.getAppTransition() - == TRANSIT_DOCK_TASK_FROM_RECENTS) { - // TODO(b/34099271): Remove this statement to add back the starting window and figure - // out why it causes flickering, the starting window appears over the thumbnail while - // the docked from recents transition occurs - return STARTING_WINDOW_TYPE_NONE; - } else if (newTask || !processRunning || (taskSwitch && !activityCreated)) { - return STARTING_WINDOW_TYPE_SPLASH_SCREEN; - } else if (taskSwitch && allowTaskSnapshot) { - return snapshot == null ? STARTING_WINDOW_TYPE_NONE - : snapshotOrientationSameAsTask(snapshot) || fromRecents - ? STARTING_WINDOW_TYPE_SNAPSHOT : STARTING_WINDOW_TYPE_SPLASH_SCREEN; - } else { - return STARTING_WINDOW_TYPE_NONE; - } - } - - void scheduleAddStartingWindow() { - // Note: we really want to do sendMessageAtFrontOfQueue() because we - // want to process the message ASAP, before any other queued - // messages. - if (!mService.mAnimationHandler.hasCallbacks(mAddStartingWindow)) { - if (DEBUG_STARTING_WINDOW) Slog.v(TAG_WM, "Enqueueing ADD_STARTING"); - mService.mAnimationHandler.postAtFrontOfQueue(mAddStartingWindow); - } - } - - private boolean createSnapshot(TaskSnapshot snapshot) { - if (snapshot == null) { - return false; - } - - if (DEBUG_STARTING_WINDOW) Slog.v(TAG_WM, "Creating SnapshotStartingData"); - mContainer.startingData = new SnapshotStartingData(mService, snapshot); - scheduleAddStartingWindow(); - return true; - } - - private boolean snapshotOrientationSameAsTask(TaskSnapshot snapshot) { - if (snapshot == null) { - return false; - } - return mContainer.getTask().getConfiguration().orientation == snapshot.getOrientation(); - } - - public void removeStartingWindow() { - synchronized (mGlobalLock) { - if (mContainer.startingWindow == null) { - if (mContainer.startingData != null) { - // Starting window has not been added yet, but it is scheduled to be added. - // Go ahead and cancel the request. - if (DEBUG_STARTING_WINDOW) Slog.v(TAG_WM, - "Clearing startingData for token=" + mContainer); - mContainer.startingData = null; - } - return; - } - - final StartingSurface surface; - if (mContainer.startingData != null) { - surface = mContainer.startingSurface; - mContainer.startingData = null; - mContainer.startingSurface = null; - mContainer.startingWindow = null; - mContainer.startingDisplayed = false; - if (surface == null) { - if (DEBUG_STARTING_WINDOW) { - Slog.v(TAG_WM, "startingWindow was set but startingSurface==null, couldn't " - + "remove"); - } - return; - } - } else { - if (DEBUG_STARTING_WINDOW) { - Slog.v(TAG_WM, "Tried to remove starting window but startingWindow was null:" - + mContainer); - } - return; - } - - if (DEBUG_STARTING_WINDOW) Slog.v(TAG_WM, "Schedule remove starting " + mContainer - + " startingWindow=" + mContainer.startingWindow - + " startingView=" + mContainer.startingSurface - + " Callers=" + Debug.getCallers(5)); - - // Use the same thread to remove the window as we used to add it, as otherwise we end up - // with things in the view hierarchy being called from different threads. - mService.mAnimationHandler.post(() -> { - if (DEBUG_STARTING_WINDOW) Slog.v(TAG_WM, "Removing startingView=" + surface); - try { - surface.remove(); - } catch (Exception e) { - Slog.w(TAG_WM, "Exception when removing starting window", e); - } - }); - } - } - - public void pauseKeyDispatching() { - synchronized (mGlobalLock) { - if (mContainer != null) { - mContainer.getDisplayContent().getInputMonitor().pauseDispatchingLw(mContainer); - } - } - } - - public void resumeKeyDispatching() { - synchronized (mGlobalLock) { - if (mContainer != null) { - mContainer.getDisplayContent().getInputMonitor().resumeDispatchingLw(mContainer); - } - } - } - - public void notifyAppResumed(boolean wasStopped) { - synchronized (mGlobalLock) { - if (mContainer == null) { - Slog.w(TAG_WM, "Attempted to notify resumed of non-existing app token: " + mToken); - return; - } - mContainer.notifyAppResumed(wasStopped); - } - } - - public void notifyAppStopping() { - synchronized (mGlobalLock) { - if (mContainer == null) { - Slog.w(TAG_WM, "Attempted to notify stopping on non-existing app token: " - + mToken); - return; - } - mContainer.detachChildren(); - } - } - - public void notifyAppStopped() { - synchronized (mGlobalLock) { - if (mContainer == null) { - Slog.w(TAG_WM, "Attempted to notify stopped of non-existing app token: " - + mToken); - return; - } - mContainer.notifyAppStopped(); - } - } - - public void startFreezingScreen(int configChanges) { - synchronized (mGlobalLock) { - if (mContainer == null) { - Slog.w(TAG_WM, - "Attempted to freeze screen with non-existing app token: " + mContainer); - return; - } - - if (configChanges == 0 && mContainer.okToDisplay()) { - if (DEBUG_ORIENTATION) Slog.v(TAG_WM, "Skipping set freeze of " + mToken); - return; - } - - mContainer.startFreezingScreen(); - } - } - - public void stopFreezingScreen(boolean force) { - synchronized (mGlobalLock) { - if (mContainer == null) { - return; - } - if (DEBUG_ORIENTATION) Slog.v(TAG_WM, "Clear freezing of " + mToken + ": hidden=" - + mContainer.isHidden() + " freezing=" + mContainer.isFreezingScreen()); - mContainer.stopFreezingScreen(true, force); - } - } - - public void registerRemoteAnimations(RemoteAnimationDefinition definition) { - synchronized (mGlobalLock) { - if (mContainer == null) { - Slog.w(TAG_WM, "Attempted to register remote animations with non-existing app" - + " token: " + mToken); - return; - } - mContainer.registerRemoteAnimations(definition); - } - } - - void reportStartingWindowDrawn() { - mHandler.sendMessage(mHandler.obtainMessage(H.NOTIFY_STARTING_WINDOW_DRAWN)); - } - - void reportWindowsDrawn() { - mHandler.sendMessage(mHandler.obtainMessage(H.NOTIFY_WINDOWS_DRAWN)); - } - - void reportWindowsNotDrawn() { - mHandler.sendMessage(mHandler.obtainMessage(H.NOTIFY_WINDOWS_NOTDRAWN)); - } - - void reportWindowsVisible() { - mHandler.post(mOnWindowsVisible); - } - - void reportWindowsGone() { - mHandler.post(mOnWindowsGone); - } - - /** Calls directly into activity manager so window manager lock shouldn't held. */ - boolean keyDispatchingTimedOut(String reason, int windowPid) { - return mListener != null && mListener.keyDispatchingTimedOut(reason, windowPid); - } - - /** - * Apply override app transition base on options & animation type. - */ - public void applyOptionsLocked(ActivityOptions pendingOptions, Intent intent) { - synchronized (mGlobalLock) { - final int animationType = pendingOptions.getAnimationType(); - final DisplayContent displayContent = mContainer.getDisplayContent(); - switch (animationType) { - case ANIM_CUSTOM: - displayContent.mAppTransition.overridePendingAppTransition( - pendingOptions.getPackageName(), - pendingOptions.getCustomEnterResId(), - pendingOptions.getCustomExitResId(), - pendingOptions.getOnAnimationStartListener()); - break; - case ANIM_CLIP_REVEAL: - displayContent.mAppTransition.overridePendingAppTransitionClipReveal( - pendingOptions.getStartX(), pendingOptions.getStartY(), - pendingOptions.getWidth(), pendingOptions.getHeight()); - if (intent.getSourceBounds() == null) { - intent.setSourceBounds(new Rect(pendingOptions.getStartX(), - pendingOptions.getStartY(), - pendingOptions.getStartX() + pendingOptions.getWidth(), - pendingOptions.getStartY() + pendingOptions.getHeight())); - } - break; - case ANIM_SCALE_UP: - displayContent.mAppTransition.overridePendingAppTransitionScaleUp( - pendingOptions.getStartX(), pendingOptions.getStartY(), - pendingOptions.getWidth(), pendingOptions.getHeight()); - if (intent.getSourceBounds() == null) { - intent.setSourceBounds(new Rect(pendingOptions.getStartX(), - pendingOptions.getStartY(), - pendingOptions.getStartX() + pendingOptions.getWidth(), - pendingOptions.getStartY() + pendingOptions.getHeight())); - } - break; - case ANIM_THUMBNAIL_SCALE_UP: - case ANIM_THUMBNAIL_SCALE_DOWN: - final boolean scaleUp = (animationType == ANIM_THUMBNAIL_SCALE_UP); - final GraphicBuffer buffer = pendingOptions.getThumbnail(); - displayContent.mAppTransition.overridePendingAppTransitionThumb(buffer, - pendingOptions.getStartX(), pendingOptions.getStartY(), - pendingOptions.getOnAnimationStartListener(), - scaleUp); - if (intent.getSourceBounds() == null && buffer != null) { - intent.setSourceBounds(new Rect(pendingOptions.getStartX(), - pendingOptions.getStartY(), - pendingOptions.getStartX() + buffer.getWidth(), - pendingOptions.getStartY() + buffer.getHeight())); - } - break; - case ANIM_THUMBNAIL_ASPECT_SCALE_UP: - case ANIM_THUMBNAIL_ASPECT_SCALE_DOWN: - final AppTransitionAnimationSpec[] specs = pendingOptions.getAnimSpecs(); - final IAppTransitionAnimationSpecsFuture specsFuture = - pendingOptions.getSpecsFuture(); - if (specsFuture != null) { - displayContent.mAppTransition.overridePendingAppTransitionMultiThumbFuture( - specsFuture, pendingOptions.getOnAnimationStartListener(), - animationType == ANIM_THUMBNAIL_ASPECT_SCALE_UP); - } else if (animationType == ANIM_THUMBNAIL_ASPECT_SCALE_DOWN - && specs != null) { - displayContent.mAppTransition.overridePendingAppTransitionMultiThumb( - specs, pendingOptions.getOnAnimationStartListener(), - pendingOptions.getAnimationFinishedListener(), false); - } else { - displayContent.mAppTransition.overridePendingAppTransitionAspectScaledThumb( - pendingOptions.getThumbnail(), - pendingOptions.getStartX(), pendingOptions.getStartY(), - pendingOptions.getWidth(), pendingOptions.getHeight(), - pendingOptions.getOnAnimationStartListener(), - (animationType == ANIM_THUMBNAIL_ASPECT_SCALE_UP)); - if (intent.getSourceBounds() == null) { - intent.setSourceBounds(new Rect(pendingOptions.getStartX(), - pendingOptions.getStartY(), - pendingOptions.getStartX() + pendingOptions.getWidth(), - pendingOptions.getStartY() + pendingOptions.getHeight())); - } - } - break; - case ANIM_OPEN_CROSS_PROFILE_APPS: - displayContent.mAppTransition - .overridePendingAppTransitionStartCrossProfileApps(); - break; - case ANIM_REMOTE_ANIMATION: - displayContent.mAppTransition.overridePendingAppTransitionRemote( - pendingOptions.getRemoteAnimationAdapter()); - break; - case ANIM_NONE: - break; - default: - Slog.e(TAG_WM, "applyOptionsLocked: Unknown animationType=" + animationType); - break; - } - } - } - - /** - * Notifies AWT that this app is waiting to pause in order to determine if it will enter PIP. - * This information helps AWT know that the app is in the process of pausing before it gets the - * signal on the WM side. - */ - public void setWillCloseOrEnterPip(boolean willCloseOrEnterPip) { - synchronized (mGlobalLock) { - if (mContainer == null) { - return; - } - - mContainer.setWillCloseOrEnterPip(willCloseOrEnterPip); - } - } - - @Override - public String toString() { - return "AppWindowContainerController{" - + " token=" + mToken - + " mContainer=" + mContainer - + " mListener=" + mListener - + "}"; - } -} diff --git a/services/core/java/com/android/server/wm/AppWindowContainerListener.java b/services/core/java/com/android/server/wm/AppWindowContainerListener.java deleted file mode 100644 index ad27669fc030..000000000000 --- a/services/core/java/com/android/server/wm/AppWindowContainerListener.java +++ /dev/null @@ -1,42 +0,0 @@ -/* - * Copyright (C) 2016 The Android Open Source Project - * - * Licensed under the Apache License, Version 2.0 (the "License"); - * you may not use this file except in compliance with the License. - * You may obtain a copy of the License at - * - * http://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, - * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - * See the License for the specific language governing permissions and - * limitations under the License - */ - -package com.android.server.wm; - -/** Interface used by the creator of the controller to listen to changes with the container. */ -public interface AppWindowContainerListener extends WindowContainerListener { - /** Called when the windows associated app window container drawn state changes. */ - void onWindowsDrawn(boolean drawn, long timestamp); - /** Called when the windows associated app window container are visible. */ - void onWindowsVisible(); - /** Called when the windows associated app window container are no longer visible. */ - void onWindowsGone(); - - /** - * Called when the starting window for this container is drawn. - */ - void onStartingWindowDrawn(long timestamp); - - /** - * Called when the key dispatching to a window associated with the app window container - * timed-out. - * - * @param reason The reason for the key dispatching time out. - * @param windowPid The pid of the window key dispatching timed out on. - * @return True if input dispatching should be aborted. - */ - boolean keyDispatchingTimedOut(String reason, int windowPid); -} diff --git a/services/core/java/com/android/server/wm/AppWindowToken.java b/services/core/java/com/android/server/wm/AppWindowToken.java index 6d402f291fcb..a7c9a4666af7 100644 --- a/services/core/java/com/android/server/wm/AppWindowToken.java +++ b/services/core/java/com/android/server/wm/AppWindowToken.java @@ -28,10 +28,12 @@ import static android.os.Trace.TRACE_TAG_WINDOW_MANAGER; import static android.view.Display.DEFAULT_DISPLAY; import static android.view.WindowManager.LayoutParams.FLAG_DISMISS_KEYGUARD; import static android.view.WindowManager.LayoutParams.FLAG_SECURE; +import static android.view.WindowManager.LayoutParams.FLAG_SHOW_WALLPAPER; import static android.view.WindowManager.LayoutParams.FLAG_SHOW_WHEN_LOCKED; import static android.view.WindowManager.LayoutParams.TYPE_APPLICATION; import static android.view.WindowManager.LayoutParams.TYPE_APPLICATION_STARTING; import static android.view.WindowManager.LayoutParams.TYPE_BASE_APPLICATION; +import static android.view.WindowManager.TRANSIT_DOCK_TASK_FROM_RECENTS; import static android.view.WindowManager.TRANSIT_UNSET; import static android.view.WindowManager.TRANSIT_WALLPAPER_OPEN; @@ -81,7 +83,9 @@ import static com.android.server.wm.WindowStateAnimator.STACK_CLIP_AFTER_ANIM; import android.annotation.CallSuper; import android.app.Activity; +import android.app.ActivityManager; import android.content.ComponentName; +import android.content.res.CompatibilityInfo; import android.content.res.Configuration; import android.graphics.GraphicBuffer; import android.graphics.Point; @@ -90,6 +94,7 @@ import android.os.Binder; import android.os.Debug; import android.os.IBinder; import android.os.RemoteException; +import android.os.SystemClock; import android.os.Trace; import android.util.Slog; import android.util.proto.ProtoOutputStream; @@ -106,6 +111,8 @@ import android.view.animation.Animation; import com.android.internal.R; import com.android.internal.annotations.VisibleForTesting; import com.android.internal.util.ToBooleanFunction; +import com.android.server.AttributeCache; +import com.android.server.policy.WindowManagerPolicy; import com.android.server.policy.WindowManagerPolicy.StartingSurface; import com.android.server.wm.WindowManagerService.H; @@ -121,7 +128,8 @@ class AppTokenList extends ArrayList<AppWindowToken> { * Version of WindowToken that is specifically for a particular application (or * really activity) that is displaying windows. */ -class AppWindowToken extends WindowToken implements WindowManagerService.AppFreezeListener { +class AppWindowToken extends WindowToken implements WindowManagerService.AppFreezeListener, + ConfigurationContainerListener { private static final String TAG = TAG_WITH_CLASS_NAME ? "AppWindowToken" : TAG_WM; /** @@ -226,6 +234,9 @@ class AppWindowToken extends WindowToken implements WindowManagerService.AppFree private Task mLastParent; + // TODO: Remove after unification + ActivityRecord mActivityRecord; + /** * See {@link #canTurnScreenOn()} */ @@ -273,14 +284,20 @@ class AppWindowToken extends WindowToken implements WindowManagerService.AppFree /** Whether this token needs to create mAnimationBoundsLayer for cropping animations. */ boolean mNeedsAnimationBoundsLayer; + private static final int STARTING_WINDOW_TYPE_NONE = 0; + private static final int STARTING_WINDOW_TYPE_SNAPSHOT = 1; + private static final int STARTING_WINDOW_TYPE_SPLASH_SCREEN = 2; + AppWindowToken(WindowManagerService service, IApplicationToken token, ComponentName activityComponent, boolean voiceInteraction, DisplayContent dc, long inputDispatchingTimeoutNanos, boolean fullscreen, boolean showForAllUsers, int targetSdk, int orientation, int rotationAnimationHint, int configChanges, boolean launchTaskBehind, boolean alwaysFocusable, - AppWindowContainerController controller) { + ActivityRecord activityRecord) { this(service, token, activityComponent, voiceInteraction, dc, fullscreen); - setController(controller); + // TODO: remove after unification + mActivityRecord = activityRecord; + mActivityRecord.registerConfigurationChangeListener(this); mInputDispatchingTimeoutNanos = inputDispatchingTimeoutNanos; mShowForAllUsers = showForAllUsers; mTargetSdk = targetSdk; @@ -320,9 +337,7 @@ class AppWindowToken extends WindowToken implements WindowManagerService.AppFree // it from behind the starting window, so there is no need for it to also be doing its // own stuff. win.cancelAnimation(); - if (getController() != null) { - getController().removeStartingWindow(); - } + removeStartingWindow(); } updateReportedVisibilityLocked(); } @@ -360,16 +375,9 @@ class AppWindowToken extends WindowToken implements WindowManagerService.AppFree } if (DEBUG_VISIBILITY) Slog.v(TAG, "VIS " + this + ": interesting=" + numInteresting + " visible=" + numVisible); - final AppWindowContainerController controller = getController(); if (nowDrawn != reportedDrawn) { - if (nowDrawn) { - if (controller != null) { - controller.reportWindowsDrawn(); - } - } else { - if (controller != null) { - controller.reportWindowsNotDrawn(); - } + if (mActivityRecord != null) { + mActivityRecord.onWindowsDrawn(nowDrawn, SystemClock.uptimeMillis()); } reportedDrawn = nowDrawn; } @@ -377,16 +385,36 @@ class AppWindowToken extends WindowToken implements WindowManagerService.AppFree if (DEBUG_VISIBILITY) Slog.v(TAG, "Visibility changed in " + this + ": vis=" + nowVisible); reportedVisible = nowVisible; - if (controller != null) { + if (mActivityRecord != null) { if (nowVisible) { - controller.reportWindowsVisible(); + onWindowsVisible(); } else { - controller.reportWindowsGone(); + onWindowsGone(); } } } } + private void onWindowsGone() { + if (mActivityRecord == null) { + return; + } + if (DEBUG_VISIBILITY) { + Slog.v(TAG_WM, "Reporting gone in " + mActivityRecord.appToken); + } + mActivityRecord.onWindowsGone(); + } + + private void onWindowsVisible() { + if (mActivityRecord == null) { + return; + } + if (DEBUG_VISIBILITY) { + Slog.v(TAG_WM, "Reporting visible in " + mActivityRecord.appToken); + } + mActivityRecord.onWindowsVisible(); + } + boolean isClientHidden() { return mClientHidden; } @@ -401,7 +429,116 @@ class AppWindowToken extends WindowToken implements WindowManagerService.AppFree sendAppVisibilityToClients(); } - boolean setVisibility(WindowManager.LayoutParams lp, + void setVisibility(boolean visible, boolean deferHidingClient) { + final AppTransition appTransition = getDisplayContent().mAppTransition; + + // Don't set visibility to false if we were already not visible. This prevents WM from + // adding the app to the closing app list which doesn't make sense for something that is + // already not visible. However, set visibility to true even if we are already visible. + // This makes sure the app is added to the opening apps list so that the right + // transition can be selected. + // TODO: Probably a good idea to separate the concept of opening/closing apps from the + // concept of setting visibility... + if (!visible && hiddenRequested) { + + if (!deferHidingClient && mDeferHidingClient) { + // We previously deferred telling the client to hide itself when visibility was + // initially set to false. Now we would like it to hide, so go ahead and set it. + mDeferHidingClient = deferHidingClient; + setClientHidden(true); + } + return; + } + + if (DEBUG_APP_TRANSITIONS || DEBUG_ORIENTATION) { + Slog.v(TAG_WM, "setAppVisibility(" + + appToken + ", visible=" + visible + "): " + appTransition + + " hidden=" + isHidden() + " hiddenRequested=" + + hiddenRequested + " Callers=" + Debug.getCallers(6)); + } + + final DisplayContent displayContent = getDisplayContent(); + displayContent.mOpeningApps.remove(this); + displayContent.mClosingApps.remove(this); + waitingToShow = false; + hiddenRequested = !visible; + mDeferHidingClient = deferHidingClient; + + if (!visible) { + // If the app is dead while it was visible, we kept its dead window on screen. + // Now that the app is going invisible, we can remove it. It will be restarted + // if made visible again. + removeDeadWindows(); + } else { + if (!appTransition.isTransitionSet() + && appTransition.isReady()) { + // Add the app mOpeningApps if transition is unset but ready. This means + // we're doing a screen freeze, and the unfreeze will wait for all opening + // apps to be ready. + displayContent.mOpeningApps.add(this); + } + startingMoved = false; + // If the token is currently hidden (should be the common case), or has been + // stopped, then we need to set up to wait for its windows to be ready. + if (isHidden() || mAppStopped) { + clearAllDrawn(); + + // If the app was already visible, don't reset the waitingToShow state. + if (isHidden()) { + waitingToShow = true; + } + } + + // In the case where we are making an app visible but holding off for a transition, + // we still need to tell the client to make its windows visible so they get drawn. + // Otherwise, we will wait on performing the transition until all windows have been + // drawn, they never will be, and we are sad. + setClientHidden(false); + + requestUpdateWallpaperIfNeeded(); + + if (DEBUG_ADD_REMOVE) Slog.v(TAG_WM, "No longer Stopped: " + this); + mAppStopped = false; + + transferStartingWindowFromHiddenAboveTokenIfNeeded(); + } + + // If we are preparing an app transition, then delay changing + // the visibility of this token until we execute that transition. + if (okToAnimate() && appTransition.isTransitionSet()) { + inPendingTransaction = true; + if (visible) { + displayContent.mOpeningApps.add(this); + mEnteringAnimation = true; + } else { + displayContent.mClosingApps.add(this); + mEnteringAnimation = false; + } + if (appTransition.getAppTransition() + == WindowManager.TRANSIT_TASK_OPEN_BEHIND) { + // We're launchingBehind, add the launching activity to mOpeningApps. + final WindowState win = getDisplayContent().findFocusedWindow(); + if (win != null) { + final AppWindowToken focusedToken = win.mAppToken; + if (focusedToken != null) { + if (DEBUG_APP_TRANSITIONS) { + Slog.d(TAG_WM, "TRANSIT_TASK_OPEN_BEHIND, " + + " adding " + focusedToken + " to mOpeningApps"); + } + // Force animation to be loaded. + focusedToken.setHidden(true); + displayContent.mOpeningApps.add(focusedToken); + } + } + } + return; + } + + commitVisibility(null, visible, TRANSIT_UNSET, true, mVoiceInteraction); + updateReportedVisibilityLocked(); + } + + boolean commitVisibility(WindowManager.LayoutParams lp, boolean visible, int transit, boolean performLayout, boolean isVoiceInteraction) { boolean delayed = false; @@ -461,8 +598,10 @@ class AppWindowToken extends WindowToken implements WindowManagerService.AppFree forAllWindows(mService::makeWindowFreezingScreenIfNeededLocked, true); } - if (DEBUG_APP_TRANSITIONS) Slog.v(TAG_WM, "setVisibility: " + this - + ": hidden=" + isHidden() + " hiddenRequested=" + hiddenRequested); + if (DEBUG_APP_TRANSITIONS) { + Slog.v(TAG_WM, "commitVisibility: " + this + + ": hidden=" + isHidden() + " hiddenRequested=" + hiddenRequested); + } if (changed) { getDisplayContent().getInputMonitor().setUpdateInputWindowsNeededLw(); @@ -499,10 +638,9 @@ class AppWindowToken extends WindowToken implements WindowManagerService.AppFree mService.mActivityManagerAppTransitionNotifier.onAppTransitionFinishedLocked(token); } - // If we're becoming visible, immediately change client visibility as well although it - // usually gets changed in AppWindowContainerController.setVisibility already. However, - // there seem to be some edge cases where we change our visibility but client visibility - // never gets updated. + // If we're becoming visible, immediately change client visibility as well. there seem + // to be some edge cases where we change our visibility but client visibility never gets + // updated. // If we're becoming invisible, update the client visibility if we are not running an // animation. Otherwise, we'll update client visibility in onAnimationFinished. if (visible || !isReallyAnimating()) { @@ -596,11 +734,6 @@ class AppWindowToken extends WindowToken implements WindowManagerService.AppFree return getWindowConfiguration().canReceiveKeys() || mAlwaysFocusable; } - AppWindowContainerController getController() { - final WindowContainerController controller = super.getController(); - return controller != null ? (AppWindowContainerController) controller : null; - } - @Override boolean isVisible() { // If the app token isn't hidden then it is considered visible and there is no need to check @@ -637,7 +770,7 @@ class AppWindowToken extends WindowToken implements WindowManagerService.AppFree if (DEBUG_APP_TRANSITIONS) Slog.v(TAG_WM, "Removing app token: " + this); - boolean delayed = setVisibility(null, false, TRANSIT_UNSET, true, mVoiceInteraction); + boolean delayed = commitVisibility(null, false, TRANSIT_UNSET, true, mVoiceInteraction); getDisplayContent().mOpeningApps.remove(this); getDisplayContent().mUnknownAppVisibilityController.appRemovedOrHidden(this); @@ -656,8 +789,8 @@ class AppWindowToken extends WindowToken implements WindowManagerService.AppFree if (DEBUG_ADD_REMOVE || DEBUG_TOKEN_MOVEMENT) Slog.v(TAG_WM, "removeAppToken: " + this + " delayed=" + delayed + " Callers=" + Debug.getCallers(4)); - if (startingData != null && getController() != null) { - getController().removeStartingWindow(); + if (startingData != null) { + removeStartingWindow(); } // If this window was animating, then we need to ensure that the app transition notifies @@ -768,9 +901,7 @@ class AppWindowToken extends WindowToken implements WindowManagerService.AppFree mAppStopped = true; destroySurfaces(); // Remove any starting window that was added for this app if they are still around. - if (getController() != null) { - getController().removeStartingWindow(); - } + removeStartingWindow(); } void clearAllDrawn() { @@ -826,9 +957,7 @@ class AppWindowToken extends WindowToken implements WindowManagerService.AppFree // TODO: Something smells about the code below...Is there a better way? if (startingWindow == win) { if (DEBUG_STARTING_WINDOW) Slog.v(TAG_WM, "Notify removed startingWindow " + win); - if (getController() != null) { - getController().removeStartingWindow(); - } + removeStartingWindow(); } else if (mChildren.size() == 0) { // If this is the last window and we had requested a starting transition window, // well there is no point now. @@ -845,9 +974,7 @@ class AppWindowToken extends WindowToken implements WindowManagerService.AppFree // we need to get rid of the starting transition. if (DEBUG_STARTING_WINDOW) Slog.v(TAG_WM, "Last window, removing starting window " + win); - if (getController() != null) { - getController().removeStartingWindow(); - } + removeStartingWindow(); } } @@ -1021,6 +1148,10 @@ class AppWindowToken extends WindowToken implements WindowManagerService.AppFree @Override void removeChild(WindowState child) { + if (!mChildren.contains(child)) { + // This can be true when testing. + return; + } super.removeChild(child); checkKeyguardFlagsChanged(); updateLetterboxSurface(child); @@ -1042,6 +1173,20 @@ class AppWindowToken extends WindowToken implements WindowManagerService.AppFree } } + void reparent(TaskWindowContainerController taskController, int position) { + if (DEBUG_ADD_REMOVE) { + Slog.i(TAG_WM, "reparent: moving app token=" + this + + " to task=" + taskController + " at " + position); + } + final Task task = taskController.mContainer; + if (task == null) { + throw new IllegalArgumentException("reparent: could not find task=" + + taskController); + } + reparent(task, position); + getDisplayContent().layoutAndAssignWindowLayersIfNeeded(); + } + void reparent(Task task, int position) { final Task currentTask = getTask(); if (task == currentTask) { @@ -1300,9 +1445,7 @@ class AppWindowToken extends WindowToken implements WindowManagerService.AppFree startingData = fromToken.startingData; fromToken.startingData = null; fromToken.startingMoved = true; - if (getController() != null) { - getController().scheduleAddStartingWindow(); - } + scheduleAddStartingWindow(); return true; } @@ -1471,6 +1614,10 @@ class AppWindowToken extends WindowToken implements WindowManagerService.AppFree } } + boolean keyDispatchingTimedOut(String reason, int windowPid) { + return mActivityRecord != null && mActivityRecord.keyDispatchingTimedOut(reason, windowPid); + } + /** * Updated this app token tracking states for interesting and drawn windows based on the window. * @@ -1533,8 +1680,8 @@ class AppWindowToken extends WindowToken implements WindowManagerService.AppFree } } } else if (w.isDrawnLw()) { - if (getController() != null) { - getController().reportStartingWindowDrawn(); + if (mActivityRecord != null) { + mActivityRecord.onStartingWindowDrawn(SystemClock.uptimeMillis()); } startingDisplayed = true; } @@ -1601,6 +1748,266 @@ class AppWindowToken extends WindowToken implements WindowManagerService.AppFree return this; } + boolean addStartingWindow(String pkg, int theme, CompatibilityInfo compatInfo, + CharSequence nonLocalizedLabel, int labelRes, int icon, int logo, int windowFlags, + IBinder transferFrom, boolean newTask, boolean taskSwitch, boolean processRunning, + boolean allowTaskSnapshot, boolean activityCreated, boolean fromRecents) { + // If the display is frozen, we won't do anything until the actual window is + // displayed so there is no reason to put in the starting window. + if (!okToDisplay()) { + return false; + } + + if (startingData != null) { + return false; + } + + final WindowState mainWin = findMainWindow(); + if (mainWin != null && mainWin.mWinAnimator.getShown()) { + // App already has a visible window...why would you want a starting window? + return false; + } + + final ActivityManager.TaskSnapshot snapshot = + mService.mTaskSnapshotController.getSnapshot( + getTask().mTaskId, getTask().mUserId, + false /* restoreFromDisk */, false /* reducedResolution */); + final int type = getStartingWindowType(newTask, taskSwitch, processRunning, + allowTaskSnapshot, activityCreated, fromRecents, snapshot); + + if (type == STARTING_WINDOW_TYPE_SNAPSHOT) { + return createSnapshot(snapshot); + } + + // If this is a translucent window, then don't show a starting window -- the current + // effect (a full-screen opaque starting window that fades away to the real contents + // when it is ready) does not work for this. + if (DEBUG_STARTING_WINDOW) { + Slog.v(TAG, "Checking theme of starting window: 0x" + Integer.toHexString(theme)); + } + if (theme != 0) { + AttributeCache.Entry ent = AttributeCache.instance().get(pkg, theme, + com.android.internal.R.styleable.Window, + mService.mCurrentUserId); + if (ent == null) { + // Whoops! App doesn't exist. Um. Okay. We'll just pretend like we didn't + // see that. + return false; + } + final boolean windowIsTranslucent = ent.array.getBoolean( + com.android.internal.R.styleable.Window_windowIsTranslucent, false); + final boolean windowIsFloating = ent.array.getBoolean( + com.android.internal.R.styleable.Window_windowIsFloating, false); + final boolean windowShowWallpaper = ent.array.getBoolean( + com.android.internal.R.styleable.Window_windowShowWallpaper, false); + final boolean windowDisableStarting = ent.array.getBoolean( + com.android.internal.R.styleable.Window_windowDisablePreview, false); + if (DEBUG_STARTING_WINDOW) { + Slog.v(TAG, "Translucent=" + windowIsTranslucent + + " Floating=" + windowIsFloating + + " ShowWallpaper=" + windowShowWallpaper); + } + if (windowIsTranslucent) { + return false; + } + if (windowIsFloating || windowDisableStarting) { + return false; + } + if (windowShowWallpaper) { + if (getDisplayContent().mWallpaperController + .getWallpaperTarget() == null) { + // If this theme is requesting a wallpaper, and the wallpaper + // is not currently visible, then this effectively serves as + // an opaque window and our starting window transition animation + // can still work. We just need to make sure the starting window + // is also showing the wallpaper. + windowFlags |= FLAG_SHOW_WALLPAPER; + } else { + return false; + } + } + + if (transferStartingWindow(transferFrom)) { + return true; + } + + // There is no existing starting window, and we don't want to create a splash screen, so + // that's it! + if (type != STARTING_WINDOW_TYPE_SPLASH_SCREEN) { + return false; + } + + if (DEBUG_STARTING_WINDOW) Slog.v(TAG_WM, "Creating SplashScreenStartingData"); + startingData = new SplashScreenStartingData(mService, pkg, + theme, compatInfo, nonLocalizedLabel, labelRes, icon, logo, windowFlags, + getMergedOverrideConfiguration()); + scheduleAddStartingWindow(); + } + return true; + } + + + private boolean createSnapshot(ActivityManager.TaskSnapshot snapshot) { + if (snapshot == null) { + return false; + } + + if (DEBUG_STARTING_WINDOW) Slog.v(TAG_WM, "Creating SnapshotStartingData"); + startingData = new SnapshotStartingData(mService, snapshot); + scheduleAddStartingWindow(); + return true; + } + + void scheduleAddStartingWindow() { + // Note: we really want to do sendMessageAtFrontOfQueue() because we + // want to process the message ASAP, before any other queued + // messages. + if (!mService.mAnimationHandler.hasCallbacks(mAddStartingWindow)) { + if (DEBUG_STARTING_WINDOW) Slog.v(TAG, "Enqueueing ADD_STARTING"); + mService.mAnimationHandler.postAtFrontOfQueue(mAddStartingWindow); + } + } + + private final Runnable mAddStartingWindow = new Runnable() { + + @Override + public void run() { + synchronized (mService.mGlobalLock) { + // There can only be one adding request, silly caller! + mService.mAnimationHandler.removeCallbacks(this); + } + + if (startingData == null) { + // Animation has been canceled... do nothing. + if (DEBUG_STARTING_WINDOW) { + Slog.v(TAG, "startingData was nulled out before handling" + + " mAddStartingWindow: " + AppWindowToken.this); + } + return; + } + + if (DEBUG_STARTING_WINDOW) { + Slog.v(TAG, "Add starting " + this + ": startingData=" + startingData); + } + + WindowManagerPolicy.StartingSurface surface = null; + try { + surface = startingData.createStartingSurface(AppWindowToken.this); + } catch (Exception e) { + Slog.w(TAG, "Exception when adding starting window", e); + } + if (surface != null) { + boolean abort = false; + synchronized (mService.mGlobalLock) { + // If the window was successfully added, then + // we need to remove it. + if (removed || startingData == null) { + if (DEBUG_STARTING_WINDOW) { + Slog.v(TAG, "Aborted starting " + AppWindowToken.this + + ": removed=" + removed + " startingData=" + startingData); + } + startingWindow = null; + startingData = null; + abort = true; + } else { + startingSurface = surface; + } + if (DEBUG_STARTING_WINDOW && !abort) { + Slog.v(TAG, "Added starting " + AppWindowToken.this + ": startingWindow=" + + startingWindow + " startingView=" + startingSurface); + } + } + if (abort) { + surface.remove(); + } + } else if (DEBUG_STARTING_WINDOW) { + Slog.v(TAG, "Surface returned was null: " + AppWindowToken.this); + } + } + }; + + private int getStartingWindowType(boolean newTask, boolean taskSwitch, boolean processRunning, + boolean allowTaskSnapshot, boolean activityCreated, boolean fromRecents, + ActivityManager.TaskSnapshot snapshot) { + if (getDisplayContent().mAppTransition.getAppTransition() + == TRANSIT_DOCK_TASK_FROM_RECENTS) { + // TODO(b/34099271): Remove this statement to add back the starting window and figure + // out why it causes flickering, the starting window appears over the thumbnail while + // the docked from recents transition occurs + return STARTING_WINDOW_TYPE_NONE; + } else if (newTask || !processRunning || (taskSwitch && !activityCreated)) { + return STARTING_WINDOW_TYPE_SPLASH_SCREEN; + } else if (taskSwitch && allowTaskSnapshot) { + return snapshot == null ? STARTING_WINDOW_TYPE_NONE + : snapshotOrientationSameAsTask(snapshot) || fromRecents + ? STARTING_WINDOW_TYPE_SNAPSHOT : STARTING_WINDOW_TYPE_SPLASH_SCREEN; + } else { + return STARTING_WINDOW_TYPE_NONE; + } + } + + + private boolean snapshotOrientationSameAsTask(ActivityManager.TaskSnapshot snapshot) { + if (snapshot == null) { + return false; + } + return getTask().getConfiguration().orientation == snapshot.getOrientation(); + } + + void removeStartingWindow() { + if (startingWindow == null) { + if (startingData != null) { + // Starting window has not been added yet, but it is scheduled to be added. + // Go ahead and cancel the request. + if (DEBUG_STARTING_WINDOW) { + Slog.v(TAG_WM, "Clearing startingData for token=" + this); + } + startingData = null; + } + return; + } + + final WindowManagerPolicy.StartingSurface surface; + if (startingData != null) { + surface = startingSurface; + startingData = null; + startingSurface = null; + startingWindow = null; + startingDisplayed = false; + if (surface == null) { + if (DEBUG_STARTING_WINDOW) { + Slog.v(TAG_WM, "startingWindow was set but startingSurface==null, couldn't " + + "remove"); + } + return; + } + } else { + if (DEBUG_STARTING_WINDOW) { + Slog.v(TAG_WM, "Tried to remove starting window but startingWindow was null:" + + this); + } + return; + } + + if (DEBUG_STARTING_WINDOW) { + Slog.v(TAG_WM, "Schedule remove starting " + this + + " startingWindow=" + startingWindow + + " startingView=" + startingSurface + + " Callers=" + Debug.getCallers(5)); + } + + // Use the same thread to remove the window as we used to add it, as otherwise we end up + // with things in the view hierarchy being called from different threads. + mService.mAnimationHandler.post(() -> { + if (DEBUG_STARTING_WINDOW) Slog.v(TAG_WM, "Removing startingView=" + surface); + try { + surface.remove(); + } catch (Exception e) { + Slog.w(TAG_WM, "Exception when removing starting window", e); + } + }); + } + @Override boolean fillsParent() { return mFillsParent; @@ -2215,9 +2622,6 @@ class AppWindowToken extends WindowToken implements WindowManagerService.AppFree if (mPendingRelaunchCount != 0) { pw.print(prefix); pw.print("mPendingRelaunchCount="); pw.println(mPendingRelaunchCount); } - if (getController() != null) { - pw.print(prefix); pw.print("controller="); pw.println(getController()); - } if (mRemovingFromDisplay) { pw.println(prefix + "mRemovingFromDisplay=" + mRemovingFromDisplay); } diff --git a/services/core/java/com/android/server/wm/DragState.java b/services/core/java/com/android/server/wm/DragState.java index 9832df6ff3a3..7279fe018055 100644 --- a/services/core/java/com/android/server/wm/DragState.java +++ b/services/core/java/com/android/server/wm/DragState.java @@ -169,7 +169,7 @@ class DragState { t.setLayer(mInputSurface, Integer.MAX_VALUE); mTmpClipRect.set(0, 0, mDisplaySize.x, mDisplaySize.y); - t.setWindowCrop(mSurfaceControl, mTmpClipRect); + t.setWindowCrop(mInputSurface, mTmpClipRect); } /** diff --git a/services/core/java/com/android/server/wm/InputManagerCallback.java b/services/core/java/com/android/server/wm/InputManagerCallback.java index 94355395b38e..639ed02a1e48 100644 --- a/services/core/java/com/android/server/wm/InputManagerCallback.java +++ b/services/core/java/com/android/server/wm/InputManagerCallback.java @@ -6,21 +6,16 @@ import static android.view.WindowManager.LayoutParams.TYPE_APPLICATION_OVERLAY; import static com.android.server.wm.WindowManagerDebugConfig.DEBUG_INPUT; import static com.android.server.wm.WindowManagerDebugConfig.TAG_WM; -import android.app.ActivityManager; import android.os.Debug; import android.os.IBinder; -import android.os.RemoteException; import android.util.Slog; +import android.view.InputApplicationHandle; import android.view.KeyEvent; import android.view.WindowManager; -import android.view.InputApplicationHandle; import com.android.server.input.InputManagerService; -import android.view.InputWindowHandle; -import android.view.InputChannel; import java.io.PrintWriter; -import java.util.HashMap; final class InputManagerCallback implements InputManagerService.WindowManagerCallbacks { private final WindowManagerService mService; @@ -112,9 +107,7 @@ final class InputManagerCallback implements InputManagerService.WindowManagerCal if (appWindowToken != null && appWindowToken.appToken != null) { // Notify the activity manager about the timeout and let it decide whether // to abort dispatching or keep waiting. - final AppWindowContainerController controller = appWindowToken.getController(); - final boolean abort = controller != null - && controller.keyDispatchingTimedOut(reason, + final boolean abort = appWindowToken.keyDispatchingTimedOut(reason, (windowState != null) ? windowState.mSession.mPid : -1); if (!abort) { // The activity manager declined to abort dispatching. diff --git a/services/core/java/com/android/server/wm/InputMonitor.java b/services/core/java/com/android/server/wm/InputMonitor.java index 55cbae15f9d8..88b22cb5e01e 100644 --- a/services/core/java/com/android/server/wm/InputMonitor.java +++ b/services/core/java/com/android/server/wm/InputMonitor.java @@ -435,10 +435,10 @@ final class InputMonitor { if (mAddPipInputConsumerHandle) { // Update the bounds of the Pip input consumer to match the window bounds. w.getBounds(mTmpRect); - // The touchable region is relative to the surface top-left - mTmpRect.top = mTmpRect.left = 0; - pipInputConsumer.layout(mInputTransaction, mTmpRect); + + // The touchable region is relative to the surface top-left + mTmpRect.offsetTo(0, 0); pipInputConsumer.mWindowHandle.touchableRegion.set(mTmpRect); pipInputConsumer.show(mInputTransaction, w); mAddPipInputConsumerHandle = false; diff --git a/services/core/java/com/android/server/wm/KeyguardController.java b/services/core/java/com/android/server/wm/KeyguardController.java index c91af73dc6dd..4ef351390c16 100644 --- a/services/core/java/com/android/server/wm/KeyguardController.java +++ b/services/core/java/com/android/server/wm/KeyguardController.java @@ -72,6 +72,7 @@ class KeyguardController { private int mVisibilityTransactionDepth; private final SparseArray<KeyguardDisplayState> mDisplayStates = new SparseArray<>(); private final ActivityTaskManagerService mService; + private RootActivityContainer mRootActivityContainer; KeyguardController(ActivityTaskManagerService service, ActivityStackSupervisor stackSupervisor) { @@ -81,6 +82,7 @@ class KeyguardController { void setWindowManager(WindowManagerService windowManager) { mWindowManager = windowManager; + mRootActivityContainer = mService.mRootActivityContainer; } /** @@ -146,7 +148,7 @@ class KeyguardController { mDismissalRequested = false; } } - mStackSupervisor.ensureActivitiesVisibleLocked(null, 0, !PRESERVE_WINDOWS); + mRootActivityContainer.ensureActivitiesVisible(null, 0, !PRESERVE_WINDOWS); updateKeyguardSleepToken(); } @@ -172,16 +174,17 @@ class KeyguardController { mWindowManager.deferSurfaceLayout(); try { setKeyguardGoingAway(true); - mStackSupervisor.getDefaultDisplay().getWindowContainerController() + mRootActivityContainer.getDefaultDisplay().getWindowContainerController() .prepareAppTransition(TRANSIT_KEYGUARD_GOING_AWAY, false /* alwaysKeepCurrent */, convertTransitFlags(flags), false /* forceOverride */); updateKeyguardSleepToken(); // Some stack visibility might change (e.g. docked stack) - mStackSupervisor.resumeFocusedStacksTopActivitiesLocked(); - mStackSupervisor.ensureActivitiesVisibleLocked(null, 0, !PRESERVE_WINDOWS); - mStackSupervisor.addStartingWindowsForVisibleActivities(true /* taskSwitch */); + mRootActivityContainer.resumeFocusedStacksTopActivities(); + mRootActivityContainer.ensureActivitiesVisible(null, 0, !PRESERVE_WINDOWS); + mRootActivityContainer.addStartingWindowsForVisibleActivities( + true /* taskSwitch */); mWindowManager.executeAppTransition(); } finally { Trace.traceBegin(TRACE_TAG_ACTIVITY_MANAGER, "keyguardGoingAway: surfaceLayout"); @@ -277,8 +280,9 @@ class KeyguardController { private void visibilitiesUpdated() { boolean requestDismissKeyguard = false; - for (int displayNdx = mStackSupervisor.getChildCount() - 1; displayNdx >= 0; displayNdx--) { - final ActivityDisplay display = mStackSupervisor.getChildAt(displayNdx); + for (int displayNdx = mRootActivityContainer.getChildCount() - 1; + displayNdx >= 0; displayNdx--) { + final ActivityDisplay display = mRootActivityContainer.getChildAt(displayNdx); final KeyguardDisplayState state = getDisplay(display.mDisplayId); state.visibilitiesUpdated(this, display); requestDismissKeyguard |= state.mRequestDismissKeyguard; @@ -298,12 +302,12 @@ class KeyguardController { if (isKeyguardLocked()) { mWindowManager.deferSurfaceLayout(); try { - mStackSupervisor.getDefaultDisplay().getWindowContainerController() + mRootActivityContainer.getDefaultDisplay().getWindowContainerController() .prepareAppTransition(resolveOccludeTransit(), false /* alwaysKeepCurrent */, 0 /* flags */, true /* forceOverride */); updateKeyguardSleepToken(); - mStackSupervisor.ensureActivitiesVisibleLocked(null, 0, !PRESERVE_WINDOWS); + mRootActivityContainer.ensureActivitiesVisible(null, 0, !PRESERVE_WINDOWS); mWindowManager.executeAppTransition(); } finally { mWindowManager.continueSurfaceLayout(); @@ -319,21 +323,23 @@ class KeyguardController { // We only allow dismissing Keyguard via the flag when Keyguard is secure for legacy // reasons, because that's how apps used to dismiss Keyguard in the secure case. In the // insecure case, we actually show it on top of the lockscreen. See #canShowWhileOccluded. - if (mWindowManager.isKeyguardSecure()) { - mWindowManager.dismissKeyguard(null /* callback */, null /* message */); - mDismissalRequested = true; - - // If we are about to unocclude the Keyguard, but we can dismiss it without security, - // we immediately dismiss the Keyguard so the activity gets shown without a flicker. - final DisplayWindowController dwc = - mStackSupervisor.getDefaultDisplay().getWindowContainerController(); - if (mKeyguardShowing && canDismissKeyguard() - && dwc.getPendingAppTransition() == TRANSIT_KEYGUARD_UNOCCLUDE) { - dwc.prepareAppTransition(mBeforeUnoccludeTransit, false /* alwaysKeepCurrent */, - 0 /* flags */, true /* forceOverride */); - mStackSupervisor.ensureActivitiesVisibleLocked(null, 0, !PRESERVE_WINDOWS); - mWindowManager.executeAppTransition(); - } + if (!mWindowManager.isKeyguardSecure()) { + return; + } + + mWindowManager.dismissKeyguard(null /* callback */, null /* message */); + mDismissalRequested = true; + + // If we are about to unocclude the Keyguard, but we can dismiss it without security, + // we immediately dismiss the Keyguard so the activity gets shown without a flicker. + final DisplayWindowController dwc = + mRootActivityContainer.getDefaultDisplay().getWindowContainerController(); + if (mKeyguardShowing && canDismissKeyguard() + && dwc.getPendingAppTransition() == TRANSIT_KEYGUARD_UNOCCLUDE) { + dwc.prepareAppTransition(mBeforeUnoccludeTransit, false /* alwaysKeepCurrent */, + 0 /* flags */, true /* forceOverride */); + mRootActivityContainer.ensureActivitiesVisible(null, 0, !PRESERVE_WINDOWS); + mWindowManager.executeAppTransition(); } } @@ -350,7 +356,7 @@ class KeyguardController { private int resolveOccludeTransit() { final DisplayWindowController dwc = - mStackSupervisor.getDefaultDisplay().getWindowContainerController(); + mRootActivityContainer.getDefaultDisplay().getWindowContainerController(); if (mBeforeUnoccludeTransit != TRANSIT_UNSET && dwc.getPendingAppTransition() == TRANSIT_KEYGUARD_UNOCCLUDE // TODO(b/113840485): Handle app transition for individual display. @@ -377,7 +383,8 @@ class KeyguardController { // show on top of the lock screen. In this can we want to dismiss the docked // 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 ActivityStack stack = mStackSupervisor.getDefaultDisplay().getSplitScreenPrimaryStack(); + final ActivityStack stack = + mRootActivityContainer.getDefaultDisplay().getSplitScreenPrimaryStack(); if (stack == null) { return; } @@ -387,8 +394,9 @@ class KeyguardController { } private void updateKeyguardSleepToken() { - for (int displayNdx = mStackSupervisor.getChildCount() - 1; displayNdx >= 0; displayNdx--) { - final ActivityDisplay display = mStackSupervisor.getChildAt(displayNdx); + for (int displayNdx = mRootActivityContainer.getChildCount() - 1; + displayNdx >= 0; displayNdx--) { + final ActivityDisplay display = mRootActivityContainer.getChildAt(displayNdx); final KeyguardDisplayState state = getDisplay(display.mDisplayId); if (isKeyguardOrAodShowing(display.mDisplayId) && state.mSleepToken == null) { state.acquiredSleepToken(); diff --git a/services/core/java/com/android/server/wm/LaunchParamsPersister.java b/services/core/java/com/android/server/wm/LaunchParamsPersister.java index 72d51439d9f7..da9a5071b100 100644 --- a/services/core/java/com/android/server/wm/LaunchParamsPersister.java +++ b/services/core/java/com/android/server/wm/LaunchParamsPersister.java @@ -223,7 +223,8 @@ class LaunchParamsPersister { private boolean saveTaskToLaunchParam(TaskRecord task, PersistableLaunchParams params) { final ActivityStack<?> stack = task.getStack(); final int displayId = stack.mDisplayId; - final ActivityDisplay display = mSupervisor.getActivityDisplay(displayId); + final ActivityDisplay display = + mSupervisor.mRootActivityContainer.getActivityDisplay(displayId); final DisplayInfo info = new DisplayInfo(); display.mDisplay.getDisplayInfo(info); @@ -259,7 +260,7 @@ class LaunchParamsPersister { return; } - final ActivityDisplay display = mSupervisor.getActivityDisplay( + final ActivityDisplay display = mSupervisor.mRootActivityContainer.getActivityDisplay( persistableParams.mDisplayUniqueId); if (display != null) { outParams.mPreferredDisplayId = display.mDisplayId; diff --git a/services/core/java/com/android/server/wm/LockTaskController.java b/services/core/java/com/android/server/wm/LockTaskController.java index 41d0777d1c78..80dc2458d7f5 100644 --- a/services/core/java/com/android/server/wm/LockTaskController.java +++ b/services/core/java/com/android/server/wm/LockTaskController.java @@ -447,7 +447,7 @@ public class LockTaskController { return; } task.performClearTaskLocked(); - mSupervisor.resumeFocusedStacksTopActivitiesLocked(); + mSupervisor.mRootActivityContainer.resumeFocusedStacksTopActivities(); } /** @@ -579,7 +579,7 @@ public class LockTaskController { if (andResume) { mSupervisor.findTaskToMoveToFront(task, 0, null, reason, lockTaskModeState != LOCK_TASK_MODE_NONE); - mSupervisor.resumeFocusedStacksTopActivitiesLocked(); + mSupervisor.mRootActivityContainer.resumeFocusedStacksTopActivities(); final ActivityStack stack = task.getStack(); if (stack != null) { stack.getDisplay().getWindowContainerController().executeAppTransition(); @@ -641,11 +641,12 @@ public class LockTaskController { taskChanged = true; } - for (int displayNdx = mSupervisor.getChildCount() - 1; displayNdx >= 0; --displayNdx) { - mSupervisor.getChildAt(displayNdx).onLockTaskPackagesUpdated(); + for (int displayNdx = mSupervisor.mRootActivityContainer.getChildCount() - 1; + displayNdx >= 0; --displayNdx) { + mSupervisor.mRootActivityContainer.getChildAt(displayNdx).onLockTaskPackagesUpdated(); } - final ActivityRecord r = mSupervisor.topRunningActivityLocked(); + final ActivityRecord r = mSupervisor.mRootActivityContainer.topRunningActivity(); final TaskRecord task = (r != null) ? r.getTask() : null; if (mLockTaskModeTasks.isEmpty() && task!= null && task.mLockTaskAuth == LOCK_TASK_AUTH_LAUNCHABLE) { @@ -657,7 +658,7 @@ public class LockTaskController { } if (taskChanged) { - mSupervisor.resumeFocusedStacksTopActivitiesLocked(); + mSupervisor.mRootActivityContainer.resumeFocusedStacksTopActivities(); } } diff --git a/services/core/java/com/android/server/wm/PinnedActivityStack.java b/services/core/java/com/android/server/wm/PinnedActivityStack.java index 3ef42e7be8d7..1c7ebd63dfb3 100644 --- a/services/core/java/com/android/server/wm/PinnedActivityStack.java +++ b/services/core/java/com/android/server/wm/PinnedActivityStack.java @@ -41,7 +41,7 @@ class PinnedActivityStack extends ActivityStack<PinnedStackWindowController> PinnedStackWindowController createStackWindowController(int displayId, boolean onTop, Rect outBounds) { return new PinnedStackWindowController(mStackId, this, displayId, onTop, outBounds, - mStackSupervisor.mWindowManager); + mRootActivityContainer.mWindowManager); } Rect getDefaultPictureInPictureBounds(float aspectRatio) { diff --git a/services/core/java/com/android/server/wm/RecentsAnimation.java b/services/core/java/com/android/server/wm/RecentsAnimation.java index 476c1f972fa9..24c5228ce0ec 100644 --- a/services/core/java/com/android/server/wm/RecentsAnimation.java +++ b/services/core/java/com/android/server/wm/RecentsAnimation.java @@ -79,7 +79,7 @@ class RecentsAnimation implements RecentsAnimationCallbacks, int callingPid) { mService = atm; mStackSupervisor = stackSupervisor; - mDefaultDisplay = stackSupervisor.getDefaultDisplay(); + mDefaultDisplay = mService.mRootActivityContainer.getDefaultDisplay(); mActivityStartController = activityStartController; mWindowManager = wm; mCallingPid = callingPid; @@ -94,7 +94,7 @@ class RecentsAnimation implements RecentsAnimationCallbacks, // TODO(multi-display) currently only support recents animation in default display. final DisplayWindowController dwc = - mStackSupervisor.getDefaultDisplay().getWindowContainerController(); + mService.mRootActivityContainer.getDefaultDisplay().getWindowContainerController(); if (!mWindowManager.canStartRecentsAnimation()) { notifyAnimationCancelBeforeStart(recentsAnimationRunner); if (DEBUG) Slog.d(TAG, "Can't start recents animation, nextAppTransition=" @@ -124,8 +124,8 @@ class RecentsAnimation implements RecentsAnimationCallbacks, // Send launch hint if we are actually launching the target. If it's already visible // (shouldn't happen in general) we don't need to send it. if (targetActivity == null || !targetActivity.visible) { - mStackSupervisor.sendPowerHintForLaunchStartIfNeeded(true /* forceSend */, - targetActivity); + mService.mRootActivityContainer.sendPowerHintForLaunchStartIfNeeded( + true /* forceSend */, targetActivity); } mStackSupervisor.getActivityMetricsLogger().notifyActivityLaunching(intent); @@ -192,7 +192,7 @@ class RecentsAnimation implements RecentsAnimationCallbacks, // If we updated the launch-behind state, update the visibility of the activities after // we fetch the visible tasks to be controlled by the animation - mStackSupervisor.ensureActivitiesVisibleLocked(null, 0, PRESERVE_WINDOWS); + mService.mRootActivityContainer.ensureActivitiesVisible(null, 0, PRESERVE_WINDOWS); mStackSupervisor.getActivityMetricsLogger().notifyActivityLaunched(START_TASK_TO_FRONT, targetActivity); @@ -215,7 +215,8 @@ class RecentsAnimation implements RecentsAnimationCallbacks, @Deprecated IAssistDataReceiver assistDataReceiver, int userId) { final AppOpsManager appOpsManager = (AppOpsManager) mService.mContext.getSystemService(Context.APP_OPS_SERVICE); - final List<IBinder> topActivities = mStackSupervisor.getTopVisibleActivities(); + final List<IBinder> topActivities = + mService.mRootActivityContainer.getTopVisibleActivities(); final AssistDataRequester.AssistDataRequesterCallbacks assistDataCallbacks; if (assistDataReceiver != null) { assistDataCallbacks = new AssistDataReceiverProxy(assistDataReceiver, @@ -283,7 +284,7 @@ class RecentsAnimation implements RecentsAnimationCallbacks, // Just to be sure end the launch hint in case the target activity was never launched. // However, if we're keeping the activity and making it visible, we can leave it on. if (reorderMode != REORDER_KEEP_IN_PLACE) { - mStackSupervisor.sendPowerHintForLaunchEndIfNeeded(); + mService.mRootActivityContainer.sendPowerHintForLaunchEndIfNeeded(); } mService.mH.post( @@ -343,8 +344,8 @@ class RecentsAnimation implements RecentsAnimationCallbacks, } mWindowManager.prepareAppTransition(TRANSIT_NONE, false); - mStackSupervisor.ensureActivitiesVisibleLocked(null, 0, false); - mStackSupervisor.resumeFocusedStacksTopActivitiesLocked(); + mService.mRootActivityContainer.ensureActivitiesVisible(null, 0, false); + mService.mRootActivityContainer.resumeFocusedStacksTopActivities(); // No reason to wait for the pausing activity in this case, as the hiding of // surfaces needs to be done immediately. diff --git a/services/core/java/com/android/server/wm/RootActivityContainer.java b/services/core/java/com/android/server/wm/RootActivityContainer.java new file mode 100644 index 000000000000..4dd48c46b891 --- /dev/null +++ b/services/core/java/com/android/server/wm/RootActivityContainer.java @@ -0,0 +1,2297 @@ +/* + * Copyright (C) 2018 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.ActivityTaskManager.INVALID_STACK_ID; +import static android.app.ActivityTaskManager.INVALID_TASK_ID; +import static android.app.WindowConfiguration.ACTIVITY_TYPE_ASSISTANT; +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.WINDOWING_MODE_FULLSCREEN; +import static android.app.WindowConfiguration.WINDOWING_MODE_FULLSCREEN_OR_SPLIT_SCREEN_SECONDARY; +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.activityTypeToString; +import static android.app.WindowConfiguration.windowingModeToString; +import static android.content.Intent.FLAG_ACTIVITY_NEW_TASK; +import static android.content.pm.ActivityInfo.LAUNCH_SINGLE_INSTANCE; +import static android.content.pm.ActivityInfo.LAUNCH_SINGLE_TASK; +import static android.os.Trace.TRACE_TAG_ACTIVITY_MANAGER; +import static android.view.Display.DEFAULT_DISPLAY; +import static android.view.Display.INVALID_DISPLAY; + +import static com.android.server.am.ActivityStackSupervisorProto.CONFIGURATION_CONTAINER; +import static com.android.server.am.ActivityStackSupervisorProto.DISPLAYS; +import static com.android.server.am.ActivityStackSupervisorProto.FOCUSED_STACK_ID; +import static com.android.server.am.ActivityStackSupervisorProto.IS_HOME_RECENTS_COMPONENT; +import static com.android.server.am.ActivityStackSupervisorProto.KEYGUARD_CONTROLLER; +import static com.android.server.am.ActivityStackSupervisorProto.PENDING_ACTIVITIES; +import static com.android.server.am.ActivityStackSupervisorProto.RESUMED_ACTIVITY; +import static com.android.server.wm.ActivityStack.ActivityState.PAUSED; +import static com.android.server.wm.ActivityStack.ActivityState.RESUMED; +import static com.android.server.wm.ActivityStack.ActivityState.STOPPED; +import static com.android.server.wm.ActivityStack.ActivityState.STOPPING; +import static com.android.server.wm.ActivityStackSupervisor.DEFER_RESUME; +import static com.android.server.wm.ActivityStackSupervisor.ON_TOP; +import static com.android.server.wm.ActivityStackSupervisor.PRESERVE_WINDOWS; +import static com.android.server.wm.ActivityStackSupervisor.dumpHistoryList; +import static com.android.server.wm.ActivityStackSupervisor.printThisActivity; +import static com.android.server.wm.ActivityTaskManagerDebugConfig.DEBUG_RECENTS; +import static com.android.server.wm.ActivityTaskManagerDebugConfig.DEBUG_RELEASE; +import static com.android.server.wm.ActivityTaskManagerDebugConfig.DEBUG_STACK; +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_RECENTS; +import static com.android.server.wm.ActivityTaskManagerDebugConfig.POSTFIX_RELEASE; +import static com.android.server.wm.ActivityTaskManagerDebugConfig.POSTFIX_STATES; +import static com.android.server.wm.ActivityTaskManagerDebugConfig.POSTFIX_TASKS; +import static com.android.server.wm.ActivityTaskManagerDebugConfig.TAG_ATM; +import static com.android.server.wm.ActivityTaskManagerDebugConfig.TAG_WITH_CLASS_NAME; +import static com.android.server.wm.ActivityTaskManagerService.ANIMATE; +import static com.android.server.wm.TaskRecord.REPARENT_LEAVE_STACK_IN_PLACE; +import static com.android.server.wm.TaskRecord.REPARENT_MOVE_STACK_TO_FRONT; + +import static java.lang.Integer.MAX_VALUE; + +import android.annotation.IntDef; +import android.annotation.NonNull; +import android.annotation.Nullable; +import android.annotation.UserIdInt; +import android.app.ActivityManager; +import android.app.ActivityOptions; +import android.app.AppGlobals; +import android.app.WindowConfiguration; +import android.content.ComponentName; +import android.content.Intent; +import android.content.pm.ActivityInfo; +import android.content.pm.ApplicationInfo; +import android.content.pm.ResolveInfo; +import android.content.res.Configuration; +import android.content.res.Resources; +import android.graphics.Rect; +import android.hardware.display.DisplayManager; +import android.hardware.display.DisplayManagerInternal; +import android.hardware.power.V1_0.PowerHint; +import android.os.Build; +import android.os.FactoryTest; +import android.os.IBinder; +import android.os.RemoteException; +import android.os.SystemClock; +import android.os.Trace; +import android.os.UserHandle; +import android.service.voice.IVoiceInteractionSession; +import android.util.ArraySet; +import android.util.DisplayMetrics; +import android.util.IntArray; +import android.util.Slog; +import android.util.SparseArray; +import android.util.SparseIntArray; +import android.util.TimeUtils; +import android.util.proto.ProtoOutputStream; +import android.view.Display; +import android.view.DisplayInfo; + +import com.android.internal.annotations.VisibleForTesting; +import com.android.server.LocalServices; +import com.android.server.am.ActivityManagerService; +import com.android.server.am.AppTimeTracker; +import com.android.server.am.UserState; + +import java.io.FileDescriptor; +import java.io.PrintWriter; +import java.lang.annotation.Retention; +import java.lang.annotation.RetentionPolicy; +import java.util.ArrayList; +import java.util.Iterator; +import java.util.List; +import java.util.Set; + +/** + * Root node for activity containers. + * TODO: This class is mostly temporary to separate things out of ActivityStackSupervisor.java. The + * intention is to have this merged with RootWindowContainer.java as part of unifying the hierarchy. + */ +class RootActivityContainer extends ConfigurationContainer + implements DisplayManager.DisplayListener { + + private static final String TAG = TAG_WITH_CLASS_NAME ? "RootActivityContainer" : TAG_ATM; + static final String TAG_TASKS = TAG + POSTFIX_TASKS; + private static final String TAG_RELEASE = TAG + POSTFIX_RELEASE; + static final String TAG_STATES = TAG + POSTFIX_STATES; + private static final String TAG_RECENTS = TAG + POSTFIX_RECENTS; + + /** + * The modes which affect which tasks are returned when calling + * {@link RootActivityContainer#anyTaskForId(int)}. + */ + @Retention(RetentionPolicy.SOURCE) + @IntDef({ + MATCH_TASK_IN_STACKS_ONLY, + MATCH_TASK_IN_STACKS_OR_RECENT_TASKS, + MATCH_TASK_IN_STACKS_OR_RECENT_TASKS_AND_RESTORE + }) + public @interface AnyTaskForIdMatchTaskMode {} + // Match only tasks in the current stacks + static final int MATCH_TASK_IN_STACKS_ONLY = 0; + // Match either tasks in the current stacks, or in the recent tasks if not found in the stacks + static final int MATCH_TASK_IN_STACKS_OR_RECENT_TASKS = 1; + // Match either tasks in the current stacks, or in the recent tasks, restoring it to the + // provided stack id + static final int MATCH_TASK_IN_STACKS_OR_RECENT_TASKS_AND_RESTORE = 2; + + ActivityTaskManagerService mService; + ActivityStackSupervisor mStackSupervisor; + WindowManagerService mWindowManager; + DisplayManager mDisplayManager; + private DisplayManagerInternal mDisplayManagerInternal; + // TODO: Remove after object merge with RootWindowContainer. + private RootWindowContainer mRootWindowContainer; + + /** + * List of displays which contain activities, sorted by z-order. + * The last entry in the list is the topmost. + */ + private final ArrayList<ActivityDisplay> mActivityDisplays = new ArrayList<>(); + + /** Reference to default display so we can quickly look it up. */ + private ActivityDisplay mDefaultDisplay; + private final SparseArray<IntArray> mDisplayAccessUIDs = new SparseArray<>(); + + /** The current user */ + int mCurrentUser; + /** Stack id of the front stack when user switched, indexed by userId. */ + SparseIntArray mUserStackInFront = new SparseIntArray(2); + + /** + * A list of tokens that cause the top activity to be put to sleep. + * They are used by components that may hide and block interaction with underlying + * activities. + */ + final ArrayList<ActivityTaskManagerInternal.SleepToken> mSleepTokens = new ArrayList<>(); + + /** Is dock currently minimized. */ + boolean mIsDockMinimized; + + /** Set when a power hint has started, but not ended. */ + private boolean mPowerHintSent; + + // The default minimal size that will be used if the activity doesn't specify its minimal size. + // It will be calculated when the default display gets added. + int mDefaultMinSizeOfResizeableTaskDp = -1; + + // Whether tasks have moved and we need to rank the tasks before next OOM scoring + private boolean mTaskLayersChanged = true; + + private final ArrayList<ActivityRecord> mTmpActivityList = new ArrayList<>(); + + private final FindTaskResult mTmpFindTaskResult = new FindTaskResult(); + static class FindTaskResult { + ActivityRecord mRecord; + boolean mIdealMatch; + + void clear() { + mRecord = null; + mIdealMatch = false; + } + + void setTo(FindTaskResult result) { + mRecord = result.mRecord; + mIdealMatch = result.mIdealMatch; + } + } + + RootActivityContainer(ActivityTaskManagerService service) { + mService = service; + mStackSupervisor = service.mStackSupervisor; + mStackSupervisor.mRootActivityContainer = this; + } + + @VisibleForTesting + void setWindowContainer(RootWindowContainer container) { + mRootWindowContainer = container; + mRootWindowContainer.setRootActivityContainer(this); + } + + void setWindowManager(WindowManagerService wm) { + mWindowManager = wm; + setWindowContainer(mWindowManager.mRoot); + mDisplayManager = mService.mContext.getSystemService(DisplayManager.class); + mDisplayManager.registerDisplayListener(this, mService.mH); + mDisplayManagerInternal = LocalServices.getService(DisplayManagerInternal.class); + + final Display[] displays = mDisplayManager.getDisplays(); + for (int displayNdx = 0; displayNdx < displays.length; ++displayNdx) { + final Display display = displays[displayNdx]; + final ActivityDisplay activityDisplay = new ActivityDisplay(this, display); + if (activityDisplay.mDisplayId == DEFAULT_DISPLAY) { + mDefaultDisplay = activityDisplay; + } + addChild(activityDisplay, ActivityDisplay.POSITION_TOP); + } + calculateDefaultMinimalSizeOfResizeableTasks(); + + final ActivityDisplay defaultDisplay = getDefaultDisplay(); + + defaultDisplay.getOrCreateStack(WINDOWING_MODE_FULLSCREEN, ACTIVITY_TYPE_HOME, ON_TOP); + positionChildAt(defaultDisplay, ActivityDisplay.POSITION_TOP); + } + + // TODO(multi-display): Look at all callpoints to make sure they make sense in multi-display. + ActivityDisplay getDefaultDisplay() { + return mDefaultDisplay; + } + + /** + * Get an existing instance of {@link ActivityDisplay} that has the given uniqueId. Unique ID is + * defined in {@link DisplayInfo#uniqueId}. + * + * @param uniqueId the unique ID of the display + * @return the {@link ActivityDisplay} or {@code null} if nothing is found. + */ + ActivityDisplay getActivityDisplay(String uniqueId) { + for (int i = mActivityDisplays.size() - 1; i >= 0; --i) { + final ActivityDisplay display = mActivityDisplays.get(i); + final boolean isValid = display.mDisplay.isValid(); + if (isValid && display.mDisplay.getUniqueId().equals(uniqueId)) { + return display; + } + } + + return null; + } + + // TODO: Look into consolidating with getActivityDisplayOrCreate() + ActivityDisplay getActivityDisplay(int displayId) { + for (int i = mActivityDisplays.size() - 1; i >= 0; --i) { + final ActivityDisplay activityDisplay = mActivityDisplays.get(i); + if (activityDisplay.mDisplayId == displayId) { + return activityDisplay; + } + } + return null; + } + + /** + * Get an existing instance of {@link ActivityDisplay} or create new if there is a + * corresponding record in display manager. + */ + // TODO: Look into consolidating with getActivityDisplay() + ActivityDisplay getActivityDisplayOrCreate(int displayId) { + ActivityDisplay activityDisplay = getActivityDisplay(displayId); + if (activityDisplay != null) { + return activityDisplay; + } + if (mDisplayManager == null) { + // The system isn't fully initialized yet. + return null; + } + final Display display = mDisplayManager.getDisplay(displayId); + if (display == null) { + // The display is not registered in DisplayManager. + return null; + } + // The display hasn't been added to ActivityManager yet, create a new record now. + activityDisplay = new ActivityDisplay(this, display); + addChild(activityDisplay, ActivityDisplay.POSITION_BOTTOM); + return activityDisplay; + } + + /** Check if display with specified id is added to the list. */ + boolean isDisplayAdded(int displayId) { + return getActivityDisplayOrCreate(displayId) != null; + } + + ActivityRecord getDefaultDisplayHomeActivity() { + return getDefaultDisplayHomeActivityForUser(mCurrentUser); + } + + ActivityRecord getDefaultDisplayHomeActivityForUser(int userId) { + return getActivityDisplay(DEFAULT_DISPLAY).getHomeActivityForUser(userId); + } + + boolean startHomeOnAllDisplays(int userId, String reason) { + boolean homeStarted = false; + for (int i = mActivityDisplays.size() - 1; i >= 0; i--) { + final int displayId = mActivityDisplays.get(i).mDisplayId; + homeStarted |= startHomeOnDisplay(userId, reason, displayId); + } + return homeStarted; + } + + /** + * This starts home activity on displays that can have system decorations and only if the + * home activity can have multiple instances. + */ + boolean startHomeOnDisplay(int userId, String reason, int displayId) { + final Intent homeIntent = mService.getHomeIntent(); + final ActivityInfo aInfo = resolveHomeActivity(userId, homeIntent); + if (aInfo == null) { + return false; + } + + if (!canStartHomeOnDisplay(aInfo, displayId, + false /* allowInstrumenting */)) { + return false; + } + + // Update the reason for ANR debugging to verify if the user activity is the one that + // actually launched. + final String myReason = reason + ":" + userId + ":" + UserHandle.getUserId( + aInfo.applicationInfo.uid); + mService.getActivityStartController().startHomeActivity(homeIntent, aInfo, myReason, + displayId); + return true; + } + + /** + * This resolves the home activity info and updates the home component of the given intent. + * @return the home activity info if any. + */ + private ActivityInfo resolveHomeActivity(int userId, Intent homeIntent) { + final int flags = ActivityManagerService.STOCK_PM_FLAGS; + final ComponentName comp = homeIntent.getComponent(); + ActivityInfo aInfo = null; + try { + if (comp != null) { + // Factory test. + aInfo = AppGlobals.getPackageManager().getActivityInfo(comp, flags, userId); + } else { + final String resolvedType = + homeIntent.resolveTypeIfNeeded(mService.mContext.getContentResolver()); + final ResolveInfo info = AppGlobals.getPackageManager() + .resolveIntent(homeIntent, resolvedType, flags, userId); + if (info != null) { + aInfo = info.activityInfo; + } + } + } catch (RemoteException e) { + // ignore + } + + if (aInfo == null) { + Slog.wtf(TAG, "No home screen found for " + homeIntent, new Throwable()); + return null; + } + + homeIntent.setComponent(new ComponentName(aInfo.applicationInfo.packageName, aInfo.name)); + aInfo = new ActivityInfo(aInfo); + aInfo.applicationInfo = mService.getAppInfoForUser(aInfo.applicationInfo, userId); + homeIntent.setFlags(homeIntent.getFlags() | FLAG_ACTIVITY_NEW_TASK); + return aInfo; + } + + boolean resumeHomeActivity(ActivityRecord prev, String reason, int displayId) { + if (!mService.isBooting() && !mService.isBooted()) { + // Not ready yet! + return false; + } + + if (displayId == INVALID_DISPLAY) { + displayId = DEFAULT_DISPLAY; + } + + final ActivityRecord r = getActivityDisplay(displayId).getHomeActivity(); + final String myReason = reason + " resumeHomeActivity"; + + // Only resume home activity if isn't finishing. + if (r != null && !r.finishing) { + r.moveFocusableActivityToTop(myReason); + return resumeFocusedStacksTopActivities(r.getStack(), prev, null); + } + return startHomeOnDisplay(mCurrentUser, myReason, displayId); + } + + /** + * Check if home activity start should be allowed on a display. + * @param homeInfo {@code ActivityInfo} of the home activity that is going to be launched. + * @param displayId The id of the target display. + * @param allowInstrumenting Whether launching home should be allowed if being instrumented. + * @return {@code true} if allow to launch, {@code false} otherwise. + */ + boolean canStartHomeOnDisplay(ActivityInfo homeInfo, int displayId, + boolean allowInstrumenting) { + if (mService.mFactoryTest == FactoryTest.FACTORY_TEST_LOW_LEVEL + && mService.mTopAction == null) { + // We are running in factory test mode, but unable to find the factory test app, so + // just sit around displaying the error message and don't try to start anything. + return false; + } + + final WindowProcessController app = + mService.getProcessController(homeInfo.processName, homeInfo.applicationInfo.uid); + if (!allowInstrumenting && app != null && app.isInstrumenting()) { + // Don't do this if the home app is currently being instrumented. + return false; + } + + if (displayId == DEFAULT_DISPLAY || (displayId != INVALID_DISPLAY + && displayId == mService.mVr2dDisplayId)) { + // No restrictions to default display or vr 2d display. + return true; + } + + final ActivityDisplay display = getActivityDisplay(displayId); + if (display == null || display.isRemoved() || !display.supportsSystemDecorations()) { + // Can't launch home on display that doesn't support system decorations. + return false; + } + + final boolean supportMultipleInstance = homeInfo.launchMode != LAUNCH_SINGLE_TASK + && homeInfo.launchMode != LAUNCH_SINGLE_INSTANCE + && homeInfo.applicationInfo.targetSdkVersion >= Build.VERSION_CODES.Q; + if (!supportMultipleInstance) { + // Can't launch home on other displays if it requested to be single instance. Also we + // don't allow home applications that target before Q to have multiple home activity + // instances because they may not be expected to have multiple home scenario and + // haven't explicitly request for single instance. + return false; + } + + return true; + } + + /** + * Ensure all activities visibility, update orientation and configuration. + * + * @param starting The currently starting activity or {@code null} if there is none. + * @param displayId The id of the display where operation is executed. + * @param markFrozenIfConfigChanged Whether to set {@link ActivityRecord#frozenBeforeDestroy} to + * {@code true} if config changed. + * @param deferResume Whether to defer resume while updating config. + * @return 'true' if starting activity was kept or wasn't provided, 'false' if it was relaunched + * because of configuration update. + */ + boolean ensureVisibilityAndConfig(ActivityRecord starting, int displayId, + boolean markFrozenIfConfigChanged, boolean deferResume) { + // First ensure visibility without updating the config just yet. We need this to know what + // activities are affecting configuration now. + // Passing null here for 'starting' param value, so that visibility of actual starting + // activity will be properly updated. + ensureActivitiesVisible(null /* starting */, 0 /* configChanges */, + false /* preserveWindows */, false /* notifyClients */); + + if (displayId == INVALID_DISPLAY) { + // The caller didn't provide a valid display id, skip updating config. + return true; + } + + // Force-update the orientation from the WindowManager, since we need the true configuration + // to send to the client now. + final Configuration config = mWindowManager.updateOrientationFromAppTokens( + getDisplayOverrideConfiguration(displayId), + starting != null && starting.mayFreezeScreenLocked(starting.app) + ? starting.appToken : null, + displayId, true /* forceUpdate */); + if (starting != null && markFrozenIfConfigChanged && config != null) { + starting.frozenBeforeDestroy = true; + } + + // Update the configuration of the activities on the display. + return mService.updateDisplayOverrideConfigurationLocked(config, starting, deferResume, + displayId); + } + + /** + * @return a list of activities which are the top ones in each visible stack. The first + * entry will be the focused activity. + */ + List<IBinder> getTopVisibleActivities() { + final ArrayList<IBinder> topActivityTokens = new ArrayList<>(); + final ActivityStack topFocusedStack = getTopDisplayFocusedStack(); + // Traverse all displays. + for (int i = mActivityDisplays.size() - 1; i >= 0; i--) { + final ActivityDisplay display = mActivityDisplays.get(i); + // Traverse all stacks on a display. + for (int j = display.getChildCount() - 1; j >= 0; --j) { + final ActivityStack stack = display.getChildAt(j); + // Get top activity from a visible stack and add it to the list. + if (stack.shouldBeVisible(null /* starting */)) { + final ActivityRecord top = stack.getTopActivity(); + if (top != null) { + if (stack == topFocusedStack) { + topActivityTokens.add(0, top.appToken); + } else { + topActivityTokens.add(top.appToken); + } + } + } + } + } + return topActivityTokens; + } + + ActivityStack getTopDisplayFocusedStack() { + for (int i = mActivityDisplays.size() - 1; i >= 0; --i) { + final ActivityStack focusedStack = mActivityDisplays.get(i).getFocusedStack(); + if (focusedStack != null) { + return focusedStack; + } + } + return null; + } + + ActivityRecord getTopResumedActivity() { + final ActivityStack focusedStack = getTopDisplayFocusedStack(); + if (focusedStack == null) { + return null; + } + final ActivityRecord resumedActivity = focusedStack.getResumedActivity(); + if (resumedActivity != null && resumedActivity.app != null) { + return resumedActivity; + } + // The top focused stack might not have a resumed activity yet - look on all displays in + // focus order. + for (int i = mActivityDisplays.size() - 1; i >= 0; --i) { + final ActivityDisplay display = mActivityDisplays.get(i); + final ActivityRecord resumedActivityOnDisplay = display.getResumedActivity(); + if (resumedActivityOnDisplay != null) { + return resumedActivityOnDisplay; + } + } + return null; + } + + boolean isFocusable(ConfigurationContainer container, boolean alwaysFocusable) { + if (container.inSplitScreenPrimaryWindowingMode() && mIsDockMinimized) { + return false; + } + + return container.getWindowConfiguration().canReceiveKeys() || alwaysFocusable; + } + + boolean isTopDisplayFocusedStack(ActivityStack stack) { + return stack != null && stack == getTopDisplayFocusedStack(); + } + + void updatePreviousProcess(ActivityRecord r) { + // Now that this process has stopped, we may want to consider it to be the previous app to + // try to keep around in case the user wants to return to it. + + // First, found out what is currently the foreground app, so that we don't blow away the + // previous app if this activity is being hosted by the process that is actually still the + // foreground. + WindowProcessController fgApp = null; + for (int displayNdx = mActivityDisplays.size() - 1; displayNdx >= 0; --displayNdx) { + final ActivityDisplay display = mActivityDisplays.get(displayNdx); + for (int stackNdx = display.getChildCount() - 1; stackNdx >= 0; --stackNdx) { + final ActivityStack stack = display.getChildAt(stackNdx); + if (isTopDisplayFocusedStack(stack)) { + final ActivityRecord resumedActivity = stack.getResumedActivity(); + if (resumedActivity != null) { + fgApp = resumedActivity.app; + } else if (stack.mPausingActivity != null) { + fgApp = stack.mPausingActivity.app; + } + break; + } + } + } + + // Now set this one as the previous process, only if that really makes sense to. + if (r.hasProcess() && fgApp != null && r.app != fgApp + && r.lastVisibleTime > mService.mPreviousProcessVisibleTime + && r.app != mService.mHomeProcess) { + mService.mPreviousProcess = r.app; + mService.mPreviousProcessVisibleTime = r.lastVisibleTime; + } + } + + boolean attachApplication(WindowProcessController app) throws RemoteException { + final String processName = app.mName; + boolean didSomething = false; + for (int displayNdx = mActivityDisplays.size() - 1; displayNdx >= 0; --displayNdx) { + final ActivityDisplay display = mActivityDisplays.get(displayNdx); + final ActivityStack stack = display.getFocusedStack(); + if (stack != null) { + stack.getAllRunningVisibleActivitiesLocked(mTmpActivityList); + final ActivityRecord top = stack.topRunningActivityLocked(); + final int size = mTmpActivityList.size(); + for (int i = 0; i < size; i++) { + final ActivityRecord activity = mTmpActivityList.get(i); + if (activity.app == null && app.mUid == activity.info.applicationInfo.uid + && processName.equals(activity.processName)) { + try { + if (mStackSupervisor.realStartActivityLocked(activity, app, + top == activity /* andResume */, true /* checkConfig */)) { + didSomething = true; + } + } catch (RemoteException e) { + Slog.w(TAG, "Exception in new application when starting activity " + + top.intent.getComponent().flattenToShortString(), e); + throw e; + } + } + } + } + } + if (!didSomething) { + ensureActivitiesVisible(null, 0, false /* preserve_windows */); + } + return didSomething; + } + + /** + * Make sure that all activities that need to be visible in the system actually are and update + * their configuration. + */ + void ensureActivitiesVisible(ActivityRecord starting, int configChanges, + boolean preserveWindows) { + ensureActivitiesVisible(starting, configChanges, preserveWindows, true /* notifyClients */); + } + + /** + * @see #ensureActivitiesVisible(ActivityRecord, int, boolean) + */ + void ensureActivitiesVisible(ActivityRecord starting, int configChanges, + boolean preserveWindows, boolean notifyClients) { + mStackSupervisor.getKeyguardController().beginActivityVisibilityUpdate(); + try { + // First the front stacks. In case any are not fullscreen and are in front of home. + for (int displayNdx = mActivityDisplays.size() - 1; displayNdx >= 0; --displayNdx) { + final ActivityDisplay display = mActivityDisplays.get(displayNdx); + for (int stackNdx = display.getChildCount() - 1; stackNdx >= 0; --stackNdx) { + final ActivityStack stack = display.getChildAt(stackNdx); + stack.ensureActivitiesVisibleLocked(starting, configChanges, preserveWindows, + notifyClients); + } + } + } finally { + mStackSupervisor.getKeyguardController().endActivityVisibilityUpdate(); + } + } + + boolean switchUser(int userId, UserState uss) { + final int focusStackId = getTopDisplayFocusedStack().getStackId(); + // We dismiss the docked stack whenever we switch users. + final ActivityStack dockedStack = getDefaultDisplay().getSplitScreenPrimaryStack(); + if (dockedStack != null) { + mStackSupervisor.moveTasksToFullscreenStackLocked( + dockedStack, dockedStack.isFocusedStackOnDisplay()); + } + // 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 + // appropriate. + removeStacksInWindowingModes(WINDOWING_MODE_PINNED); + + mUserStackInFront.put(mCurrentUser, focusStackId); + final int restoreStackId = + mUserStackInFront.get(userId, getDefaultDisplay().getHomeStack().mStackId); + mCurrentUser = userId; + + mStackSupervisor.mStartingUsers.add(uss); + for (int displayNdx = mActivityDisplays.size() - 1; displayNdx >= 0; --displayNdx) { + final ActivityDisplay display = mActivityDisplays.get(displayNdx); + for (int stackNdx = display.getChildCount() - 1; stackNdx >= 0; --stackNdx) { + final ActivityStack stack = display.getChildAt(stackNdx); + stack.switchUserLocked(userId); + TaskRecord task = stack.topTask(); + if (task != null) { + stack.positionChildWindowContainerAtTop(task); + } + } + } + + ActivityStack stack = getStack(restoreStackId); + if (stack == null) { + stack = getDefaultDisplay().getHomeStack(); + } + final boolean homeInFront = stack.isActivityTypeHome(); + if (stack.isOnHomeDisplay()) { + stack.moveToFront("switchUserOnHomeDisplay"); + } else { + // Stack was moved to another display while user was swapped out. + resumeHomeActivity(null, "switchUserOnOtherDisplay", DEFAULT_DISPLAY); + } + return homeInFront; + } + + void removeUser(int userId) { + mUserStackInFront.delete(userId); + } + + /** + * Update the last used stack id for non-current user (current user's last + * used stack is the focused stack) + */ + void updateUserStack(int userId, ActivityStack stack) { + if (userId != mCurrentUser) { + mUserStackInFront.put(userId, stack != null ? stack.getStackId() + : getDefaultDisplay().getHomeStack().mStackId); + } + } + + void resizeStack(ActivityStack stack, Rect bounds, Rect tempTaskBounds, + Rect tempTaskInsetBounds, boolean preserveWindows, boolean allowResizeInDockedMode, + boolean deferResume) { + + if (stack.inSplitScreenPrimaryWindowingMode()) { + mStackSupervisor.resizeDockedStackLocked(bounds, tempTaskBounds, + tempTaskInsetBounds, null, null, preserveWindows, deferResume); + return; + } + + final boolean splitScreenActive = getDefaultDisplay().hasSplitScreenPrimaryStack(); + if (!allowResizeInDockedMode + && !stack.getWindowConfiguration().tasksAreFloating() && splitScreenActive) { + // If the docked stack exists, don't resize non-floating stacks independently of the + // size computed from the docked stack size (otherwise they will be out of sync) + return; + } + + Trace.traceBegin(TRACE_TAG_ACTIVITY_MANAGER, "am.resizeStack_" + stack.mStackId); + mWindowManager.deferSurfaceLayout(); + try { + if (stack.affectedBySplitScreenResize()) { + if (bounds == null && stack.inSplitScreenWindowingMode()) { + // null bounds = fullscreen windowing mode...at least for now. + stack.setWindowingMode(WINDOWING_MODE_FULLSCREEN); + } else if (splitScreenActive) { + // If we are in split-screen mode and this stack support split-screen, then + // it should be split-screen secondary mode. i.e. adjacent to the docked stack. + stack.setWindowingMode(WINDOWING_MODE_SPLIT_SCREEN_SECONDARY); + } + } + stack.resize(bounds, tempTaskBounds, tempTaskInsetBounds); + if (!deferResume) { + stack.ensureVisibleActivitiesConfigurationLocked( + stack.topRunningActivityLocked(), preserveWindows); + } + } finally { + mWindowManager.continueSurfaceLayout(); + Trace.traceEnd(TRACE_TAG_ACTIVITY_MANAGER); + } + } + + /** + * Move stack with all its existing content to specified display. + * @param stackId Id of stack to move. + * @param displayId Id of display to move stack to. + * @param onTop Indicates whether container should be place on top or on bottom. + */ + void moveStackToDisplay(int stackId, int displayId, boolean onTop) { + final ActivityDisplay activityDisplay = getActivityDisplayOrCreate(displayId); + if (activityDisplay == null) { + throw new IllegalArgumentException("moveStackToDisplay: Unknown displayId=" + + displayId); + } + final ActivityStack stack = getStack(stackId); + if (stack == null) { + throw new IllegalArgumentException("moveStackToDisplay: Unknown stackId=" + + stackId); + } + + final ActivityDisplay currentDisplay = stack.getDisplay(); + if (currentDisplay == null) { + throw new IllegalStateException("moveStackToDisplay: Stack with stack=" + stack + + " is not attached to any display."); + } + + if (currentDisplay.mDisplayId == displayId) { + throw new IllegalArgumentException("Trying to move stack=" + stack + + " to its current displayId=" + displayId); + } + + stack.reparent(activityDisplay, onTop, false /* displayRemoved */); + // TODO(multi-display): resize stacks properly if moved from split-screen. + } + + boolean moveTopStackActivityToPinnedStack(int stackId) { + final ActivityStack stack = getStack(stackId); + if (stack == null) { + throw new IllegalArgumentException( + "moveTopStackActivityToPinnedStack: Unknown stackId=" + stackId); + } + + final ActivityRecord r = stack.topRunningActivityLocked(); + if (r == null) { + Slog.w(TAG, "moveTopStackActivityToPinnedStack: No top running activity" + + " in stack=" + stack); + return false; + } + + if (!mService.mForceResizableActivities && !r.supportsPictureInPicture()) { + Slog.w(TAG, "moveTopStackActivityToPinnedStack: Picture-In-Picture not supported for " + + " r=" + r); + return false; + } + + moveActivityToPinnedStack(r, null /* sourceBounds */, 0f /* aspectRatio */, + "moveTopActivityToPinnedStack"); + return true; + } + + void moveActivityToPinnedStack(ActivityRecord r, Rect sourceHintBounds, float aspectRatio, + String reason) { + + mWindowManager.deferSurfaceLayout(); + + final ActivityDisplay display = r.getStack().getDisplay(); + PinnedActivityStack stack = display.getPinnedStack(); + + // This will clear the pinned stack by moving an existing task to the full screen stack, + // ensuring only one task is present. + if (stack != null) { + mStackSupervisor.moveTasksToFullscreenStackLocked(stack, !ON_TOP); + } + + // Need to make sure the pinned stack exist so we can resize it below... + stack = display.getOrCreateStack(WINDOWING_MODE_PINNED, r.getActivityType(), ON_TOP); + + // Calculate the target bounds here before the task is reparented back into pinned windowing + // mode (which will reset the saved bounds) + final Rect destBounds = stack.getDefaultPictureInPictureBounds(aspectRatio); + + try { + final TaskRecord task = r.getTask(); + // Resize the pinned stack to match the current size of the task the activity we are + // going to be moving is currently contained in. We do this to have the right starting + // animation bounds for the pinned stack to the desired bounds the caller wants. + resizeStack(stack, task.getOverrideBounds(), null /* tempTaskBounds */, + null /* tempTaskInsetBounds */, !PRESERVE_WINDOWS, + true /* allowResizeInDockedMode */, !DEFER_RESUME); + + if (task.mActivities.size() == 1) { + // Defer resume until below, and do not schedule PiP changes until we animate below + task.reparent(stack, ON_TOP, REPARENT_MOVE_STACK_TO_FRONT, !ANIMATE, DEFER_RESUME, + false /* schedulePictureInPictureModeChange */, reason); + } else { + // There are multiple activities in the task and moving the top activity should + // reveal/leave the other activities in their original task. + + // Currently, we don't support reparenting activities across tasks in two different + // stacks, so instead, just create a new task in the same stack, reparent the + // activity into that task, and then reparent the whole task to the new stack. This + // ensures that all the necessary work to migrate states in the old and new stacks + // is also done. + final TaskRecord newTask = task.getStack().createTaskRecord( + mStackSupervisor.getNextTaskIdForUserLocked(r.userId), r.info, + r.intent, null, null, true); + r.reparent(newTask, MAX_VALUE, "moveActivityToStack"); + + // Defer resume until below, and do not schedule PiP changes until we animate below + newTask.reparent(stack, ON_TOP, REPARENT_MOVE_STACK_TO_FRONT, !ANIMATE, + DEFER_RESUME, false /* schedulePictureInPictureModeChange */, reason); + } + + // Reset the state that indicates it can enter PiP while pausing after we've moved it + // to the pinned stack + r.supportsEnterPipOnTaskSwitch = false; + } finally { + mWindowManager.continueSurfaceLayout(); + } + + stack.animateResizePinnedStack(sourceHintBounds, destBounds, -1 /* animationDuration */, + true /* fromFullscreen */); + + // Update the visibility of all activities after the they have been reparented to the new + // stack. This MUST run after the animation above is scheduled to ensure that the windows + // drawn signal is scheduled after the bounds animation start call on the bounds animator + // thread. + ensureActivitiesVisible(null, 0, false /* preserveWindows */); + resumeFocusedStacksTopActivities(); + + mService.getTaskChangeNotificationController().notifyActivityPinned(r); + } + + void executeAppTransitionForAllDisplay() { + for (int displayNdx = mActivityDisplays.size() - 1; displayNdx >= 0; --displayNdx) { + final ActivityDisplay display = mActivityDisplays.get(displayNdx); + display.getWindowContainerController().executeAppTransition(); + } + } + + void setDockedStackMinimized(boolean minimized) { + // Get currently focused stack before setting mIsDockMinimized. We do this because if + // split-screen is active, primary stack will not be focusable (see #isFocusable) while + // still occluding other stacks. This will cause getTopDisplayFocusedStack() to return null. + final ActivityStack current = getTopDisplayFocusedStack(); + mIsDockMinimized = minimized; + if (mIsDockMinimized) { + if (current.inSplitScreenPrimaryWindowingMode()) { + // The primary split-screen stack can't be focused while it is minimize, so move + // focus to something else. + current.adjustFocusToNextFocusableStack("setDockedStackMinimized"); + } + } + } + + ActivityRecord findTask(ActivityRecord r, int preferredDisplayId) { + if (DEBUG_TASKS) Slog.d(TAG_TASKS, "Looking for task of " + r); + mTmpFindTaskResult.clear(); + + // Looking up task on preferred display first + final ActivityDisplay preferredDisplay = getActivityDisplay(preferredDisplayId); + if (preferredDisplay != null) { + preferredDisplay.findTaskLocked(r, true /* isPreferredDisplay */, mTmpFindTaskResult); + if (mTmpFindTaskResult.mIdealMatch) { + return mTmpFindTaskResult.mRecord; + } + } + + for (int displayNdx = mActivityDisplays.size() - 1; displayNdx >= 0; --displayNdx) { + final ActivityDisplay display = mActivityDisplays.get(displayNdx); + if (display.mDisplayId == preferredDisplayId) { + continue; + } + + display.findTaskLocked(r, false /* isPreferredDisplay */, mTmpFindTaskResult); + if (mTmpFindTaskResult.mIdealMatch) { + return mTmpFindTaskResult.mRecord; + } + } + + if (DEBUG_TASKS && mTmpFindTaskResult.mRecord == null) Slog.d(TAG_TASKS, "No task found"); + return mTmpFindTaskResult.mRecord; + } + + /** + * Finish the topmost activities in all stacks that belong to the crashed app. + * @param app The app that crashed. + * @param reason Reason to perform this action. + * @return The task id that was finished in this stack, or INVALID_TASK_ID if none was finished. + */ + int finishTopCrashedActivities(WindowProcessController app, String reason) { + TaskRecord finishedTask = null; + ActivityStack focusedStack = getTopDisplayFocusedStack(); + for (int displayNdx = mActivityDisplays.size() - 1; displayNdx >= 0; --displayNdx) { + final ActivityDisplay display = mActivityDisplays.get(displayNdx); + // It is possible that request to finish activity might also remove its task and stack, + // so we need to be careful with indexes in the loop and check child count every time. + for (int stackNdx = 0; stackNdx < display.getChildCount(); ++stackNdx) { + final ActivityStack stack = display.getChildAt(stackNdx); + final TaskRecord t = stack.finishTopCrashedActivityLocked(app, reason); + if (stack == focusedStack || finishedTask == null) { + finishedTask = t; + } + } + } + return finishedTask != null ? finishedTask.taskId : INVALID_TASK_ID; + } + + boolean resumeFocusedStacksTopActivities() { + return resumeFocusedStacksTopActivities(null, null, null); + } + + boolean resumeFocusedStacksTopActivities( + ActivityStack targetStack, ActivityRecord target, ActivityOptions targetOptions) { + + if (!mStackSupervisor.readyToResume()) { + return false; + } + + if (targetStack != null && (targetStack.isTopStackOnDisplay() + || getTopDisplayFocusedStack() == targetStack)) { + return targetStack.resumeTopActivityUncheckedLocked(target, targetOptions); + } + + // Resume all top activities in focused stacks on all displays. + for (int displayNdx = mActivityDisplays.size() - 1; displayNdx >= 0; --displayNdx) { + final ActivityDisplay display = mActivityDisplays.get(displayNdx); + final ActivityStack focusedStack = display.getFocusedStack(); + if (focusedStack == null) { + continue; + } + final ActivityRecord r = focusedStack.topRunningActivityLocked(); + if (r == null || !r.isState(RESUMED)) { + focusedStack.resumeTopActivityUncheckedLocked(null, null); + } else if (r.isState(RESUMED)) { + // Kick off any lingering app transitions form the MoveTaskToFront operation. + focusedStack.executeAppTransition(targetOptions); + } + } + + return false; + } + + void applySleepTokens(boolean applyToStacks) { + for (int displayNdx = mActivityDisplays.size() - 1; displayNdx >= 0; --displayNdx) { + // Set the sleeping state of the display. + final ActivityDisplay display = mActivityDisplays.get(displayNdx); + final boolean displayShouldSleep = display.shouldSleep(); + if (displayShouldSleep == display.isSleeping()) { + continue; + } + display.setIsSleeping(displayShouldSleep); + + if (!applyToStacks) { + continue; + } + + // Set the sleeping state of the stacks on the display. + for (int stackNdx = display.getChildCount() - 1; stackNdx >= 0; --stackNdx) { + final ActivityStack stack = display.getChildAt(stackNdx); + if (displayShouldSleep) { + stack.goToSleepIfPossible(false /* shuttingDown */); + } else { + stack.awakeFromSleepingLocked(); + if (stack.isFocusedStackOnDisplay() + && !mStackSupervisor.getKeyguardController() + .isKeyguardOrAodShowing(display.mDisplayId)) { + // If the keyguard is unlocked - resume immediately. + // It is possible that the display will not be awake at the time we + // process the keyguard going away, which can happen before the sleep token + // is released. As a result, it is important we resume the activity here. + resumeFocusedStacksTopActivities(); + } + } + } + + if (displayShouldSleep || mStackSupervisor.mGoingToSleepActivities.isEmpty()) { + continue; + } + // The display is awake now, so clean up the going to sleep list. + for (Iterator<ActivityRecord> it = + mStackSupervisor.mGoingToSleepActivities.iterator(); it.hasNext(); ) { + final ActivityRecord r = it.next(); + if (r.getDisplayId() == display.mDisplayId) { + it.remove(); + } + } + } + } + + protected <T extends ActivityStack> T getStack(int stackId) { + for (int i = mActivityDisplays.size() - 1; i >= 0; --i) { + final T stack = mActivityDisplays.get(i).getStack(stackId); + if (stack != null) { + return stack; + } + } + return null; + } + + /** @see ActivityDisplay#getStack(int, int) */ + private <T extends ActivityStack> T getStack(int windowingMode, int activityType) { + for (int i = mActivityDisplays.size() - 1; i >= 0; --i) { + final T stack = mActivityDisplays.get(i).getStack(windowingMode, activityType); + if (stack != null) { + return stack; + } + } + return null; + } + + private ActivityManager.StackInfo getStackInfo(ActivityStack stack) { + final int displayId = stack.mDisplayId; + final ActivityDisplay display = getActivityDisplay(displayId); + ActivityManager.StackInfo info = new ActivityManager.StackInfo(); + stack.getWindowContainerBounds(info.bounds); + info.displayId = displayId; + info.stackId = stack.mStackId; + info.userId = stack.mCurrentUser; + info.visible = stack.shouldBeVisible(null); + // A stack might be not attached to a display. + info.position = display != null ? display.getIndexOf(stack) : 0; + info.configuration.setTo(stack.getConfiguration()); + + ArrayList<TaskRecord> tasks = stack.getAllTasks(); + final int numTasks = tasks.size(); + int[] taskIds = new int[numTasks]; + String[] taskNames = new String[numTasks]; + Rect[] taskBounds = new Rect[numTasks]; + int[] taskUserIds = new int[numTasks]; + for (int i = 0; i < numTasks; ++i) { + final TaskRecord task = tasks.get(i); + taskIds[i] = task.taskId; + taskNames[i] = task.origActivity != null ? task.origActivity.flattenToString() + : task.realActivity != null ? task.realActivity.flattenToString() + : task.getTopActivity() != null ? task.getTopActivity().packageName + : "unknown"; + taskBounds[i] = new Rect(); + task.getWindowContainerBounds(taskBounds[i]); + taskUserIds[i] = task.userId; + } + info.taskIds = taskIds; + info.taskNames = taskNames; + info.taskBounds = taskBounds; + info.taskUserIds = taskUserIds; + + final ActivityRecord top = stack.topRunningActivityLocked(); + info.topActivity = top != null ? top.intent.getComponent() : null; + return info; + } + + ActivityManager.StackInfo getStackInfo(int stackId) { + ActivityStack stack = getStack(stackId); + if (stack != null) { + return getStackInfo(stack); + } + return null; + } + + ActivityManager.StackInfo getStackInfo(int windowingMode, int activityType) { + final ActivityStack stack = getStack(windowingMode, activityType); + return (stack != null) ? getStackInfo(stack) : null; + } + + ArrayList<ActivityManager.StackInfo> getAllStackInfos() { + ArrayList<ActivityManager.StackInfo> list = new ArrayList<>(); + for (int displayNdx = 0; displayNdx < mActivityDisplays.size(); ++displayNdx) { + final ActivityDisplay display = mActivityDisplays.get(displayNdx); + for (int stackNdx = display.getChildCount() - 1; stackNdx >= 0; --stackNdx) { + final ActivityStack stack = display.getChildAt(stackNdx); + list.add(getStackInfo(stack)); + } + } + return list; + } + + void deferUpdateBounds(int activityType) { + final ActivityStack stack = getStack(WINDOWING_MODE_UNDEFINED, activityType); + if (stack != null) { + stack.deferUpdateBounds(); + } + } + + void continueUpdateBounds(int activityType) { + final ActivityStack stack = getStack(WINDOWING_MODE_UNDEFINED, activityType); + if (stack != null) { + stack.continueUpdateBounds(); + } + } + + @Override + public void onDisplayAdded(int displayId) { + if (DEBUG_STACK) Slog.v(TAG, "Display added displayId=" + displayId); + synchronized (mService.mGlobalLock) { + getActivityDisplayOrCreate(displayId); + // Do not start home before booting, or it may accidentally finish booting before it + // starts. Instead, we expect home activities to be launched when the system is ready + // (ActivityManagerService#systemReady). + if (mService.isBooted() || mService.isBooting()) { + startHomeOnDisplay(mCurrentUser, "displayAdded", displayId); + } + } + } + + @Override + public void onDisplayRemoved(int displayId) { + if (DEBUG_STACK) Slog.v(TAG, "Display removed displayId=" + displayId); + if (displayId == DEFAULT_DISPLAY) { + throw new IllegalArgumentException("Can't remove the primary display."); + } + + synchronized (mService.mGlobalLock) { + final ActivityDisplay activityDisplay = getActivityDisplay(displayId); + if (activityDisplay == null) { + return; + } + + activityDisplay.remove(); + } + } + + @Override + public void onDisplayChanged(int displayId) { + if (DEBUG_STACK) Slog.v(TAG, "Display changed displayId=" + displayId); + synchronized (mService.mGlobalLock) { + final ActivityDisplay activityDisplay = getActivityDisplay(displayId); + if (activityDisplay != null) { + activityDisplay.onDisplayChanged(); + } + } + } + + /** Update lists of UIDs that are present on displays and have access to them. */ + void updateUIDsPresentOnDisplay() { + mDisplayAccessUIDs.clear(); + for (int displayNdx = mActivityDisplays.size() - 1; displayNdx >= 0; --displayNdx) { + final ActivityDisplay activityDisplay = mActivityDisplays.get(displayNdx); + // Only bother calculating the whitelist for private displays + if (activityDisplay.isPrivate()) { + mDisplayAccessUIDs.append( + activityDisplay.mDisplayId, activityDisplay.getPresentUIDs()); + } + } + // Store updated lists in DisplayManager. Callers from outside of AM should get them there. + mDisplayManagerInternal.setDisplayAccessUIDs(mDisplayAccessUIDs); + } + + ActivityStack findStackBehind(ActivityStack stack) { + final ActivityDisplay display = getActivityDisplay(stack.mDisplayId); + if (display != null) { + for (int i = display.getChildCount() - 1; i >= 0; i--) { + if (display.getChildAt(i) == stack && i > 0) { + return display.getChildAt(i - 1); + } + } + } + throw new IllegalStateException("Failed to find a stack behind stack=" + stack + + " in=" + display); + } + + @Override + protected int getChildCount() { + return mActivityDisplays.size(); + } + + @Override + protected ActivityDisplay getChildAt(int index) { + return mActivityDisplays.get(index); + } + + @Override + protected ConfigurationContainer getParent() { + return null; + } + + // TODO: remove after object merge with RootWindowContainer + void onChildPositionChanged(DisplayWindowController childController, int position) { + // Assume AM lock is held from positionChildAt of controller in each hierarchy. + final ActivityDisplay display = getActivityDisplay(childController.getDisplayId()); + if (display != null) { + positionChildAt(display, position); + } + } + + /** Change the z-order of the given display. */ + private void positionChildAt(ActivityDisplay display, int position) { + if (position >= mActivityDisplays.size()) { + position = mActivityDisplays.size() - 1; + } else if (position < 0) { + position = 0; + } + + if (mActivityDisplays.isEmpty()) { + mActivityDisplays.add(display); + } else if (mActivityDisplays.get(position) != display) { + mActivityDisplays.remove(display); + mActivityDisplays.add(position, display); + } + } + + @VisibleForTesting + void addChild(ActivityDisplay activityDisplay, int position) { + positionChildAt(activityDisplay, position); + mRootWindowContainer.positionChildAt(position, + activityDisplay.getWindowContainerController().mContainer); + } + + void removeChild(ActivityDisplay activityDisplay) { + // The caller must tell the controller of {@link ActivityDisplay} to release its container + // {@link DisplayContent}. That is done in {@link ActivityDisplay#releaseSelfIfNeeded}). + mActivityDisplays.remove(activityDisplay); + } + + Configuration getDisplayOverrideConfiguration(int displayId) { + final ActivityDisplay activityDisplay = getActivityDisplayOrCreate(displayId); + if (activityDisplay == null) { + throw new IllegalArgumentException("No display found with id: " + displayId); + } + + return activityDisplay.getOverrideConfiguration(); + } + + void setDisplayOverrideConfiguration(Configuration overrideConfiguration, int displayId) { + final ActivityDisplay activityDisplay = getActivityDisplayOrCreate(displayId); + if (activityDisplay == null) { + throw new IllegalArgumentException("No display found with id: " + displayId); + } + + activityDisplay.onOverrideConfigurationChanged(overrideConfiguration); + } + + void prepareForShutdown() { + for (int i = 0; i < mActivityDisplays.size(); i++) { + createSleepToken("shutdown", mActivityDisplays.get(i).mDisplayId); + } + } + + ActivityTaskManagerInternal.SleepToken createSleepToken(String tag, int displayId) { + final ActivityDisplay display = getActivityDisplay(displayId); + if (display == null) { + throw new IllegalArgumentException("Invalid display: " + displayId); + } + + final SleepTokenImpl token = new SleepTokenImpl(tag, displayId); + mSleepTokens.add(token); + display.mAllSleepTokens.add(token); + return token; + } + + private void removeSleepToken(SleepTokenImpl token) { + mSleepTokens.remove(token); + + final ActivityDisplay display = getActivityDisplay(token.mDisplayId); + if (display != null) { + display.mAllSleepTokens.remove(token); + if (display.mAllSleepTokens.isEmpty()) { + mService.updateSleepIfNeededLocked(); + } + } + } + + void addStartingWindowsForVisibleActivities(boolean taskSwitch) { + for (int displayNdx = mActivityDisplays.size() - 1; displayNdx >= 0; --displayNdx) { + final ActivityDisplay display = mActivityDisplays.get(displayNdx); + for (int stackNdx = display.getChildCount() - 1; stackNdx >= 0; --stackNdx) { + final ActivityStack stack = display.getChildAt(stackNdx); + stack.addStartingWindowsForVisibleActivities(taskSwitch); + } + } + } + + void invalidateTaskLayers() { + mTaskLayersChanged = true; + } + + void rankTaskLayersIfNeeded() { + if (!mTaskLayersChanged) { + return; + } + mTaskLayersChanged = false; + for (int displayNdx = 0; displayNdx < mActivityDisplays.size(); displayNdx++) { + final ActivityDisplay display = mActivityDisplays.get(displayNdx); + int baseLayer = 0; + for (int stackNdx = display.getChildCount() - 1; stackNdx >= 0; --stackNdx) { + final ActivityStack stack = display.getChildAt(stackNdx); + baseLayer += stack.rankTaskLayers(baseLayer); + } + } + } + + void clearOtherAppTimeTrackers(AppTimeTracker except) { + for (int displayNdx = mActivityDisplays.size() - 1; displayNdx >= 0; --displayNdx) { + final ActivityDisplay display = mActivityDisplays.get(displayNdx); + for (int stackNdx = display.getChildCount() - 1; stackNdx >= 0; --stackNdx) { + final ActivityStack stack = display.getChildAt(stackNdx); + stack.clearOtherAppTimeTrackers(except); + } + } + } + + void scheduleDestroyAllActivities(WindowProcessController app, String reason) { + for (int displayNdx = mActivityDisplays.size() - 1; displayNdx >= 0; --displayNdx) { + final ActivityDisplay display = mActivityDisplays.get(displayNdx); + for (int stackNdx = display.getChildCount() - 1; stackNdx >= 0; --stackNdx) { + final ActivityStack stack = display.getChildAt(stackNdx); + stack.scheduleDestroyActivities(app, reason); + } + } + } + + void releaseSomeActivitiesLocked(WindowProcessController app, String reason) { + // Tasks is non-null only if two or more tasks are found. + ArraySet<TaskRecord> tasks = app.getReleaseSomeActivitiesTasks(); + if (tasks == null) { + if (DEBUG_RELEASE) Slog.d(TAG_RELEASE, "Didn't find two or more tasks to release"); + return; + } + // If we have activities in multiple tasks that are in a position to be destroyed, + // let's iterate through the tasks and release the oldest one. + final int numDisplays = mActivityDisplays.size(); + for (int displayNdx = 0; displayNdx < numDisplays; ++displayNdx) { + final ActivityDisplay display = mActivityDisplays.get(displayNdx); + final int stackCount = display.getChildCount(); + // Step through all stacks starting from behind, to hit the oldest things first. + for (int stackNdx = 0; stackNdx < stackCount; stackNdx++) { + final ActivityStack stack = display.getChildAt(stackNdx); + // Try to release activities in this stack; if we manage to, we are done. + if (stack.releaseSomeActivitiesLocked(app, tasks, reason) > 0) { + return; + } + } + } + } + + // Tries to put all activity stacks to sleep. Returns true if all stacks were + // successfully put to sleep. + boolean putStacksToSleep(boolean allowDelay, boolean shuttingDown) { + boolean allSleep = true; + for (int displayNdx = mActivityDisplays.size() - 1; displayNdx >= 0; --displayNdx) { + final ActivityDisplay display = mActivityDisplays.get(displayNdx); + for (int stackNdx = display.getChildCount() - 1; stackNdx >= 0; --stackNdx) { + final ActivityStack stack = display.getChildAt(stackNdx); + if (allowDelay) { + allSleep &= stack.goToSleepIfPossible(shuttingDown); + } else { + stack.goToSleep(); + } + } + } + return allSleep; + } + + void handleAppCrash(WindowProcessController app) { + for (int displayNdx = mActivityDisplays.size() - 1; displayNdx >= 0; --displayNdx) { + final ActivityDisplay display = mActivityDisplays.get(displayNdx); + for (int stackNdx = display.getChildCount() - 1; stackNdx >= 0; --stackNdx) { + final ActivityStack stack = display.getChildAt(stackNdx); + stack.handleAppCrash(app); + } + } + } + + ActivityRecord findActivity(Intent intent, ActivityInfo info, boolean compareIntentFilters) { + for (int displayNdx = mActivityDisplays.size() - 1; displayNdx >= 0; --displayNdx) { + final ActivityDisplay display = mActivityDisplays.get(displayNdx); + for (int stackNdx = display.getChildCount() - 1; stackNdx >= 0; --stackNdx) { + final ActivityStack stack = display.getChildAt(stackNdx); + final ActivityRecord ar = stack.findActivityLocked( + intent, info, compareIntentFilters); + if (ar != null) { + return ar; + } + } + } + return null; + } + + boolean hasAwakeDisplay() { + for (int displayNdx = mActivityDisplays.size() - 1; displayNdx >= 0; --displayNdx) { + final ActivityDisplay display = mActivityDisplays.get(displayNdx); + if (!display.shouldSleep()) { + return true; + } + } + return false; + } + + <T extends ActivityStack> T getLaunchStack(@Nullable ActivityRecord r, + @Nullable ActivityOptions options, @Nullable TaskRecord candidateTask, boolean onTop) { + return getLaunchStack(r, options, candidateTask, onTop, null /* launchParams */); + } + + /** + * Returns the right stack to use for launching factoring in all the input parameters. + * + * @param r The activity we are trying to launch. Can be null. + * @param options The activity options used to the launch. Can be null. + * @param candidateTask The possible task the activity might be launched in. Can be null. + * @params launchParams The resolved launch params to use. + * + * @return The stack to use for the launch or INVALID_STACK_ID. + */ + <T extends ActivityStack> T getLaunchStack(@Nullable ActivityRecord r, + @Nullable ActivityOptions options, @Nullable TaskRecord candidateTask, boolean onTop, + @Nullable LaunchParamsController.LaunchParams launchParams) { + int taskId = INVALID_TASK_ID; + int displayId = INVALID_DISPLAY; + //Rect bounds = null; + + // We give preference to the launch preference in activity options. + if (options != null) { + taskId = options.getLaunchTaskId(); + displayId = options.getLaunchDisplayId(); + } + + // First preference for stack goes to the task Id set in the activity options. Use the stack + // associated with that if possible. + if (taskId != INVALID_TASK_ID) { + // Temporarily set the task id to invalid in case in re-entry. + options.setLaunchTaskId(INVALID_TASK_ID); + final TaskRecord task = anyTaskForId(taskId, + MATCH_TASK_IN_STACKS_OR_RECENT_TASKS_AND_RESTORE, options, onTop); + options.setLaunchTaskId(taskId); + if (task != null) { + return task.getStack(); + } + } + + final int activityType = resolveActivityType(r, options, candidateTask); + T stack; + + // Next preference for stack goes to the display Id set the candidate display. + if (launchParams != null && launchParams.mPreferredDisplayId != INVALID_DISPLAY) { + displayId = launchParams.mPreferredDisplayId; + } + if (displayId != INVALID_DISPLAY && canLaunchOnDisplay(r, displayId)) { + if (r != null) { + stack = (T) getValidLaunchStackOnDisplay(displayId, r, candidateTask, options, + launchParams); + if (stack != null) { + return stack; + } + } + final ActivityDisplay display = getActivityDisplayOrCreate(displayId); + if (display != null) { + stack = display.getOrCreateStack(r, options, candidateTask, activityType, onTop); + if (stack != null) { + return stack; + } + } + } + + // Give preference to the stack and display of the input task and activity if they match the + // mode we want to launch into. + stack = null; + ActivityDisplay display = null; + if (candidateTask != null) { + stack = candidateTask.getStack(); + } + if (stack == null && r != null) { + stack = r.getStack(); + } + if (stack != null) { + display = stack.getDisplay(); + if (display != null && canLaunchOnDisplay(r, display.mDisplayId)) { + int windowingMode = launchParams != null ? launchParams.mWindowingMode + : WindowConfiguration.WINDOWING_MODE_UNDEFINED; + if (windowingMode == WindowConfiguration.WINDOWING_MODE_UNDEFINED) { + windowingMode = display.resolveWindowingMode(r, options, candidateTask, + activityType); + } + if (stack.isCompatible(windowingMode, activityType)) { + return stack; + } + if (windowingMode == WINDOWING_MODE_FULLSCREEN_OR_SPLIT_SCREEN_SECONDARY + && display.getSplitScreenPrimaryStack() == stack + && candidateTask == stack.topTask()) { + // This is a special case when we try to launch an activity that is currently on + // top of split-screen primary stack, but is targeting split-screen secondary. + // In this case we don't want to move it to another stack. + // TODO(b/78788972): Remove after differentiating between preferred and required + // launch options. + return stack; + } + } + } + + if (display == null || !canLaunchOnDisplay(r, display.mDisplayId)) { + display = getDefaultDisplay(); + } + + return display.getOrCreateStack(r, options, candidateTask, activityType, onTop); + } + + /** @return true if activity record is null or can be launched on provided display. */ + private boolean canLaunchOnDisplay(ActivityRecord r, int displayId) { + if (r == null) { + return true; + } + return r.canBeLaunchedOnDisplay(displayId); + } + + /** + * Get a topmost stack on the display, that is a valid launch stack for specified activity. + * If there is no such stack, new dynamic stack can be created. + * @param displayId Target display. + * @param r Activity that should be launched there. + * @param candidateTask The possible task the activity might be put in. + * @return Existing stack if there is a valid one, new dynamic stack if it is valid or null. + */ + private ActivityStack getValidLaunchStackOnDisplay(int displayId, @NonNull ActivityRecord r, + @Nullable TaskRecord candidateTask, @Nullable ActivityOptions options, + @Nullable LaunchParamsController.LaunchParams launchParams) { + final ActivityDisplay activityDisplay = getActivityDisplayOrCreate(displayId); + if (activityDisplay == null) { + throw new IllegalArgumentException( + "Display with displayId=" + displayId + " not found."); + } + + if (!r.canBeLaunchedOnDisplay(displayId)) { + return null; + } + + // If {@code r} is already in target display and its task is the same as the candidate task, + // the intention should be getting a launch stack for the reusable activity, so we can use + // the existing stack. + if (r.getDisplayId() == displayId && r.getTask() == candidateTask) { + return candidateTask.getStack(); + } + + // Return the topmost valid stack on the display. + for (int i = activityDisplay.getChildCount() - 1; i >= 0; --i) { + final ActivityStack stack = activityDisplay.getChildAt(i); + if (isValidLaunchStack(stack, r)) { + return stack; + } + } + + // If there is no valid stack on the external display - check if new dynamic stack will do. + if (displayId != DEFAULT_DISPLAY) { + final int windowingMode; + if (launchParams != null) { + // When launch params is not null, we always defer to its windowing mode. Sometimes + // it could be unspecified, which indicates it should inherit windowing mode from + // display. + windowingMode = launchParams.mWindowingMode; + } else { + windowingMode = options != null ? options.getLaunchWindowingMode() + : r.getWindowingMode(); + } + final int activityType = + options != null && options.getLaunchActivityType() != ACTIVITY_TYPE_UNDEFINED + ? options.getLaunchActivityType() : r.getActivityType(); + return activityDisplay.createStack(windowingMode, activityType, true /*onTop*/); + } + + Slog.w(TAG, "getValidLaunchStackOnDisplay: can't launch on displayId " + displayId); + return null; + } + + ActivityStack getValidLaunchStackOnDisplay(int displayId, @NonNull ActivityRecord r, + @Nullable ActivityOptions options, + @Nullable LaunchParamsController.LaunchParams launchParams) { + return getValidLaunchStackOnDisplay(displayId, r, null /* candidateTask */, options, + launchParams); + } + + // TODO: Can probably be consolidated into getLaunchStack()... + private boolean isValidLaunchStack(ActivityStack stack, ActivityRecord r) { + switch (stack.getActivityType()) { + case ACTIVITY_TYPE_HOME: return r.isActivityTypeHome(); + case ACTIVITY_TYPE_RECENTS: return r.isActivityTypeRecents(); + case ACTIVITY_TYPE_ASSISTANT: return r.isActivityTypeAssistant(); + } + // There is a 1-to-1 relationship between stack and task when not in + // primary split-windowing mode. + if (stack.getWindowingMode() != WINDOWING_MODE_SPLIT_SCREEN_PRIMARY) { + return false; + } else { + return r.supportsSplitScreenWindowingMode(); + } + } + + int resolveActivityType(@Nullable ActivityRecord r, @Nullable ActivityOptions options, + @Nullable TaskRecord task) { + // Preference is given to the activity type for the activity then the task since the type + // once set shouldn't change. + int activityType = r != null ? r.getActivityType() : ACTIVITY_TYPE_UNDEFINED; + if (activityType == ACTIVITY_TYPE_UNDEFINED && task != null) { + activityType = task.getActivityType(); + } + if (activityType != ACTIVITY_TYPE_UNDEFINED) { + return activityType; + } + if (options != null) { + activityType = options.getLaunchActivityType(); + } + return activityType != ACTIVITY_TYPE_UNDEFINED ? activityType : ACTIVITY_TYPE_STANDARD; + } + + /** + * Get next focusable stack in the system. This will search through the stack on the same + * display as the current focused stack, looking for a focusable and visible stack, different + * from the target stack. If no valid candidates will be found, it will then go through all + * displays and stacks in last-focused order. + * + * @param currentFocus The stack that previously had focus. + * @param ignoreCurrent If we should ignore {@param currentFocus} when searching for next + * candidate. + * @return Next focusable {@link ActivityStack}, {@code null} if not found. + */ + ActivityStack getNextFocusableStack(@NonNull ActivityStack currentFocus, + boolean ignoreCurrent) { + // First look for next focusable stack on the same display + final ActivityDisplay preferredDisplay = currentFocus.getDisplay(); + final ActivityStack preferredFocusableStack = preferredDisplay.getNextFocusableStack( + currentFocus, ignoreCurrent); + if (preferredFocusableStack != null) { + return preferredFocusableStack; + } + if (preferredDisplay.supportsSystemDecorations()) { + // Stop looking for focusable stack on other displays because the preferred display + // supports system decorations. Home activity would be launched on the same display if + // no focusable stack found. + return null; + } + + // Now look through all displays + for (int i = mActivityDisplays.size() - 1; i >= 0; --i) { + final ActivityDisplay display = mActivityDisplays.get(i); + if (display == preferredDisplay) { + // We've already checked this one + continue; + } + final ActivityStack nextFocusableStack = display.getNextFocusableStack(currentFocus, + ignoreCurrent); + if (nextFocusableStack != null) { + return nextFocusableStack; + } + } + + return null; + } + + /** + * Get next valid stack for launching provided activity in the system. This will search across + * displays and stacks in last-focused order for a focusable and visible stack, except those + * that are on a currently focused display. + * + * @param r The activity that is being launched. + * @param currentFocus The display that previously had focus and thus needs to be ignored when + * searching for the next candidate. + * @return Next valid {@link ActivityStack}, null if not found. + */ + ActivityStack getNextValidLaunchStack(@NonNull ActivityRecord r, int currentFocus) { + for (int i = mActivityDisplays.size() - 1; i >= 0; --i) { + final ActivityDisplay display = mActivityDisplays.get(i); + if (display.mDisplayId == currentFocus) { + continue; + } + final ActivityStack stack = getValidLaunchStackOnDisplay(display.mDisplayId, r, + null /* options */, null /* launchParams */); + if (stack != null) { + return stack; + } + } + return null; + } + + boolean handleAppDied(WindowProcessController app) { + boolean hasVisibleActivities = false; + for (int displayNdx = mActivityDisplays.size() - 1; displayNdx >= 0; --displayNdx) { + final ActivityDisplay display = mActivityDisplays.get(displayNdx); + for (int stackNdx = display.getChildCount() - 1; stackNdx >= 0; --stackNdx) { + final ActivityStack stack = display.getChildAt(stackNdx); + hasVisibleActivities |= stack.handleAppDiedLocked(app); + } + } + return hasVisibleActivities; + } + + void closeSystemDialogs() { + for (int displayNdx = mActivityDisplays.size() - 1; displayNdx >= 0; --displayNdx) { + final ActivityDisplay display = mActivityDisplays.get(displayNdx); + for (int stackNdx = display.getChildCount() - 1; stackNdx >= 0; --stackNdx) { + final ActivityStack stack = display.getChildAt(stackNdx); + stack.closeSystemDialogsLocked(); + } + } + } + + /** @return true if some activity was finished (or would have finished if doit were true). */ + boolean finishDisabledPackageActivities(String packageName, Set<String> filterByClasses, + boolean doit, boolean evenPersistent, int userId) { + boolean didSomething = false; + for (int displayNdx = mActivityDisplays.size() - 1; displayNdx >= 0; --displayNdx) { + final ActivityDisplay display = mActivityDisplays.get(displayNdx); + for (int stackNdx = display.getChildCount() - 1; stackNdx >= 0; --stackNdx) { + final ActivityStack stack = display.getChildAt(stackNdx); + if (stack.finishDisabledPackageActivitiesLocked( + packageName, filterByClasses, doit, evenPersistent, userId)) { + didSomething = true; + } + } + } + return didSomething; + } + + void updateActivityApplicationInfo(ApplicationInfo aInfo) { + for (int displayNdx = mActivityDisplays.size() - 1; displayNdx >= 0; --displayNdx) { + final ActivityDisplay display = mActivityDisplays.get(displayNdx); + for (int stackNdx = display.getChildCount() - 1; stackNdx >= 0; --stackNdx) { + final ActivityStack stack = display.getChildAt(stackNdx); + stack.updateActivityApplicationInfoLocked(aInfo); + } + } + } + + void finishVoiceTask(IVoiceInteractionSession session) { + for (int displayNdx = mActivityDisplays.size() - 1; displayNdx >= 0; --displayNdx) { + final ActivityDisplay display = mActivityDisplays.get(displayNdx); + final int numStacks = display.getChildCount(); + for (int stackNdx = 0; stackNdx < numStacks; ++stackNdx) { + final ActivityStack stack = display.getChildAt(stackNdx); + stack.finishVoiceTask(session); + } + } + } + + /** + * 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) { + for (int i = mActivityDisplays.size() - 1; i >= 0; --i) { + mActivityDisplays.get(i).removeStacksInWindowingModes(windowingModes); + } + } + + void removeStacksWithActivityTypes(int... activityTypes) { + for (int i = mActivityDisplays.size() - 1; i >= 0; --i) { + mActivityDisplays.get(i).removeStacksWithActivityTypes(activityTypes); + } + } + + ActivityRecord topRunningActivity() { + for (int i = mActivityDisplays.size() - 1; i >= 0; --i) { + final ActivityRecord topActivity = mActivityDisplays.get(i).topRunningActivity(); + if (topActivity != null) { + return topActivity; + } + } + return null; + } + + boolean allResumedActivitiesIdle() { + for (int displayNdx = mActivityDisplays.size() - 1; displayNdx >= 0; --displayNdx) { + // TODO(b/117135575): Check resumed activities on all visible stacks. + final ActivityDisplay display = mActivityDisplays.get(displayNdx); + if (display.isSleeping()) { + // No resumed activities while display is sleeping. + continue; + } + + // If the focused stack is not null or not empty, there should have some activities + // resuming or resumed. Make sure these activities are idle. + final ActivityStack stack = display.getFocusedStack(); + if (stack == null || stack.numActivities() == 0) { + continue; + } + final ActivityRecord resumedActivity = stack.getResumedActivity(); + if (resumedActivity == null || !resumedActivity.idle) { + if (DEBUG_STATES) { + Slog.d(TAG_STATES, "allResumedActivitiesIdle: stack=" + + stack.mStackId + " " + resumedActivity + " not idle"); + } + return false; + } + } + // Send launch end powerhint when idle + sendPowerHintForLaunchEndIfNeeded(); + return true; + } + + boolean allResumedActivitiesVisible() { + boolean foundResumed = false; + for (int displayNdx = mActivityDisplays.size() - 1; displayNdx >= 0; --displayNdx) { + final ActivityDisplay display = mActivityDisplays.get(displayNdx); + for (int stackNdx = display.getChildCount() - 1; stackNdx >= 0; --stackNdx) { + final ActivityStack stack = display.getChildAt(stackNdx); + final ActivityRecord r = stack.getResumedActivity(); + if (r != null) { + if (!r.nowVisible + || mStackSupervisor.mActivitiesWaitingForVisibleActivity.contains(r)) { + return false; + } + foundResumed = true; + } + } + } + return foundResumed; + } + + boolean allPausedActivitiesComplete() { + boolean pausing = true; + for (int displayNdx = mActivityDisplays.size() - 1; displayNdx >= 0; --displayNdx) { + final ActivityDisplay display = mActivityDisplays.get(displayNdx); + for (int stackNdx = display.getChildCount() - 1; stackNdx >= 0; --stackNdx) { + final ActivityStack stack = display.getChildAt(stackNdx); + final ActivityRecord r = stack.mPausingActivity; + if (r != null && !r.isState(PAUSED, STOPPED, STOPPING)) { + if (DEBUG_STATES) { + Slog.d(TAG_STATES, + "allPausedActivitiesComplete: r=" + r + " state=" + r.getState()); + pausing = false; + } else { + return false; + } + } + } + } + return pausing; + } + + /** + * Find all visible task stacks containing {@param userId} and intercept them with an activity + * to block out the contents and possibly start a credential-confirming intent. + * + * @param userId user handle for the locked managed profile. + */ + void lockAllProfileTasks(@UserIdInt int userId) { + mWindowManager.deferSurfaceLayout(); + try { + for (int displayNdx = mActivityDisplays.size() - 1; displayNdx >= 0; --displayNdx) { + final ActivityDisplay display = mActivityDisplays.get(displayNdx); + for (int stackNdx = display.getChildCount() - 1; stackNdx >= 0; --stackNdx) { + final ActivityStack stack = display.getChildAt(stackNdx); + final List<TaskRecord> tasks = stack.getAllTasks(); + for (int taskNdx = tasks.size() - 1; taskNdx >= 0; taskNdx--) { + final TaskRecord task = tasks.get(taskNdx); + + // Check the task for a top activity belonging to userId, or returning a + // result to an activity belonging to userId. Example case: a document + // picker for personal files, opened by a work app, should still get locked. + if (taskTopActivityIsUser(task, userId)) { + mService.getTaskChangeNotificationController().notifyTaskProfileLocked( + task.taskId, userId); + } + } + } + } + } finally { + mWindowManager.continueSurfaceLayout(); + } + } + + /** + * Detects whether we should show a lock screen in front of this task for a locked user. + * <p> + * We'll do this if either of the following holds: + * <ul> + * <li>The top activity explicitly belongs to {@param userId}.</li> + * <li>The top activity returns a result to an activity belonging to {@param userId}.</li> + * </ul> + * + * @return {@code true} if the top activity looks like it belongs to {@param userId}. + */ + private boolean taskTopActivityIsUser(TaskRecord task, @UserIdInt int userId) { + // To handle the case that work app is in the task but just is not the top one. + final ActivityRecord activityRecord = task.getTopActivity(); + final ActivityRecord resultTo = (activityRecord != null ? activityRecord.resultTo : null); + + return (activityRecord != null && activityRecord.userId == userId) + || (resultTo != null && resultTo.userId == userId); + } + + void cancelInitializingActivities() { + for (int displayNdx = mActivityDisplays.size() - 1; displayNdx >= 0; --displayNdx) { + final ActivityDisplay display = mActivityDisplays.get(displayNdx); + for (int stackNdx = display.getChildCount() - 1; stackNdx >= 0; --stackNdx) { + final ActivityStack stack = display.getChildAt(stackNdx); + stack.cancelInitializingActivities(); + } + } + } + + TaskRecord anyTaskForId(int id) { + return anyTaskForId(id, MATCH_TASK_IN_STACKS_OR_RECENT_TASKS_AND_RESTORE); + } + + TaskRecord anyTaskForId(int id, @AnyTaskForIdMatchTaskMode int matchMode) { + return anyTaskForId(id, matchMode, null, !ON_TOP); + } + + /** + * Returns a {@link TaskRecord} for the input id if available. {@code null} otherwise. + * @param id Id of the task we would like returned. + * @param matchMode The mode to match the given task id in. + * @param aOptions The activity options to use for restoration. Can be null. + * @param onTop If the stack for the task should be the topmost on the display. + */ + TaskRecord anyTaskForId(int id, @AnyTaskForIdMatchTaskMode int matchMode, + @Nullable ActivityOptions aOptions, boolean onTop) { + // If options are set, ensure that we are attempting to actually restore a task + if (matchMode != MATCH_TASK_IN_STACKS_OR_RECENT_TASKS_AND_RESTORE && aOptions != null) { + throw new IllegalArgumentException("Should not specify activity options for non-restore" + + " lookup"); + } + + int numDisplays = mActivityDisplays.size(); + for (int displayNdx = 0; displayNdx < numDisplays; ++displayNdx) { + final ActivityDisplay display = mActivityDisplays.get(displayNdx); + for (int stackNdx = display.getChildCount() - 1; stackNdx >= 0; --stackNdx) { + final ActivityStack stack = display.getChildAt(stackNdx); + final TaskRecord task = stack.taskForIdLocked(id); + if (task == null) { + continue; + } + if (aOptions != null) { + // Resolve the stack the task should be placed in now based on options + // and reparent if needed. + final ActivityStack launchStack = + getLaunchStack(null, aOptions, task, onTop); + if (launchStack != null && stack != launchStack) { + final int reparentMode = onTop + ? REPARENT_MOVE_STACK_TO_FRONT : REPARENT_LEAVE_STACK_IN_PLACE; + task.reparent(launchStack, onTop, reparentMode, ANIMATE, DEFER_RESUME, + "anyTaskForId"); + } + } + return task; + } + } + + // If we are matching stack tasks only, return now + if (matchMode == MATCH_TASK_IN_STACKS_ONLY) { + return null; + } + + // Otherwise, check the recent tasks and return if we find it there and we are not restoring + // the task from recents + if (DEBUG_RECENTS) Slog.v(TAG_RECENTS, "Looking for task id=" + id + " in recents"); + final TaskRecord task = mStackSupervisor.mRecentTasks.getTask(id); + + if (task == null) { + if (DEBUG_RECENTS) { + Slog.d(TAG_RECENTS, "\tDidn't find task id=" + id + " in recents"); + } + + return null; + } + + if (matchMode == MATCH_TASK_IN_STACKS_OR_RECENT_TASKS) { + return task; + } + + // Implicitly, this case is MATCH_TASK_IN_STACKS_OR_RECENT_TASKS_AND_RESTORE + if (!mStackSupervisor.restoreRecentTaskLocked(task, aOptions, onTop)) { + if (DEBUG_RECENTS) Slog.w(TAG_RECENTS, + "Couldn't restore task id=" + id + " found in recents"); + return null; + } + if (DEBUG_RECENTS) Slog.w(TAG_RECENTS, "Restored task id=" + id + " from in recents"); + return task; + } + + ActivityRecord isInAnyStack(IBinder token) { + int numDisplays = mActivityDisplays.size(); + for (int displayNdx = 0; displayNdx < numDisplays; ++displayNdx) { + final ActivityDisplay display = mActivityDisplays.get(displayNdx); + for (int stackNdx = display.getChildCount() - 1; stackNdx >= 0; --stackNdx) { + final ActivityStack stack = display.getChildAt(stackNdx); + final ActivityRecord r = stack.isInStackLocked(token); + if (r != null) { + return r; + } + } + } + return null; + } + + @VisibleForTesting + void getRunningTasks(int maxNum, List<ActivityManager.RunningTaskInfo> list, + @WindowConfiguration.ActivityType int ignoreActivityType, + @WindowConfiguration.WindowingMode int ignoreWindowingMode, int callingUid, + boolean allowed) { + mStackSupervisor.mRunningTasks.getTasks(maxNum, list, ignoreActivityType, + ignoreWindowingMode, mActivityDisplays, callingUid, allowed); + } + + void sendPowerHintForLaunchStartIfNeeded(boolean forceSend, ActivityRecord targetActivity) { + boolean sendHint = forceSend; + + if (!sendHint) { + // Send power hint if we don't know what we're launching yet + sendHint = targetActivity == null || targetActivity.app == null; + } + + if (!sendHint) { // targetActivity != null + // Send power hint when the activity's process is different than the current resumed + // activity on all displays, or if there are no resumed activities in the system. + boolean noResumedActivities = true; + boolean allFocusedProcessesDiffer = true; + for (int displayNdx = 0; displayNdx < mActivityDisplays.size(); ++displayNdx) { + final ActivityDisplay activityDisplay = mActivityDisplays.get(displayNdx); + final ActivityRecord resumedActivity = activityDisplay.getResumedActivity(); + final WindowProcessController resumedActivityProcess = + resumedActivity == null ? null : resumedActivity.app; + + noResumedActivities &= resumedActivityProcess == null; + if (resumedActivityProcess != null) { + allFocusedProcessesDiffer &= !resumedActivityProcess.equals(targetActivity.app); + } + } + sendHint = noResumedActivities || allFocusedProcessesDiffer; + } + + if (sendHint && mService.mPowerManagerInternal != null) { + mService.mPowerManagerInternal.powerHint(PowerHint.LAUNCH, 1); + mPowerHintSent = true; + } + } + + void sendPowerHintForLaunchEndIfNeeded() { + // Trigger launch power hint if activity is launched + if (mPowerHintSent && mService.mPowerManagerInternal != null) { + mService.mPowerManagerInternal.powerHint(PowerHint.LAUNCH, 0); + mPowerHintSent = false; + } + } + + private void calculateDefaultMinimalSizeOfResizeableTasks() { + final Resources res = mService.mContext.getResources(); + final float minimalSize = res.getDimension( + com.android.internal.R.dimen.default_minimal_size_resizable_task); + final DisplayMetrics dm = res.getDisplayMetrics(); + + mDefaultMinSizeOfResizeableTaskDp = (int) (minimalSize / dm.density); + } + + /** + * Dumps the activities matching the given {@param name} in the either the focused stack + * or all visible stacks if {@param dumpVisibleStacks} is true. + */ + ArrayList<ActivityRecord> getDumpActivities(String name, boolean dumpVisibleStacksOnly, + boolean dumpFocusedStackOnly) { + if (dumpFocusedStackOnly) { + return getTopDisplayFocusedStack().getDumpActivitiesLocked(name); + } else { + ArrayList<ActivityRecord> activities = new ArrayList<>(); + int numDisplays = mActivityDisplays.size(); + for (int displayNdx = 0; displayNdx < numDisplays; ++displayNdx) { + final ActivityDisplay display = mActivityDisplays.get(displayNdx); + for (int stackNdx = display.getChildCount() - 1; stackNdx >= 0; --stackNdx) { + final ActivityStack stack = display.getChildAt(stackNdx); + if (!dumpVisibleStacksOnly || stack.shouldBeVisible(null)) { + activities.addAll(stack.getDumpActivitiesLocked(name)); + } + } + } + return activities; + } + } + + public void dump(PrintWriter pw, String prefix) { + pw.print(prefix); + pw.println("topDisplayFocusedStack=" + getTopDisplayFocusedStack()); + for (int i = mActivityDisplays.size() - 1; i >= 0; --i) { + final ActivityDisplay display = mActivityDisplays.get(i); + display.dump(pw, prefix); + } + } + + /** + * Dump all connected displays' configurations. + * @param prefix Prefix to apply to each line of the dump. + */ + void dumpDisplayConfigs(PrintWriter pw, String prefix) { + pw.print(prefix); pw.println("Display override configurations:"); + final int displayCount = mActivityDisplays.size(); + for (int i = 0; i < displayCount; i++) { + final ActivityDisplay activityDisplay = mActivityDisplays.get(i); + pw.print(prefix); pw.print(" "); pw.print(activityDisplay.mDisplayId); pw.print(": "); + pw.println(activityDisplay.getOverrideConfiguration()); + } + } + + public void dumpDisplays(PrintWriter pw) { + for (int i = mActivityDisplays.size() - 1; i >= 0; --i) { + final ActivityDisplay display = mActivityDisplays.get(i); + pw.print("[id:" + display.mDisplayId + " stacks:"); + display.dumpStacks(pw); + pw.print("]"); + } + } + + boolean dumpActivities(FileDescriptor fd, PrintWriter pw, boolean dumpAll, boolean dumpClient, + String dumpPackage) { + boolean printed = false; + boolean needSep = false; + for (int displayNdx = mActivityDisplays.size() - 1; displayNdx >= 0; --displayNdx) { + ActivityDisplay activityDisplay = mActivityDisplays.get(displayNdx); + pw.print("Display #"); pw.print(activityDisplay.mDisplayId); + pw.println(" (activities from top to bottom):"); + final ActivityDisplay display = mActivityDisplays.get(displayNdx); + for (int stackNdx = display.getChildCount() - 1; stackNdx >= 0; --stackNdx) { + final ActivityStack stack = display.getChildAt(stackNdx); + pw.println(); + pw.println(" Stack #" + stack.mStackId + + ": type=" + activityTypeToString(stack.getActivityType()) + + " mode=" + windowingModeToString(stack.getWindowingMode())); + pw.println(" isSleeping=" + stack.shouldSleepActivities()); + pw.println(" mBounds=" + stack.getOverrideBounds()); + + printed |= stack.dumpActivitiesLocked(fd, pw, dumpAll, dumpClient, dumpPackage, + needSep); + + printed |= dumpHistoryList(fd, pw, stack.mLRUActivities, " ", "Run", false, + !dumpAll, false, dumpPackage, true, + " Running activities (most recent first):", null); + + needSep = printed; + boolean pr = printThisActivity(pw, stack.mPausingActivity, dumpPackage, needSep, + " mPausingActivity: "); + if (pr) { + printed = true; + needSep = false; + } + pr = printThisActivity(pw, stack.getResumedActivity(), dumpPackage, needSep, + " mResumedActivity: "); + if (pr) { + printed = true; + needSep = false; + } + if (dumpAll) { + pr = printThisActivity(pw, stack.mLastPausedActivity, dumpPackage, needSep, + " mLastPausedActivity: "); + if (pr) { + printed = true; + needSep = true; + } + printed |= printThisActivity(pw, stack.mLastNoHistoryActivity, dumpPackage, + needSep, " mLastNoHistoryActivity: "); + } + needSep = printed; + } + printThisActivity(pw, activityDisplay.getResumedActivity(), dumpPackage, needSep, + " ResumedActivity:"); + } + + printed |= dumpHistoryList(fd, pw, mStackSupervisor.mFinishingActivities, " ", + "Fin", false, !dumpAll, + false, dumpPackage, true, " Activities waiting to finish:", null); + printed |= dumpHistoryList(fd, pw, mStackSupervisor.mStoppingActivities, " ", + "Stop", false, !dumpAll, + false, dumpPackage, true, " Activities waiting to stop:", null); + printed |= dumpHistoryList(fd, pw, + mStackSupervisor.mActivitiesWaitingForVisibleActivity, " ", "Wait", + false, !dumpAll, false, dumpPackage, true, + " Activities waiting for another to become visible:", null); + printed |= dumpHistoryList(fd, pw, mStackSupervisor.mGoingToSleepActivities, + " ", "Sleep", false, !dumpAll, + false, dumpPackage, true, " Activities waiting to sleep:", null); + + return printed; + } + + void writeToProto(ProtoOutputStream proto, long fieldId) { + final long token = proto.start(fieldId); + super.writeToProto(proto, CONFIGURATION_CONTAINER, false /* trim */); + for (int displayNdx = 0; displayNdx < mActivityDisplays.size(); ++displayNdx) { + final ActivityDisplay activityDisplay = mActivityDisplays.get(displayNdx); + activityDisplay.writeToProto(proto, DISPLAYS); + } + mStackSupervisor.getKeyguardController().writeToProto(proto, KEYGUARD_CONTROLLER); + // TODO(b/111541062): Update tests to look for resumed activities on all displays + final ActivityStack focusedStack = getTopDisplayFocusedStack(); + if (focusedStack != null) { + proto.write(FOCUSED_STACK_ID, focusedStack.mStackId); + final ActivityRecord focusedActivity = focusedStack.getDisplay().getResumedActivity(); + if (focusedActivity != null) { + focusedActivity.writeIdentifierToProto(proto, RESUMED_ACTIVITY); + } + } else { + proto.write(FOCUSED_STACK_ID, INVALID_STACK_ID); + } + proto.write(IS_HOME_RECENTS_COMPONENT, + mStackSupervisor.mRecentTasks.isRecentsComponentHomeActivity(mCurrentUser)); + mService.getActivityStartController().writeToProto(proto, PENDING_ACTIVITIES); + proto.end(token); + } + + private final class SleepTokenImpl extends ActivityTaskManagerInternal.SleepToken { + private final String mTag; + private final long mAcquireTime; + private final int mDisplayId; + + public SleepTokenImpl(String tag, int displayId) { + mTag = tag; + mDisplayId = displayId; + mAcquireTime = SystemClock.uptimeMillis(); + } + + @Override + public void release() { + synchronized (mService.mGlobalLock) { + removeSleepToken(this); + } + } + + @Override + public String toString() { + return "{\"" + mTag + "\", display " + mDisplayId + + ", acquire at " + TimeUtils.formatUptime(mAcquireTime) + "}"; + } + } +} diff --git a/services/core/java/com/android/server/wm/RootWindowContainer.java b/services/core/java/com/android/server/wm/RootWindowContainer.java index fd9120a6c12e..c2bc677f5e39 100644 --- a/services/core/java/com/android/server/wm/RootWindowContainer.java +++ b/services/core/java/com/android/server/wm/RootWindowContainer.java @@ -82,12 +82,16 @@ import java.util.ArrayList; import java.util.function.Consumer; /** Root {@link WindowContainer} for the device. */ -class RootWindowContainer extends WindowContainer<DisplayContent> { +class RootWindowContainer extends WindowContainer<DisplayContent> + implements ConfigurationContainerListener { private static final String TAG = TAG_WITH_CLASS_NAME ? "RootWindowContainer" : TAG_WM; private static final int SET_SCREEN_BRIGHTNESS_OVERRIDE = 1; private static final int SET_USER_ACTIVITY_TIMEOUT = 2; + // TODO: Remove after object merge with RootActivityContainer. + private RootActivityContainer mRootActivityContainer; + private Object mLastWindowFreezeSource = null; private Session mHoldScreen = null; private float mScreenBrightness = -1; @@ -145,6 +149,13 @@ class RootWindowContainer extends WindowContainer<DisplayContent> { mHandler = new MyHandler(service.mH.getLooper()); } + void setRootActivityContainer(RootActivityContainer container) { + mRootActivityContainer = container; + if (container != null) { + container.registerConfigurationChangeListener(this); + } + } + boolean updateFocusedWindowLocked(int mode, boolean updateInputWindows) { boolean changed = false; int topFocusedDisplayId = INVALID_DISPLAY; @@ -495,9 +506,8 @@ class RootWindowContainer extends WindowContainer<DisplayContent> { if (SHOW_TRANSACTIONS || SHOW_SURFACE_ALLOC) logSurface(winAnimator.mWin, "RECOVER DESTROY", false); winAnimator.destroySurface(); - if (winAnimator.mWin.mAppToken != null - && winAnimator.mWin.mAppToken.getController() != null) { - winAnimator.mWin.mAppToken.getController().removeStartingWindow(); + if (winAnimator.mWin.mAppToken != null) { + winAnimator.mWin.mAppToken.removeStartingWindow(); } } @@ -1013,9 +1023,8 @@ class RootWindowContainer extends WindowContainer<DisplayContent> { @Override void positionChildAt(int position, DisplayContent child, boolean includingParents) { super.positionChildAt(position, child, includingParents); - final RootWindowContainerController controller = getController(); - if (controller != null) { - controller.onChildPositionChanged(child, position); + if (mRootActivityContainer != null) { + mRootActivityContainer.onChildPositionChanged(child.getController(), position); } } @@ -1025,11 +1034,6 @@ class RootWindowContainer extends WindowContainer<DisplayContent> { } @Override - RootWindowContainerController getController() { - return (RootWindowContainerController) super.getController(); - } - - @Override void scheduleAnimation() { mService.scheduleAnimationLocked(); } diff --git a/services/core/java/com/android/server/wm/RootWindowContainerController.java b/services/core/java/com/android/server/wm/RootWindowContainerController.java deleted file mode 100644 index 11762201852b..000000000000 --- a/services/core/java/com/android/server/wm/RootWindowContainerController.java +++ /dev/null @@ -1,46 +0,0 @@ -/* - * Copyright (C) 2018 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; - -/** - * Controller for the root container. This is created by activity manager to link activity - * stack supervisor to the root window container they use in window manager. - */ -public class RootWindowContainerController - extends WindowContainerController<RootWindowContainer, RootWindowContainerListener> { - - public RootWindowContainerController(RootWindowContainerListener listener) { - super(listener, WindowManagerService.getInstance()); - synchronized (mGlobalLock) { - mRoot.setController(this); - } - } - - void onChildPositionChanged(DisplayContent child, int position) { - // This callback invokes to AM directly so here assumes AM lock is held. If there is another - // path called only with WM lock, it should change to use handler to post or move outside of - // WM lock with adding AM lock. - mListener.onChildPositionChanged(child.getController(), position); - } - - /** Move the display to the given position. */ - public void positionChildAt(DisplayWindowController child, int position) { - synchronized (mGlobalLock) { - mContainer.positionChildAt(position, child.mContainer); - } - } -} diff --git a/services/core/java/com/android/server/wm/RootWindowContainerListener.java b/services/core/java/com/android/server/wm/RootWindowContainerListener.java deleted file mode 100644 index f413e3f7c2ea..000000000000 --- a/services/core/java/com/android/server/wm/RootWindowContainerListener.java +++ /dev/null @@ -1,26 +0,0 @@ -/* - * Copyright (C) 2018 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; - -/** - * Interface used by the creator of {@link RootWindowContainerController} to notify the changes to - * the display container in activity manager. - */ -public interface RootWindowContainerListener extends WindowContainerListener { - /** Called when the z-order of display is changed. */ - void onChildPositionChanged(DisplayWindowController childController, int position); -} diff --git a/services/core/java/com/android/server/wm/TEST_MAPPING b/services/core/java/com/android/server/wm/TEST_MAPPING index 0c9a14be964f..bbe542458b87 100644 --- a/services/core/java/com/android/server/wm/TEST_MAPPING +++ b/services/core/java/com/android/server/wm/TEST_MAPPING @@ -1,17 +1,6 @@ { "presubmit": [ { - "name": "CtsWindowManagerDeviceTestCases", - "options": [ - { - "include-annotation": "android.platform.test.annotations.Presubmit" - }, - { - "exclude-annotation": "android.support.test.filters.FlakyTest" - } - ] - }, - { "name": "FrameworksServicesTests", "options": [ { diff --git a/services/core/java/com/android/server/wm/TaskLaunchParamsModifier.java b/services/core/java/com/android/server/wm/TaskLaunchParamsModifier.java index 117984af67e7..4ae2a79e2697 100644 --- a/services/core/java/com/android/server/wm/TaskLaunchParamsModifier.java +++ b/services/core/java/com/android/server/wm/TaskLaunchParamsModifier.java @@ -138,7 +138,7 @@ class TaskLaunchParamsModifier implements LaunchParamsModifier { // STEP 1: Determine the display to launch the activity/task. final int displayId = getPreferredLaunchDisplay(task, options, source, currentParams); outParams.mPreferredDisplayId = displayId; - ActivityDisplay display = mSupervisor.getActivityDisplay(displayId); + ActivityDisplay display = mSupervisor.mRootActivityContainer.getActivityDisplay(displayId); if (DEBUG) { appendLog("display-id=" + outParams.mPreferredDisplayId + " display-windowing-mode=" + display.getWindowingMode()); @@ -300,12 +300,14 @@ class TaskLaunchParamsModifier implements LaunchParamsModifier { displayId = stack.mDisplayId; } - if (displayId != INVALID_DISPLAY && mSupervisor.getActivityDisplay(displayId) == null) { + if (displayId != INVALID_DISPLAY + && mSupervisor.mRootActivityContainer.getActivityDisplay(displayId) == null) { displayId = currentParams.mPreferredDisplayId; } displayId = (displayId == INVALID_DISPLAY) ? currentParams.mPreferredDisplayId : displayId; - return (displayId != INVALID_DISPLAY && mSupervisor.getActivityDisplay(displayId) != null) + return (displayId != INVALID_DISPLAY + && mSupervisor.mRootActivityContainer.getActivityDisplay(displayId) != null) ? displayId : DEFAULT_DISPLAY; } @@ -606,7 +608,8 @@ class TaskLaunchParamsModifier implements LaunchParamsModifier { || displayBounds.height() < inOutBounds.height()) { // There is no way for us to fit the bounds in the display without changing width // or height. Just move the start to align with the display. - final int layoutDirection = mSupervisor.getConfiguration().getLayoutDirection(); + final int layoutDirection = + mSupervisor.mRootActivityContainer.getConfiguration().getLayoutDirection(); final int left = layoutDirection == View.LAYOUT_DIRECTION_RTL ? displayBounds.width() - inOutBounds.width() : 0; diff --git a/services/core/java/com/android/server/wm/TaskPersister.java b/services/core/java/com/android/server/wm/TaskPersister.java index 8120dec7e48f..d50af385865e 100644 --- a/services/core/java/com/android/server/wm/TaskPersister.java +++ b/services/core/java/com/android/server/wm/TaskPersister.java @@ -16,7 +16,7 @@ package com.android.server.wm; -import static com.android.server.wm.ActivityStackSupervisor.MATCH_TASK_IN_STACKS_OR_RECENT_TASKS; +import static com.android.server.wm.RootActivityContainer.MATCH_TASK_IN_STACKS_OR_RECENT_TASKS; import android.annotation.NonNull; import android.graphics.Bitmap; @@ -330,7 +330,7 @@ public class TaskPersister implements PersisterQueue.Listener { // mWriteQueue.add(new TaskWriteQueueItem(task)); final int taskId = task.taskId; - if (mStackSupervisor.anyTaskForIdLocked(taskId, + if (mService.mRootActivityContainer.anyTaskForId(taskId, MATCH_TASK_IN_STACKS_OR_RECENT_TASKS) != null) { // Should not happen. Slog.wtf(TAG, "Existing task with taskId " + taskId + "found"); diff --git a/services/core/java/com/android/server/wm/TaskRecord.java b/services/core/java/com/android/server/wm/TaskRecord.java index eec10aba5df2..8a3dbada8d3f 100644 --- a/services/core/java/com/android/server/wm/TaskRecord.java +++ b/services/core/java/com/android/server/wm/TaskRecord.java @@ -472,8 +472,8 @@ public class TaskRecord extends ConfigurationContainer implements TaskWindowCont } mResizeMode = resizeMode; mWindowContainerController.setResizeable(resizeMode); - mService.mStackSupervisor.ensureActivitiesVisibleLocked(null, 0, !PRESERVE_WINDOWS); - mService.mStackSupervisor.resumeFocusedStacksTopActivitiesLocked(); + mService.mRootActivityContainer.ensureActivitiesVisible(null, 0, !PRESERVE_WINDOWS); + mService.mRootActivityContainer.resumeFocusedStacksTopActivities(); } void setTaskDockedResizing(boolean resizing) { @@ -544,10 +544,9 @@ public class TaskRecord extends ConfigurationContainer implements TaskWindowCont // this won't cause tons of irrelevant windows being preserved because only // activities in this task may experience a bounds change. Configs for other // activities stay the same. - mService.mStackSupervisor.ensureActivitiesVisibleLocked(r, 0, - preserveWindow); + mService.mRootActivityContainer.ensureActivitiesVisible(r, 0, preserveWindow); if (!kept) { - mService.mStackSupervisor.resumeFocusedStacksTopActivitiesLocked(); + mService.mRootActivityContainer.resumeFocusedStacksTopActivities(); } } } @@ -623,6 +622,7 @@ public class TaskRecord extends ConfigurationContainer implements TaskWindowCont @ReparentMoveStackMode int moveStackMode, boolean animate, boolean deferResume, boolean schedulePictureInPictureModeChange, String reason) { final ActivityStackSupervisor supervisor = mService.mStackSupervisor; + final RootActivityContainer root = mService.mRootActivityContainer; final WindowManagerService windowManager = mService.mWindowManager; final ActivityStack sourceStack = getStack(); final ActivityStack toStack = supervisor.getReparentTargetStack(this, preferredStack, @@ -655,7 +655,7 @@ public class TaskRecord extends ConfigurationContainer implements TaskWindowCont boolean kept = true; try { final ActivityRecord r = topRunningActivityLocked(); - final boolean wasFocused = r != null && supervisor.isTopDisplayFocusedStack(sourceStack) + final boolean wasFocused = r != null && root.isTopDisplayFocusedStack(sourceStack) && (topRunningActivityLocked() == r); final boolean wasResumed = r != null && sourceStack.getResumedActivity() == r; final boolean wasPaused = r != null && sourceStack.mPausingActivity == r; @@ -748,8 +748,8 @@ public class TaskRecord extends ConfigurationContainer implements TaskWindowCont if (!deferResume) { // The task might have already been running and its visibility needs to be synchronized // with the visibility of the stack / windows. - supervisor.ensureActivitiesVisibleLocked(null, 0, !mightReplaceWindow); - supervisor.resumeFocusedStacksTopActivitiesLocked(); + root.ensureActivitiesVisible(null, 0, !mightReplaceWindow); + root.resumeFocusedStacksTopActivities(); } // TODO: Handle incorrect request to move before the actual move, not after. @@ -982,7 +982,7 @@ public class TaskRecord extends ConfigurationContainer implements TaskWindowCont @Override protected void onParentChanged() { super.onParentChanged(); - mService.mStackSupervisor.updateUIDsPresentOnDisplay(); + mService.mRootActivityContainer.updateUIDsPresentOnDisplay(); } // Close up recents linked list. @@ -1143,7 +1143,7 @@ public class TaskRecord extends ConfigurationContainer implements TaskWindowCont } boolean okToShowLocked() { - // NOTE: If {@link TaskRecord#topRunningActivityLocked} return is not null then it is + // NOTE: If {@link TaskRecord#topRunningActivity} return is not null then it is // okay to show the activity when locked. return mService.mStackSupervisor.isCurrentProfileLocked(userId) || topRunningActivityLocked() != null; @@ -1182,7 +1182,7 @@ public class TaskRecord extends ConfigurationContainer implements TaskWindowCont mActivities.add(newTop); // Make sure window manager is aware of the position change. - mWindowContainerController.positionChildAtTop(newTop.mWindowContainerController); + mWindowContainerController.positionChildAtTop(newTop.mAppWindowToken); updateEffectiveIntent(); setFrontOfTask(); @@ -1264,17 +1264,15 @@ public class TaskRecord extends ConfigurationContainer implements TaskWindowCont mService.notifyTaskPersisterLocked(this, false); } - // Sync. with window manager - final AppWindowContainerController appController = r.getWindowContainerController(); - if (appController != null) { + if (r.mAppWindowToken != null) { // Only attempt to move in WM if the child has a controller. It is possible we haven't // created controller for the activity we are starting yet. - mWindowContainerController.positionChildAt(appController, index); + mWindowContainerController.positionChildAt(r.mAppWindowToken, index); } // Make sure the list of display UID whitelists is updated // now that this record is in a new task. - mService.mStackSupervisor.updateUIDsPresentOnDisplay(); + mService.mRootActivityContainer.updateUIDsPresentOnDisplay(); } /** @@ -1683,9 +1681,9 @@ public class TaskRecord extends ConfigurationContainer implements TaskWindowCont // to do this for the pinned stack as the bounds are controlled by the system. if (!inPinnedWindowingMode()) { final int defaultMinSizeDp = - mService.mStackSupervisor.mDefaultMinSizeOfResizeableTaskDp; + mService.mRootActivityContainer.mDefaultMinSizeOfResizeableTaskDp; final ActivityDisplay display = - mService.mStackSupervisor.getActivityDisplay(mStack.mDisplayId); + mService.mRootActivityContainer.getActivityDisplay(mStack.mDisplayId); final float density = (float) display.getConfiguration().densityDpi / DisplayMetrics.DENSITY_DEFAULT; final int defaultMinSize = (int) (defaultMinSizeDp * density); diff --git a/services/core/java/com/android/server/wm/TaskWindowContainerController.java b/services/core/java/com/android/server/wm/TaskWindowContainerController.java index 59b2055193ae..ec64d2e82895 100644 --- a/services/core/java/com/android/server/wm/TaskWindowContainerController.java +++ b/services/core/java/com/android/server/wm/TaskWindowContainerController.java @@ -16,6 +16,14 @@ package com.android.server.wm; +import static com.android.server.EventLogTags.WM_TASK_CREATED; +import static com.android.server.wm.ConfigurationContainer.BOUNDS_CHANGE_NONE; +import static com.android.server.wm.DragResizeMode.DRAG_RESIZE_MODE_DOCKED_DIVIDER; +import static com.android.server.wm.WindowContainer.POSITION_BOTTOM; +import static com.android.server.wm.WindowContainer.POSITION_TOP; +import static com.android.server.wm.WindowManagerDebugConfig.DEBUG_STACK; +import static com.android.server.wm.WindowManagerDebugConfig.TAG_WM; + import android.app.ActivityManager.TaskDescription; import android.app.ActivityManager.TaskSnapshot; import android.graphics.Rect; @@ -24,18 +32,11 @@ import android.os.Looper; import android.os.Message; import android.util.EventLog; import android.util.Slog; + import com.android.internal.annotations.VisibleForTesting; import java.lang.ref.WeakReference; -import static com.android.server.EventLogTags.WM_TASK_CREATED; -import static com.android.server.wm.ConfigurationContainer.BOUNDS_CHANGE_NONE; -import static com.android.server.wm.DragResizeMode.DRAG_RESIZE_MODE_DOCKED_DIVIDER; -import static com.android.server.wm.WindowContainer.POSITION_BOTTOM; -import static com.android.server.wm.WindowContainer.POSITION_TOP; -import static com.android.server.wm.WindowManagerDebugConfig.DEBUG_STACK; -import static com.android.server.wm.WindowManagerDebugConfig.TAG_WM; - /** * Controller for the task container. This is created by activity manager to link task records to * the task container they use in window manager. @@ -103,16 +104,15 @@ public class TaskWindowContainerController } } - public void positionChildAtTop(AppWindowContainerController childController) { - positionChildAt(childController, POSITION_TOP); + void positionChildAtTop(AppWindowToken aToken) { + positionChildAt(aToken, POSITION_TOP); } - public void positionChildAt(AppWindowContainerController childController, int position) { + void positionChildAt(AppWindowToken aToken, int position) { synchronized (mService.mGlobalLock) { - final AppWindowToken aToken = childController.mContainer; if (aToken == null) { Slog.w(TAG_WM, - "Attempted to position of non-existing app : " + childController); + "Attempted to position of non-existing app"); return; } diff --git a/services/core/java/com/android/server/wm/WindowProcessController.java b/services/core/java/com/android/server/wm/WindowProcessController.java index 484bd8c30462..578af2eebe88 100644 --- a/services/core/java/com/android/server/wm/WindowProcessController.java +++ b/services/core/java/com/android/server/wm/WindowProcessController.java @@ -755,9 +755,9 @@ public class WindowProcessController extends ConfigurationContainer<Configuratio return; } final ActivityDisplay activityDisplay = - mAtm.mStackSupervisor.getActivityDisplay(mDisplayId); + mAtm.mRootActivityContainer.getActivityDisplay(mDisplayId); if (activityDisplay != null) { - mAtm.mStackSupervisor.getActivityDisplay( + mAtm.mRootActivityContainer.getActivityDisplay( mDisplayId).unregisterConfigurationChangeListener(this); } mDisplayId = INVALID_DISPLAY; diff --git a/services/tests/servicestests/src/com/android/server/NetworkPolicyManagerServiceTest.java b/services/tests/servicestests/src/com/android/server/NetworkPolicyManagerServiceTest.java index 113ee2df768e..92efc3cc2b3c 100644 --- a/services/tests/servicestests/src/com/android/server/NetworkPolicyManagerServiceTest.java +++ b/services/tests/servicestests/src/com/android/server/NetworkPolicyManagerServiceTest.java @@ -148,6 +148,9 @@ import com.android.server.net.NetworkStatsManagerInternal; import com.google.common.util.concurrent.AbstractFuture; +import libcore.io.IoUtils; +import libcore.io.Streams; + import org.junit.After; import org.junit.Before; import org.junit.Rule; @@ -162,9 +165,6 @@ import org.mockito.MockitoAnnotations; import org.mockito.invocation.InvocationOnMock; import org.mockito.stubbing.Answer; -import libcore.io.IoUtils; -import libcore.io.Streams; - import java.io.File; import java.io.FileOutputStream; import java.io.InputStream; @@ -843,6 +843,18 @@ public class NetworkPolicyManagerServiceTest { assertTrue(mService.isUidForeground(UID_B)); } + @Test + public void testAppIdleTempWhitelisting() throws Exception { + mService.setAppIdleWhitelist(UID_A, true); + mService.setAppIdleWhitelist(UID_B, false); + int[] whitelistedIds = mService.getAppIdleWhitelist(); + assertTrue(Arrays.binarySearch(whitelistedIds, UID_A) >= 0); + assertTrue(Arrays.binarySearch(whitelistedIds, UID_B) < 0); + assertFalse(mService.isUidIdle(UID_A)); + // Can't currently guarantee UID_B's app idle state. + // TODO: expand with multiple app idle states. + } + private static long computeLastCycleBoundary(long currentTime, NetworkPolicy policy) { RecurrenceRule.sClock = Clock.fixed(Instant.ofEpochMilli(currentTime), ZoneId.systemDefault()); diff --git a/services/tests/servicestests/src/com/android/server/wm/AppTransitionTests.java b/services/tests/servicestests/src/com/android/server/wm/AppTransitionTests.java index f12619c6e337..19d18cab931e 100644 --- a/services/tests/servicestests/src/com/android/server/wm/AppTransitionTests.java +++ b/services/tests/servicestests/src/com/android/server/wm/AppTransitionTests.java @@ -35,7 +35,6 @@ import static org.mockito.Mockito.spy; import android.graphics.Rect; import android.platform.test.annotations.Presubmit; import android.view.Display; -import android.view.IApplicationToken; import androidx.test.filters.SmallTest; @@ -111,16 +110,9 @@ public class AppTransitionTests extends WindowTestsBase { final WindowTestUtils.TestAppWindowToken token2 = createTestAppWindowToken(dc2, WINDOWING_MODE_FULLSCREEN, ACTIVITY_TYPE_STANDARD); - // Set TestAppWindowContainerController & assign first app token state to be good to go. - final WindowTestUtils.TestAppWindowContainerController controller1 = - createAppWindowController(dc1, token1.appToken); - final WindowTestUtils.TestAppWindowContainerController controller2 = - createAppWindowController(dc1, token2.appToken); - controller1.setContainer(token1); token1.allDrawn = true; token1.startingDisplayed = true; token1.startingMoved = true; - controller2.setContainer(token2); // Simulate activity resume / finish flows to prepare app transition & set visibility, // make sure transition is set as expected for each display. @@ -132,8 +124,8 @@ public class AppTransitionTests extends WindowTestsBase { assertEquals(TRANSIT_ACTIVITY_CLOSE, dc2.mAppTransition.getAppTransition()); // One activity window is visible for resuming & the other activity window is invisible // for finishing in different display. - controller1.setVisibility(true, false); - controller2.setVisibility(false, false); + token1.setVisibility(true, false); + token2.setVisibility(false, false); // Make sure each display is in animating stage. assertTrue(dc1.mOpeningApps.size() > 0); @@ -174,16 +166,4 @@ public class AppTransitionTests extends WindowTestsBase { assertFalse(dc1.mOpeningApps.contains(token1)); } - private WindowTestUtils.TestAppWindowContainerController createAppWindowController( - DisplayContent dc, IApplicationToken token) { - return createAppWindowController( - new WindowTestUtils.TestTaskWindowContainerController( - createStackControllerOnDisplay(dc)), token); - } - - private WindowTestUtils.TestAppWindowContainerController createAppWindowController( - WindowTestUtils.TestTaskWindowContainerController taskController, - IApplicationToken token) { - return new WindowTestUtils.TestAppWindowContainerController(taskController, token); - } } diff --git a/services/tests/servicestests/src/com/android/server/wm/AppWindowContainerControllerTests.java b/services/tests/servicestests/src/com/android/server/wm/AppWindowContainerControllerTests.java deleted file mode 100644 index 415b5d93d90e..000000000000 --- a/services/tests/servicestests/src/com/android/server/wm/AppWindowContainerControllerTests.java +++ /dev/null @@ -1,253 +0,0 @@ -/* - * Copyright (C) 2017 The Android Open Source Project - * - * Licensed under the Apache License, Version 2.0 (the "License"); - * you may not use this file except in compliance with the License. - * You may obtain a copy of the License at - * - * http://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, - * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - * See the License for the specific language governing permissions and - * limitations under the License - */ - -package com.android.server.wm; - -import static android.content.pm.ActivityInfo.SCREEN_ORIENTATION_LANDSCAPE; -import static android.content.pm.ActivityInfo.SCREEN_ORIENTATION_UNSPECIFIED; -import static android.content.res.Configuration.EMPTY; -import static android.view.WindowManager.LayoutParams.TYPE_APPLICATION_STARTING; - -import static androidx.test.platform.app.InstrumentationRegistry.getInstrumentation; - -import static org.junit.Assert.assertEquals; -import static org.junit.Assert.assertFalse; -import static org.junit.Assert.assertNotNull; -import static org.junit.Assert.assertNull; -import static org.junit.Assert.fail; - -import android.platform.test.annotations.Presubmit; - -import androidx.test.filters.FlakyTest; -import androidx.test.filters.SmallTest; - -import com.android.server.wm.WindowTestUtils.TestTaskWindowContainerController; - -import org.junit.Test; - -/** - * Test class for {@link AppWindowContainerController}. - * - * atest FrameworksServicesTests:AppWindowContainerControllerTests - */ -@FlakyTest(bugId = 74078662) -@SmallTest -@Presubmit -public class AppWindowContainerControllerTests extends WindowTestsBase { - - private final String mPackageName = getInstrumentation().getTargetContext().getPackageName(); - - @Test - public void testRemoveContainer() { - final WindowTestUtils.TestAppWindowContainerController controller = - createAppWindowController(); - - // Assert token was added to display. - assertNotNull(mDisplayContent.getWindowToken(controller.mToken.asBinder())); - // Assert that the container was created and linked. - assertNotNull(controller.mContainer); - - controller.removeContainer(mDisplayContent.getDisplayId()); - - // Assert token was remove from display. - assertNull(mDisplayContent.getWindowToken(controller.mToken.asBinder())); - // Assert that the container was removed. - assertNull(controller.mContainer); - } - - @Test - public void testSetOrientation() { - final WindowTestUtils.TestAppWindowContainerController controller = - createAppWindowController(); - - // Assert orientation is unspecified to start. - assertEquals(SCREEN_ORIENTATION_UNSPECIFIED, controller.getOrientation()); - - controller.setOrientation(SCREEN_ORIENTATION_LANDSCAPE, mDisplayContent.getDisplayId(), - EMPTY /* displayConfig */, false /* freezeScreenIfNeeded */); - assertEquals(SCREEN_ORIENTATION_LANDSCAPE, controller.getOrientation()); - - controller.removeContainer(mDisplayContent.getDisplayId()); - // Assert orientation is unspecified to after container is removed. - assertEquals(SCREEN_ORIENTATION_UNSPECIFIED, controller.getOrientation()); - - // Reset display frozen state - mWm.mDisplayFrozen = false; - } - - private void assertHasStartingWindow(AppWindowToken atoken) { - assertNotNull(atoken.startingSurface); - assertNotNull(atoken.startingData); - assertNotNull(atoken.startingWindow); - } - - private void assertNoStartingWindow(AppWindowToken atoken) { - assertNull(atoken.startingSurface); - assertNull(atoken.startingWindow); - assertNull(atoken.startingData); - atoken.forAllWindows(windowState -> { - assertFalse(windowState.getBaseType() == TYPE_APPLICATION_STARTING); - }, true); - } - - @Test - public void testCreateRemoveStartingWindow() { - final WindowTestUtils.TestAppWindowContainerController controller = - createAppWindowController(); - controller.addStartingWindow(mPackageName, - android.R.style.Theme, null, "Test", 0, 0, 0, 0, null, true, true, false, true, - false, false); - waitUntilHandlersIdle(); - final AppWindowToken atoken = controller.getAppWindowToken(mDisplayContent); - assertHasStartingWindow(atoken); - controller.removeStartingWindow(); - waitUntilHandlersIdle(); - assertNoStartingWindow(atoken); - } - - @Test - public void testAddRemoveRace() { - // There was once a race condition between adding and removing starting windows - for (int i = 0; i < 1000; i++) { - final WindowTestUtils.TestAppWindowContainerController controller = - createAppWindowController(); - controller.addStartingWindow(mPackageName, - android.R.style.Theme, null, "Test", 0, 0, 0, 0, null, true, true, false, true, - false, false); - controller.removeStartingWindow(); - waitUntilHandlersIdle(); - assertNoStartingWindow(controller.getAppWindowToken(mDisplayContent)); - - controller.getAppWindowToken( - mDisplayContent).getParent().getParent().removeImmediately(); - } - } - - @Test - public void testTransferStartingWindow() { - final WindowTestUtils.TestAppWindowContainerController controller1 = - createAppWindowController(); - final WindowTestUtils.TestAppWindowContainerController controller2 = - createAppWindowController(); - controller1.addStartingWindow(mPackageName, - android.R.style.Theme, null, "Test", 0, 0, 0, 0, null, true, true, false, true, - false, false); - waitUntilHandlersIdle(); - controller2.addStartingWindow(mPackageName, - android.R.style.Theme, null, "Test", 0, 0, 0, 0, controller1.mToken.asBinder(), - true, true, false, true, false, false); - waitUntilHandlersIdle(); - assertNoStartingWindow(controller1.getAppWindowToken(mDisplayContent)); - assertHasStartingWindow(controller2.getAppWindowToken(mDisplayContent)); - } - - @Test - public void testTransferStartingWindowWhileCreating() { - final WindowTestUtils.TestAppWindowContainerController controller1 = - createAppWindowController(); - final WindowTestUtils.TestAppWindowContainerController controller2 = - createAppWindowController(); - ((TestWindowManagerPolicy) mWm.mPolicy).setRunnableWhenAddingSplashScreen(() -> { - - // Surprise, ...! Transfer window in the middle of the creation flow. - controller2.addStartingWindow(mPackageName, - android.R.style.Theme, null, "Test", 0, 0, 0, 0, controller1.mToken.asBinder(), - true, true, false, true, false, false); - }); - controller1.addStartingWindow(mPackageName, - android.R.style.Theme, null, "Test", 0, 0, 0, 0, null, true, true, false, true, - false, false); - waitUntilHandlersIdle(); - assertNoStartingWindow(controller1.getAppWindowToken(mDisplayContent)); - assertHasStartingWindow(controller2.getAppWindowToken(mDisplayContent)); - } - - @Test - public void testTryTransferStartingWindowFromHiddenAboveToken() { - - // Add two tasks on top of each other. - TestTaskWindowContainerController taskController = - new WindowTestUtils.TestTaskWindowContainerController(this); - final WindowTestUtils.TestAppWindowContainerController controllerTop = - createAppWindowController(taskController); - final WindowTestUtils.TestAppWindowContainerController controllerBottom = - createAppWindowController(taskController); - - // Add a starting window. - controllerTop.addStartingWindow(mPackageName, - android.R.style.Theme, null, "Test", 0, 0, 0, 0, null, true, true, false, true, - false, false); - waitUntilHandlersIdle(); - - // Make the top one invisible, and try transfering the starting window from the top to the - // bottom one. - controllerTop.setVisibility(false, false); - controllerBottom.mContainer.transferStartingWindowFromHiddenAboveTokenIfNeeded(); - - // Assert that the bottom window now has the starting window. - assertNoStartingWindow(controllerTop.getAppWindowToken(mDisplayContent)); - assertHasStartingWindow(controllerBottom.getAppWindowToken(mDisplayContent)); - } - - @Test - public void testReparent() { - final StackWindowController stackController = - createStackControllerOnDisplay(mDisplayContent); - final WindowTestUtils.TestTaskWindowContainerController taskController1 = - new WindowTestUtils.TestTaskWindowContainerController(stackController); - final WindowTestUtils.TestAppWindowContainerController appWindowController1 = - createAppWindowController(taskController1); - final WindowTestUtils.TestTaskWindowContainerController taskController2 = - new WindowTestUtils.TestTaskWindowContainerController(stackController); - final WindowTestUtils.TestAppWindowContainerController appWindowController2 = - createAppWindowController(taskController2); - final WindowTestUtils.TestTaskWindowContainerController taskController3 = - new WindowTestUtils.TestTaskWindowContainerController(stackController); - - try { - appWindowController1.reparent(taskController1, 0); - fail("Should not be able to reparent to the same parent"); - } catch (IllegalArgumentException e) { - // Expected - } - - try { - taskController3.setContainer(null); - appWindowController1.reparent(taskController3, 0); - fail("Should not be able to reparent to a task that doesn't have a container"); - } catch (IllegalArgumentException e) { - // Expected - } - - // Reparent the app window and ensure that it is moved - appWindowController1.reparent(taskController2, 0); - assertEquals(taskController2.mContainer, appWindowController1.mContainer.getParent()); - assertEquals(0, ((WindowTestUtils.TestAppWindowToken) appWindowController1.mContainer) - .positionInParent()); - assertEquals(1, ((WindowTestUtils.TestAppWindowToken) appWindowController2.mContainer) - .positionInParent()); - } - - private WindowTestUtils.TestAppWindowContainerController createAppWindowController() { - return createAppWindowController( - new WindowTestUtils.TestTaskWindowContainerController(this)); - } - - private WindowTestUtils.TestAppWindowContainerController createAppWindowController( - WindowTestUtils.TestTaskWindowContainerController taskController) { - return new WindowTestUtils.TestAppWindowContainerController(taskController); - } -} diff --git a/services/tests/servicestests/src/com/android/server/wm/AppWindowTokenTests.java b/services/tests/servicestests/src/com/android/server/wm/AppWindowTokenTests.java index 552390d19419..99abbf7b1f3a 100644 --- a/services/tests/servicestests/src/com/android/server/wm/AppWindowTokenTests.java +++ b/services/tests/servicestests/src/com/android/server/wm/AppWindowTokenTests.java @@ -18,6 +18,7 @@ package com.android.server.wm; import static android.app.WindowConfiguration.WINDOWING_MODE_FREEFORM; import static android.app.WindowConfiguration.WINDOWING_MODE_SPLIT_SCREEN_SECONDARY; +import static android.content.ActivityInfoProto.SCREEN_ORIENTATION_UNSPECIFIED; import static android.content.pm.ActivityInfo.SCREEN_ORIENTATION_BEHIND; import static android.content.pm.ActivityInfo.SCREEN_ORIENTATION_LANDSCAPE; import static android.content.pm.ActivityInfo.SCREEN_ORIENTATION_REVERSE_LANDSCAPE; @@ -31,8 +32,11 @@ import static android.view.WindowManager.LayoutParams.TYPE_APPLICATION_STARTING; import static android.view.WindowManager.LayoutParams.TYPE_BASE_APPLICATION; import static android.view.WindowManager.TRANSIT_UNSET; +import static androidx.test.platform.app.InstrumentationRegistry.getInstrumentation; + import static org.junit.Assert.assertEquals; import static org.junit.Assert.assertFalse; +import static org.junit.Assert.assertNotNull; import static org.junit.Assert.assertNull; import static org.junit.Assert.assertTrue; import static org.mockito.ArgumentMatchers.anyInt; @@ -66,6 +70,8 @@ public class AppWindowTokenTests extends WindowTestsBase { Task mTask; WindowTestUtils.TestAppWindowToken mToken; + private final String mPackageName = getInstrumentation().getTargetContext().getPackageName(); + @Before public void setUp() throws Exception { mStack = createTaskStackOnDisplay(mDisplayContent); @@ -251,7 +257,7 @@ public class AppWindowTokenTests extends WindowTestsBase { "closingWindow"); closingWindow.mAnimatingExit = true; closingWindow.mRemoveOnExit = true; - closingWindow.mAppToken.setVisibility(null, false /* visible */, TRANSIT_UNSET, + closingWindow.mAppToken.commitVisibility(null, false /* visible */, TRANSIT_UNSET, true /* performLayout */, false /* isVoiceInteraction */); // We pretended that we were running an exit animation, but that should have been cleared up @@ -261,6 +267,124 @@ public class AppWindowTokenTests extends WindowTestsBase { } @Test + public void testSetOrientation() { + // Assert orientation is unspecified to start. + assertEquals(SCREEN_ORIENTATION_UNSPECIFIED, mToken.getOrientation()); + + mToken.setOrientation(SCREEN_ORIENTATION_LANDSCAPE); + assertEquals(SCREEN_ORIENTATION_LANDSCAPE, mToken.getOrientation()); + + mDisplayContent.removeAppToken(mToken.token); + // Assert orientation is unset to after container is removed. + assertEquals(SCREEN_ORIENTATION_UNSET, mToken.getOrientation()); + + // Reset display frozen state + mWm.mDisplayFrozen = false; + } + + @Test + public void testCreateRemoveStartingWindow() { + mToken.addStartingWindow(mPackageName, + android.R.style.Theme, null, "Test", 0, 0, 0, 0, null, true, true, false, true, + false, false); + waitUntilHandlersIdle(); + assertHasStartingWindow(mToken); + mToken.removeStartingWindow(); + waitUntilHandlersIdle(); + assertNoStartingWindow(mToken); + } + + @Test + public void testAddRemoveRace() { + // There was once a race condition between adding and removing starting windows + for (int i = 0; i < 1000; i++) { + final WindowTestUtils.TestAppWindowToken appToken = createIsolatedTestAppWindowToken(); + + appToken.addStartingWindow(mPackageName, + android.R.style.Theme, null, "Test", 0, 0, 0, 0, null, true, true, false, true, + false, false); + appToken.removeStartingWindow(); + waitUntilHandlersIdle(); + assertNoStartingWindow(appToken); + + appToken.getParent().getParent().removeImmediately(); + } + } + + @Test + public void testTransferStartingWindow() { + final WindowTestUtils.TestAppWindowToken token1 = createIsolatedTestAppWindowToken(); + final WindowTestUtils.TestAppWindowToken token2 = createIsolatedTestAppWindowToken(); + token1.addStartingWindow(mPackageName, + android.R.style.Theme, null, "Test", 0, 0, 0, 0, null, true, true, false, true, + false, false); + waitUntilHandlersIdle(); + token2.addStartingWindow(mPackageName, + android.R.style.Theme, null, "Test", 0, 0, 0, 0, token1.appToken.asBinder(), + true, true, false, true, false, false); + waitUntilHandlersIdle(); + assertNoStartingWindow(token1); + assertHasStartingWindow(token2); + } + + @Test + public void testTransferStartingWindowWhileCreating() { + final WindowTestUtils.TestAppWindowToken token1 = createIsolatedTestAppWindowToken(); + final WindowTestUtils.TestAppWindowToken token2 = createIsolatedTestAppWindowToken(); + ((TestWindowManagerPolicy) token1.mService.mPolicy).setRunnableWhenAddingSplashScreen( + () -> { + // Surprise, ...! Transfer window in the middle of the creation flow. + token2.addStartingWindow(mPackageName, + android.R.style.Theme, null, "Test", 0, 0, 0, 0, + token1.appToken.asBinder(), true, true, false, + true, false, false); + }); + token1.addStartingWindow(mPackageName, + android.R.style.Theme, null, "Test", 0, 0, 0, 0, null, true, true, false, true, + false, false); + waitUntilHandlersIdle(); + assertNoStartingWindow(token1); + assertHasStartingWindow(token2); + } + + private WindowTestUtils.TestAppWindowToken createIsolatedTestAppWindowToken() { + final TaskStack taskStack = createTaskStackOnDisplay(mDisplayContent); + final Task task = createTaskInStack(taskStack, 0 /* userId */); + return createTestAppWindowTokenForGivenTask(task); + } + + private WindowTestUtils.TestAppWindowToken createTestAppWindowTokenForGivenTask(Task task) { + final WindowTestUtils.TestAppWindowToken appToken = + WindowTestUtils.createTestAppWindowToken(mDisplayContent); + task.addChild(appToken, 0); + waitUntilHandlersIdle(); + return appToken; + } + + @Test + public void testTryTransferStartingWindowFromHiddenAboveToken() { + // Add two tasks on top of each other. + final WindowTestUtils.TestAppWindowToken tokenTop = createIsolatedTestAppWindowToken(); + final WindowTestUtils.TestAppWindowToken tokenBottom = + createTestAppWindowTokenForGivenTask(tokenTop.getTask()); + + // Add a starting window. + tokenTop.addStartingWindow(mPackageName, + android.R.style.Theme, null, "Test", 0, 0, 0, 0, null, true, true, false, true, + false, false); + waitUntilHandlersIdle(); + + // Make the top one invisible, and try transferring the starting window from the top to the + // bottom one. + tokenTop.setVisibility(false, false); + tokenBottom.transferStartingWindowFromHiddenAboveTokenIfNeeded(); + + // Assert that the bottom window now has the starting window. + assertNoStartingWindow(tokenTop); + assertHasStartingWindow(tokenBottom); + } + + @Test public void testTransitionAnimationPositionAndBounds() { final Rect stackBounds = new Rect( 0/* left */, 0 /* top */, 1000 /* right */, 1000 /* bottom */); @@ -285,4 +409,19 @@ public class AppWindowTokenTests extends WindowTestsBase { assertEquals(expectedY, outPosition.y); assertEquals(expectedBounds, outBounds); } + + private void assertHasStartingWindow(AppWindowToken atoken) { + assertNotNull(atoken.startingSurface); + assertNotNull(atoken.startingData); + assertNotNull(atoken.startingWindow); + } + + private void assertNoStartingWindow(AppWindowToken atoken) { + assertNull(atoken.startingSurface); + assertNull(atoken.startingWindow); + assertNull(atoken.startingData); + atoken.forAllWindows(windowState -> { + assertFalse(windowState.getBaseType() == TYPE_APPLICATION_STARTING); + }, true); + } } diff --git a/services/tests/servicestests/src/com/android/server/wm/TaskSnapshotControllerTest.java b/services/tests/servicestests/src/com/android/server/wm/TaskSnapshotControllerTest.java index d2c07651d83c..792e8a6f7582 100644 --- a/services/tests/servicestests/src/com/android/server/wm/TaskSnapshotControllerTest.java +++ b/services/tests/servicestests/src/com/android/server/wm/TaskSnapshotControllerTest.java @@ -48,7 +48,7 @@ public class TaskSnapshotControllerTest extends WindowTestsBase { public void testGetClosingApps_closing() { final WindowState closingWindow = createWindow(null, FIRST_APPLICATION_WINDOW, "closingWindow"); - closingWindow.mAppToken.setVisibility(null, false /* visible */, TRANSIT_UNSET, + closingWindow.mAppToken.commitVisibility(null, false /* visible */, TRANSIT_UNSET, true /* performLayout */, false /* isVoiceInteraction */); final ArraySet<AppWindowToken> closingApps = new ArraySet<>(); closingApps.add(closingWindow.mAppToken); @@ -64,9 +64,9 @@ public class TaskSnapshotControllerTest extends WindowTestsBase { "closingWindow"); final WindowState openingWindow = createAppWindow(closingWindow.getTask(), FIRST_APPLICATION_WINDOW, "openingWindow"); - closingWindow.mAppToken.setVisibility(null, false /* visible */, TRANSIT_UNSET, + closingWindow.mAppToken.commitVisibility(null, false /* visible */, TRANSIT_UNSET, true /* performLayout */, false /* isVoiceInteraction */); - openingWindow.mAppToken.setVisibility(null, true /* visible */, TRANSIT_UNSET, + openingWindow.mAppToken.commitVisibility(null, true /* visible */, TRANSIT_UNSET, true /* performLayout */, false /* isVoiceInteraction */); final ArraySet<AppWindowToken> closingApps = new ArraySet<>(); closingApps.add(closingWindow.mAppToken); @@ -79,7 +79,7 @@ public class TaskSnapshotControllerTest extends WindowTestsBase { public void testGetClosingApps_skipClosingAppsSnapshotTasks() { final WindowState closingWindow = createWindow(null, FIRST_APPLICATION_WINDOW, "closingWindow"); - closingWindow.mAppToken.setVisibility(null, false /* visible */, TRANSIT_UNSET, + closingWindow.mAppToken.commitVisibility(null, false /* visible */, TRANSIT_UNSET, true /* performLayout */, false /* isVoiceInteraction */); final ArraySet<AppWindowToken> closingApps = new ArraySet<>(); closingApps.add(closingWindow.mAppToken); diff --git a/services/tests/servicestests/src/com/android/server/wm/TaskWindowContainerControllerTests.java b/services/tests/servicestests/src/com/android/server/wm/TaskWindowContainerControllerTests.java index 1af79e41fe37..bbf508dd1630 100644 --- a/services/tests/servicestests/src/com/android/server/wm/TaskWindowContainerControllerTests.java +++ b/services/tests/servicestests/src/com/android/server/wm/TaskWindowContainerControllerTests.java @@ -17,8 +17,6 @@ package com.android.server.wm; import static org.junit.Assert.assertEquals; -import static org.junit.Assert.assertNotNull; -import static org.junit.Assert.assertNull; import static org.junit.Assert.assertTrue; import android.platform.test.annotations.Presubmit; @@ -37,6 +35,7 @@ import org.junit.Test; @Presubmit public class TaskWindowContainerControllerTests extends WindowTestsBase { + /* Comment out due to removal of AppWindowContainerController @Test public void testRemoveContainer() { final WindowTestUtils.TestTaskWindowContainerController taskController = @@ -49,7 +48,9 @@ public class TaskWindowContainerControllerTests extends WindowTestsBase { assertNull(taskController.mContainer); assertNull(appController.mContainer); } + */ + /* Comment out due to removal of AppWindowContainerController @Test public void testRemoveContainer_deferRemoval() { final WindowTestUtils.TestTaskWindowContainerController taskController = @@ -74,6 +75,7 @@ public class TaskWindowContainerControllerTests extends WindowTestsBase { assertNull(appController.mContainer); assertNull(app.getController()); } + */ @Test public void testReparent() { diff --git a/services/tests/servicestests/src/com/android/server/wm/WindowTestUtils.java b/services/tests/servicestests/src/com/android/server/wm/WindowTestUtils.java index 60c045991d69..2e47c353d8c2 100644 --- a/services/tests/servicestests/src/com/android/server/wm/WindowTestUtils.java +++ b/services/tests/servicestests/src/com/android/server/wm/WindowTestUtils.java @@ -18,7 +18,6 @@ package com.android.server.wm; import static android.app.AppOpsManager.OP_NONE; import static android.content.pm.ActivityInfo.RESIZE_MODE_UNRESIZEABLE; -import static android.content.pm.ActivityInfo.SCREEN_ORIENTATION_UNSPECIFIED; import static com.android.server.wm.WindowContainer.POSITION_TOP; @@ -116,12 +115,11 @@ public class WindowTestUtils { ComponentName activityComponent, boolean voiceInteraction, DisplayContent dc, long inputDispatchingTimeoutNanos, boolean fullscreen, boolean showForAllUsers, int targetSdk, int orientation, int rotationAnimationHint, int configChanges, - boolean launchTaskBehind, boolean alwaysFocusable, - AppWindowContainerController controller) { + boolean launchTaskBehind, boolean alwaysFocusable, ActivityRecord activityRecord) { super(service, token, activityComponent, voiceInteraction, dc, inputDispatchingTimeoutNanos, fullscreen, showForAllUsers, targetSdk, orientation, rotationAnimationHint, configChanges, launchTaskBehind, - alwaysFocusable, controller); + alwaysFocusable, activityRecord); } int getWindowsCount() { @@ -275,45 +273,6 @@ public class WindowTestUtils { } } - public static class TestAppWindowContainerController extends AppWindowContainerController { - - final IApplicationToken mToken; - - TestAppWindowContainerController(TestTaskWindowContainerController taskController) { - this(taskController, new TestIApplicationToken()); - } - - TestAppWindowContainerController(TestTaskWindowContainerController taskController, - IApplicationToken token) { - super(taskController, token, new ComponentName("", "") /* activityComponent */, - null /* listener */, 0 /* index */, SCREEN_ORIENTATION_UNSPECIFIED, - true /* fullscreen */, true /* showForAllUsers */, 0 /* configChanges */, - false /* voiceInteraction */, false /* launchTaskBehind */, - false /* alwaysFocusable */, 0 /* targetSdkVersion */, - 0 /* rotationAnimationHint */, 0 /* inputDispatchingTimeoutNanos */, - taskController.mService); - mToken = token; - } - - @Override - AppWindowToken createAppWindow(WindowManagerService service, IApplicationToken token, - ComponentName activityComponent, boolean voiceInteraction, DisplayContent dc, - long inputDispatchingTimeoutNanos, - boolean fullscreen, boolean showForAllUsers, int targetSdk, int orientation, - int rotationAnimationHint, int configChanges, boolean launchTaskBehind, - boolean alwaysFocusable, AppWindowContainerController controller) { - return new TestAppWindowToken(service, token, activityComponent, voiceInteraction, dc, - inputDispatchingTimeoutNanos, fullscreen, showForAllUsers, targetSdk, - orientation, - rotationAnimationHint, configChanges, launchTaskBehind, alwaysFocusable, - controller); - } - - AppWindowToken getAppWindowToken(DisplayContent dc) { - return (AppWindowToken) dc.getWindowToken(mToken.asBinder()); - } - } - public static class TestIApplicationToken implements IApplicationToken { private final Binder mBinder = new Binder(); 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 cb2a8ec8a274..5bf3d2dabe24 100644 --- a/services/tests/wmtests/src/com/android/server/wm/ActivityDisplayTests.java +++ b/services/tests/wmtests/src/com/android/server/wm/ActivityDisplayTests.java @@ -62,8 +62,9 @@ public class ActivityDisplayTests extends ActivityTestsBase { @Test public void testLastFocusedStackIsUpdatedWhenMovingStack() { // Create a stack at bottom. - final ActivityDisplay display = mSupervisor.getDefaultDisplay(); - final ActivityStack stack = new StackBuilder(mSupervisor).setOnTop(!ON_TOP).build(); + final ActivityDisplay display = mRootActivityContainer.getDefaultDisplay(); + final ActivityStack stack = + new StackBuilder(mRootActivityContainer).setOnTop(!ON_TOP).build(); final ActivityStack prevFocusedStack = display.getFocusedStack(); stack.moveToFront("moveStackToFront"); @@ -83,7 +84,7 @@ public class ActivityDisplayTests extends ActivityTestsBase { @Test public void testFullscreenStackCanBeFocusedWhenFocusablePinnedStackExists() { // Create a pinned stack and move to front. - final ActivityStack pinnedStack = mSupervisor.getDefaultDisplay().createStack( + final ActivityStack pinnedStack = mRootActivityContainer.getDefaultDisplay().createStack( WINDOWING_MODE_PINNED, ACTIVITY_TYPE_STANDARD, ON_TOP); final TaskRecord pinnedTask = new TaskBuilder(mService.mStackSupervisor) .setStack(pinnedStack).build(); @@ -96,7 +97,7 @@ public class ActivityDisplayTests extends ActivityTestsBase { // Create a fullscreen stack and move to front. final ActivityStack fullscreenStack = createFullscreenStackWithSimpleActivityAt( - mSupervisor.getDefaultDisplay()); + mRootActivityContainer.getDefaultDisplay()); fullscreenStack.moveToFront("moveFullscreenStackToFront"); // The focused stack should be the fullscreen stack. @@ -138,7 +139,7 @@ public class ActivityDisplayTests extends ActivityTestsBase { final ActivityDisplay display = spy(createNewActivityDisplay()); doReturn(false).when(display).shouldDestroyContentOnRemove(); doReturn(true).when(display).supportsSystemDecorations(); - mSupervisor.addChild(display, ActivityDisplay.POSITION_TOP); + mRootActivityContainer.addChild(display, ActivityDisplay.POSITION_TOP); // Put home stack on the display. final ActivityStack homeStack = display.createStack( @@ -175,14 +176,14 @@ public class ActivityDisplayTests extends ActivityTestsBase { */ @Test public void testTopRunningActivity() { - final ActivityDisplay display = mSupervisor.getDefaultDisplay(); + final ActivityDisplay display = mRootActivityContainer.getDefaultDisplay(); final KeyguardController keyguard = mSupervisor.getKeyguardController(); - final ActivityStack stack = new StackBuilder(mSupervisor).build(); + final ActivityStack stack = new StackBuilder(mRootActivityContainer).build(); final ActivityRecord activity = stack.getTopActivity(); // Create empty stack on top. final ActivityStack emptyStack = - new StackBuilder(mSupervisor).setCreateActivity(false).build(); + new StackBuilder(mRootActivityContainer).setCreateActivity(false).build(); // Make sure the top running activity is not affected when keyguard is not locked. assertTopRunningActivity(activity, display); @@ -225,7 +226,7 @@ public class ActivityDisplayTests extends ActivityTestsBase { */ @Test public void testAlwaysOnTopStackLocation() { - final ActivityDisplay display = mSupervisor.getDefaultDisplay(); + final ActivityDisplay display = mRootActivityContainer.getDefaultDisplay(); final ActivityStack alwaysOnTopStack = display.createStack(WINDOWING_MODE_FREEFORM, ACTIVITY_TYPE_STANDARD, true /* onTop */); final ActivityRecord activity = new ActivityBuilder(mService).setCreateTask(true) diff --git a/services/tests/wmtests/src/com/android/server/wm/ActivityMetricsLaunchObserverTests.java b/services/tests/wmtests/src/com/android/server/wm/ActivityMetricsLaunchObserverTests.java index c7f0521adb7b..0e30037bde6c 100644 --- a/services/tests/wmtests/src/com/android/server/wm/ActivityMetricsLaunchObserverTests.java +++ b/services/tests/wmtests/src/com/android/server/wm/ActivityMetricsLaunchObserverTests.java @@ -74,7 +74,7 @@ public class ActivityMetricsLaunchObserverTests extends ActivityTestsBase { // Sometimes we need an ActivityRecord for ActivityMetricsLogger to do anything useful. // This seems to be the easiest way to create an ActivityRecord. - mStack = mSupervisor.getDefaultDisplay().createStack( + mStack = mRootActivityContainer.getDefaultDisplay().createStack( WINDOWING_MODE_FULLSCREEN, ACTIVITY_TYPE_STANDARD, true /* onTop */); mTask = new TaskBuilder(mSupervisor).setStack(mStack).build(); mActivityRecord = new ActivityBuilder(mService).setTask(mTask).build(); diff --git a/services/tests/wmtests/src/com/android/server/wm/ActivityRecordTests.java b/services/tests/wmtests/src/com/android/server/wm/ActivityRecordTests.java index 170bd3311de2..b6f181758d3a 100644 --- a/services/tests/wmtests/src/com/android/server/wm/ActivityRecordTests.java +++ b/services/tests/wmtests/src/com/android/server/wm/ActivityRecordTests.java @@ -29,10 +29,9 @@ import static com.android.server.wm.ActivityStack.ActivityState.PAUSING; import static com.android.server.wm.ActivityStack.ActivityState.STOPPED; import static com.android.server.wm.ActivityStack.REMOVE_TASK_MODE_MOVING; -import static junit.framework.TestCase.assertNotNull; - import static org.junit.Assert.assertEquals; import static org.junit.Assert.assertFalse; +import static org.junit.Assert.assertNotNull; import static org.junit.Assert.assertNull; import static org.junit.Assert.assertTrue; @@ -65,7 +64,7 @@ public class ActivityRecordTests extends ActivityTestsBase { @Before public void setUp() throws Exception { setupActivityTaskManagerService(); - mStack = new StackBuilder(mSupervisor).build(); + mStack = new StackBuilder(mRootActivityContainer).build(); mTask = mStack.getChildAt(0); mActivity = mTask.getTopActivity(); } @@ -86,7 +85,7 @@ public class ActivityRecordTests extends ActivityTestsBase { public void testStackCleanupOnTaskRemoval() { mStack.removeTask(mTask, null /*reason*/, REMOVE_TASK_MODE_MOVING); // Stack should be gone on task removal. - assertNull(mService.mStackSupervisor.getStack(mStack.mStackId)); + assertNull(mService.mRootActivityContainer.getStack(mStack.mStackId)); } @Test @@ -116,7 +115,7 @@ public class ActivityRecordTests extends ActivityTestsBase { assertFalse(pauseFound.value); // Clear focused stack - final ActivityDisplay display = mActivity.mStackSupervisor.getDefaultDisplay(); + final ActivityDisplay display = mRootActivityContainer.getDefaultDisplay(); when(display.getFocusedStack()).thenReturn(null); // In the unfocused stack, the activity should move to paused. diff --git a/services/tests/wmtests/src/com/android/server/wm/ActivityStackSupervisorTests.java b/services/tests/wmtests/src/com/android/server/wm/ActivityStackSupervisorTests.java index 8a6d5873927a..78a67d212f24 100644 --- a/services/tests/wmtests/src/com/android/server/wm/ActivityStackSupervisorTests.java +++ b/services/tests/wmtests/src/com/android/server/wm/ActivityStackSupervisorTests.java @@ -38,8 +38,7 @@ import static com.android.dx.mockito.inline.extended.ExtendedMockito.times; import static com.android.dx.mockito.inline.extended.ExtendedMockito.verify; import static com.android.server.wm.ActivityDisplay.POSITION_TOP; import static com.android.server.wm.ActivityStack.REMOVE_TASK_MODE_DESTROYING; -import static com.android.server.wm.ActivityStackSupervisor - .MATCH_TASK_IN_STACKS_OR_RECENT_TASKS_AND_RESTORE; +import static com.android.server.wm.RootActivityContainer.MATCH_TASK_IN_STACKS_OR_RECENT_TASKS_AND_RESTORE; import static com.google.common.truth.Truth.assertThat; @@ -83,78 +82,11 @@ public class ActivityStackSupervisorTests extends ActivityTestsBase { @Before public void setUp() throws Exception { setupActivityTaskManagerService(); - mFullscreenStack = mService.mStackSupervisor.getDefaultDisplay().createStack( + mFullscreenStack = mRootActivityContainer.getDefaultDisplay().createStack( WINDOWING_MODE_FULLSCREEN, ACTIVITY_TYPE_STANDARD, true /* onTop */); } /** - * This test ensures that we do not try to restore a task based off an invalid task id. We - * should expect {@code null} to be returned in this case. - */ - @Test - public void testRestoringInvalidTask() { - ((TestActivityDisplay) mSupervisor.getDefaultDisplay()).removeAllTasks(); - TaskRecord task = mSupervisor.anyTaskForIdLocked(0 /*taskId*/, - MATCH_TASK_IN_STACKS_OR_RECENT_TASKS_AND_RESTORE, null, false /* onTop */); - assertNull(task); - } - - /** - * This test ensures that an existing task in the pinned stack is moved to the fullscreen - * activity stack when a new task is added. - */ - @Test - public void testReplacingTaskInPinnedStack() { - final ActivityRecord firstActivity = new ActivityBuilder(mService).setCreateTask(true) - .setStack(mFullscreenStack).build(); - final TaskRecord firstTask = firstActivity.getTask(); - - final ActivityRecord secondActivity = new ActivityBuilder(mService).setCreateTask(true) - .setStack(mFullscreenStack).build(); - final TaskRecord secondTask = secondActivity.getTask(); - - mFullscreenStack.moveToFront("testReplacingTaskInPinnedStack"); - - // Ensure full screen stack has both tasks. - ensureStackPlacement(mFullscreenStack, firstTask, secondTask); - - // Move first activity to pinned stack. - final Rect sourceBounds = new Rect(); - mSupervisor.moveActivityToPinnedStackLocked(firstActivity, sourceBounds, - 0f /*aspectRatio*/, "initialMove"); - - final ActivityDisplay display = mFullscreenStack.getDisplay(); - ActivityStack pinnedStack = display.getPinnedStack(); - // Ensure a task has moved over. - ensureStackPlacement(pinnedStack, firstTask); - ensureStackPlacement(mFullscreenStack, secondTask); - - // Move second activity to pinned stack. - mSupervisor.moveActivityToPinnedStackLocked(secondActivity, sourceBounds, - 0f /*aspectRatio*/, "secondMove"); - - // Need to get stacks again as a new instance might have been created. - pinnedStack = display.getPinnedStack(); - mFullscreenStack = display.getStack(WINDOWING_MODE_FULLSCREEN, ACTIVITY_TYPE_STANDARD); - // Ensure stacks have swapped tasks. - ensureStackPlacement(pinnedStack, secondTask); - ensureStackPlacement(mFullscreenStack, firstTask); - } - - private static void ensureStackPlacement(ActivityStack stack, TaskRecord... tasks) { - final ArrayList<TaskRecord> stackTasks = stack.getAllTasks(); - assertEquals(stackTasks.size(), tasks != null ? tasks.length : 0); - - if (tasks == null) { - return; - } - - for (TaskRecord task : tasks) { - assertTrue(stackTasks.contains(task)); - } - } - - /** * Ensures that an activity is removed from the stopping activities list once it is resumed. */ @Test @@ -179,7 +111,7 @@ public class ActivityStackSupervisorTests extends ActivityTestsBase { // #notifyAll will be called on the ActivityManagerService. we must hold the object lock // when this happens. - synchronized (mSupervisor.mService.mGlobalLock) { + synchronized (mService.mGlobalLock) { final WaitResult taskToFrontWait = new WaitResult(); mSupervisor.mWaitingActivityLaunched.add(taskToFrontWait); mSupervisor.reportWaitingActivityLaunchedIfNeeded(firstActivity, START_TASK_TO_FRONT); @@ -198,334 +130,4 @@ public class ActivityStackSupervisorTests extends ActivityTestsBase { assertEquals(deliverToTopWait.who, firstActivity.realActivity); } } - - @Test - public void testApplySleepTokensLocked() { - final ActivityDisplay display = mSupervisor.getDefaultDisplay(); - final KeyguardController keyguard = mSupervisor.getKeyguardController(); - final ActivityStack stack = mock(ActivityStack.class); - display.addChild(stack, 0 /* position */); - - // Make sure we wake and resume in the case the display is turning on and the keyguard is - // not showing. - verifySleepTokenBehavior(display, keyguard, stack, true /*displaySleeping*/, - false /* displayShouldSleep */, true /* isFocusedStack */, - false /* keyguardShowing */, true /* expectWakeFromSleep */, - true /* expectResumeTopActivity */); - - // Make sure we wake and don't resume when the display is turning on and the keyguard is - // showing. - verifySleepTokenBehavior(display, keyguard, stack, true /*displaySleeping*/, - false /* displayShouldSleep */, true /* isFocusedStack */, - true /* keyguardShowing */, true /* expectWakeFromSleep */, - false /* expectResumeTopActivity */); - - // Make sure we wake and don't resume when the display is turning on and the keyguard is - // not showing as unfocused. - verifySleepTokenBehavior(display, keyguard, stack, true /*displaySleeping*/, - false /* displayShouldSleep */, false /* isFocusedStack */, - false /* keyguardShowing */, true /* expectWakeFromSleep */, - false /* expectResumeTopActivity */); - - // Should not do anything if the display state hasn't changed. - verifySleepTokenBehavior(display, keyguard, stack, false /*displaySleeping*/, - false /* displayShouldSleep */, true /* isFocusedStack */, - false /* keyguardShowing */, false /* expectWakeFromSleep */, - false /* expectResumeTopActivity */); - } - - private void verifySleepTokenBehavior(ActivityDisplay display, KeyguardController keyguard, - ActivityStack stack, boolean displaySleeping, boolean displayShouldSleep, - boolean isFocusedStack, boolean keyguardShowing, boolean expectWakeFromSleep, - boolean expectResumeTopActivity) { - reset(stack); - - doReturn(displayShouldSleep).when(display).shouldSleep(); - doReturn(displaySleeping).when(display).isSleeping(); - doReturn(keyguardShowing).when(keyguard).isKeyguardOrAodShowing(anyInt()); - - doReturn(isFocusedStack).when(stack).isFocusedStackOnDisplay(); - doReturn(isFocusedStack ? stack : null).when(display).getFocusedStack(); - mSupervisor.applySleepTokensLocked(true); - verify(stack, times(expectWakeFromSleep ? 1 : 0)).awakeFromSleepingLocked(); - verify(stack, times(expectResumeTopActivity ? 1 : 0)).resumeTopActivityUncheckedLocked( - null /* target */, null /* targetOptions */); - } - - /** - * Verifies that removal of activity with task and stack is done correctly. - */ - @Test - public void testRemovingStackOnAppCrash() { - final ActivityDisplay defaultDisplay = mService.mStackSupervisor.getDefaultDisplay(); - final int originalStackCount = defaultDisplay.getChildCount(); - final ActivityStack stack = mService.mStackSupervisor.getDefaultDisplay().createStack( - WINDOWING_MODE_FULLSCREEN, ACTIVITY_TYPE_STANDARD, false /* onTop */); - final ActivityRecord firstActivity = new ActivityBuilder(mService).setCreateTask(true) - .setStack(stack).build(); - - assertEquals(originalStackCount + 1, defaultDisplay.getChildCount()); - - // Let's pretend that the app has crashed. - firstActivity.app.setThread(null); - mService.mStackSupervisor.finishTopCrashedActivitiesLocked(firstActivity.app, "test"); - - // Verify that the stack was removed. - assertEquals(originalStackCount, defaultDisplay.getChildCount()); - } - - @Test - public void testFocusability() { - final ActivityStack stack = mService.mStackSupervisor.getDefaultDisplay().createStack( - WINDOWING_MODE_SPLIT_SCREEN_PRIMARY, ACTIVITY_TYPE_STANDARD, true /* onTop */); - final ActivityRecord activity = new ActivityBuilder(mService).setCreateTask(true) - .setStack(stack).build(); - - // Under split screen primary we should be focusable when not minimized - mService.mStackSupervisor.setDockedStackMinimized(false); - assertTrue(stack.isFocusable()); - assertTrue(activity.isFocusable()); - - // Under split screen primary we should not be focusable when minimized - mService.mStackSupervisor.setDockedStackMinimized(true); - assertFalse(stack.isFocusable()); - assertFalse(activity.isFocusable()); - - final ActivityStack pinnedStack = mService.mStackSupervisor.getDefaultDisplay().createStack( - WINDOWING_MODE_PINNED, ACTIVITY_TYPE_STANDARD, true /* onTop */); - final ActivityRecord pinnedActivity = new ActivityBuilder(mService).setCreateTask(true) - .setStack(pinnedStack).build(); - - // We should not be focusable when in pinned mode - assertFalse(pinnedStack.isFocusable()); - assertFalse(pinnedActivity.isFocusable()); - - // Add flag forcing focusability. - pinnedActivity.info.flags |= FLAG_ALWAYS_FOCUSABLE; - - // We should not be focusable when in pinned mode - assertTrue(pinnedStack.isFocusable()); - assertTrue(pinnedActivity.isFocusable()); - - // Without the overridding activity, stack should not be focusable. - pinnedStack.removeTask(pinnedActivity.getTask(), "testFocusability", - REMOVE_TASK_MODE_DESTROYING); - assertFalse(pinnedStack.isFocusable()); - } - - /** - * Verify that split-screen primary stack will be chosen if activity is launched that targets - * split-screen secondary, but a matching existing instance is found on top of split-screen - * primary stack. - */ - @Test - public void testSplitScreenPrimaryChosenWhenTopActivityLaunchedToSecondary() { - // Create primary split-screen stack with a task and an activity. - final ActivityStack primaryStack = mService.mStackSupervisor.getDefaultDisplay() - .createStack(WINDOWING_MODE_SPLIT_SCREEN_PRIMARY, ACTIVITY_TYPE_STANDARD, - true /* onTop */); - final TaskRecord task = new TaskBuilder(mSupervisor).setStack(primaryStack).build(); - final ActivityRecord r = new ActivityBuilder(mService).setTask(task).build(); - - // Find a launch stack for the top activity in split-screen primary, while requesting - // split-screen secondary. - final ActivityOptions options = ActivityOptions.makeBasic(); - options.setLaunchWindowingMode(WINDOWING_MODE_FULLSCREEN_OR_SPLIT_SCREEN_SECONDARY); - final ActivityStack result = mSupervisor.getLaunchStack(r, options, task, true /* onTop */); - - // Assert that the primary stack is returned. - assertEquals(primaryStack, result); - } - - /** - * Verify split-screen primary stack & task can resized by - * {@link android.app.IActivityTaskManager#resizeDockedStack} as expect. - */ - @Test - public void testResizeDockedStackForSplitScreenPrimary() { - final Rect taskSize = new Rect(0, 0, 600, 600); - final Rect stackSize = new Rect(0, 0, 300, 300); - - // Create primary split-screen stack with a task. - final ActivityStack primaryStack = mService.mStackSupervisor.getDefaultDisplay() - .createStack(WINDOWING_MODE_SPLIT_SCREEN_PRIMARY, ACTIVITY_TYPE_STANDARD, - true /* onTop */); - final TaskRecord task = new TaskBuilder(mSupervisor).setStack(primaryStack).build(); - - // Resize dock stack. - mService.resizeDockedStack(stackSize, taskSize, null, null, null); - - // Verify dock stack & its task bounds if is equal as resized result. - assertEquals(primaryStack.getBounds(), stackSize); - assertEquals(task.getBounds(), taskSize); - } - - /** - * Verify that home stack would be moved to front when the top activity is Recents. - */ - @Test - public void testFindTaskToMoveToFrontWhenRecentsOnTop() { - // Create stack/task on default display. - final ActivityDisplay display = mSupervisor.getDefaultDisplay(); - final TestActivityStack targetStack = new StackBuilder(mSupervisor).setOnTop(false).build(); - final TaskRecord targetTask = targetStack.getChildAt(0); - - // Create Recents on top of the display. - final ActivityStack stack = - new StackBuilder(mSupervisor).setActivityType(ACTIVITY_TYPE_RECENTS).build(); - - final String reason = "findTaskToMoveToFront"; - mSupervisor.findTaskToMoveToFront(targetTask, 0, ActivityOptions.makeBasic(), reason, - false); - - verify(display).moveHomeStackToFront(contains(reason)); - } - - /** - * Verify that home stack won't be moved to front if the top activity on other display is - * Recents. - */ - @Test - public void testFindTaskToMoveToFrontWhenRecentsOnOtherDisplay() { - // Create stack/task on default display. - final ActivityDisplay display = mSupervisor.getDefaultDisplay(); - final ActivityStack targetStack = display.createStack(WINDOWING_MODE_FULLSCREEN, - ACTIVITY_TYPE_STANDARD, false /* onTop */); - final TaskRecord targetTask = new TaskBuilder(mSupervisor).setStack(targetStack).build(); - - // Create Recents on secondary display. - final TestActivityDisplay secondDisplay = addNewActivityDisplayAt( - ActivityDisplay.POSITION_TOP); - final ActivityStack stack = secondDisplay.createStack(WINDOWING_MODE_FULLSCREEN, - ACTIVITY_TYPE_RECENTS, true /* onTop */); - final TaskRecord task = new TaskBuilder(mSupervisor).setStack(stack).build(); - new ActivityBuilder(mService).setTask(task).build(); - - final String reason = "findTaskToMoveToFront"; - mSupervisor.findTaskToMoveToFront(targetTask, 0, ActivityOptions.makeBasic(), reason, - false); - - verify(display, never()).moveHomeStackToFront(contains(reason)); - } - - /** - * Verify if a stack is not at the topmost position, it should be able to resume its activity if - * the stack is the top focused. - */ - @Test - public void testResumeActivityWhenNonTopmostStackIsTopFocused() { - // Create a stack at bottom. - final ActivityDisplay display = mSupervisor.getDefaultDisplay(); - final ActivityStack targetStack = spy(display.createStack(WINDOWING_MODE_FULLSCREEN, - ACTIVITY_TYPE_STANDARD, false /* onTop */)); - final TaskRecord task = new TaskBuilder(mSupervisor).setStack(targetStack).build(); - final ActivityRecord activity = new ActivityBuilder(mService).setTask(task).build(); - display.positionChildAtBottom(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. - assertFalse(targetStack.isTopStackOnDisplay()); - doReturn(targetStack).when(mSupervisor).getTopDisplayFocusedStack(); - - // Use the stack as target to resume. - mSupervisor.resumeFocusedStacksTopActivitiesLocked( - targetStack, activity, null /* targetOptions */); - - // Verify the target stack should resume its activity. - verify(targetStack, times(1)).resumeTopActivityUncheckedLocked( - eq(activity), eq(null /* targetOptions */)); - } - - /** - * Tests home activities that targeted sdk before Q cannot start on secondary display. - */ - @Test - public void testStartHomeTargetSdkBeforeQ() throws Exception { - final TestActivityDisplay secondDisplay = spy(createNewActivityDisplay()); - mSupervisor.addChild(secondDisplay, POSITION_TOP); - doReturn(true).when(secondDisplay).supportsSystemDecorations(); - - final ActivityInfo info = new ActivityInfo(); - info.launchMode = LAUNCH_MULTIPLE; - info.applicationInfo = new ApplicationInfo(); - info.applicationInfo.targetSdkVersion = Build.VERSION_CODES.Q; - assertTrue(mSupervisor.canStartHomeOnDisplay(info, secondDisplay.mDisplayId, - false /* allowInstrumenting */)); - - info.applicationInfo.targetSdkVersion = Build.VERSION_CODES.P; - assertFalse(mSupervisor.canStartHomeOnDisplay(info, secondDisplay.mDisplayId, - false /* allowInstrumenting */)); - } - - /** - * Tests that home activities can be started on the displays that supports system decorations. - */ - @Test - public void testStartHomeOnAllDisplays() { - // Create secondary displays. - final TestActivityDisplay secondDisplay = spy(createNewActivityDisplay()); - mSupervisor.addChild(secondDisplay, POSITION_TOP); - doReturn(true).when(secondDisplay).supportsSystemDecorations(); - - // Create mock tasks and other necessary mocks. - TaskBuilder taskBuilder = new TaskBuilder(mService.mStackSupervisor).setCreateStack(false); - final TaskRecord.TaskRecordFactory factory = mock(TaskRecord.TaskRecordFactory.class); - TaskRecord.setTaskRecordFactory(factory); - doAnswer(i -> taskBuilder.build()).when(factory) - .create(any(), anyInt(), any(), any(), any(), any()); - doReturn(true).when(mService.mStackSupervisor) - .ensureVisibilityAndConfig(any(), anyInt(), anyBoolean(), anyBoolean()); - doReturn(true).when(mSupervisor).canStartHomeOnDisplay(any(), anyInt(), anyBoolean()); - - mSupervisor.startHomeOnAllDisplays(0, "testStartHome"); - - assertTrue(mSupervisor.getDefaultDisplay().getTopStack().isActivityTypeHome()); - assertNotNull(secondDisplay.getTopStack()); - assertTrue(secondDisplay.getTopStack().isActivityTypeHome()); - } - - /** - * Tests that home activities won't be started before booting when display added. - */ - @Test - public void testNotStartHomeBeforeBoot() { - final int displayId = 1; - final boolean isBooting = mService.mAmInternal.isBooting(); - final boolean isBooted = mService.mAmInternal.isBooted(); - try { - mService.mAmInternal.setBooting(false); - mService.mAmInternal.setBooted(false); - mSupervisor.onDisplayAdded(displayId); - verify(mSupervisor, never()).startHomeOnDisplay(anyInt(), any(), anyInt()); - } finally { - mService.mAmInternal.setBooting(isBooting); - mService.mAmInternal.setBooted(isBooted); - } - } - - /** - * Tests whether home can be started if being instrumented. - */ - @Test - public void testCanStartHomeWhenInstrumented() { - final ActivityInfo info = new ActivityInfo(); - info.applicationInfo = new ApplicationInfo(); - final WindowProcessController app = mock(WindowProcessController.class); - doReturn(app).when(mService).getProcessController(any(), anyInt()); - - // Can not start home if we don't want to start home while home is being instrumented. - doReturn(true).when(app).isInstrumenting(); - assertFalse(mSupervisor.canStartHomeOnDisplay(info, DEFAULT_DISPLAY, - false /* allowInstrumenting*/)); - - // Can start home for other cases. - assertTrue(mSupervisor.canStartHomeOnDisplay(info, DEFAULT_DISPLAY, - true /* allowInstrumenting*/)); - - doReturn(false).when(app).isInstrumenting(); - assertTrue(mSupervisor.canStartHomeOnDisplay(info, DEFAULT_DISPLAY, - false /* allowInstrumenting*/)); - assertTrue(mSupervisor.canStartHomeOnDisplay(info, DEFAULT_DISPLAY, - true /* allowInstrumenting*/)); - } } 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 2fe45b86df4a..0da0b247b60c 100644 --- a/services/tests/wmtests/src/com/android/server/wm/ActivityStackTests.java +++ b/services/tests/wmtests/src/com/android/server/wm/ActivityStackTests.java @@ -74,7 +74,7 @@ public class ActivityStackTests extends ActivityTestsBase { @Before public void setUp() throws Exception { setupActivityTaskManagerService(); - mDefaultDisplay = mSupervisor.getDefaultDisplay(); + mDefaultDisplay = mRootActivityContainer.getDefaultDisplay(); mStack = spy(mDefaultDisplay.createStack(WINDOWING_MODE_UNDEFINED, ACTIVITY_TYPE_STANDARD, true /* onTop */)); mTask = new TaskBuilder(mSupervisor).setStack(mStack).build(); @@ -112,7 +112,7 @@ public class ActivityStackTests extends ActivityTestsBase { r.setState(RESUMED, "testResumedActivityFromTaskReparenting"); assertEquals(r, mStack.getResumedActivity()); - final ActivityStack destStack = mService.mStackSupervisor.getDefaultDisplay().createStack( + final ActivityStack destStack = mRootActivityContainer.getDefaultDisplay().createStack( WINDOWING_MODE_FULLSCREEN, ACTIVITY_TYPE_STANDARD, true /* onTop */); mTask.reparent(destStack, true /* toTop */, TaskRecord.REPARENT_KEEP_STACK_AT_FRONT, @@ -130,7 +130,7 @@ public class ActivityStackTests extends ActivityTestsBase { r.setState(RESUMED, "testResumedActivityFromActivityReparenting"); assertEquals(r, mStack.getResumedActivity()); - final ActivityStack destStack = mService.mStackSupervisor.getDefaultDisplay().createStack( + final ActivityStack destStack = mRootActivityContainer.getDefaultDisplay().createStack( WINDOWING_MODE_FULLSCREEN, ACTIVITY_TYPE_STANDARD, true /* onTop */); final TaskRecord destTask = new TaskBuilder(mSupervisor).setStack(destStack).build(); @@ -239,8 +239,8 @@ public class ActivityStackTests extends ActivityTestsBase { .setUid(UserHandle.PER_USER_RANGE * 2).build(); taskOverlay.mTaskOverlay = true; - final ActivityStackSupervisor.FindTaskResult result = - new ActivityStackSupervisor.FindTaskResult(); + final RootActivityContainer.FindTaskResult result = + new RootActivityContainer.FindTaskResult(); mStack.findTaskLocked(r, result); assertEquals(r, task.getTopActivity(false /* includeOverlays */)); @@ -700,7 +700,7 @@ public class ActivityStackTests extends ActivityTestsBase { // should be destroyed immediately with updating configuration to restore original state. final ActivityRecord activity1 = finishCurrentActivity(stack1); assertEquals(DESTROYING, activity1.getState()); - verify(mSupervisor).ensureVisibilityAndConfig(eq(null) /* starting */, + verify(mRootActivityContainer).ensureVisibilityAndConfig(eq(null) /* starting */, eq(display.mDisplayId), anyBoolean(), anyBoolean()); } @@ -778,7 +778,7 @@ public class ActivityStackTests extends ActivityTestsBase { final ActivityDisplay display = mock(ActivityDisplay.class); final KeyguardController keyguardController = mSupervisor.getKeyguardController(); - doReturn(display).when(mSupervisor).getActivityDisplay(anyInt()); + doReturn(display).when(mRootActivityContainer).getActivityDisplay(anyInt()); doReturn(keyguardGoingAway).when(keyguardController).isKeyguardGoingAway(); doReturn(displaySleeping).when(display).isSleeping(); doReturn(focusedStack).when(mStack).isFocusedStackOnDisplay(); diff --git a/services/tests/wmtests/src/com/android/server/wm/ActivityStartControllerTests.java b/services/tests/wmtests/src/com/android/server/wm/ActivityStartControllerTests.java index 9d93c85480bc..2ba2fdbcb959 100644 --- a/services/tests/wmtests/src/com/android/server/wm/ActivityStartControllerTests.java +++ b/services/tests/wmtests/src/com/android/server/wm/ActivityStartControllerTests.java @@ -74,7 +74,7 @@ public class ActivityStartControllerTests extends ActivityTestsBase { final ActivityRecord activity = new ActivityBuilder(mService).build(); final ActivityRecord source = new ActivityBuilder(mService).build(); final int startFlags = random.nextInt(); - final ActivityStack stack = mService.mStackSupervisor.getDefaultDisplay().createStack( + final ActivityStack stack = mService.mRootActivityContainer.getDefaultDisplay().createStack( WINDOWING_MODE_FULLSCREEN, ACTIVITY_TYPE_STANDARD, true /* onTop */); final WindowProcessController wpc = new WindowProcessController(mService, mService.mContext.getApplicationInfo(), "name", 12345, diff --git a/services/tests/wmtests/src/com/android/server/wm/ActivityStartInterceptorTest.java b/services/tests/wmtests/src/com/android/server/wm/ActivityStartInterceptorTest.java index 27fa20b91a80..350114c792bf 100644 --- a/services/tests/wmtests/src/com/android/server/wm/ActivityStartInterceptorTest.java +++ b/services/tests/wmtests/src/com/android/server/wm/ActivityStartInterceptorTest.java @@ -90,6 +90,8 @@ public class ActivityStartInterceptorTest { @Mock private ActivityTaskManagerService mService; @Mock + private RootActivityContainer mRootActivityContainer; + @Mock private ActivityStackSupervisor mSupervisor; @Mock private DevicePolicyManagerInternal mDevicePolicyManager; @@ -111,7 +113,8 @@ public class ActivityStartInterceptorTest { public void setUp() { MockitoAnnotations.initMocks(this); mService.mAmInternal = mAmInternal; - mInterceptor = new ActivityStartInterceptor(mService, mSupervisor, mContext); + mInterceptor = new ActivityStartInterceptor( + mService, mSupervisor, mRootActivityContainer, mContext); mInterceptor.setStates(TEST_USER_ID, TEST_REAL_CALLING_PID, TEST_REAL_CALLING_UID, TEST_START_FLAGS, TEST_CALLING_PACKAGE); diff --git a/services/tests/wmtests/src/com/android/server/wm/ActivityStarterTests.java b/services/tests/wmtests/src/com/android/server/wm/ActivityStarterTests.java index 50aa541a9549..4840a64bcbb4 100644 --- a/services/tests/wmtests/src/com/android/server/wm/ActivityStarterTests.java +++ b/services/tests/wmtests/src/com/android/server/wm/ActivityStarterTests.java @@ -125,7 +125,7 @@ public class ActivityStarterTests extends ActivityTestsBase { public void testUpdateLaunchBounds() { // When in a non-resizeable stack, the task bounds should be updated. final TaskRecord task = new TaskBuilder(mService.mStackSupervisor) - .setStack(mService.mStackSupervisor.getDefaultDisplay().createStack( + .setStack(mService.mRootActivityContainer.getDefaultDisplay().createStack( WINDOWING_MODE_FULLSCREEN, ACTIVITY_TYPE_STANDARD, true /* onTop */)) .build(); final Rect bounds = new Rect(10, 10, 100, 100); @@ -136,7 +136,7 @@ public class ActivityStarterTests extends ActivityTestsBase { // When in a resizeable stack, the stack bounds should be updated as well. final TaskRecord task2 = new TaskBuilder(mService.mStackSupervisor) - .setStack(mService.mStackSupervisor.getDefaultDisplay().createStack( + .setStack(mService.mRootActivityContainer.getDefaultDisplay().createStack( WINDOWING_MODE_PINNED, ACTIVITY_TYPE_STANDARD, true /* onTop */)) .build(); assertThat((Object) task2.getStack()).isInstanceOf(PinnedActivityStack.class); @@ -314,7 +314,7 @@ public class ActivityStarterTests extends ActivityTestsBase { * Creates a {@link ActivityStarter} with default parameters and necessary mocks. * * @param launchFlags The intent flags to launch activity. - * @param mockGetLaunchStack Whether to mock {@link ActivityStackSupervisor#getLaunchStack} for + * @param mockGetLaunchStack Whether to mock {@link RootActivityContainer#getLaunchStack} for * always launching to the testing stack. Set to false when allowing * the activity can be launched to any stack that is decided by real * implementation. @@ -323,14 +323,14 @@ public class ActivityStarterTests extends ActivityTestsBase { private ActivityStarter prepareStarter(@Intent.Flags int launchFlags, boolean mockGetLaunchStack) { // always allow test to start activity. - doReturn(true).when(mService.mStackSupervisor).checkStartAnyActivityPermission( + doReturn(true).when(mSupervisor).checkStartAnyActivityPermission( any(), any(), any(), anyInt(), anyInt(), anyInt(), any(), anyBoolean(), anyBoolean(), any(), any(), any()); // instrument the stack and task used. - final ActivityStack stack = mService.mStackSupervisor.getDefaultDisplay().createStack( + final ActivityStack stack = mRootActivityContainer.getDefaultDisplay().createStack( WINDOWING_MODE_FULLSCREEN, ACTIVITY_TYPE_STANDARD, true /* onTop */); - final TaskRecord task = new TaskBuilder(mService.mStackSupervisor) + final TaskRecord task = new TaskBuilder(mSupervisor) .setCreateStack(false) .build(); @@ -343,9 +343,9 @@ public class ActivityStarterTests extends ActivityTestsBase { if (mockGetLaunchStack) { // Direct starter to use spy stack. - doReturn(stack).when(mService.mStackSupervisor) + doReturn(stack).when(mRootActivityContainer) .getLaunchStack(any(), any(), any(), anyBoolean()); - doReturn(stack).when(mService.mStackSupervisor) + doReturn(stack).when(mRootActivityContainer) .getLaunchStack(any(), any(), any(), anyBoolean(), any()); } @@ -441,7 +441,7 @@ public class ActivityStarterTests extends ActivityTestsBase { final ActivityStack focusStack = focusActivity.getStack(); focusStack.moveToFront("testSplitScreenDeliverToTop"); - doReturn(reusableActivity).when(mService.mStackSupervisor).findTaskLocked(any(), anyInt()); + doReturn(reusableActivity).when(mRootActivityContainer).findTask(any(), anyInt()); final int result = starter.setReason("testSplitScreenDeliverToTop").execute(); @@ -473,7 +473,7 @@ public class ActivityStarterTests extends ActivityTestsBase { // Enter split-screen. Primary stack should have focus. focusActivity.getStack().setWindowingMode(WINDOWING_MODE_SPLIT_SCREEN_PRIMARY); - doReturn(reusableActivity).when(mService.mStackSupervisor).findTaskLocked(any(), anyInt()); + doReturn(reusableActivity).when(mRootActivityContainer).findTask(any(), anyInt()); final int result = starter.setReason("testSplitScreenMoveToFront").execute(); @@ -486,7 +486,7 @@ public class ActivityStarterTests extends ActivityTestsBase { */ @Test public void testTaskModeViolation() { - final ActivityDisplay display = mService.mStackSupervisor.getDefaultDisplay(); + final ActivityDisplay display = mService.mRootActivityContainer.getDefaultDisplay(); ((TestActivityDisplay) display).removeAllTasks(); assertNoTasks(display); @@ -562,7 +562,7 @@ public class ActivityStarterTests extends ActivityTestsBase { // Create a secondary display at bottom. final TestActivityDisplay secondaryDisplay = spy(createNewActivityDisplay()); - mSupervisor.addChild(secondaryDisplay, POSITION_BOTTOM); + mRootActivityContainer.addChild(secondaryDisplay, POSITION_BOTTOM); final ActivityStack stack = secondaryDisplay.createStack(WINDOWING_MODE_FULLSCREEN, ACTIVITY_TYPE_STANDARD, true /* onTop */); @@ -600,7 +600,7 @@ public class ActivityStarterTests extends ActivityTestsBase { // Create a secondary display with an activity. final TestActivityDisplay secondaryDisplay = spy(createNewActivityDisplay()); - mSupervisor.addChild(secondaryDisplay, POSITION_TOP); + mRootActivityContainer.addChild(secondaryDisplay, POSITION_TOP); final ActivityRecord singleTaskActivity = createSingleTaskActivityOn( secondaryDisplay.createStack(WINDOWING_MODE_FULLSCREEN, ACTIVITY_TYPE_STANDARD, false /* onTop */)); 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 c2ab3acd4933..a937ab30ea2f 100644 --- a/services/tests/wmtests/src/com/android/server/wm/ActivityTestsBase.java +++ b/services/tests/wmtests/src/com/android/server/wm/ActivityTestsBase.java @@ -94,6 +94,7 @@ class ActivityTestsBase { final TestInjector mTestInjector = new TestInjector(); ActivityTaskManagerService mService; + RootActivityContainer mRootActivityContainer; ActivityStackSupervisor mSupervisor; // Default package name @@ -120,6 +121,7 @@ class ActivityTestsBase { ActivityTaskManagerService createActivityTaskManagerService() { mService = new TestActivityTaskManagerService(mContext); mSupervisor = mService.mStackSupervisor; + mRootActivityContainer = mService.mRootActivityContainer; return mService; } @@ -139,7 +141,7 @@ class ActivityTestsBase { /** Creates and adds a {@link TestActivityDisplay} to supervisor at the given position. */ TestActivityDisplay addNewActivityDisplayAt(int position) { final TestActivityDisplay display = createNewActivityDisplay(); - mSupervisor.addChild(display, position); + mRootActivityContainer.addChild(display, position); return display; } @@ -231,7 +233,7 @@ class ActivityTestsBase { aInfo /*aInfo*/, new Configuration(), null /* resultTo */, null /* resultWho */, 0 /* reqCode */, false /*componentSpecified*/, false /* rootVoiceInteraction */, mService.mStackSupervisor, null /* options */, null /* sourceRecord */); - activity.mWindowContainerController = mock(AppWindowContainerController.class); + activity.mAppWindowToken = mock(AppWindowToken.class); if (mTaskRecord != null) { mTaskRecord.addActivityToTop(activity); @@ -243,6 +245,7 @@ class ActivityTestsBase { mock(WindowProcessListener.class)); wpc.setThread(mock(IApplicationThread.class)); activity.setProcess(wpc); + activity.service.mWindowManager.mRoot = mock(RootWindowContainer.class); return activity; } } @@ -317,7 +320,7 @@ class ActivityTestsBase { TaskRecord build() { if (mStack == null && mCreateStack) { - mStack = mSupervisor.getDefaultDisplay().createStack( + mStack = mSupervisor.mRootActivityContainer.getDefaultDisplay().createStack( WINDOWING_MODE_FULLSCREEN, ACTIVITY_TYPE_STANDARD, true /* onTop */); } @@ -375,6 +378,8 @@ class ActivityTestsBase { // We keep the reference in order to prevent creating it twice. ActivityStackSupervisor mTestStackSupervisor; + ActivityDisplay mDefaultDisplay; + TestActivityTaskManagerService(Context context) { super(context); spyOn(this); @@ -390,15 +395,6 @@ class ActivityTestsBase { final TestActivityManagerService am = new TestActivityManagerService(mTestInjector, this); - // Put a home stack on the default display, so that we'll always have something - // focusable. - final TestActivityStackSupervisor supervisor = - (TestActivityStackSupervisor) mStackSupervisor; - supervisor.mDisplay.createStack(WINDOWING_MODE_FULLSCREEN, ACTIVITY_TYPE_HOME, ON_TOP); - final TaskRecord task = new TaskBuilder(mStackSupervisor) - .setStack(supervisor.getDefaultDisplay().getHomeStack()).build(); - new ActivityBuilder(this).setTask(task).build(); - spyOn(getLifecycleManager()); spyOn(getLockTaskController()); doReturn(mock(IPackageManager.class)).when(this).getPackageManager(); @@ -409,9 +405,38 @@ class ActivityTestsBase { WindowManagerService wm) { mAmInternal = amInternal; setActivityManagerService(intentFirewall, intentController); + initRootActivityContainerMocks(wm); setWindowManager(wm); } + void initRootActivityContainerMocks(WindowManagerService wm) { + spyOn(mRootActivityContainer); + mRootActivityContainer.setWindowContainer(mock(RootWindowContainer.class)); + mRootActivityContainer.mWindowManager = wm; + mRootActivityContainer.mDisplayManager = + (DisplayManager) mContext.getSystemService(Context.DISPLAY_SERVICE); + doNothing().when(mRootActivityContainer).setWindowManager(any()); + // Invoked during {@link ActivityStack} creation. + doNothing().when(mRootActivityContainer).updateUIDsPresentOnDisplay(); + // Always keep things awake. + doReturn(true).when(mRootActivityContainer).hasAwakeDisplay(); + // Called when moving activity to pinned stack. + doNothing().when(mRootActivityContainer).ensureActivitiesVisible(any(), anyInt(), + anyBoolean()); + + // Create a default display and put a home stack on it so that we'll always have + // something focusable. + mDefaultDisplay = TestActivityDisplay.create(mStackSupervisor, DEFAULT_DISPLAY); + spyOn(mDefaultDisplay); + mRootActivityContainer.addChild(mDefaultDisplay, ActivityDisplay.POSITION_TOP); + mDefaultDisplay.createStack(WINDOWING_MODE_FULLSCREEN, ACTIVITY_TYPE_HOME, ON_TOP); + final TaskRecord task = new TaskBuilder(mStackSupervisor) + .setStack(mDefaultDisplay.getHomeStack()).build(); + new ActivityBuilder(this).setTask(task).build(); + + doReturn(mDefaultDisplay).when(mRootActivityContainer).getDefaultDisplay(); + } + @Override int handleIncomingUser(int callingPid, int callingUid, int userId, String name) { return userId; @@ -508,25 +533,14 @@ class ActivityTestsBase { * setup not available in the test environment. Also specifies an injector for */ protected class TestActivityStackSupervisor extends ActivityStackSupervisor { - private ActivityDisplay mDisplay; private KeyguardController mKeyguardController; TestActivityStackSupervisor(ActivityTaskManagerService service, Looper looper) { super(service, looper); spyOn(this); - mDisplayManager = - (DisplayManager) mService.mContext.getSystemService(Context.DISPLAY_SERVICE); mWindowManager = prepareMockWindowManager(); mKeyguardController = mock(KeyguardController.class); - setWindowContainerController(mock(RootWindowContainerController.class)); - // Invoked during {@link ActivityStack} creation. - doNothing().when(this).updateUIDsPresentOnDisplay(); - // Always keep things awake. - doReturn(true).when(this).hasAwakeDisplay(); - // Called when moving activity to pinned stack. - doNothing().when(this).ensureActivitiesVisibleLocked(any(), anyInt(), - anyBoolean()); // Do not schedule idle timeouts doNothing().when(this).scheduleIdleTimeoutLocked(any()); // unit test version does not handle launch wake lock @@ -537,24 +551,11 @@ class ActivityTestsBase { } @Override - public void initialize() { - super.initialize(); - mDisplay = TestActivityDisplay.create(this, DEFAULT_DISPLAY); - spyOn(mDisplay); - addChild(mDisplay, ActivityDisplay.POSITION_TOP); - } - - @Override public KeyguardController getKeyguardController() { return mKeyguardController; } @Override - ActivityDisplay getDefaultDisplay() { - return mDisplay; - } - - @Override void setWindowManager(WindowManagerService wm) { mWindowManager = wm; } @@ -571,7 +572,7 @@ class ActivityTestsBase { DisplayInfo info) { if (displayId == DEFAULT_DISPLAY) { return new TestActivityDisplay(supervisor, - supervisor.mDisplayManager.getDisplay(displayId)); + supervisor.mRootActivityContainer.mDisplayManager.getDisplay(displayId)); } final Display display = new Display(DisplayManagerGlobal.getInstance(), displayId, info, DEFAULT_DISPLAY_ADJUSTMENTS); @@ -579,7 +580,7 @@ class ActivityTestsBase { } TestActivityDisplay(ActivityStackSupervisor supervisor, Display display) { - super(supervisor, display); + super(supervisor.mService.mRootActivityContainer, display); // Normally this comes from display-properties as exposed by WM. Without that, just // hard-code to FULLSCREEN for tests. setWindowingMode(WINDOWING_MODE_FULLSCREEN); @@ -590,7 +591,7 @@ class ActivityTestsBase { @Override <T extends ActivityStack> T createStackUnchecked(int windowingMode, int activityType, int stackId, boolean onTop) { - return new StackBuilder(mSupervisor).setDisplay(this) + return new StackBuilder(mSupervisor.mRootActivityContainer).setDisplay(this) .setWindowingMode(windowingMode).setActivityType(activityType) .setStackId(stackId).setOnTop(onTop).setCreateActivity(false).build(); } @@ -732,8 +733,8 @@ class ActivityTestsBase { } } - protected static class StackBuilder { - private final ActivityStackSupervisor mSupervisor; + static class StackBuilder { + private final RootActivityContainer mRootActivityContainer; private ActivityDisplay mDisplay; private int mStackId = -1; private int mWindowingMode = WINDOWING_MODE_FULLSCREEN; @@ -741,9 +742,9 @@ class ActivityTestsBase { private boolean mOnTop = true; private boolean mCreateActivity = true; - StackBuilder(ActivityStackSupervisor supervisor) { - mSupervisor = supervisor; - mDisplay = mSupervisor.getDefaultDisplay(); + StackBuilder(RootActivityContainer root) { + mRootActivityContainer = root; + mDisplay = mRootActivityContainer.getDefaultDisplay(); } StackBuilder setWindowingMode(int windowingMode) { @@ -780,7 +781,8 @@ class ActivityTestsBase { <T extends ActivityStack> T build() { final int stackId = mStackId >= 0 ? mStackId : mDisplay.getNextStackId(); if (mWindowingMode == WINDOWING_MODE_PINNED) { - return (T) new PinnedActivityStack(mDisplay, stackId, mSupervisor, mOnTop) { + return (T) new PinnedActivityStack(mDisplay, stackId, + mRootActivityContainer.mStackSupervisor, mOnTop) { @Override Rect getDefaultPictureInPictureBounds(float aspectRatio) { return new Rect(50, 50, 100, 100); @@ -796,7 +798,8 @@ class ActivityTestsBase { } }; } else { - return (T) new TestActivityStack(mDisplay, stackId, mSupervisor, mWindowingMode, + return (T) new TestActivityStack(mDisplay, stackId, + mRootActivityContainer.mStackSupervisor, mWindowingMode, mActivityType, mOnTop, mCreateActivity); } } diff --git a/services/tests/wmtests/src/com/android/server/wm/LaunchParamsPersisterTests.java b/services/tests/wmtests/src/com/android/server/wm/LaunchParamsPersisterTests.java index dc22bc1d5820..2c3c66be6248 100644 --- a/services/tests/wmtests/src/com/android/server/wm/LaunchParamsPersisterTests.java +++ b/services/tests/wmtests/src/com/android/server/wm/LaunchParamsPersisterTests.java @@ -110,8 +110,9 @@ public class LaunchParamsPersisterTests extends ActivityTestsBase { final DisplayInfo info = new DisplayInfo(); info.uniqueId = mDisplayUniqueId; mTestDisplay = createNewActivityDisplay(info); - mSupervisor.addChild(mTestDisplay, ActivityDisplay.POSITION_TOP); - when(mSupervisor.getActivityDisplay(eq(mDisplayUniqueId))).thenReturn(mTestDisplay); + mRootActivityContainer.addChild(mTestDisplay, ActivityDisplay.POSITION_TOP); + when(mRootActivityContainer.getActivityDisplay(eq(mDisplayUniqueId))) + .thenReturn(mTestDisplay); ActivityStack stack = mTestDisplay.createStack(TEST_WINDOWING_MODE, ACTIVITY_TYPE_STANDARD, /* onTop */ true); @@ -184,7 +185,7 @@ public class LaunchParamsPersisterTests extends ActivityTestsBase { public void testReturnsEmptyDisplayIfDisplayIsNotFound() { mTarget.saveTask(mTestTask); - when(mSupervisor.getActivityDisplay(eq(mDisplayUniqueId))).thenReturn(null); + when(mRootActivityContainer.getActivityDisplay(eq(mDisplayUniqueId))).thenReturn(null); mTarget.getLaunchParams(mTestTask, null, mResult); diff --git a/services/tests/wmtests/src/com/android/server/wm/LockTaskControllerTest.java b/services/tests/wmtests/src/com/android/server/wm/LockTaskControllerTest.java index 33e6063bbb97..6259fa669ce8 100644 --- a/services/tests/wmtests/src/com/android/server/wm/LockTaskControllerTest.java +++ b/services/tests/wmtests/src/com/android/server/wm/LockTaskControllerTest.java @@ -104,6 +104,7 @@ public class LockTaskControllerTest { new DexmakerShareClassLoaderRule(); @Mock private ActivityStackSupervisor mSupervisor; + @Mock private RootActivityContainer mRootActivityContainer; @Mock private IDevicePolicyManager mDevicePolicyManager; @Mock private IStatusBarService mStatusBarService; @Mock private WindowManagerService mWindowManager; @@ -129,6 +130,7 @@ public class LockTaskControllerTest { } mSupervisor.mRecentTasks = mRecentTasks; + mSupervisor.mRootActivityContainer = mRootActivityContainer; mLockTaskController = new LockTaskController(mContext, mSupervisor, new ImmediatelyExecuteHandler()); diff --git a/services/tests/wmtests/src/com/android/server/wm/RecentTasksTest.java b/services/tests/wmtests/src/com/android/server/wm/RecentTasksTest.java index 8596c77bc73a..3c7b4b1248b5 100644 --- a/services/tests/wmtests/src/com/android/server/wm/RecentTasksTest.java +++ b/services/tests/wmtests/src/com/android/server/wm/RecentTasksTest.java @@ -113,9 +113,10 @@ public class RecentTasksTest extends ActivityTestsBase { mTestService = new MyTestActivityTaskManagerService(mContext); mRecentTasks = (TestRecentTasks) mTestService.getRecentTasks(); mRecentTasks.loadParametersFromResources(mContext.getResources()); - mHomeStack = mTestService.mStackSupervisor.getDefaultDisplay().getOrCreateStack( + mRunningTasks = (TestRunningTasks) mTestService.mStackSupervisor.mRunningTasks; + mHomeStack = mTestService.mRootActivityContainer.getDefaultDisplay().getOrCreateStack( WINDOWING_MODE_FULLSCREEN, ACTIVITY_TYPE_HOME, true /* onTop */); - mStack = mTestService.mStackSupervisor.getDefaultDisplay().createStack( + mStack = mTestService.mRootActivityContainer.getDefaultDisplay().createStack( WINDOWING_MODE_FULLSCREEN, ACTIVITY_TYPE_STANDARD, true /* onTop */); mCallbacksRecorder = new CallbacksRecorder(); mRecentTasks.registerCallback(mCallbacksRecorder); @@ -872,6 +873,15 @@ public class RecentTasksTest extends ActivityTestsBase { } return mTestStackSupervisor; } + + @Override + void initRootActivityContainerMocks(WindowManagerService wm) { + super.initRootActivityContainerMocks(wm); + mDisplay = mRootActivityContainer.getActivityDisplay(DEFAULT_DISPLAY); + mOtherDisplay = TestActivityDisplay.create(mTestStackSupervisor, DEFAULT_DISPLAY + 1); + mRootActivityContainer.addChild(mOtherDisplay, ActivityDisplay.POSITION_TOP); + mRootActivityContainer.addChild(mDisplay, ActivityDisplay.POSITION_TOP); + } } private class MyTestActivityStackSupervisor extends TestActivityStackSupervisor { @@ -880,15 +890,6 @@ public class RecentTasksTest extends ActivityTestsBase { } @Override - public void initialize() { - super.initialize(); - mDisplay = getActivityDisplay(DEFAULT_DISPLAY); - mOtherDisplay = TestActivityDisplay.create(this, DEFAULT_DISPLAY + 1); - addChild(mOtherDisplay, ActivityDisplay.POSITION_TOP); - addChild(mDisplay, ActivityDisplay.POSITION_TOP); - } - - @Override RunningTasks createRunningTasks() { mRunningTasks = new TestRunningTasks(); return mRunningTasks; 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 070f0731ba2f..0ff67d7b1f83 100644 --- a/services/tests/wmtests/src/com/android/server/wm/RecentsAnimationTest.java +++ b/services/tests/wmtests/src/com/android/server/wm/RecentsAnimationTest.java @@ -67,9 +67,9 @@ public class RecentsAnimationTest extends ActivityTestsBase { @Test public void testCancelAnimationOnStackOrderChange() { ActivityStack fullscreenStack = - mService.mStackSupervisor.getDefaultDisplay().createStack( + mService.mRootActivityContainer.getDefaultDisplay().createStack( WINDOWING_MODE_FULLSCREEN, ACTIVITY_TYPE_STANDARD, true /* onTop */); - ActivityStack recentsStack = mService.mStackSupervisor.getDefaultDisplay().createStack( + ActivityStack recentsStack = mService.mRootActivityContainer.getDefaultDisplay().createStack( WINDOWING_MODE_FULLSCREEN, ACTIVITY_TYPE_RECENTS, true /* onTop */); ActivityRecord recentsActivity = new ActivityBuilder(mService) .setComponent(mRecentsComponent) @@ -77,7 +77,7 @@ public class RecentsAnimationTest extends ActivityTestsBase { .setStack(recentsStack) .build(); ActivityStack fullscreenStack2 = - mService.mStackSupervisor.getDefaultDisplay().createStack( + mService.mRootActivityContainer.getDefaultDisplay().createStack( WINDOWING_MODE_FULLSCREEN, ACTIVITY_TYPE_STANDARD, true /* onTop */); ActivityRecord fsActivity = new ActivityBuilder(mService) .setComponent(new ComponentName(mContext.getPackageName(), "App1")) diff --git a/services/tests/wmtests/src/com/android/server/wm/RootActivityContainerTests.java b/services/tests/wmtests/src/com/android/server/wm/RootActivityContainerTests.java new file mode 100644 index 000000000000..631de99dc3ad --- /dev/null +++ b/services/tests/wmtests/src/com/android/server/wm/RootActivityContainerTests.java @@ -0,0 +1,483 @@ +/* + * Copyright (C) 2018 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.ActivityManager.START_DELIVERED_TO_TOP; +import static android.app.ActivityManager.START_TASK_TO_FRONT; +import static android.app.WindowConfiguration.ACTIVITY_TYPE_RECENTS; +import static android.app.WindowConfiguration.ACTIVITY_TYPE_STANDARD; +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_PINNED; +import static android.app.WindowConfiguration.WINDOWING_MODE_SPLIT_SCREEN_PRIMARY; +import static android.content.pm.ActivityInfo.FLAG_ALWAYS_FOCUSABLE; +import static android.content.pm.ActivityInfo.LAUNCH_MULTIPLE; +import static android.view.Display.DEFAULT_DISPLAY; +import static com.android.dx.mockito.inline.extended.ExtendedMockito.doAnswer; +import static com.android.dx.mockito.inline.extended.ExtendedMockito.doReturn; +import static com.android.dx.mockito.inline.extended.ExtendedMockito.mock; +import static com.android.dx.mockito.inline.extended.ExtendedMockito.never; +import static com.android.dx.mockito.inline.extended.ExtendedMockito.reset; +import static com.android.dx.mockito.inline.extended.ExtendedMockito.spy; +import static com.android.dx.mockito.inline.extended.ExtendedMockito.times; +import static com.android.dx.mockito.inline.extended.ExtendedMockito.verify; +import static com.android.server.wm.ActivityDisplay.POSITION_TOP; +import static com.android.server.wm.ActivityStack.REMOVE_TASK_MODE_DESTROYING; +import static com.android.server.wm.RootActivityContainer.MATCH_TASK_IN_STACKS_OR_RECENT_TASKS_AND_RESTORE; +import static com.google.common.truth.Truth.assertThat; +import static org.junit.Assert.assertEquals; +import static org.junit.Assert.assertFalse; +import static org.junit.Assert.assertNotNull; +import static org.junit.Assert.assertNull; +import static org.junit.Assert.assertTrue; +import static org.mockito.ArgumentMatchers.any; +import static org.mockito.ArgumentMatchers.anyBoolean; +import static org.mockito.ArgumentMatchers.anyInt; +import static org.mockito.ArgumentMatchers.contains; +import static org.mockito.ArgumentMatchers.eq; + +import android.app.ActivityOptions; +import android.app.WaitResult; +import android.content.pm.ActivityInfo; +import android.content.pm.ApplicationInfo; +import android.graphics.Rect; +import android.os.Build; +import android.platform.test.annotations.Presubmit; +import androidx.test.filters.MediumTest; +import org.junit.Before; +import org.junit.Test; + +import java.util.ArrayList; + +/** + * Tests for the {@link ActivityStackSupervisor} class. + * + * Build/Install/Run: + * atest WmTests:ActivityStackSupervisorTests + */ +@MediumTest +@Presubmit +public class RootActivityContainerTests extends ActivityTestsBase { + private ActivityStack mFullscreenStack; + + @Before + public void setUp() throws Exception { + setupActivityTaskManagerService(); + mFullscreenStack = mRootActivityContainer.getDefaultDisplay().createStack( + WINDOWING_MODE_FULLSCREEN, ACTIVITY_TYPE_STANDARD, true /* onTop */); + } + + /** + * This test ensures that we do not try to restore a task based off an invalid task id. We + * should expect {@code null} to be returned in this case. + */ + @Test + public void testRestoringInvalidTask() { + ((TestActivityDisplay) mRootActivityContainer.getDefaultDisplay()).removeAllTasks(); + TaskRecord task = mRootActivityContainer.anyTaskForId(0 /*taskId*/, + MATCH_TASK_IN_STACKS_OR_RECENT_TASKS_AND_RESTORE, null, false /* onTop */); + assertNull(task); + } + + /** + * This test ensures that an existing task in the pinned stack is moved to the fullscreen + * activity stack when a new task is added. + */ + @Test + public void testReplacingTaskInPinnedStack() { + final ActivityRecord firstActivity = new ActivityBuilder(mService).setCreateTask(true) + .setStack(mFullscreenStack).build(); + final TaskRecord firstTask = firstActivity.getTask(); + + final ActivityRecord secondActivity = new ActivityBuilder(mService).setCreateTask(true) + .setStack(mFullscreenStack).build(); + final TaskRecord secondTask = secondActivity.getTask(); + + mFullscreenStack.moveToFront("testReplacingTaskInPinnedStack"); + + // Ensure full screen stack has both tasks. + ensureStackPlacement(mFullscreenStack, firstTask, secondTask); + + // Move first activity to pinned stack. + final Rect sourceBounds = new Rect(); + mRootActivityContainer.moveActivityToPinnedStack(firstActivity, sourceBounds, + 0f /*aspectRatio*/, "initialMove"); + + final ActivityDisplay display = mFullscreenStack.getDisplay(); + ActivityStack pinnedStack = display.getPinnedStack(); + // Ensure a task has moved over. + ensureStackPlacement(pinnedStack, firstTask); + ensureStackPlacement(mFullscreenStack, secondTask); + + // Move second activity to pinned stack. + mRootActivityContainer.moveActivityToPinnedStack(secondActivity, sourceBounds, + 0f /*aspectRatio*/, "secondMove"); + + // Need to get stacks again as a new instance might have been created. + pinnedStack = display.getPinnedStack(); + mFullscreenStack = display.getStack(WINDOWING_MODE_FULLSCREEN, ACTIVITY_TYPE_STANDARD); + // Ensure stacks have swapped tasks. + ensureStackPlacement(pinnedStack, secondTask); + ensureStackPlacement(mFullscreenStack, firstTask); + } + + private static void ensureStackPlacement(ActivityStack stack, TaskRecord... tasks) { + final ArrayList<TaskRecord> stackTasks = stack.getAllTasks(); + assertEquals(stackTasks.size(), tasks != null ? tasks.length : 0); + + if (tasks == null) { + return; + } + + for (TaskRecord task : tasks) { + assertTrue(stackTasks.contains(task)); + } + } + + @Test + public void testApplySleepTokens() { + final ActivityDisplay display = mRootActivityContainer.getDefaultDisplay(); + final KeyguardController keyguard = mSupervisor.getKeyguardController(); + final ActivityStack stack = mock(ActivityStack.class); + display.addChild(stack, 0 /* position */); + + // Make sure we wake and resume in the case the display is turning on and the keyguard is + // not showing. + verifySleepTokenBehavior(display, keyguard, stack, true /*displaySleeping*/, + false /* displayShouldSleep */, true /* isFocusedStack */, + false /* keyguardShowing */, true /* expectWakeFromSleep */, + true /* expectResumeTopActivity */); + + // Make sure we wake and don't resume when the display is turning on and the keyguard is + // showing. + verifySleepTokenBehavior(display, keyguard, stack, true /*displaySleeping*/, + false /* displayShouldSleep */, true /* isFocusedStack */, + true /* keyguardShowing */, true /* expectWakeFromSleep */, + false /* expectResumeTopActivity */); + + // Make sure we wake and don't resume when the display is turning on and the keyguard is + // not showing as unfocused. + verifySleepTokenBehavior(display, keyguard, stack, true /*displaySleeping*/, + false /* displayShouldSleep */, false /* isFocusedStack */, + false /* keyguardShowing */, true /* expectWakeFromSleep */, + false /* expectResumeTopActivity */); + + // Should not do anything if the display state hasn't changed. + verifySleepTokenBehavior(display, keyguard, stack, false /*displaySleeping*/, + false /* displayShouldSleep */, true /* isFocusedStack */, + false /* keyguardShowing */, false /* expectWakeFromSleep */, + false /* expectResumeTopActivity */); + } + + private void verifySleepTokenBehavior(ActivityDisplay display, KeyguardController keyguard, + ActivityStack stack, boolean displaySleeping, boolean displayShouldSleep, + boolean isFocusedStack, boolean keyguardShowing, boolean expectWakeFromSleep, + boolean expectResumeTopActivity) { + reset(stack); + + doReturn(displayShouldSleep).when(display).shouldSleep(); + doReturn(displaySleeping).when(display).isSleeping(); + doReturn(keyguardShowing).when(keyguard).isKeyguardOrAodShowing(anyInt()); + + doReturn(isFocusedStack).when(stack).isFocusedStackOnDisplay(); + doReturn(isFocusedStack ? stack : null).when(display).getFocusedStack(); + mRootActivityContainer.applySleepTokens(true); + verify(stack, times(expectWakeFromSleep ? 1 : 0)).awakeFromSleepingLocked(); + verify(stack, times(expectResumeTopActivity ? 1 : 0)).resumeTopActivityUncheckedLocked( + null /* target */, null /* targetOptions */); + } + + /** + * Verifies that removal of activity with task and stack is done correctly. + */ + @Test + public void testRemovingStackOnAppCrash() { + final ActivityDisplay defaultDisplay = mRootActivityContainer.getDefaultDisplay(); + final int originalStackCount = defaultDisplay.getChildCount(); + final ActivityStack stack = mRootActivityContainer.getDefaultDisplay().createStack( + WINDOWING_MODE_FULLSCREEN, ACTIVITY_TYPE_STANDARD, false /* onTop */); + final ActivityRecord firstActivity = new ActivityBuilder(mService).setCreateTask(true) + .setStack(stack).build(); + + assertEquals(originalStackCount + 1, defaultDisplay.getChildCount()); + + // Let's pretend that the app has crashed. + firstActivity.app.setThread(null); + mRootActivityContainer.finishTopCrashedActivities(firstActivity.app, "test"); + + // Verify that the stack was removed. + assertEquals(originalStackCount, defaultDisplay.getChildCount()); + } + + @Test + public void testFocusability() { + final ActivityStack stack = mRootActivityContainer.getDefaultDisplay().createStack( + WINDOWING_MODE_SPLIT_SCREEN_PRIMARY, ACTIVITY_TYPE_STANDARD, true /* onTop */); + final ActivityRecord activity = new ActivityBuilder(mService).setCreateTask(true) + .setStack(stack).build(); + + // Under split screen primary we should be focusable when not minimized + mRootActivityContainer.setDockedStackMinimized(false); + assertTrue(stack.isFocusable()); + assertTrue(activity.isFocusable()); + + // Under split screen primary we should not be focusable when minimized + mRootActivityContainer.setDockedStackMinimized(true); + assertFalse(stack.isFocusable()); + assertFalse(activity.isFocusable()); + + final ActivityStack pinnedStack = mRootActivityContainer.getDefaultDisplay().createStack( + WINDOWING_MODE_PINNED, ACTIVITY_TYPE_STANDARD, true /* onTop */); + final ActivityRecord pinnedActivity = new ActivityBuilder(mService).setCreateTask(true) + .setStack(pinnedStack).build(); + + // We should not be focusable when in pinned mode + assertFalse(pinnedStack.isFocusable()); + assertFalse(pinnedActivity.isFocusable()); + + // Add flag forcing focusability. + pinnedActivity.info.flags |= FLAG_ALWAYS_FOCUSABLE; + + // We should not be focusable when in pinned mode + assertTrue(pinnedStack.isFocusable()); + assertTrue(pinnedActivity.isFocusable()); + + // Without the overridding activity, stack should not be focusable. + pinnedStack.removeTask(pinnedActivity.getTask(), "testFocusability", + REMOVE_TASK_MODE_DESTROYING); + assertFalse(pinnedStack.isFocusable()); + } + + /** + * Verify that split-screen primary stack will be chosen if activity is launched that targets + * split-screen secondary, but a matching existing instance is found on top of split-screen + * primary stack. + */ + @Test + public void testSplitScreenPrimaryChosenWhenTopActivityLaunchedToSecondary() { + // Create primary split-screen stack with a task and an activity. + final ActivityStack primaryStack = mRootActivityContainer.getDefaultDisplay() + .createStack(WINDOWING_MODE_SPLIT_SCREEN_PRIMARY, ACTIVITY_TYPE_STANDARD, + true /* onTop */); + final TaskRecord task = new TaskBuilder(mSupervisor).setStack(primaryStack).build(); + final ActivityRecord r = new ActivityBuilder(mService).setTask(task).build(); + + // Find a launch stack for the top activity in split-screen primary, while requesting + // split-screen secondary. + final ActivityOptions options = ActivityOptions.makeBasic(); + options.setLaunchWindowingMode(WINDOWING_MODE_FULLSCREEN_OR_SPLIT_SCREEN_SECONDARY); + final ActivityStack result = + mRootActivityContainer.getLaunchStack(r, options, task, true /* onTop */); + + // Assert that the primary stack is returned. + assertEquals(primaryStack, result); + } + + /** + * Verify split-screen primary stack & task can resized by + * {@link android.app.IActivityTaskManager#resizeDockedStack} as expect. + */ + @Test + public void testResizeDockedStackForSplitScreenPrimary() { + final Rect taskSize = new Rect(0, 0, 600, 600); + final Rect stackSize = new Rect(0, 0, 300, 300); + + // Create primary split-screen stack with a task. + final ActivityStack primaryStack = mRootActivityContainer.getDefaultDisplay() + .createStack(WINDOWING_MODE_SPLIT_SCREEN_PRIMARY, ACTIVITY_TYPE_STANDARD, + true /* onTop */); + final TaskRecord task = new TaskBuilder(mSupervisor).setStack(primaryStack).build(); + + // Resize dock stack. + mService.resizeDockedStack(stackSize, taskSize, null, null, null); + + // Verify dock stack & its task bounds if is equal as resized result. + assertEquals(primaryStack.getBounds(), stackSize); + assertEquals(task.getBounds(), taskSize); + } + + /** + * Verify that home stack would be moved to front when the top activity is Recents. + */ + @Test + public void testFindTaskToMoveToFrontWhenRecentsOnTop() { + // Create stack/task on default display. + final ActivityDisplay display = mRootActivityContainer.getDefaultDisplay(); + final TestActivityStack targetStack = + new StackBuilder(mRootActivityContainer).setOnTop(false).build(); + final TaskRecord targetTask = targetStack.getChildAt(0); + + // Create Recents on top of the display. + final ActivityStack stack = new StackBuilder(mRootActivityContainer).setActivityType( + ACTIVITY_TYPE_RECENTS).build(); + + final String reason = "findTaskToMoveToFront"; + mSupervisor.findTaskToMoveToFront(targetTask, 0, ActivityOptions.makeBasic(), reason, + false); + + verify(display).moveHomeStackToFront(contains(reason)); + } + + /** + * Verify that home stack won't be moved to front if the top activity on other display is + * Recents. + */ + @Test + public void testFindTaskToMoveToFrontWhenRecentsOnOtherDisplay() { + // Create stack/task on default display. + final ActivityDisplay display = mRootActivityContainer.getDefaultDisplay(); + final ActivityStack targetStack = display.createStack(WINDOWING_MODE_FULLSCREEN, + ACTIVITY_TYPE_STANDARD, false /* onTop */); + final TaskRecord targetTask = new TaskBuilder(mSupervisor).setStack(targetStack).build(); + + // Create Recents on secondary display. + final TestActivityDisplay secondDisplay = addNewActivityDisplayAt( + ActivityDisplay.POSITION_TOP); + final ActivityStack stack = secondDisplay.createStack(WINDOWING_MODE_FULLSCREEN, + ACTIVITY_TYPE_RECENTS, true /* onTop */); + final TaskRecord task = new TaskBuilder(mSupervisor).setStack(stack).build(); + new ActivityBuilder(mService).setTask(task).build(); + + final String reason = "findTaskToMoveToFront"; + mSupervisor.findTaskToMoveToFront(targetTask, 0, ActivityOptions.makeBasic(), reason, + false); + + verify(display, never()).moveHomeStackToFront(contains(reason)); + } + + /** + * Verify if a stack is not at the topmost position, it should be able to resume its activity if + * the stack is the top focused. + */ + @Test + public void testResumeActivityWhenNonTopmostStackIsTopFocused() { + // Create a stack at bottom. + final ActivityDisplay display = mRootActivityContainer.getDefaultDisplay(); + final ActivityStack targetStack = spy(display.createStack(WINDOWING_MODE_FULLSCREEN, + ACTIVITY_TYPE_STANDARD, false /* onTop */)); + final TaskRecord task = new TaskBuilder(mSupervisor).setStack(targetStack).build(); + final ActivityRecord activity = new ActivityBuilder(mService).setTask(task).build(); + display.positionChildAtBottom(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. + assertFalse(targetStack.isTopStackOnDisplay()); + doReturn(targetStack).when(mRootActivityContainer).getTopDisplayFocusedStack(); + + // Use the stack as target to resume. + mRootActivityContainer.resumeFocusedStacksTopActivities( + targetStack, activity, null /* targetOptions */); + + // Verify the target stack should resume its activity. + verify(targetStack, times(1)).resumeTopActivityUncheckedLocked( + eq(activity), eq(null /* targetOptions */)); + } + + /** + * Tests home activities that targeted sdk before Q cannot start on secondary display. + */ + @Test + public void testStartHomeTargetSdkBeforeQ() throws Exception { + final TestActivityDisplay secondDisplay = spy(createNewActivityDisplay()); + mRootActivityContainer.addChild(secondDisplay, POSITION_TOP); + doReturn(true).when(secondDisplay).supportsSystemDecorations(); + + final ActivityInfo info = new ActivityInfo(); + info.launchMode = LAUNCH_MULTIPLE; + info.applicationInfo = new ApplicationInfo(); + info.applicationInfo.targetSdkVersion = Build.VERSION_CODES.Q; + assertTrue(mRootActivityContainer.canStartHomeOnDisplay(info, secondDisplay.mDisplayId, + false /* allowInstrumenting */)); + + info.applicationInfo.targetSdkVersion = Build.VERSION_CODES.P; + assertFalse(mRootActivityContainer.canStartHomeOnDisplay(info, secondDisplay.mDisplayId, + false /* allowInstrumenting */)); + } + + /** + * Tests that home activities can be started on the displays that supports system decorations. + */ + @Test + public void testStartHomeOnAllDisplays() { + // Create secondary displays. + final TestActivityDisplay secondDisplay = spy(createNewActivityDisplay()); + mRootActivityContainer.addChild(secondDisplay, POSITION_TOP); + doReturn(true).when(secondDisplay).supportsSystemDecorations(); + + // Create mock tasks and other necessary mocks. + TaskBuilder taskBuilder = new TaskBuilder(mService.mStackSupervisor).setCreateStack(false); + final TaskRecord.TaskRecordFactory factory = mock(TaskRecord.TaskRecordFactory.class); + TaskRecord.setTaskRecordFactory(factory); + doAnswer(i -> taskBuilder.build()).when(factory) + .create(any(), anyInt(), any(), any(), any(), any()); + doReturn(true).when(mRootActivityContainer) + .ensureVisibilityAndConfig(any(), anyInt(), anyBoolean(), anyBoolean()); + doReturn(true).when(mRootActivityContainer).canStartHomeOnDisplay( + any(), anyInt(), anyBoolean()); + + mRootActivityContainer.startHomeOnAllDisplays(0, "testStartHome"); + + assertTrue(mRootActivityContainer.getDefaultDisplay().getTopStack().isActivityTypeHome()); + assertNotNull(secondDisplay.getTopStack()); + assertTrue(secondDisplay.getTopStack().isActivityTypeHome()); + } + + /** + * Tests that home activities won't be started before booting when display added. + */ + @Test + public void testNotStartHomeBeforeBoot() { + final int displayId = 1; + final boolean isBooting = mService.mAmInternal.isBooting(); + final boolean isBooted = mService.mAmInternal.isBooted(); + try { + mService.mAmInternal.setBooting(false); + mService.mAmInternal.setBooted(false); + mRootActivityContainer.onDisplayAdded(displayId); + verify(mRootActivityContainer, never()).startHomeOnDisplay(anyInt(), any(), anyInt()); + } finally { + mService.mAmInternal.setBooting(isBooting); + mService.mAmInternal.setBooted(isBooted); + } + } + + /** + * Tests whether home can be started if being instrumented. + */ + @Test + public void testCanStartHomeWhenInstrumented() { + final ActivityInfo info = new ActivityInfo(); + info.applicationInfo = new ApplicationInfo(); + final WindowProcessController app = mock(WindowProcessController.class); + doReturn(app).when(mService).getProcessController(any(), anyInt()); + + // Can not start home if we don't want to start home while home is being instrumented. + doReturn(true).when(app).isInstrumenting(); + assertFalse(mRootActivityContainer.canStartHomeOnDisplay(info, DEFAULT_DISPLAY, + false /* allowInstrumenting*/)); + + // Can start home for other cases. + assertTrue(mRootActivityContainer.canStartHomeOnDisplay(info, DEFAULT_DISPLAY, + true /* allowInstrumenting*/)); + + doReturn(false).when(app).isInstrumenting(); + assertTrue(mRootActivityContainer.canStartHomeOnDisplay(info, DEFAULT_DISPLAY, + false /* allowInstrumenting*/)); + assertTrue(mRootActivityContainer.canStartHomeOnDisplay(info, DEFAULT_DISPLAY, + true /* allowInstrumenting*/)); + } +} diff --git a/services/tests/wmtests/src/com/android/server/wm/RunningTasksTest.java b/services/tests/wmtests/src/com/android/server/wm/RunningTasksTest.java index 0e1624ebef63..a8b6dc373cbf 100644 --- a/services/tests/wmtests/src/com/android/server/wm/RunningTasksTest.java +++ b/services/tests/wmtests/src/com/android/server/wm/RunningTasksTest.java @@ -63,7 +63,7 @@ public class RunningTasksTest extends ActivityTestsBase { final int numStacks = 2; for (int stackIndex = 0; stackIndex < numStacks; stackIndex++) { final ActivityStack stack = - new StackBuilder(mSupervisor).setCreateActivity(false).build(); + new StackBuilder(mRootActivityContainer).setCreateActivity(false).build(); display.addChild(stack, POSITION_BOTTOM); } diff --git a/services/tests/wmtests/src/com/android/server/wm/TaskLaunchParamsModifierTests.java b/services/tests/wmtests/src/com/android/server/wm/TaskLaunchParamsModifierTests.java index 6638eeb8af19..bd8cd1f37f5e 100644 --- a/services/tests/wmtests/src/com/android/server/wm/TaskLaunchParamsModifierTests.java +++ b/services/tests/wmtests/src/com/android/server/wm/TaskLaunchParamsModifierTests.java @@ -888,10 +888,10 @@ public class TaskLaunchParamsModifierTests extends ActivityTestsBase { @Test public void testAdjustBoundsToFitNewDisplay_LargerThanDisplay_RTL() { - final Configuration overrideConfig = mSupervisor.getOverrideConfiguration(); + final Configuration overrideConfig = mRootActivityContainer.getOverrideConfiguration(); // Egyptian Arabic is a RTL language. overrideConfig.setLayoutDirection(new Locale("ar", "EG")); - mSupervisor.onOverrideConfigurationChanged(overrideConfig); + mRootActivityContainer.onOverrideConfigurationChanged(overrideConfig); final TestActivityDisplay freeformDisplay = createNewActivityDisplay( WINDOWING_MODE_FREEFORM); |