Merge changes from topic "am-983c3c67c6104e739b86553d061025dd" into main
* changes:
[automerger skipped] Merge "Import translations. DO NOT MERGE ANYWHERE" into 24D1-dev am: 67e78fe002 -s ours
[automerger skipped] Import translations. DO NOT MERGE ANYWHERE am: edfccdffef -s ours
[automerger skipped] Merge "Import translations. DO NOT MERGE ANYWHERE" into 24D1-dev am: 0f657fce42 -s ours
[automerger skipped] Import translations. DO NOT MERGE ANYWHERE am: b75a2f9426 -s ours
[automerger skipped] Merge "Import translations. DO NOT MERGE ANYWHERE" into 24D1-dev am: 6c6deb6a5a -s ours
[automerger skipped] Import translations. DO NOT MERGE ANYWHERE am: 72659ffa58 -s ours
[automerger skipped] Merge "Import translations. DO NOT MERGE ANYWHERE" into 24D1-dev am: 6b9a9116c4 -s ours
[automerger skipped] Import translations. DO NOT MERGE ANYWHERE am: 90fb4a3d6c -s ours
[automerger skipped] Merge "Import translations. DO NOT MERGE ANYWHERE" into 24D1-dev am: 0a2f08e35d -s ours
[automerger skipped] Import translations. DO NOT MERGE ANYWHERE am: c751242dc5 -s ours
[automerger skipped] Merge "Import translations. DO NOT MERGE ANYWHERE" into 24D1-dev am: d5c148dc3b -s ours
[automerger skipped] Import translations. DO NOT MERGE ANYWHERE am: d9337dbeee -s ours
[automerger skipped] Merge "Import translations. DO NOT MERGE ANYWHERE" into 24D1-dev am: 1865347417 -s ours
[automerger skipped] Import translations. DO NOT MERGE ANYWHERE am: e55ce378ea -s ours
[automerger skipped] Merge "Import translations. DO NOT MERGE ANYWHERE" into 24D1-dev am: 63d6a0243c -s ours
[automerger skipped] Import translations. DO NOT MERGE ANYWHERE am: b7fd25133d -s ours
[automerger skipped] Merge "Import translations. DO NOT MERGE ANYWHERE" into 24D1-dev am: b2949a9ac3 -s ours
[automerger skipped] Import translations. DO NOT MERGE ANYWHERE am: a1a0333ac2 -s ours
[automerger skipped] Merge "Import translations. DO NOT MERGE ANYWHERE" into 24D1-dev am: d7fef212ee -s ours
[automerger skipped] Import translations. DO NOT MERGE ANYWHERE am: e35fa37b46 -s ours
[automerger skipped] Merge "Import translations. DO NOT MERGE ANYWHERE" into 24D1-dev am: 06c22c478e -s ours
[automerger skipped] Import translations. DO NOT MERGE ANYWHERE am: 96836aa3a8 -s ours
[automerger skipped] Merge "Import translations. DO NOT MERGE ANYWHERE" into 24D1-dev am: ff77eedede -s ours
[automerger skipped] Import translations. DO NOT MERGE ANYWHERE am: cb89a261d9 -s ours
[automerger skipped] Merge "Import translations. DO NOT MERGE ANYWHERE" into 24D1-dev am: 5f3659caa8 -s ours
[automerger skipped] Import translations. DO NOT MERGE ANYWHERE am: 3ddd21d824 -s ours
[automerger skipped] Merge "Import translations. DO NOT MERGE ANYWHERE" into 24D1-dev am: ac9bf27e79 -s ours
[automerger skipped] Import translations. DO NOT MERGE ANYWHERE am: a91f0ca6cc -s ours
[automerger skipped] Merge "Import translations. DO NOT MERGE ANYWHERE" into 24D1-dev am: 645f04b2bd -s ours
[automerger skipped] Import translations. DO NOT MERGE ANYWHERE am: b9d45627c9 -s ours
[automerger skipped] Merge "Import translations. DO NOT MERGE ANYWHERE" into 24D1-dev am: d83624ef23 -s ours
[automerger skipped] Import translations. DO NOT MERGE ANYWHERE am: edf040e0a3 -s ours
[automerger skipped] Merge "Import translations. DO NOT MERGE ANYWHERE" into 24D1-dev am: 3db3b38e3a -s ours
[automerger skipped] Import translations. DO NOT MERGE ANYWHERE am: 2d7d29bd6d -s ours
[automerger skipped] Merge "Import translations. DO NOT MERGE ANYWHERE" into 24D1-dev am: de2079f8ea -s ours
[automerger skipped] Import translations. DO NOT MERGE ANYWHERE am: c4b85abb8e -s ours
diff --git a/api/coverage/tools/ExtractFlaggedApis.kt b/api/coverage/tools/ExtractFlaggedApis.kt
index 5178f09..5efda98 100644
--- a/api/coverage/tools/ExtractFlaggedApis.kt
+++ b/api/coverage/tools/ExtractFlaggedApis.kt
@@ -17,6 +17,7 @@
package android.platform.coverage
import com.android.tools.metalava.model.ClassItem
+import com.android.tools.metalava.model.Item
import com.android.tools.metalava.model.MethodItem
import com.android.tools.metalava.model.text.ApiFile
import java.io.File
@@ -44,20 +45,9 @@
builder: FlagApiMap.Builder
) {
if (methods.isEmpty()) return
- val classFlag =
- classItem.modifiers
- .findAnnotation("android.annotation.FlaggedApi")
- ?.findAttribute("value")
- ?.value
- ?.value() as? String
+ val classFlag = getClassFlag(classItem)
for (method in methods) {
- val methodFlag =
- method.modifiers
- .findAnnotation("android.annotation.FlaggedApi")
- ?.findAttribute("value")
- ?.value
- ?.value() as? String
- ?: classFlag
+ val methodFlag = getFlagAnnotation(method) ?: classFlag
val api =
JavaMethod.newBuilder()
.setPackageName(packageName)
@@ -81,3 +71,23 @@
builder.putFlagToApi(flag, apis)
}
}
+
+fun getClassFlag(classItem: ClassItem): String? {
+ var classFlag = getFlagAnnotation(classItem)
+ var cur = classItem
+ // If a class is not an inner class, use its @FlaggedApi annotation value.
+ // Otherwise, use the flag value of the closest outer class that is annotated by @FlaggedApi.
+ while (cur.isInnerClass() && classFlag == null) {
+ cur = cur.parent() as ClassItem
+ classFlag = getFlagAnnotation(cur)
+ }
+ return classFlag
+}
+
+fun getFlagAnnotation(item: Item): String? {
+ return item.modifiers
+ .findAnnotation("android.annotation.FlaggedApi")
+ ?.findAttribute("value")
+ ?.value
+ ?.value() as? String
+}
diff --git a/api/coverage/tools/ExtractFlaggedApisTest.kt b/api/coverage/tools/ExtractFlaggedApisTest.kt
index ee5aaf1..427be36 100644
--- a/api/coverage/tools/ExtractFlaggedApisTest.kt
+++ b/api/coverage/tools/ExtractFlaggedApisTest.kt
@@ -141,6 +141,84 @@
assertThat(result).isEqualTo(expected.build())
}
+ @Test
+ fun extractFlaggedApis_unflaggedNestedClassShouldUseOuterClassFlag() {
+ val apiText =
+ """
+ // Signature format: 2.0
+ package android.location.provider {
+ @FlaggedApi(Flags.FLAG_NEW_GEOCODER) public final class ForwardGeocodeRequest implements android.os.Parcelable {
+ method public int describeContents();
+ }
+ public static final class ForwardGeocodeRequest.Builder {
+ method @NonNull public android.location.provider.ForwardGeocodeRequest build();
+ }
+ }
+ """
+ .trimIndent()
+ Files.write(apiTextFile, apiText.toByteArray(Charsets.UTF_8), StandardOpenOption.APPEND)
+
+ val process = Runtime.getRuntime().exec(createCommand())
+ process.waitFor()
+
+ val content = Files.readAllBytes(flagToApiMap).toString(Charsets.UTF_8)
+ val result = TextFormat.parse(content, FlagApiMap::class.java)
+
+ val expected = FlagApiMap.newBuilder()
+ val api1 =
+ JavaMethod.newBuilder()
+ .setPackageName("android.location.provider")
+ .setClassName("ForwardGeocodeRequest")
+ .setMethodName("describeContents")
+ addFlaggedApi(expected, api1, "Flags.FLAG_NEW_GEOCODER")
+ val api2 =
+ JavaMethod.newBuilder()
+ .setPackageName("android.location.provider")
+ .setClassName("ForwardGeocodeRequest.Builder")
+ .setMethodName("build")
+ addFlaggedApi(expected, api2, "Flags.FLAG_NEW_GEOCODER")
+ assertThat(result).ignoringRepeatedFieldOrder().isEqualTo(expected.build())
+ }
+
+ @Test
+ fun extractFlaggedApis_unflaggedNestedClassShouldUseOuterClassFlag_deeplyNested() {
+ val apiText =
+ """
+ // Signature format: 2.0
+ package android.package.xyz {
+ @FlaggedApi(outer_class_flag) public final class OuterClass {
+ method public int apiInOuterClass();
+ }
+ public final class OuterClass.Deeply.NestedClass {
+ method public void apiInNestedClass();
+ }
+ }
+ """
+ .trimIndent()
+ Files.write(apiTextFile, apiText.toByteArray(Charsets.UTF_8), StandardOpenOption.APPEND)
+
+ val process = Runtime.getRuntime().exec(createCommand())
+ process.waitFor()
+
+ val content = Files.readAllBytes(flagToApiMap).toString(Charsets.UTF_8)
+ val result = TextFormat.parse(content, FlagApiMap::class.java)
+
+ val expected = FlagApiMap.newBuilder()
+ val api1 =
+ JavaMethod.newBuilder()
+ .setPackageName("android.package.xyz")
+ .setClassName("OuterClass")
+ .setMethodName("apiInOuterClass")
+ addFlaggedApi(expected, api1, "outer_class_flag")
+ val api2 =
+ JavaMethod.newBuilder()
+ .setPackageName("android.package.xyz")
+ .setClassName("OuterClass.Deeply.NestedClass")
+ .setMethodName("apiInNestedClass")
+ addFlaggedApi(expected, api2, "outer_class_flag")
+ assertThat(result).ignoringRepeatedFieldOrder().isEqualTo(expected.build())
+ }
+
private fun addFlaggedApi(builder: FlagApiMap.Builder, api: JavaMethod.Builder, flag: String) {
if (builder.containsFlagToApi(flag)) {
val updatedApis =
diff --git a/core/java/android/view/InsetsController.java b/core/java/android/view/InsetsController.java
index b52003f..1f6ceca 100644
--- a/core/java/android/view/InsetsController.java
+++ b/core/java/android/view/InsetsController.java
@@ -22,7 +22,6 @@
import static android.view.InsetsControllerProto.STATE;
import static android.view.InsetsSource.ID_IME;
import static android.view.InsetsSource.ID_IME_CAPTION_BAR;
-import static android.view.ViewRootImpl.CAPTION_ON_SHELL;
import static android.view.WindowInsets.Type.FIRST;
import static android.view.WindowInsets.Type.LAST;
import static android.view.WindowInsets.Type.all;
@@ -685,9 +684,6 @@
@Override
public void onIdNotFoundInState2(int index1, InsetsSource source1) {
- if (!CAPTION_ON_SHELL && source1.getType() == captionBar()) {
- return;
- }
if (source1.getId() == ID_IME_CAPTION_BAR) {
return;
}
@@ -848,15 +844,8 @@
}
public boolean onStateChanged(InsetsState state) {
- boolean stateChanged = false;
- if (!CAPTION_ON_SHELL) {
- stateChanged = !mState.equals(state, true /* excludesCaptionBar */,
- false /* excludesInvisibleIme */)
- || captionInsetsUnchanged();
- } else {
- stateChanged = !mState.equals(state, false /* excludesCaptionBar */,
- false /* excludesInvisibleIme */);
- }
+ boolean stateChanged = !mState.equals(state, false /* excludesCaptionBar */,
+ false /* excludesInvisibleIme */);
if (!stateChanged && mLastDispatchedState.equals(state)) {
return false;
}
@@ -924,21 +913,6 @@
}
}
- private boolean captionInsetsUnchanged() {
- if (CAPTION_ON_SHELL) {
- return false;
- }
- final InsetsSource source = mState.peekSource(ID_CAPTION_BAR);
- if (source == null && mCaptionInsetsHeight == 0) {
- return false;
- }
- if (source != null && mCaptionInsetsHeight == source.getFrame().height()) {
- return false;
- }
-
- return true;
- }
-
/**
* @see InsetsState#calculateInsets(Rect, InsetsState, boolean, int, int, int, int, int,
* android.util.SparseIntArray)
@@ -1889,24 +1863,6 @@
}
@Override
- public void setCaptionInsetsHeight(int height) {
- // This method is to be removed once the caption is moved to the shell.
- if (CAPTION_ON_SHELL) {
- return;
- }
- if (mCaptionInsetsHeight != height) {
- mCaptionInsetsHeight = height;
- if (mCaptionInsetsHeight != 0) {
- mState.getOrCreateSource(ID_CAPTION_BAR, captionBar()).setFrame(
- mFrame.left, mFrame.top, mFrame.right, mFrame.top + mCaptionInsetsHeight);
- } else {
- mState.removeSource(ID_CAPTION_BAR);
- }
- mHost.notifyInsetsChanged();
- }
- }
-
- @Override
public void setImeCaptionBarInsetsHeight(int height) {
if (!ENABLE_HIDE_IME_CAPTION_BAR) {
return;
diff --git a/core/java/android/view/PendingInsetsController.java b/core/java/android/view/PendingInsetsController.java
index 00a5806..5f461c5 100644
--- a/core/java/android/view/PendingInsetsController.java
+++ b/core/java/android/view/PendingInsetsController.java
@@ -45,7 +45,6 @@
private InsetsController mReplayedInsetsController;
private ArrayList<OnControllableInsetsChangedListener> mControllableInsetsChangedListeners
= new ArrayList<>();
- private int mCaptionInsetsHeight = 0;
private int mImeCaptionBarInsetsHeight = 0;
private WindowInsetsAnimationControlListener mLoggingListener;
private @InsetsType int mRequestedVisibleTypes = WindowInsets.Type.defaultVisible();
@@ -99,11 +98,6 @@
}
@Override
- public void setCaptionInsetsHeight(int height) {
- mCaptionInsetsHeight = height;
- }
-
- @Override
public void setImeCaptionBarInsetsHeight(int height) {
mImeCaptionBarInsetsHeight = height;
}
@@ -187,9 +181,6 @@
controller.setSystemBarsAppearanceFromResource(
mAppearanceFromResource, mAppearanceFromResourceMask);
}
- if (mCaptionInsetsHeight != 0) {
- controller.setCaptionInsetsHeight(mCaptionInsetsHeight);
- }
if (mImeCaptionBarInsetsHeight != 0) {
controller.setImeCaptionBarInsetsHeight(mImeCaptionBarInsetsHeight);
}
diff --git a/core/java/android/view/ViewRootImpl.java b/core/java/android/view/ViewRootImpl.java
index f039a19..cd0602c 100644
--- a/core/java/android/view/ViewRootImpl.java
+++ b/core/java/android/view/ViewRootImpl.java
@@ -257,7 +257,6 @@
import com.android.internal.inputmethod.InputMethodDebug;
import com.android.internal.os.IResultReceiver;
import com.android.internal.os.SomeArgs;
-import com.android.internal.policy.DecorView;
import com.android.internal.policy.PhoneFallbackEventHandler;
import com.android.internal.view.BaseSurfaceHolder;
import com.android.internal.view.RootViewSurfaceTaker;
@@ -335,13 +334,6 @@
private static final boolean USE_ASYNC_PERFORM_HAPTIC_FEEDBACK = true;
/**
- * Whether the caption is drawn by the shell.
- * @hide
- */
- public static final boolean CAPTION_ON_SHELL =
- SystemProperties.getBoolean("persist.wm.debug.caption_on_shell", true);
-
- /**
* Whether the client (system UI) is handling the transient gesture and the corresponding
* animation.
* @hide
@@ -3176,22 +3168,6 @@
Trace.traceEnd(Trace.TRACE_TAG_VIEW);
}
- private boolean updateCaptionInsets() {
- if (CAPTION_ON_SHELL) {
- return false;
- }
- if (!(mView instanceof DecorView)) return false;
- final int captionInsetsHeight = ((DecorView) mView).getCaptionInsetsHeight();
- final Rect captionFrame = new Rect();
- if (captionInsetsHeight != 0) {
- captionFrame.set(mWinFrame.left, mWinFrame.top, mWinFrame.right,
- mWinFrame.top + captionInsetsHeight);
- }
- if (mAttachInfo.mCaptionInsets.equals(captionFrame)) return false;
- mAttachInfo.mCaptionInsets.set(captionFrame);
- return true;
- }
-
private boolean shouldDispatchCutout() {
return mWindowAttributes.layoutInDisplayCutoutMode
== LAYOUT_IN_DISPLAY_CUTOUT_MODE_ALWAYS
@@ -3645,9 +3621,6 @@
mAttachInfo.mAlwaysConsumeSystemBars = mPendingAlwaysConsumeSystemBars;
dispatchApplyInsets = true;
}
- if (updateCaptionInsets()) {
- dispatchApplyInsets = true;
- }
if (dispatchApplyInsets || mLastSystemUiVisibility !=
mAttachInfo.mSystemUiVisibility || mApplyInsetsRequested) {
mLastSystemUiVisibility = mAttachInfo.mSystemUiVisibility;
diff --git a/core/java/android/view/WindowInsetsController.java b/core/java/android/view/WindowInsetsController.java
index 1ffffb3..19c98a2 100644
--- a/core/java/android/view/WindowInsetsController.java
+++ b/core/java/android/view/WindowInsetsController.java
@@ -284,15 +284,6 @@
@Appearance int getSystemBarsAppearance();
/**
- * Notify the caption insets height change. The information will be used on the client side to,
- * make sure the InsetsState has the correct caption insets.
- *
- * @param height the height of caption bar insets.
- * @hide
- */
- void setCaptionInsetsHeight(int height);
-
- /**
* Sets the insets height for the IME caption bar, which corresponds to the
* "fake" IME navigation bar.
*
diff --git a/core/java/com/android/internal/policy/BackdropFrameRenderer.java b/core/java/com/android/internal/policy/BackdropFrameRenderer.java
index 6848646..c6e8bf7 100644
--- a/core/java/com/android/internal/policy/BackdropFrameRenderer.java
+++ b/core/java/com/android/internal/policy/BackdropFrameRenderer.java
@@ -56,7 +56,6 @@
// during a configuration change.
private int mLastContentWidth;
private int mLastContentHeight;
- private int mLastCaptionHeight;
private int mLastXOffset;
private int mLastYOffset;
@@ -269,7 +268,7 @@
final boolean firstCall = mLastContentWidth == 0;
// The current content buffer is drawn here.
mLastContentWidth = xSize;
- mLastContentHeight = ySize - mLastCaptionHeight;
+ mLastContentHeight = ySize;
mLastXOffset = xOffset;
mLastYOffset = yOffset;
@@ -278,12 +277,11 @@
mLastXOffset,
mLastYOffset,
mLastXOffset + mLastContentWidth,
- mLastYOffset + mLastCaptionHeight + mLastContentHeight);
+ mLastYOffset + mLastContentHeight);
// If this was the first call and redrawLocked got already called prior
// to us, we should re-issue a redrawLocked now.
- return firstCall
- && (mLastCaptionHeight != 0 || !mDecorView.isShowingCaption());
+ return firstCall;
}
}
@@ -303,22 +301,9 @@
*/
private void redrawLocked(Rect newBounds, boolean fullscreen) {
- // While a configuration change is taking place the view hierarchy might become
- // inaccessible. For that case we remember the previous metrics to avoid flashes.
- // Note that even when there is no visible caption, the caption child will exist.
- final int captionHeight = mDecorView.getCaptionHeight();
-
- // The caption height will probably never dynamically change while we are resizing.
- // Once set to something other then 0 it should be kept that way.
- if (captionHeight != 0) {
- // Remember the height of the caption.
- mLastCaptionHeight = captionHeight;
- }
-
// Make sure that the other thread has already prepared the render draw calls for the
// content. If any size is 0, we have to wait for it to be drawn first.
- if ((mLastCaptionHeight == 0 && mDecorView.isShowingCaption()) ||
- mLastContentWidth == 0 || mLastContentHeight == 0) {
+ if (mLastContentWidth == 0 || mLastContentHeight == 0) {
return;
}
@@ -337,13 +322,13 @@
? mUserCaptionBackgroundDrawable : mCaptionBackgroundDrawable;
if (drawable != null) {
- drawable.setBounds(0, 0, left + width, top + mLastCaptionHeight);
+ drawable.setBounds(0, 0, left + width, top);
drawable.draw(canvas);
}
// The backdrop: clear everything with the background. Clipping is done elsewhere.
if (mResizingBackgroundDrawable != null) {
- mResizingBackgroundDrawable.setBounds(0, mLastCaptionHeight, left + width, top + height);
+ mResizingBackgroundDrawable.setBounds(0, 0, left + width, top + height);
mResizingBackgroundDrawable.draw(canvas);
}
mFrameAndBackdropNode.endRecording();
diff --git a/core/java/com/android/internal/policy/DecorView.java b/core/java/com/android/internal/policy/DecorView.java
index a65a1bb..ab37252 100644
--- a/core/java/com/android/internal/policy/DecorView.java
+++ b/core/java/com/android/internal/policy/DecorView.java
@@ -26,9 +26,6 @@
import static android.view.View.MeasureSpec.getMode;
import static android.view.ViewGroup.LayoutParams.MATCH_PARENT;
import static android.view.ViewGroup.LayoutParams.WRAP_CONTENT;
-import static android.view.ViewRootImpl.CAPTION_ON_SHELL;
-import static android.view.Window.DECOR_CAPTION_SHADE_DARK;
-import static android.view.Window.DECOR_CAPTION_SHADE_LIGHT;
import static android.view.WindowInsetsController.APPEARANCE_FORCE_LIGHT_NAVIGATION_BARS;
import static android.view.WindowInsetsController.APPEARANCE_LIGHT_NAVIGATION_BARS;
import static android.view.WindowInsetsController.APPEARANCE_LIGHT_STATUS_BARS;
@@ -38,9 +35,6 @@
import static android.view.WindowManager.LayoutParams.FLAG_LAYOUT_IN_SCREEN;
import static android.view.WindowManager.LayoutParams.FLAG_TRANSLUCENT_NAVIGATION;
import static android.view.WindowManager.LayoutParams.FLAG_TRANSLUCENT_STATUS;
-import static android.view.WindowManager.LayoutParams.TYPE_APPLICATION;
-import static android.view.WindowManager.LayoutParams.TYPE_BASE_APPLICATION;
-import static android.view.WindowManager.LayoutParams.TYPE_DRAWN_APPLICATION;
import static android.view.WindowManager.LayoutParams.TYPE_INPUT_METHOD;
import static com.android.internal.policy.PhoneWindow.FEATURE_OPTIONS_PANEL;
@@ -83,7 +77,6 @@
import android.view.MenuItem;
import android.view.MotionEvent;
import android.view.PendingInsetsController;
-import android.view.ThreadedRenderer;
import android.view.View;
import android.view.ViewGroup;
import android.view.ViewOutlineProvider;
@@ -116,7 +109,6 @@
import com.android.internal.view.menu.MenuHelper;
import com.android.internal.widget.ActionBarContextView;
import com.android.internal.widget.BackgroundFallback;
-import com.android.internal.widget.DecorCaptionView;
import com.android.internal.widget.floatingtoolbar.FloatingToolbar;
import java.util.List;
@@ -189,8 +181,6 @@
private final Rect mFrameOffsets = new Rect();
- private boolean mHasCaption = false;
-
private boolean mChanging;
private Drawable mMenuBackground;
@@ -247,11 +237,6 @@
private Rect mTempRect;
- // This is the caption view for the window, containing the caption and window control
- // buttons. The visibility of this decor depends on the workspace and the window type.
- // If the window type does not require such a view, this member might be null.
- private DecorCaptionView mDecorCaptionView;
-
private boolean mWindowResizeCallbacksAdded = false;
private Drawable.Callback mLastBackgroundDrawableCb = null;
private BackdropFrameRenderer mBackdropFrameRenderer = null;
@@ -524,24 +509,6 @@
@Override
public boolean onInterceptTouchEvent(MotionEvent event) {
int action = event.getAction();
- if (mHasCaption && isShowingCaption()) {
- // Don't dispatch ACTION_DOWN to the captionr if the window is resizable and the event
- // was (starting) outside the window. Window resizing events should be handled by
- // WindowManager.
- // TODO: Investigate how to handle the outside touch in window manager
- // without generating these events.
- // Currently we receive these because we need to enlarge the window's
- // touch region so that the monitor channel receives the events
- // in the outside touch area.
- if (action == MotionEvent.ACTION_DOWN) {
- final int x = (int) event.getX();
- final int y = (int) event.getY();
- if (isOutOfInnerBounds(x, y)) {
- return true;
- }
- }
- }
-
if (mFeatureId >= 0) {
if (action == MotionEvent.ACTION_DOWN) {
int x = (int)event.getX();
@@ -1041,7 +1008,6 @@
@Override
public void onWindowSystemUiVisibilityChanged(int visible) {
updateColorViews(null /* insets */, true /* animate */);
- updateDecorCaptionStatus(getResources().getConfiguration());
if (mStatusGuard != null && mStatusGuard.getVisibility() == VISIBLE) {
updateStatusGuardColor();
@@ -1214,11 +1180,6 @@
mLastTopInset, false /* matchVertical */, statusBarNeedsLeftInset,
statusBarSideInset, animate && !disallowAnimate,
mForceWindowDrawsBarBackgrounds, requestedVisibleTypes);
-
- if (mHasCaption) {
- mDecorCaptionView.getCaption().setBackgroundColor(statusBarColor);
- updateDecorCaptionShade();
- }
}
// When we expand the window with FLAG_DRAWS_SYSTEM_BAR_BACKGROUNDS or
@@ -1483,7 +1444,7 @@
mWindow.getAttributes().flags, force);
boolean show = state.attributes.isVisible(state.present, color,
mWindow.getAttributes().flags, force);
- boolean showView = show && !isResizing() && !mHasCaption && size > 0;
+ boolean showView = show && !isResizing() && size > 0;
boolean visibilityChanged = false;
View view = state.view;
@@ -1950,9 +1911,6 @@
@Override
public void onRootViewScrollYChanged(int rootScrollY) {
mRootScrollY = rootScrollY;
- if (mDecorCaptionView != null) {
- mDecorCaptionView.onRootViewScrollYChanged(rootScrollY);
- }
updateColorViewTranslations();
}
@@ -2141,31 +2099,6 @@
.addOnPreDrawListener(mFloatingToolbarPreDrawListener);
}
- /**
- * Informs the decor if the caption is attached and visible.
- * @param attachedAndVisible true when the decor is visible.
- * Note that this will even be called if there is no caption.
- **/
- void enableCaption(boolean attachedAndVisible) {
- if (mHasCaption != attachedAndVisible) {
- mHasCaption = attachedAndVisible;
- if (getForeground() != null) {
- drawableChanged();
- }
- notifyCaptionHeightChanged();
- }
- }
-
- /**
- * An interface to be called when the caption visibility or height changed, to report the
- * corresponding insets change to the InsetsController.
- */
- public void notifyCaptionHeightChanged() {
- if (!CAPTION_ON_SHELL) {
- getWindowInsetsController().setCaptionInsetsHeight(getCaptionInsetsHeight());
- }
- }
-
void setWindow(PhoneWindow phoneWindow) {
mWindow = phoneWindow;
Context context = getContext();
@@ -2191,8 +2124,6 @@
protected void onConfigurationChanged(Configuration newConfig) {
super.onConfigurationChanged(newConfig);
- updateDecorCaptionStatus(newConfig);
-
initializeElevation();
}
@@ -2214,29 +2145,6 @@
& View.SYSTEM_UI_FLAG_FULLSCREEN));
}
- private void updateDecorCaptionStatus(Configuration config) {
- final boolean displayWindowDecor = config.windowConfiguration.hasWindowDecorCaption()
- && !isFillingScreen(config);
- if (mDecorCaptionView == null && displayWindowDecor) {
- // Configuration now requires a caption.
- final LayoutInflater inflater = mWindow.getLayoutInflater();
- mDecorCaptionView = createDecorCaptionView(inflater);
- if (mDecorCaptionView != null) {
- if (mDecorCaptionView.getParent() == null) {
- addView(mDecorCaptionView, 0,
- new ViewGroup.LayoutParams(MATCH_PARENT, MATCH_PARENT));
- }
- removeView(mContentRoot);
- mDecorCaptionView.addView(mContentRoot,
- new ViewGroup.MarginLayoutParams(MATCH_PARENT, MATCH_PARENT));
- }
- } else if (mDecorCaptionView != null) {
- // We might have to change the kind of surface before we do anything else.
- mDecorCaptionView.onConfigurationChanged(displayWindowDecor);
- enableCaption(displayWindowDecor);
- }
- }
-
void onResourcesLoaded(LayoutInflater inflater, int layoutResource) {
if (mBackdropFrameRenderer != null) {
loadBackgroundDrawablesIfNeeded();
@@ -2246,20 +2154,10 @@
getCurrentColor(mNavigationColorViewState));
}
- mDecorCaptionView = createDecorCaptionView(inflater);
final View root = inflater.inflate(layoutResource, null);
- if (mDecorCaptionView != null) {
- if (mDecorCaptionView.getParent() == null) {
- addView(mDecorCaptionView,
- new ViewGroup.LayoutParams(MATCH_PARENT, MATCH_PARENT));
- }
- mDecorCaptionView.addView(root,
- new ViewGroup.MarginLayoutParams(MATCH_PARENT, MATCH_PARENT));
- } else {
- // Put it below the color views.
- addView(root, 0, new ViewGroup.LayoutParams(MATCH_PARENT, MATCH_PARENT));
- }
+ // Put it below the color views.
+ addView(root, 0, new ViewGroup.LayoutParams(MATCH_PARENT, MATCH_PARENT));
mContentRoot = (ViewGroup) root;
initializeElevation();
}
@@ -2285,89 +2183,6 @@
}
}
- // Free floating overlapping windows require a caption.
- private DecorCaptionView createDecorCaptionView(LayoutInflater inflater) {
- DecorCaptionView decorCaptionView = null;
- for (int i = getChildCount() - 1; i >= 0 && decorCaptionView == null; i--) {
- View view = getChildAt(i);
- if (view instanceof DecorCaptionView) {
- // The decor was most likely saved from a relaunch - so reuse it.
- decorCaptionView = (DecorCaptionView) view;
- removeViewAt(i);
- }
- }
- final WindowManager.LayoutParams attrs = mWindow.getAttributes();
- final boolean isApplication = attrs.type == TYPE_BASE_APPLICATION ||
- attrs.type == TYPE_APPLICATION || attrs.type == TYPE_DRAWN_APPLICATION;
- final WindowConfiguration winConfig = getResources().getConfiguration().windowConfiguration;
- // Only a non floating application window on one of the allowed workspaces can get a caption
- if (!mWindow.isFloating() && isApplication && winConfig.hasWindowDecorCaption()
- && !CAPTION_ON_SHELL) {
- // Dependent on the brightness of the used title we either use the
- // dark or the light button frame.
- if (decorCaptionView == null) {
- decorCaptionView = inflateDecorCaptionView(inflater);
- }
- decorCaptionView.setPhoneWindow(mWindow, true /*showDecor*/);
- } else {
- decorCaptionView = null;
- }
-
- // Tell the decor if it has a visible caption.
- enableCaption(decorCaptionView != null);
- return decorCaptionView;
- }
-
- private DecorCaptionView inflateDecorCaptionView(LayoutInflater inflater) {
- final Context context = getContext();
- // We make a copy of the inflater, so it has the right context associated with it.
- inflater = inflater.from(context);
- final DecorCaptionView view = (DecorCaptionView) inflater.inflate(R.layout.decor_caption,
- null);
- setDecorCaptionShade(view);
- return view;
- }
-
- private void setDecorCaptionShade(DecorCaptionView view) {
- final int shade = mWindow.getDecorCaptionShade();
- switch (shade) {
- case DECOR_CAPTION_SHADE_LIGHT:
- setLightDecorCaptionShade(view);
- break;
- case DECOR_CAPTION_SHADE_DARK:
- setDarkDecorCaptionShade(view);
- break;
- default: {
- if ((getWindowSystemUiVisibility() & SYSTEM_UI_FLAG_LIGHT_STATUS_BAR) != 0) {
- setDarkDecorCaptionShade(view);
- } else {
- setLightDecorCaptionShade(view);
- }
- break;
- }
- }
- }
-
- void updateDecorCaptionShade() {
- if (mDecorCaptionView != null) {
- setDecorCaptionShade(mDecorCaptionView);
- }
- }
-
- private void setLightDecorCaptionShade(DecorCaptionView view) {
- view.findViewById(R.id.maximize_window).setBackgroundResource(
- R.drawable.decor_maximize_button_light);
- view.findViewById(R.id.close_window).setBackgroundResource(
- R.drawable.decor_close_button_light);
- }
-
- private void setDarkDecorCaptionShade(DecorCaptionView view) {
- view.findViewById(R.id.maximize_window).setBackgroundResource(
- R.drawable.decor_maximize_button_dark);
- view.findViewById(R.id.close_window).setBackgroundResource(
- R.drawable.decor_close_button_dark);
- }
-
/**
* Returns the color used to fill areas the app has not rendered content to yet when the
* user is resizing the window of an activity in multi-window mode.
@@ -2405,17 +2220,11 @@
}
void clearContentView() {
- if (mDecorCaptionView != null) {
- mDecorCaptionView.removeContentView();
- } else {
- // This window doesn't have caption, so we need to remove everything except our views
- // we might have added.
- for (int i = getChildCount() - 1; i >= 0; i--) {
- View v = getChildAt(i);
- if (v != mStatusColorViewState.view && v != mNavigationColorViewState.view
- && v != mStatusGuard) {
- removeViewAt(i);
- }
+ for (int i = getChildCount() - 1; i >= 0; i--) {
+ View v = getChildAt(i);
+ if (v != mStatusColorViewState.view && v != mNavigationColorViewState.view
+ && v != mStatusGuard) {
+ removeViewAt(i);
}
}
}
@@ -2439,23 +2248,6 @@
if (mBackdropFrameRenderer != null) {
return;
}
- final ThreadedRenderer renderer = getThreadedRenderer();
- if (renderer != null && !CAPTION_ON_SHELL) {
- loadBackgroundDrawablesIfNeeded();
- WindowInsets rootInsets = getRootWindowInsets();
- mBackdropFrameRenderer = new BackdropFrameRenderer(this, renderer,
- initialBounds, mResizingBackgroundDrawable, mCaptionBackgroundDrawable,
- mUserCaptionBackgroundDrawable, getCurrentColor(mStatusColorViewState),
- getCurrentColor(mNavigationColorViewState), fullscreen,
- rootInsets.getInsets(WindowInsets.Type.systemBars()));
-
- // Get rid of the shadow while we are resizing. Shadow drawing takes considerable time.
- // If we want to get the shadow shown while resizing, we would need to elevate a new
- // element which owns the caption and has the elevation.
- updateElevation();
-
- updateColorViews(null /* insets */, false);
- }
getViewRootImpl().requestInvalidateRootRenderNode();
}
@@ -2576,23 +2368,6 @@
}
}
- boolean isShowingCaption() {
- return mDecorCaptionView != null && mDecorCaptionView.isCaptionShowing();
- }
-
- int getCaptionHeight() {
- return isShowingCaption() ? mDecorCaptionView.getCaptionHeight() : 0;
- }
-
- /**
- * @hide
- * @return the height of insets covering the top of window content area.
- */
- public int getCaptionInsetsHeight() {
- if (!mWindow.isOverlayWithDecorCaptionEnabled()) return 0;
- return getCaptionHeight();
- }
-
/**
* Converts a DIP measure into physical pixels.
* @param dip The dip value.
diff --git a/core/java/com/android/internal/policy/PhoneWindow.java b/core/java/com/android/internal/policy/PhoneWindow.java
index e6a2a6c..1841353 100644
--- a/core/java/com/android/internal/policy/PhoneWindow.java
+++ b/core/java/com/android/internal/policy/PhoneWindow.java
@@ -363,8 +363,6 @@
private boolean mIsStartingWindow;
private int mTheme = -1;
- private int mDecorCaptionShade = DECOR_CAPTION_SHADE_AUTO;
-
private boolean mUseDecorContext = false;
/** @see ViewRootImpl#mActivityConfigCallback */
@@ -4048,14 +4046,7 @@
@Override
public void setDecorCaptionShade(int decorCaptionShade) {
- mDecorCaptionShade = decorCaptionShade;
- if (mDecor != null) {
- mDecor.updateDecorCaptionShade();
- }
- }
-
- int getDecorCaptionShade() {
- return mDecorCaptionShade;
+ // TODO(b/328668781): Make proper treatment to this public API per the outcome of the bug.
}
@Override
diff --git a/core/java/com/android/internal/widget/DecorCaptionView.java b/core/java/com/android/internal/widget/DecorCaptionView.java
deleted file mode 100644
index 362fd7b..0000000
--- a/core/java/com/android/internal/widget/DecorCaptionView.java
+++ /dev/null
@@ -1,418 +0,0 @@
-/*
- * Copyright (C) 2015 The Android Open Source Project
- *
- * Licensed under the Apache License, Version 2.0 (the "License");
- * you may not use this file except in compliance with the License.
- * You may obtain a copy of the License at
- *
- * http://www.apache.org/licenses/LICENSE-2.0
- *
- * Unless required by applicable law or agreed to in writing, software
- * distributed under the License is distributed on an "AS IS" BASIS,
- * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
- * See the License for the specific language governing permissions and
- * limitations under the License.
- */
-
-package com.android.internal.widget;
-
-import android.content.Context;
-import android.graphics.Rect;
-import android.util.AttributeSet;
-import android.view.GestureDetector;
-import android.view.MotionEvent;
-import android.view.View;
-import android.view.ViewConfiguration;
-import android.view.ViewGroup;
-import android.view.ViewOutlineProvider;
-import android.view.Window;
-
-import com.android.internal.R;
-import com.android.internal.policy.DecorView;
-import com.android.internal.policy.PhoneWindow;
-
-import java.util.ArrayList;
-
-/**
- * This class represents the special screen elements to control a window on freeform
- * environment.
- * As such this class handles the following things:
- * <ul>
- * <li>The caption, containing the system buttons like maximize, close and such as well as
- * allowing the user to drag the window around.</li>
- * </ul>
- * After creating the view, the function {@link #setPhoneWindow} needs to be called to make
- * the connection to it's owning PhoneWindow.
- * Note: At this time the application can change various attributes of the DecorView which
- * will break things (in subtle/unexpected ways):
- * <ul>
- * <li>setOutlineProvider</li>
- * <li>setSurfaceFormat</li>
- * <li>..</li>
- * </ul>
- *
- * Here describe the behavior of overlaying caption on the content and drawing.
- *
- * First, no matter where the content View gets added, it will always be the first child and the
- * caption will be the second. This way the caption will always be drawn on top of the content when
- * overlaying is enabled.
- *
- * Second, the touch dispatch is customized to handle overlaying. This is what happens when touch
- * is dispatched on the caption area while overlaying it on content:
- * <ul>
- * <li>DecorCaptionView.onInterceptTouchEvent() will try intercepting the touch events if the
- * down action is performed on top close or maximize buttons; the reason for that is we want these
- * buttons to always work.</li>
- * <li>The caption view will try to consume the event to apply the dragging logic.</li>
- * <li>If the touch event is not consumed by the caption, the content View will receive the touch
- * event</li>
- * </ul>
- */
-public class DecorCaptionView extends ViewGroup implements View.OnTouchListener,
- GestureDetector.OnGestureListener {
- private PhoneWindow mOwner = null;
- private boolean mShow = false;
-
- // True if the window is being dragged.
- private boolean mDragging = false;
-
- private boolean mOverlayWithAppContent = false;
-
- private View mCaption;
- private View mContent;
- private View mMaximize;
- private View mClose;
-
- // Fields for detecting drag events.
- private int mTouchDownX;
- private int mTouchDownY;
- private boolean mCheckForDragging;
- private int mDragSlop;
-
- // Fields for detecting and intercepting click events on close/maximize.
- private ArrayList<View> mTouchDispatchList = new ArrayList<>(2);
- // We use the gesture detector to detect clicks on close/maximize buttons and to be consistent
- // with existing click detection.
- private GestureDetector mGestureDetector;
- private final Rect mCloseRect = new Rect();
- private final Rect mMaximizeRect = new Rect();
- private View mClickTarget;
- private int mRootScrollY;
-
- public DecorCaptionView(Context context) {
- super(context);
- init(context);
- }
-
- public DecorCaptionView(Context context, AttributeSet attrs) {
- super(context, attrs);
- init(context);
- }
-
- public DecorCaptionView(Context context, AttributeSet attrs, int defStyle) {
- super(context, attrs, defStyle);
- init(context);
- }
-
- private void init(Context context) {
- mDragSlop = ViewConfiguration.get(context).getScaledTouchSlop();
- mGestureDetector = new GestureDetector(context, this);
- setContentDescription(context.getString(R.string.accessibility_freeform_caption,
- context.getPackageManager().getApplicationLabel(context.getApplicationInfo())));
- }
-
- @Override
- protected void onFinishInflate() {
- super.onFinishInflate();
- mCaption = getChildAt(0);
- }
-
- public void setPhoneWindow(PhoneWindow owner, boolean show) {
- mOwner = owner;
- mShow = show;
- mOverlayWithAppContent = owner.isOverlayWithDecorCaptionEnabled();
- updateCaptionVisibility();
- // By changing the outline provider to BOUNDS, the window can remove its
- // background without removing the shadow.
- mOwner.getDecorView().setOutlineProvider(ViewOutlineProvider.BOUNDS);
- mMaximize = findViewById(R.id.maximize_window);
- mClose = findViewById(R.id.close_window);
- }
-
- @Override
- public boolean onInterceptTouchEvent(MotionEvent ev) {
- // If the user starts touch on the maximize/close buttons, we immediately intercept, so
- // that these buttons are always clickable.
- if (ev.getAction() == MotionEvent.ACTION_DOWN) {
- final int x = (int) ev.getX();
- final int y = (int) ev.getY();
- // Only offset y for containment tests because the actual views are already translated.
- if (mMaximizeRect.contains(x, y - mRootScrollY)) {
- mClickTarget = mMaximize;
- }
- if (mCloseRect.contains(x, y - mRootScrollY)) {
- mClickTarget = mClose;
- }
- }
- return mClickTarget != null;
- }
-
- @Override
- public boolean onTouchEvent(MotionEvent event) {
- if (mClickTarget != null) {
- mGestureDetector.onTouchEvent(event);
- final int action = event.getAction();
- if (action == MotionEvent.ACTION_UP || action == MotionEvent.ACTION_CANCEL) {
- mClickTarget = null;
- }
- return true;
- }
- return false;
- }
-
- @Override
- public boolean onTouch(View v, MotionEvent e) {
- // Note: There are no mixed events. When a new device gets used (e.g. 1. Mouse, 2. touch)
- // the old input device events get cancelled first. So no need to remember the kind of
- // input device we are listening to.
- final int x = (int) e.getX();
- final int y = (int) e.getY();
- final boolean fromMouse = e.getToolType(e.getActionIndex()) == MotionEvent.TOOL_TYPE_MOUSE;
- final boolean primaryButton = (e.getButtonState() & MotionEvent.BUTTON_PRIMARY) != 0;
- final int actionMasked = e.getActionMasked();
- switch (actionMasked) {
- case MotionEvent.ACTION_DOWN:
- if (!mShow) {
- // When there is no caption we should not react to anything.
- return false;
- }
- // Checking for a drag action is started if we aren't dragging already and the
- // starting event is either a left mouse button or any other input device.
- if (!fromMouse || primaryButton) {
- mCheckForDragging = true;
- mTouchDownX = x;
- mTouchDownY = y;
- }
- break;
-
- case MotionEvent.ACTION_MOVE:
- if (!mDragging && mCheckForDragging && (fromMouse || passedSlop(x, y))) {
- mCheckForDragging = false;
- mDragging = true;
- startMovingTask(e.getRawX(), e.getRawY());
- // After the above call the framework will take over the input.
- // This handler will receive ACTION_CANCEL soon (possible after a few spurious
- // ACTION_MOVE events which are safe to ignore).
- }
- break;
-
- case MotionEvent.ACTION_UP:
- case MotionEvent.ACTION_CANCEL:
- if (!mDragging) {
- break;
- }
- // Abort the ongoing dragging.
- if (actionMasked == MotionEvent.ACTION_UP) {
- // If it receives ACTION_UP event, the dragging is already finished and also
- // the system can not end drag on ACTION_UP event. So request to finish
- // dragging.
- finishMovingTask();
- }
- mDragging = false;
- return !mCheckForDragging;
- }
- return mDragging || mCheckForDragging;
- }
-
- @Override
- public boolean shouldDelayChildPressedState() {
- return false;
- }
-
- private boolean passedSlop(int x, int y) {
- return Math.abs(x - mTouchDownX) > mDragSlop || Math.abs(y - mTouchDownY) > mDragSlop;
- }
-
- /**
- * The phone window configuration has changed and the caption needs to be updated.
- * @param show True if the caption should be shown.
- */
- public void onConfigurationChanged(boolean show) {
- mShow = show;
- updateCaptionVisibility();
- }
-
- @Override
- public void addView(View child, int index, ViewGroup.LayoutParams params) {
- if (!(params instanceof MarginLayoutParams)) {
- throw new IllegalArgumentException(
- "params " + params + " must subclass MarginLayoutParams");
- }
- // Make sure that we never get more then one client area in our view.
- if (index >= 2 || getChildCount() >= 2) {
- throw new IllegalStateException("DecorCaptionView can only handle 1 client view");
- }
- // To support the overlaying content in the caption, we need to put the content view as the
- // first child to get the right Z-Ordering.
- super.addView(child, 0, params);
- mContent = child;
- }
-
- @Override
- protected void onMeasure(int widthMeasureSpec, int heightMeasureSpec) {
- final int captionHeight;
- if (mCaption.getVisibility() != View.GONE) {
- measureChildWithMargins(mCaption, widthMeasureSpec, 0, heightMeasureSpec, 0);
- captionHeight = mCaption.getMeasuredHeight();
- } else {
- captionHeight = 0;
- }
- if (mContent != null) {
- if (mOverlayWithAppContent) {
- measureChildWithMargins(mContent, widthMeasureSpec, 0, heightMeasureSpec, 0);
- } else {
- measureChildWithMargins(mContent, widthMeasureSpec, 0, heightMeasureSpec,
- captionHeight);
- }
- }
-
- setMeasuredDimension(MeasureSpec.getSize(widthMeasureSpec),
- MeasureSpec.getSize(heightMeasureSpec));
- }
-
- @Override
- protected void onLayout(boolean changed, int left, int top, int right, int bottom) {
- final int captionHeight;
- if (mCaption.getVisibility() != View.GONE) {
- mCaption.layout(0, 0, mCaption.getMeasuredWidth(), mCaption.getMeasuredHeight());
- captionHeight = mCaption.getBottom() - mCaption.getTop();
- mMaximize.getHitRect(mMaximizeRect);
- mClose.getHitRect(mCloseRect);
- } else {
- captionHeight = 0;
- mMaximizeRect.setEmpty();
- mCloseRect.setEmpty();
- }
-
- if (mContent != null) {
- if (mOverlayWithAppContent) {
- mContent.layout(0, 0, mContent.getMeasuredWidth(), mContent.getMeasuredHeight());
- } else {
- mContent.layout(0, captionHeight, mContent.getMeasuredWidth(),
- captionHeight + mContent.getMeasuredHeight());
- }
- }
-
- ((DecorView) mOwner.getDecorView()).notifyCaptionHeightChanged();
-
- // This assumes that the caption bar is at the top.
- mOwner.notifyRestrictedCaptionAreaCallback(mMaximize.getLeft(), mMaximize.getTop(),
- mClose.getRight(), mClose.getBottom());
- }
-
- /**
- * Updates the visibility of the caption.
- **/
- private void updateCaptionVisibility() {
- mCaption.setVisibility(mShow ? VISIBLE : GONE);
- mCaption.setOnTouchListener(this);
- }
-
- /**
- * Maximize or restore the window by moving it to the maximized or freeform workspace stack.
- **/
- private void toggleFreeformWindowingMode() {
- Window.WindowControllerCallback callback = mOwner.getWindowControllerCallback();
- if (callback != null) {
- callback.toggleFreeformWindowingMode();
- }
- }
-
- public boolean isCaptionShowing() {
- return mShow;
- }
-
- public int getCaptionHeight() {
- return (mCaption != null) ? mCaption.getHeight() : 0;
- }
-
- public void removeContentView() {
- if (mContent != null) {
- removeView(mContent);
- mContent = null;
- }
- }
-
- public View getCaption() {
- return mCaption;
- }
-
- @Override
- public LayoutParams generateLayoutParams(AttributeSet attrs) {
- return new MarginLayoutParams(getContext(), attrs);
- }
-
- @Override
- protected LayoutParams generateDefaultLayoutParams() {
- return new MarginLayoutParams(MarginLayoutParams.MATCH_PARENT,
- MarginLayoutParams.MATCH_PARENT);
- }
-
- @Override
- protected LayoutParams generateLayoutParams(LayoutParams p) {
- return new MarginLayoutParams(p);
- }
-
- @Override
- protected boolean checkLayoutParams(ViewGroup.LayoutParams p) {
- return p instanceof MarginLayoutParams;
- }
-
- @Override
- public boolean onDown(MotionEvent e) {
- return false;
- }
-
- @Override
- public void onShowPress(MotionEvent e) {
-
- }
-
- @Override
- public boolean onSingleTapUp(MotionEvent e) {
- if (mClickTarget == mMaximize) {
- toggleFreeformWindowingMode();
- } else if (mClickTarget == mClose) {
- mOwner.dispatchOnWindowDismissed(
- true /*finishTask*/, false /*suppressWindowTransition*/);
- }
- return true;
- }
-
- @Override
- public boolean onScroll(MotionEvent e1, MotionEvent e2, float distanceX, float distanceY) {
- return false;
- }
-
- @Override
- public void onLongPress(MotionEvent e) {
-
- }
-
- @Override
- public boolean onFling(MotionEvent e1, MotionEvent e2, float velocityX, float velocityY) {
- return false;
- }
-
- /**
- * Called when {@link android.view.ViewRootImpl} scrolls for adjustPan.
- */
- public void onRootViewScrollYChanged(int scrollY) {
- // Offset the caption opposite the root scroll. This keeps the caption at the
- // top of the window during adjustPan.
- if (mCaption != null) {
- mRootScrollY = scrollY;
- mCaption.setTranslationY(scrollY);
- }
- }
-}
diff --git a/core/jni/Android.bp b/core/jni/Android.bp
index 03b57d0..773823d 100644
--- a/core/jni/Android.bp
+++ b/core/jni/Android.bp
@@ -21,6 +21,7 @@
config_namespace: "ANDROID",
bool_variables: [
"release_binder_death_recipient_weak_from_jni",
+ "release_package_libandroid_runtime_punch_holes",
],
properties: [
"cflags",
@@ -63,6 +64,9 @@
release_binder_death_recipient_weak_from_jni: {
cflags: ["-DBINDER_DEATH_RECIPIENT_WEAK_FROM_JNI"],
},
+ release_package_libandroid_runtime_punch_holes: {
+ cflags: ["-DENABLE_PUNCH_HOLES"],
+ },
},
cpp_std: "gnu++20",
@@ -123,6 +127,7 @@
srcs: [
"AndroidRuntime.cpp",
"com_android_internal_content_F2fsUtils.cpp",
+ "com_android_internal_content_FileSystemUtils.cpp",
"com_android_internal_content_NativeLibraryHelper.cpp",
"com_google_android_gles_jni_EGLImpl.cpp",
"com_google_android_gles_jni_GLImpl.cpp", // TODO: .arm
diff --git a/core/jni/com_android_internal_content_FileSystemUtils.cpp b/core/jni/com_android_internal_content_FileSystemUtils.cpp
new file mode 100644
index 0000000..4bd2d72
--- /dev/null
+++ b/core/jni/com_android_internal_content_FileSystemUtils.cpp
@@ -0,0 +1,218 @@
+/*
+ * Copyright (C) 2024 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.
+ */
+
+#define LOG_TAG "FileSystemUtils"
+
+#include "com_android_internal_content_FileSystemUtils.h"
+
+#include <android-base/file.h>
+#include <android-base/hex.h>
+#include <android-base/unique_fd.h>
+#include <elf.h>
+#include <errno.h>
+#include <fcntl.h>
+#include <inttypes.h>
+#include <linux/fs.h>
+#include <sys/ioctl.h>
+#include <sys/stat.h>
+#include <sys/types.h>
+#include <utils/Log.h>
+
+#include <array>
+#include <fstream>
+#include <vector>
+
+using android::base::HexString;
+using android::base::ReadFullyAtOffset;
+
+namespace android {
+bool punchHoles(const char *filePath, const uint64_t offset,
+ const std::vector<Elf64_Phdr> &programHeaders) {
+ struct stat64 beforePunch;
+ lstat64(filePath, &beforePunch);
+ uint64_t blockSize = beforePunch.st_blksize;
+ IF_ALOGD() {
+ ALOGD("Total number of LOAD segments %zu", programHeaders.size());
+
+ ALOGD("Size before punching holes st_blocks: %" PRIu64
+ ", st_blksize: %ld, st_size: %" PRIu64 "",
+ beforePunch.st_blocks, beforePunch.st_blksize,
+ static_cast<uint64_t>(beforePunch.st_size));
+ }
+
+ android::base::unique_fd fd(open(filePath, O_RDWR | O_CLOEXEC));
+ if (!fd.ok()) {
+ ALOGE("Can't open file to punch %s", filePath);
+ return false;
+ }
+
+ // read in chunks of 64KB
+ constexpr uint64_t kChunkSize = 64 * 1024;
+
+ // malloc is used to gracefully handle oom which might occur during the allocation of buffer.
+ // allocating using new or vector here results in oom/exception on failure where as malloc will
+ // return nullptr.
+ std::unique_ptr<uint8_t, decltype(&free)> buffer(static_cast<uint8_t *>(malloc(kChunkSize)),
+ &free);
+ if (buffer == nullptr) {
+ ALOGE("Failed to allocate read buffer");
+ return false;
+ }
+
+ for (size_t index = 0; programHeaders.size() >= 2 && index < programHeaders.size() - 1;
+ index++) {
+ // find LOAD segments from program headers, calculate padding and punch holes
+ uint64_t punchOffset;
+ if (__builtin_add_overflow(programHeaders[index].p_offset, programHeaders[index].p_filesz,
+ &punchOffset)) {
+ ALOGE("Overflow occurred when adding offset and filesize");
+ return false;
+ }
+
+ uint64_t punchLen;
+ if (__builtin_sub_overflow(programHeaders[index + 1].p_offset, punchOffset, &punchLen)) {
+ ALOGE("Overflow occurred when calculating length");
+ return false;
+ }
+
+ if (punchLen < blockSize) {
+ continue;
+ }
+
+ uint64_t punchStartOffset;
+ if (__builtin_add_overflow(offset, punchOffset, &punchStartOffset)) {
+ ALOGE("Overflow occurred when calculating length");
+ return false;
+ }
+
+ uint64_t position = punchStartOffset;
+ uint64_t endPosition;
+ if (__builtin_add_overflow(position, punchLen, &endPosition)) {
+ ALOGE("Overflow occurred when calculating length");
+ return false;
+ }
+
+ // Read content in kChunkSize and verify it is zero
+ while (position <= endPosition) {
+ uint64_t uncheckedChunkEnd;
+ if (__builtin_add_overflow(position, kChunkSize, &uncheckedChunkEnd)) {
+ ALOGE("Overflow occurred when calculating uncheckedChunkEnd");
+ return false;
+ }
+
+ uint64_t readLength;
+ if (__builtin_sub_overflow(std::min(uncheckedChunkEnd, endPosition), position,
+ &readLength)) {
+ ALOGE("Overflow occurred when calculating readLength");
+ return false;
+ }
+
+ if (!ReadFullyAtOffset(fd, buffer.get(), readLength, position)) {
+ ALOGE("Failed to read content to punch holes");
+ return false;
+ }
+
+ IF_ALOGD() {
+ ALOGD("Punching holes for length:%" PRIu64 " content which should be zero: %s",
+ readLength, HexString(buffer.get(), readLength).c_str());
+ }
+
+ bool isZero = std::all_of(buffer.get(), buffer.get() + readLength,
+ [](uint8_t i) constexpr { return i == 0; });
+ if (!isZero) {
+ ALOGE("Found non zero content while trying to punch hole. Skipping operation");
+ return false;
+ }
+
+ position = uncheckedChunkEnd;
+ }
+
+ // if we have a uncompressed file which is being opened from APK, use the offset to
+ // punch native lib inside Apk.
+ int result = fallocate(fd, FALLOC_FL_PUNCH_HOLE | FALLOC_FL_KEEP_SIZE, punchStartOffset,
+ punchLen);
+ if (result < 0) {
+ ALOGE("fallocate failed to punch hole, error:%d", errno);
+ return false;
+ }
+ }
+
+ IF_ALOGD() {
+ struct stat64 afterPunch;
+ lstat64(filePath, &afterPunch);
+ ALOGD("Size after punching holes st_blocks: %" PRIu64 ", st_blksize: %ld, st_size: %" PRIu64
+ "",
+ afterPunch.st_blocks, afterPunch.st_blksize,
+ static_cast<uint64_t>(afterPunch.st_size));
+ }
+
+ return true;
+}
+
+bool punchHolesInElf64(const char *filePath, const uint64_t offset) {
+ // Open Elf file
+ Elf64_Ehdr ehdr;
+ std::ifstream inputStream(filePath, std::ifstream::in);
+
+ // If this is a zip file, set the offset so that we can read elf file directly
+ inputStream.seekg(offset);
+ // read executable headers
+ inputStream.read((char *)&ehdr, sizeof(ehdr));
+ if (!inputStream.good()) {
+ return false;
+ }
+
+ // only consider elf64 for punching holes
+ if (ehdr.e_ident[EI_CLASS] != ELFCLASS64) {
+ ALOGE("Provided file is not ELF64");
+ return false;
+ }
+
+ // read the program headers from elf file
+ uint64_t programHeaderOffset = ehdr.e_phoff;
+ uint16_t programHeaderNum = ehdr.e_phnum;
+
+ IF_ALOGD() {
+ ALOGD("Punching holes in file: %s programHeaderOffset: %" PRIu64 " programHeaderNum: %hu",
+ filePath, programHeaderOffset, programHeaderNum);
+ }
+
+ // if this is a zip file, also consider elf offset inside a file
+ uint64_t phOffset;
+ if (__builtin_add_overflow(offset, programHeaderOffset, &phOffset)) {
+ ALOGE("Overflow occurred when calculating phOffset");
+ return false;
+ }
+ inputStream.seekg(phOffset);
+
+ std::vector<Elf64_Phdr> programHeaders;
+ for (int headerIndex = 0; headerIndex < programHeaderNum; headerIndex++) {
+ Elf64_Phdr header;
+ inputStream.read((char *)&header, sizeof(header));
+ if (!inputStream.good()) {
+ return false;
+ }
+
+ if (header.p_type != PT_LOAD) {
+ continue;
+ }
+ programHeaders.push_back(header);
+ }
+
+ return punchHoles(filePath, offset, programHeaders);
+}
+
+}; // namespace android
diff --git a/core/jni/com_android_internal_content_FileSystemUtils.h b/core/jni/com_android_internal_content_FileSystemUtils.h
new file mode 100644
index 0000000..a6b145c
--- /dev/null
+++ b/core/jni/com_android_internal_content_FileSystemUtils.h
@@ -0,0 +1,31 @@
+/*
+ * Copyright (C) 2024 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.
+ */
+#pragma once
+
+#include <sys/types.h>
+
+namespace android {
+
+/*
+ * This function deallocates space used by zero padding at the end of LOAD segments in given
+ * uncompressed ELF file. Read ELF headers and find out the offset and sizes of LOAD segments.
+ * [fallocate(2)](http://man7.org/linux/man-pages/man2/fallocate.2.html) is used to deallocate the
+ * zero ranges at the end of LOAD segments. If ELF file is present inside of ApK/Zip file, offset to
+ * the start of the ELF file should be provided.
+ */
+bool punchHolesInElf64(const char* filePath, uint64_t offset);
+
+} // namespace android
\ No newline at end of file
diff --git a/core/jni/com_android_internal_content_NativeLibraryHelper.cpp b/core/jni/com_android_internal_content_NativeLibraryHelper.cpp
index 149e57a..faa83f8 100644
--- a/core/jni/com_android_internal_content_NativeLibraryHelper.cpp
+++ b/core/jni/com_android_internal_content_NativeLibraryHelper.cpp
@@ -36,6 +36,7 @@
#include <memory>
+#include "com_android_internal_content_FileSystemUtils.h"
#include "core_jni_helpers.h"
#define RS_BITCODE_SUFFIX ".bc"
@@ -169,6 +170,15 @@
return INSTALL_FAILED_INVALID_APK;
}
+#ifdef ENABLE_PUNCH_HOLES
+ // if library is uncompressed, punch hole in it in place
+ if (!punchHolesInElf64(zipFile->getZipFileName(), offset)) {
+ ALOGW("Failed to punch uncompressed elf file :%s inside apk : %s at offset: "
+ "%" PRIu64 "",
+ fileName, zipFile->getZipFileName(), offset);
+ }
+#endif // ENABLE_PUNCH_HOLES
+
return INSTALL_SUCCEEDED;
}
diff --git a/core/tests/FileSystemUtilsTest/Android.bp b/core/tests/FileSystemUtilsTest/Android.bp
new file mode 100644
index 0000000..53c22df
--- /dev/null
+++ b/core/tests/FileSystemUtilsTest/Android.bp
@@ -0,0 +1,78 @@
+// Copyright (C) 2024 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 {
+ default_applicable_licenses: ["frameworks_base_license"],
+ default_team: "trendy_team_android_kernel",
+}
+
+cc_library {
+ name: "libpunchtest",
+ stl: "none",
+ host_supported: true,
+ srcs: ["jni/android_test_jni_source.cpp"],
+ header_libs: ["jni_headers"],
+}
+
+android_test_helper_app {
+ name: "embedded_native_libs_test_app",
+ srcs: ["apk_embedded_native_libs/src/**/*.java"],
+ manifest: "apk_embedded_native_libs/embedded_native_libs_test_app.xml",
+ compile_multilib: "64",
+ jni_libs: [
+ "libpunchtest",
+ ],
+ static_libs: [
+ "androidx.test.rules",
+ "platform-test-annotations",
+ ],
+ use_embedded_native_libs: true,
+}
+
+android_test_helper_app {
+ name: "extract_native_libs_test_app",
+ srcs: ["apk_extract_native_libs/src/**/*.java"],
+ manifest: "apk_extract_native_libs/extract_native_libs_test_app.xml",
+ compile_multilib: "64",
+ jni_libs: [
+ "libpunchtest",
+ ],
+ static_libs: [
+ "androidx.test.rules",
+ "platform-test-annotations",
+ ],
+ use_embedded_native_libs: false,
+}
+
+java_test_host {
+ name: "FileSystemUtilsTests",
+ // Include all test java files
+ srcs: ["src/**/*.java"],
+ static_libs: [
+ "junit",
+ "platform-test-annotations",
+ "truth",
+ ],
+ libs: [
+ "tradefed",
+ "compatibility-host-util",
+ "compatibility-tradefed",
+ ],
+ data: [
+ ":embedded_native_libs_test_app",
+ ":extract_native_libs_test_app",
+ ],
+ test_suites: ["general-tests"],
+ test_config: "AndroidTest.xml",
+}
diff --git a/core/tests/FileSystemUtilsTest/AndroidManifest.xml b/core/tests/FileSystemUtilsTest/AndroidManifest.xml
new file mode 100644
index 0000000..acd5ef3
--- /dev/null
+++ b/core/tests/FileSystemUtilsTest/AndroidManifest.xml
@@ -0,0 +1,27 @@
+<?xml version="1.0" encoding="utf-8"?>
+<!--
+ ~ Copyright (C) 2024 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.
+ -->
+
+<manifest xmlns:android="http://schemas.android.com/apk/res/android"
+ android:installLocation="internalOnly"
+ package="com.android.internal.content.fstests">
+
+ <instrumentation
+ android:name="androidx.test.runner.AndroidJUnitRunner"
+ android:targetPackage="com.android.internal.content.fstests"
+ android:label="Frameworks FileSystemUtils Tests" />
+
+</manifest>
diff --git a/core/tests/FileSystemUtilsTest/AndroidTest.xml b/core/tests/FileSystemUtilsTest/AndroidTest.xml
new file mode 100644
index 0000000..27f49b2
--- /dev/null
+++ b/core/tests/FileSystemUtilsTest/AndroidTest.xml
@@ -0,0 +1,30 @@
+<?xml version="1.0" encoding="utf-8"?>
+<!--
+ ~ Copyright (C) 2024 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.
+ -->
+
+<configuration description="Runs FileSystemUtilsTest.">
+ <option name="test-suite-tag" value="apct"/>
+
+ <target_preparer class="com.android.tradefed.targetprep.suite.SuiteApkInstaller">
+ <option name="cleanup-apks" value="true" />
+ <option name="test-file-name" value="embedded_native_libs_test_app.apk" />
+ <option name="test-file-name" value="extract_native_libs_test_app.apk" />
+ </target_preparer>
+
+ <test class="com.android.compatibility.common.tradefed.testtype.JarHostTest" >
+ <option name="jar" value="FileSystemUtilsTests.jar" />
+ </test>
+</configuration>
diff --git a/core/tests/FileSystemUtilsTest/apk_embedded_native_libs/embedded_native_libs_test_app.xml b/core/tests/FileSystemUtilsTest/apk_embedded_native_libs/embedded_native_libs_test_app.xml
new file mode 100644
index 0000000..868f7f3
--- /dev/null
+++ b/core/tests/FileSystemUtilsTest/apk_embedded_native_libs/embedded_native_libs_test_app.xml
@@ -0,0 +1,35 @@
+<?xml version="1.0" encoding="utf-8"?>
+<!--
+ ~ Copyright (C) 2024 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.
+ -->
+
+<manifest xmlns:android="http://schemas.android.com/apk/res/android"
+ package="android.test.embedded">
+ <application android:extractNativeLibs="false">
+ <uses-library android:name="android.test.runner"/>
+ <activity android:name=".MainActivity"
+ android:exported="true"
+ android:process=":NewProcess">
+ <intent-filter>
+ <action android:name="android.intent.action.MAIN"/>
+ <category android:name="android.intent.category.LAUNCHER"/>
+ <category android:name="android.intent.category.DEFAULT"/>
+ </intent-filter>
+ </activity>
+ </application>
+ <instrumentation
+ android:name="androidx.test.runner.AndroidJUnitRunner"
+ android:targetPackage="android.test.embedded"/>
+</manifest>
\ No newline at end of file
diff --git a/core/tests/FileSystemUtilsTest/apk_embedded_native_libs/src/android/test/embedded/MainActivity.java b/core/tests/FileSystemUtilsTest/apk_embedded_native_libs/src/android/test/embedded/MainActivity.java
new file mode 100644
index 0000000..efa2a39
--- /dev/null
+++ b/core/tests/FileSystemUtilsTest/apk_embedded_native_libs/src/android/test/embedded/MainActivity.java
@@ -0,0 +1,60 @@
+/*
+ * Copyright (C) 2024 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package android.test.embedded;
+
+import android.app.Activity;
+import android.content.Intent;
+import android.os.Bundle;
+
+import androidx.annotation.VisibleForTesting;
+
+public class MainActivity extends Activity {
+
+ static {
+ System.loadLibrary("punchtest");
+ }
+
+ @VisibleForTesting
+ static final String INTENT_TYPE = "android.test.embedded.EMBEDDED_LIB_LOADED";
+
+ @VisibleForTesting
+ static final String KEY_OPERAND_1 = "OP1";
+
+ @VisibleForTesting
+ static final String KEY_OPERAND_2 = "OP2";
+
+ @VisibleForTesting
+ static final String KEY_RESULT = "RESULT";
+
+ @Override
+ public void onCreate(Bundle savedOnstanceState) {
+ super.onCreate(savedOnstanceState);
+
+ Intent received = getIntent();
+ int op1 = received.getIntExtra(KEY_OPERAND_1, -1);
+ int op2 = received.getIntExtra(KEY_OPERAND_2, -1);
+ int result = add(op1, op2);
+
+ // Send broadcast so that test can know app has launched and lib is loaded
+ // attach result which has been fetched from JNI lib
+ Intent intent = new Intent(INTENT_TYPE);
+ intent.putExtra(KEY_RESULT, result);
+ sendBroadcast(intent);
+ }
+
+ private native int add(int op1, int op2);
+}
diff --git a/core/tests/FileSystemUtilsTest/apk_embedded_native_libs/src/android/test/embedded/PunchEmbeddedLibTest.java b/core/tests/FileSystemUtilsTest/apk_embedded_native_libs/src/android/test/embedded/PunchEmbeddedLibTest.java
new file mode 100644
index 0000000..d7d67b8
--- /dev/null
+++ b/core/tests/FileSystemUtilsTest/apk_embedded_native_libs/src/android/test/embedded/PunchEmbeddedLibTest.java
@@ -0,0 +1,66 @@
+/*
+ * Copyright (C) 2024 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package android.test.embedded;
+
+import android.content.BroadcastReceiver;
+import android.content.Context;
+import android.content.Intent;
+import android.content.IntentFilter;
+
+import androidx.test.InstrumentationRegistry;
+import androidx.test.runner.AndroidJUnit4;
+
+import org.junit.Assert;
+import org.junit.Test;
+import org.junit.runner.RunWith;
+
+import java.util.concurrent.CountDownLatch;
+import java.util.concurrent.TimeUnit;
+
+@RunWith(AndroidJUnit4.class)
+public class PunchEmbeddedLibTest {
+
+ @Test
+ public void testPunchedNativeLibs_embeddedLib() throws Exception {
+ Context context = InstrumentationRegistry.getContext();
+ CountDownLatch receivedSignal = new CountDownLatch(1);
+
+ // Test app is expected to receive this and perform addition of operands using punched lib
+ int op1 = 48;
+ int op2 = 75;
+ IntentFilter intentFilter = new IntentFilter(MainActivity.INTENT_TYPE);
+ BroadcastReceiver broadcastReceiver =
+ new BroadcastReceiver() {
+ @Override
+ public void onReceive(Context context, Intent intent) {
+ receivedSignal.countDown();
+ int result = intent.getIntExtra(MainActivity.KEY_RESULT, 1000);
+ Assert.assertEquals(result, op1 + op2);
+
+ }
+ };
+ context.registerReceiver(broadcastReceiver, intentFilter, Context.RECEIVER_EXPORTED);
+
+ Intent launchIntent =
+ context.getPackageManager().getLaunchIntentForPackage(context.getPackageName());
+ launchIntent.putExtra(MainActivity.KEY_OPERAND_1, op1);
+ launchIntent.putExtra(MainActivity.KEY_OPERAND_2, op2);
+ context.startActivity(launchIntent);
+
+ Assert.assertTrue("Failed to launch app", receivedSignal.await(10, TimeUnit.SECONDS));
+ }
+}
diff --git a/core/tests/FileSystemUtilsTest/apk_extract_native_libs/extract_native_libs_test_app.xml b/core/tests/FileSystemUtilsTest/apk_extract_native_libs/extract_native_libs_test_app.xml
new file mode 100644
index 0000000..6db96f7
--- /dev/null
+++ b/core/tests/FileSystemUtilsTest/apk_extract_native_libs/extract_native_libs_test_app.xml
@@ -0,0 +1,35 @@
+<?xml version="1.0" encoding="utf-8"?>
+<!--
+ ~ Copyright (C) 2024 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.
+ -->
+
+<manifest xmlns:android="http://schemas.android.com/apk/res/android"
+ package="android.test.extract">
+ <application android:extractNativeLibs="true">
+ <uses-library android:name="android.test.runner"/>
+ <activity android:name=".MainActivity"
+ android:exported="true"
+ android:process=":NewProcess">
+ <intent-filter>
+ <action android:name="android.intent.action.MAIN"/>
+ <category android:name="android.intent.category.LAUNCHER"/>
+ <category android:name="android.intent.category.DEFAULT"/>
+ </intent-filter>
+ </activity>
+ </application>
+ <instrumentation
+ android:name="androidx.test.runner.AndroidJUnitRunner"
+ android:targetPackage="android.test.extract"/>
+</manifest>
\ No newline at end of file
diff --git a/core/tests/FileSystemUtilsTest/apk_extract_native_libs/src/android/test/extract/MainActivity.java b/core/tests/FileSystemUtilsTest/apk_extract_native_libs/src/android/test/extract/MainActivity.java
new file mode 100644
index 0000000..b1c157e
--- /dev/null
+++ b/core/tests/FileSystemUtilsTest/apk_extract_native_libs/src/android/test/extract/MainActivity.java
@@ -0,0 +1,60 @@
+/*
+ * Copyright (C) 2024 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package android.test.extract;
+
+import android.app.Activity;
+import android.content.Intent;
+import android.os.Bundle;
+
+import androidx.annotation.VisibleForTesting;
+
+public class MainActivity extends Activity {
+
+ static {
+ System.loadLibrary("punchtest");
+ }
+
+ @VisibleForTesting
+ static final String INTENT_TYPE = "android.test.extract.EXTRACTED_LIB_LOADED";
+
+ @VisibleForTesting
+ static final String KEY_OPERAND_1 = "OP1";
+
+ @VisibleForTesting
+ static final String KEY_OPERAND_2 = "OP2";
+
+ @VisibleForTesting
+ static final String KEY_RESULT = "RESULT";
+
+ @Override
+ public void onCreate(Bundle savedOnstanceState) {
+ super.onCreate(savedOnstanceState);
+
+ Intent received = getIntent();
+ int op1 = received.getIntExtra(KEY_OPERAND_1, -1);
+ int op2 = received.getIntExtra(KEY_OPERAND_2, -1);
+ int result = subtract(op1, op2);
+
+ // Send broadcast so that test can know app has launched and lib is loaded
+ // attach result which has been fetched from JNI lib
+ Intent intent = new Intent(INTENT_TYPE);
+ intent.putExtra(KEY_RESULT, result);
+ sendBroadcast(intent);
+ }
+
+ private native int subtract(int op1, int op2);
+}
diff --git a/core/tests/FileSystemUtilsTest/apk_extract_native_libs/src/android/test/extract/PunchExtractedLibTest.java b/core/tests/FileSystemUtilsTest/apk_extract_native_libs/src/android/test/extract/PunchExtractedLibTest.java
new file mode 100644
index 0000000..7cc1017
--- /dev/null
+++ b/core/tests/FileSystemUtilsTest/apk_extract_native_libs/src/android/test/extract/PunchExtractedLibTest.java
@@ -0,0 +1,65 @@
+/*
+ * Copyright (C) 2024 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package android.test.extract;
+
+import android.content.BroadcastReceiver;
+import android.content.Context;
+import android.content.Intent;
+import android.content.IntentFilter;
+
+import androidx.test.InstrumentationRegistry;
+import androidx.test.runner.AndroidJUnit4;
+
+import org.junit.Assert;
+import org.junit.Test;
+import org.junit.runner.RunWith;
+
+import java.util.concurrent.CountDownLatch;
+import java.util.concurrent.TimeUnit;
+
+@RunWith(AndroidJUnit4.class)
+public class PunchExtractedLibTest {
+
+ @Test
+ public void testPunchedNativeLibs_extractedLib() throws Exception {
+ Context context = InstrumentationRegistry.getContext();
+ CountDownLatch receivedSignal = new CountDownLatch(1);
+
+ // Test app is expected to receive this and perform subtraction using extracted lib
+ int op1 = 100;
+ int op2 = 71;
+ IntentFilter intentFilter = new IntentFilter(MainActivity.INTENT_TYPE);
+ BroadcastReceiver broadcastReceiver =
+ new BroadcastReceiver() {
+ @Override
+ public void onReceive(Context context, Intent intent) {
+ receivedSignal.countDown();
+ int result = intent.getIntExtra(MainActivity.KEY_RESULT, 1000);
+ Assert.assertEquals(result, op1 - op2);
+ }
+ };
+ context.registerReceiver(broadcastReceiver, intentFilter, Context.RECEIVER_EXPORTED);
+
+ Intent launchIntent =
+ context.getPackageManager().getLaunchIntentForPackage(context.getPackageName());
+ launchIntent.putExtra(MainActivity.KEY_OPERAND_1, op1);
+ launchIntent.putExtra(MainActivity.KEY_OPERAND_2, op2);
+ context.startActivity(launchIntent);
+
+ Assert.assertTrue("Failed to launch app", receivedSignal.await(10, TimeUnit.SECONDS));
+ }
+}
diff --git a/core/tests/FileSystemUtilsTest/jni/android_test_jni_source.cpp b/core/tests/FileSystemUtilsTest/jni/android_test_jni_source.cpp
new file mode 100644
index 0000000..2a5ba81
--- /dev/null
+++ b/core/tests/FileSystemUtilsTest/jni/android_test_jni_source.cpp
@@ -0,0 +1,41 @@
+/*
+ * Copyright (C) 2024 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.
+ */
+
+#include <jni.h>
+
+// This will be called from embedded_native_libs_test_app
+extern "C" JNIEXPORT
+jint JNICALL Java_android_test_embedded_MainActivity_add(JNIEnv*, jclass, jint op1, jint op2) {
+ return op1 + op2;
+}
+
+// This will be called from extract_native_libs_test_app
+extern "C" JNIEXPORT
+jint JNICALL Java_android_test_extract_MainActivity_subtract(JNIEnv*, jclass, jint op1, jint op2) {
+ return op1 - op2;
+}
+
+// Initialize JNI
+jint JNI_OnLoad(JavaVM *jvm, void */* reserved */) {
+ JNIEnv *e;
+
+ // Check JNI version
+ if (jvm->GetEnv((void **) &e, JNI_VERSION_1_6)) {
+ return JNI_ERR;
+ }
+
+ return JNI_VERSION_1_6;
+}
diff --git a/core/tests/FileSystemUtilsTest/src/com/android/internal/content/FileSystemUtilsTest.java b/core/tests/FileSystemUtilsTest/src/com/android/internal/content/FileSystemUtilsTest.java
new file mode 100644
index 0000000..77802e5
--- /dev/null
+++ b/core/tests/FileSystemUtilsTest/src/com/android/internal/content/FileSystemUtilsTest.java
@@ -0,0 +1,50 @@
+/*
+ * Copyright (C) 2024 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package com.android.internal.content;
+
+import static org.junit.Assert.assertTrue;
+
+import android.platform.test.annotations.AppModeFull;
+
+import com.android.tradefed.device.DeviceNotAvailableException;
+import com.android.tradefed.testtype.DeviceJUnit4ClassRunner;
+import com.android.tradefed.testtype.junit4.BaseHostJUnit4Test;
+
+import org.junit.Test;
+import org.junit.runner.RunWith;
+
+@RunWith(DeviceJUnit4ClassRunner.class)
+public class FileSystemUtilsTest extends BaseHostJUnit4Test {
+
+ @Test
+ @AppModeFull
+ public void runPunchedApp_embeddedNativeLibs() throws DeviceNotAvailableException {
+ String appPackage = "android.test.embedded";
+ String testName = "PunchEmbeddedLibTest";
+ assertTrue(isPackageInstalled(appPackage));
+ runDeviceTests(appPackage, appPackage + "." + testName);
+ }
+
+ @Test
+ @AppModeFull
+ public void runPunchedApp_extractedNativeLibs() throws DeviceNotAvailableException {
+ String appPackage = "android.test.extract";
+ String testName = "PunchExtractedLibTest";
+ assertTrue(isPackageInstalled(appPackage));
+ runDeviceTests(appPackage, appPackage + "." + testName);
+ }
+}
diff --git a/core/tests/coretests/src/android/view/InsetsControllerTest.java b/core/tests/coretests/src/android/view/InsetsControllerTest.java
index 97f894f..4fb85c1f 100644
--- a/core/tests/coretests/src/android/view/InsetsControllerTest.java
+++ b/core/tests/coretests/src/android/view/InsetsControllerTest.java
@@ -25,9 +25,7 @@
import static android.view.InsetsSource.ID_IME;
import static android.view.InsetsSourceConsumer.ShowResult.IME_SHOW_DELAYED;
import static android.view.InsetsSourceConsumer.ShowResult.SHOW_IMMEDIATELY;
-import static android.view.ViewRootImpl.CAPTION_ON_SHELL;
import static android.view.WindowInsets.Type.all;
-import static android.view.WindowInsets.Type.captionBar;
import static android.view.WindowInsets.Type.defaultVisible;
import static android.view.WindowInsets.Type.ime;
import static android.view.WindowInsets.Type.navigationBars;
@@ -49,7 +47,6 @@
import static org.mockito.Mockito.clearInvocations;
import static org.mockito.Mockito.mock;
import static org.mockito.Mockito.never;
-import static org.mockito.Mockito.reset;
import static org.mockito.Mockito.spy;
import static org.mockito.Mockito.times;
import static org.mockito.Mockito.verify;
@@ -805,48 +802,6 @@
}
@Test
- public void testCaptionInsetsStateAssemble() {
- if (CAPTION_ON_SHELL) {
- // For this case, the test is covered by WindowContainerInsetsSourceProviderTest, This
- // test can be removed after the caption is moved to shell completely.
- return;
- }
- InstrumentationRegistry.getInstrumentation().runOnMainSync(() -> {
- final Rect frame = new Rect(0, 0, 100, 300);
- final int captionBarHeight = 100;
- final InsetsState state = mController.getState();
- mController.onFrameChanged(frame);
- mController.setCaptionInsetsHeight(captionBarHeight);
- // The caption bar insets height should be the same as the caption bar height.
- assertEquals(captionBarHeight, state.calculateInsets(frame, captionBar(), false).top);
- // Test update to remove the caption bar
- mController.setCaptionInsetsHeight(0);
- // The caption bar source should not be there at all, because we don't add empty
- // caption to the state from the server.
- for (int i = state.sourceSize() - 1; i >= 0; i--) {
- assertNotEquals(captionBar(), state.sourceAt(i).getType());
- }
- });
- }
-
- @Test
- public void testNotifyCaptionInsetsOnlyChange() {
- if (CAPTION_ON_SHELL) {
- // For this case, the test is covered by WindowContainerInsetsSourceProviderTest, This
- // test can be removed after the caption is moved to shell completely.
- return;
- }
- InstrumentationRegistry.getInstrumentation().runOnMainSync(() -> {
- reset(mTestHost);
- mController.setCaptionInsetsHeight(100);
- verify(mTestHost).notifyInsetsChanged();
- reset(mTestHost);
- mController.setCaptionInsetsHeight(0);
- verify(mTestHost).notifyInsetsChanged();
- });
- }
-
- @Test
public void testRequestedState() {
InstrumentationRegistry.getInstrumentation().runOnMainSync(() -> {
mController.hide(statusBars() | navigationBars());
diff --git a/libs/WindowManager/Shell/src/com/android/wm/shell/windowdecor/WindowDecoration.java b/libs/WindowManager/Shell/src/com/android/wm/shell/windowdecor/WindowDecoration.java
index 32c2d1e..51b0a24 100644
--- a/libs/WindowManager/Shell/src/com/android/wm/shell/windowdecor/WindowDecoration.java
+++ b/libs/WindowManager/Shell/src/com/android/wm/shell/windowdecor/WindowDecoration.java
@@ -40,7 +40,6 @@
import android.view.SurfaceControl;
import android.view.SurfaceControlViewHost;
import android.view.View;
-import android.view.ViewRootImpl;
import android.view.WindowInsets;
import android.view.WindowManager;
import android.view.WindowlessWindowManager;
@@ -293,60 +292,56 @@
.setLayer(mCaptionContainerSurface, CAPTION_LAYER_Z_ORDER)
.show(mCaptionContainerSurface);
- if (ViewRootImpl.CAPTION_ON_SHELL) {
- outResult.mRootView.setTaskFocusState(mTaskInfo.isFocused);
+ outResult.mRootView.setTaskFocusState(mTaskInfo.isFocused);
- // Caption insets
- if (mIsCaptionVisible) {
- // Caption inset is the full width of the task with the |captionHeight| and
- // positioned at the top of the task bounds, also in absolute coordinates.
- // So just reuse the task bounds and adjust the bottom coordinate.
- mCaptionInsetsRect.set(taskBounds);
- mCaptionInsetsRect.bottom = mCaptionInsetsRect.top + outResult.mCaptionHeight;
+ // Caption insets
+ if (mIsCaptionVisible) {
+ // Caption inset is the full width of the task with the |captionHeight| and
+ // positioned at the top of the task bounds, also in absolute coordinates.
+ // So just reuse the task bounds and adjust the bottom coordinate.
+ mCaptionInsetsRect.set(taskBounds);
+ mCaptionInsetsRect.bottom = mCaptionInsetsRect.top + outResult.mCaptionHeight;
- // Caption bounding rectangles: these are optional, and are used to present finer
- // insets than traditional |Insets| to apps about where their content is occluded.
- // These are also in absolute coordinates.
- final Rect[] boundingRects;
- final int numOfElements = params.mOccludingCaptionElements.size();
- if (numOfElements == 0) {
- boundingRects = null;
- } else {
- // The customizable region can at most be equal to the caption bar.
+ // Caption bounding rectangles: these are optional, and are used to present finer
+ // insets than traditional |Insets| to apps about where their content is occluded.
+ // These are also in absolute coordinates.
+ final Rect[] boundingRects;
+ final int numOfElements = params.mOccludingCaptionElements.size();
+ if (numOfElements == 0) {
+ boundingRects = null;
+ } else {
+ // The customizable region can at most be equal to the caption bar.
+ if (params.mAllowCaptionInputFallthrough) {
+ outResult.mCustomizableCaptionRegion.set(mCaptionInsetsRect);
+ }
+ boundingRects = new Rect[numOfElements];
+ for (int i = 0; i < numOfElements; i++) {
+ final OccludingCaptionElement element =
+ params.mOccludingCaptionElements.get(i);
+ final int elementWidthPx =
+ resources.getDimensionPixelSize(element.mWidthResId);
+ boundingRects[i] =
+ calculateBoundingRect(element, elementWidthPx, mCaptionInsetsRect);
+ // Subtract the regions used by the caption elements, the rest is
+ // customizable.
if (params.mAllowCaptionInputFallthrough) {
- outResult.mCustomizableCaptionRegion.set(mCaptionInsetsRect);
- }
- boundingRects = new Rect[numOfElements];
- for (int i = 0; i < numOfElements; i++) {
- final OccludingCaptionElement element =
- params.mOccludingCaptionElements.get(i);
- final int elementWidthPx =
- resources.getDimensionPixelSize(element.mWidthResId);
- boundingRects[i] =
- calculateBoundingRect(element, elementWidthPx, mCaptionInsetsRect);
- // Subtract the regions used by the caption elements, the rest is
- // customizable.
- if (params.mAllowCaptionInputFallthrough) {
- outResult.mCustomizableCaptionRegion.op(boundingRects[i],
- Region.Op.DIFFERENCE);
- }
+ outResult.mCustomizableCaptionRegion.op(boundingRects[i],
+ Region.Op.DIFFERENCE);
}
}
- // Add this caption as an inset source.
- wct.addInsetsSource(mTaskInfo.token,
- mOwner, 0 /* index */, WindowInsets.Type.captionBar(), mCaptionInsetsRect,
- boundingRects);
- wct.addInsetsSource(mTaskInfo.token,
- mOwner, 0 /* index */, WindowInsets.Type.mandatorySystemGestures(),
- mCaptionInsetsRect, null /* boundingRects */);
- } else {
- wct.removeInsetsSource(mTaskInfo.token, mOwner, 0 /* index */,
- WindowInsets.Type.captionBar());
- wct.removeInsetsSource(mTaskInfo.token, mOwner, 0 /* index */,
- WindowInsets.Type.mandatorySystemGestures());
}
+ // Add this caption as an inset source.
+ wct.addInsetsSource(mTaskInfo.token,
+ mOwner, 0 /* index */, WindowInsets.Type.captionBar(), mCaptionInsetsRect,
+ boundingRects);
+ wct.addInsetsSource(mTaskInfo.token,
+ mOwner, 0 /* index */, WindowInsets.Type.mandatorySystemGestures(),
+ mCaptionInsetsRect, null /* boundingRects */);
} else {
- startT.hide(mCaptionContainerSurface);
+ wct.removeInsetsSource(mTaskInfo.token, mOwner, 0 /* index */,
+ WindowInsets.Type.captionBar());
+ wct.removeInsetsSource(mTaskInfo.token, mOwner, 0 /* index */,
+ WindowInsets.Type.mandatorySystemGestures());
}
// Task surface itself
@@ -594,8 +589,7 @@
*/
public void addCaptionInset(WindowContainerTransaction wct) {
final int captionHeightId = getCaptionHeightId(mTaskInfo.getWindowingMode());
- if (!ViewRootImpl.CAPTION_ON_SHELL || captionHeightId == Resources.ID_NULL
- || !mIsCaptionVisible) {
+ if (captionHeightId == Resources.ID_NULL || !mIsCaptionVisible) {
return;
}
diff --git a/libs/WindowManager/Shell/tests/unittest/src/com/android/wm/shell/windowdecor/WindowDecorationTests.java b/libs/WindowManager/Shell/tests/unittest/src/com/android/wm/shell/windowdecor/WindowDecorationTests.java
index 228b25c..5464937 100644
--- a/libs/WindowManager/Shell/tests/unittest/src/com/android/wm/shell/windowdecor/WindowDecorationTests.java
+++ b/libs/WindowManager/Shell/tests/unittest/src/com/android/wm/shell/windowdecor/WindowDecorationTests.java
@@ -61,7 +61,6 @@
import android.view.SurfaceControl;
import android.view.SurfaceControlViewHost;
import android.view.View;
-import android.view.ViewRootImpl;
import android.view.WindowInsets;
import android.view.WindowManager.LayoutParams;
import android.window.SurfaceSyncGroup;
@@ -252,16 +251,14 @@
argThat(lp -> lp.height == 64
&& lp.width == 300
&& (lp.flags & LayoutParams.FLAG_NOT_FOCUSABLE) != 0));
- if (ViewRootImpl.CAPTION_ON_SHELL) {
- verify(mMockView).setTaskFocusState(true);
- verify(mMockWindowContainerTransaction).addInsetsSource(
- eq(taskInfo.token),
- any(),
- eq(0 /* index */),
- eq(WindowInsets.Type.captionBar()),
- eq(new Rect(100, 300, 400, 364)),
- any());
- }
+ verify(mMockView).setTaskFocusState(true);
+ verify(mMockWindowContainerTransaction).addInsetsSource(
+ eq(taskInfo.token),
+ any(),
+ eq(0 /* index */),
+ eq(WindowInsets.Type.captionBar()),
+ eq(new Rect(100, 300, 400, 364)),
+ any());
verify(mMockSurfaceControlStartT).setCornerRadius(mMockTaskSurface, CORNER_RADIUS);
verify(mMockSurfaceControlFinishT).setCornerRadius(mMockTaskSurface, CORNER_RADIUS);
diff --git a/libs/androidfw/ZipFileRO.cpp b/libs/androidfw/ZipFileRO.cpp
index 9d4b426..34a6bc2 100644
--- a/libs/androidfw/ZipFileRO.cpp
+++ b/libs/androidfw/ZipFileRO.cpp
@@ -310,3 +310,7 @@
return true;
}
+
+const char* ZipFileRO::getZipFileName() {
+ return mFileName;
+}
diff --git a/libs/androidfw/include/androidfw/ZipFileRO.h b/libs/androidfw/include/androidfw/ZipFileRO.h
index be1f98f..031d2e8 100644
--- a/libs/androidfw/include/androidfw/ZipFileRO.h
+++ b/libs/androidfw/include/androidfw/ZipFileRO.h
@@ -187,6 +187,8 @@
*/
bool uncompressEntry(ZipEntryRO entry, int fd) const;
+ const char* getZipFileName();
+
~ZipFileRO();
private:
diff --git a/packages/SystemUI/src/com/android/systemui/deviceentry/data/repository/DeviceEntryFaceAuthRepository.kt b/packages/SystemUI/src/com/android/systemui/deviceentry/data/repository/DeviceEntryFaceAuthRepository.kt
index a8f3029..e418641 100644
--- a/packages/SystemUI/src/com/android/systemui/deviceentry/data/repository/DeviceEntryFaceAuthRepository.kt
+++ b/packages/SystemUI/src/com/android/systemui/deviceentry/data/repository/DeviceEntryFaceAuthRepository.kt
@@ -296,7 +296,7 @@
private fun listenForSchedulingWatchdog() {
keyguardTransitionInteractor
- .transition(from = null, to = KeyguardState.GONE)
+ .transition(to = KeyguardState.GONE)
.filter { it.transitionState == TransitionState.FINISHED }
.onEach {
// We deliberately want to run this in background because scheduleWatchdog does
diff --git a/packages/SystemUI/src/com/android/systemui/dreams/ui/viewmodel/DreamViewModel.kt b/packages/SystemUI/src/com/android/systemui/dreams/ui/viewmodel/DreamViewModel.kt
index 04edd25..2d9c14e 100644
--- a/packages/SystemUI/src/com/android/systemui/dreams/ui/viewmodel/DreamViewModel.kt
+++ b/packages/SystemUI/src/com/android/systemui/dreams/ui/viewmodel/DreamViewModel.kt
@@ -98,7 +98,7 @@
.distinctUntilChanged()
val transitionEnded =
- keyguardTransitionInteractor.transition(from = DREAMING, to = null).filter { step ->
+ keyguardTransitionInteractor.transition(from = DREAMING).filter { step ->
step.transitionState == TransitionState.FINISHED ||
step.transitionState == TransitionState.CANCELED
}
diff --git a/packages/SystemUI/src/com/android/systemui/flags/Flags.kt b/packages/SystemUI/src/com/android/systemui/flags/Flags.kt
index 612ae6c..db6b8fe 100644
--- a/packages/SystemUI/src/com/android/systemui/flags/Flags.kt
+++ b/packages/SystemUI/src/com/android/systemui/flags/Flags.kt
@@ -337,11 +337,6 @@
namespace = DeviceConfig.NAMESPACE_WINDOW_MANAGER,
)
- @Keep
- @JvmField
- val WM_CAPTION_ON_SHELL =
- sysPropBooleanFlag("persist.wm.debug.caption_on_shell", default = true)
-
// TODO(b/256873975): Tracking Bug
@JvmField
@Keep
diff --git a/packages/SystemUI/src/com/android/systemui/keyguard/ResourceTrimmer.kt b/packages/SystemUI/src/com/android/systemui/keyguard/ResourceTrimmer.kt
index c835599..a65a882 100644
--- a/packages/SystemUI/src/com/android/systemui/keyguard/ResourceTrimmer.kt
+++ b/packages/SystemUI/src/com/android/systemui/keyguard/ResourceTrimmer.kt
@@ -84,7 +84,7 @@
applicationScope.launch(bgDispatcher) {
// We drop 1 to avoid triggering on initial collect().
- keyguardTransitionInteractor.transition(from = null, to = GONE).collect { transition ->
+ keyguardTransitionInteractor.transition(to = GONE).collect { transition ->
if (transition.transitionState == TransitionState.FINISHED) {
onKeyguardGone()
}
diff --git a/packages/SystemUI/src/com/android/systemui/keyguard/domain/interactor/KeyguardTransitionInteractor.kt b/packages/SystemUI/src/com/android/systemui/keyguard/domain/interactor/KeyguardTransitionInteractor.kt
index d3ad0c2a..a18579d 100644
--- a/packages/SystemUI/src/com/android/systemui/keyguard/domain/interactor/KeyguardTransitionInteractor.kt
+++ b/packages/SystemUI/src/com/android/systemui/keyguard/domain/interactor/KeyguardTransitionInteractor.kt
@@ -157,7 +157,7 @@
* Receive all [TransitionStep] matching a filter of [from]->[to]. Allow nulls in order to match
* any transition, for instance (any)->GONE.
*/
- fun transition(from: KeyguardState?, to: KeyguardState?): Flow<TransitionStep> {
+ fun transition(from: KeyguardState? = null, to: KeyguardState? = null): Flow<TransitionStep> {
if (from == null && to == null) {
throw IllegalArgumentException("from and to cannot both be null")
}
@@ -198,8 +198,7 @@
/** The last [TransitionStep] with a [TransitionState] of FINISHED */
val finishedKeyguardTransitionStep: Flow<TransitionStep> =
- repository.transitions
- .filter { step -> step.transitionState == TransitionState.FINISHED }
+ repository.transitions.filter { step -> step.transitionState == TransitionState.FINISHED }
/** The destination state of the last [TransitionState.STARTED] transition. */
val startedKeyguardState: SharedFlow<KeyguardState> =
@@ -377,7 +376,7 @@
state: KeyguardState,
): Flow<Boolean> {
return getOrCreateFlow(Edge(from = null, to = state))
- .mapLatest { it.transitionState.isActive() }
+ .mapLatest { it.transitionState.isTransitioning() }
.onStart { emit(false) }
.distinctUntilChanged()
}
@@ -391,7 +390,7 @@
to: KeyguardState,
): Flow<Boolean> {
return getOrCreateFlow(Edge(from = from, to = to))
- .mapLatest { it.transitionState.isActive() }
+ .mapLatest { it.transitionState.isTransitioning() }
.onStart { emit(false) }
.distinctUntilChanged()
}
@@ -403,7 +402,7 @@
state: KeyguardState,
): Flow<Boolean> {
return getOrCreateFlow(Edge(from = state, to = null))
- .mapLatest { it.transitionState.isActive() }
+ .mapLatest { it.transitionState.isTransitioning() }
.onStart { emit(false) }
.distinctUntilChanged()
}
diff --git a/packages/SystemUI/src/com/android/systemui/keyguard/shared/model/TransitionState.kt b/packages/SystemUI/src/com/android/systemui/keyguard/shared/model/TransitionState.kt
index f6567a6..1cd188c 100644
--- a/packages/SystemUI/src/com/android/systemui/keyguard/shared/model/TransitionState.kt
+++ b/packages/SystemUI/src/com/android/systemui/keyguard/shared/model/TransitionState.kt
@@ -19,20 +19,20 @@
enum class TransitionState {
/* Transition has begun. */
STARTED {
- override fun isActive() = true
+ override fun isTransitioning() = true
},
/* Transition is actively running. */
RUNNING {
- override fun isActive() = true
+ override fun isTransitioning() = true
},
/* Transition has completed successfully. */
FINISHED {
- override fun isActive() = false
+ override fun isTransitioning() = false
},
/* Transition has been interrupted, and not completed successfully. */
CANCELED {
- override fun isActive() = false
+ override fun isTransitioning() = false
};
- abstract fun isActive(): Boolean
+ abstract fun isTransitioning(): Boolean
}
diff --git a/packages/SystemUI/src/com/android/systemui/keyguard/ui/viewmodel/AodBurnInViewModel.kt b/packages/SystemUI/src/com/android/systemui/keyguard/ui/viewmodel/AodBurnInViewModel.kt
index e2177e6..d4844e2 100644
--- a/packages/SystemUI/src/com/android/systemui/keyguard/ui/viewmodel/AodBurnInViewModel.kt
+++ b/packages/SystemUI/src/com/android/systemui/keyguard/ui/viewmodel/AodBurnInViewModel.kt
@@ -94,9 +94,9 @@
occludedToLockscreen,
aodToLockscreen ->
val translationY =
- if (aodToLockscreen.transitionState.isActive()) {
+ if (aodToLockscreen.transitionState.isTransitioning()) {
aodToLockscreen.value ?: 0f
- } else if (goneToAod.transitionState.isActive()) {
+ } else if (goneToAod.transitionState.isTransitioning()) {
(goneToAod.value ?: 0f) + burnInModel.translationY
} else {
burnInModel.translationY + occludedToLockscreen + keyguardTranslationY
diff --git a/packages/SystemUI/src/com/android/systemui/media/controls/ui/controller/MediaCarouselController.kt b/packages/SystemUI/src/com/android/systemui/media/controls/ui/controller/MediaCarouselController.kt
index 5b39ed3..d15d45a 100644
--- a/packages/SystemUI/src/com/android/systemui/media/controls/ui/controller/MediaCarouselController.kt
+++ b/packages/SystemUI/src/com/android/systemui/media/controls/ui/controller/MediaCarouselController.kt
@@ -603,7 +603,7 @@
internal fun listenForAnyStateToGoneKeyguardTransition(scope: CoroutineScope): Job {
return scope.launch {
keyguardTransitionInteractor
- .transition(from = null, to = GONE)
+ .transition(to = GONE)
.filter { it.transitionState == TransitionState.FINISHED }
.collect {
showMediaCarousel()
@@ -616,7 +616,7 @@
internal fun listenForAnyStateToLockscreenTransition(scope: CoroutineScope): Job {
return scope.launch {
keyguardTransitionInteractor
- .transition(from = null, to = LOCKSCREEN)
+ .transition(to = LOCKSCREEN)
.filter { it.transitionState == TransitionState.FINISHED }
.collect {
if (!allowMediaPlayerOnLockScreen) {