summaryrefslogtreecommitdiff
path: root/libs
diff options
context:
space:
mode:
Diffstat (limited to 'libs')
-rw-r--r--libs/WindowManager/Shell/src/com/android/wm/shell/bubbles/BubbleController.java7
-rw-r--r--libs/WindowManager/Shell/src/com/android/wm/shell/common/split/DividerView.java6
-rw-r--r--libs/WindowManager/Shell/src/com/android/wm/shell/common/split/SplitLayout.java23
-rw-r--r--libs/WindowManager/Shell/src/com/android/wm/shell/freeform/FreeformTaskListener.java2
-rw-r--r--libs/WindowManager/Shell/src/com/android/wm/shell/freeform/FreeformTaskTransitionObserver.java22
-rw-r--r--libs/WindowManager/Shell/src/com/android/wm/shell/fullscreen/FullscreenTaskListener.java2
-rw-r--r--libs/WindowManager/Shell/src/com/android/wm/shell/pip/PipTaskOrganizer.java46
-rw-r--r--libs/WindowManager/Shell/src/com/android/wm/shell/pip/phone/PipController.java9
-rw-r--r--libs/WindowManager/Shell/src/com/android/wm/shell/splitscreen/StageCoordinator.java22
-rw-r--r--libs/WindowManager/Shell/src/com/android/wm/shell/splitscreen/StageTaskListener.java6
-rw-r--r--libs/WindowManager/Shell/src/com/android/wm/shell/transition/OneShotRemoteHandler.java22
-rw-r--r--libs/WindowManager/Shell/src/com/android/wm/shell/transition/RemoteTransitionHandler.java43
-rw-r--r--libs/WindowManager/Shell/src/com/android/wm/shell/transition/Transitions.java20
-rw-r--r--libs/WindowManager/Shell/src/com/android/wm/shell/windowdecor/CaptionWindowDecorViewModel.java156
-rw-r--r--libs/WindowManager/Shell/src/com/android/wm/shell/windowdecor/CaptionWindowDecoration.java45
-rw-r--r--libs/WindowManager/Shell/src/com/android/wm/shell/windowdecor/WindowDecorViewModel.java46
-rw-r--r--libs/WindowManager/Shell/tests/unittest/src/com/android/wm/shell/freeform/FreeformTaskTransitionObserverTest.java4
-rw-r--r--libs/WindowManager/Shell/tests/unittest/src/com/android/wm/shell/windowdecor/CaptionWindowDecorViewModelTests.java217
-rw-r--r--[-rwxr-xr-x]libs/androidfw/ApkAssets.cpp10
-rw-r--r--libs/androidfw/AssetManager2.cpp174
-rw-r--r--libs/androidfw/AssetsProvider.cpp103
-rw-r--r--libs/androidfw/ConfigDescription.cpp2
-rw-r--r--libs/androidfw/Idmap.cpp39
-rw-r--r--libs/androidfw/LoadedArsc.cpp35
-rw-r--r--libs/androidfw/Locale.cpp6
-rw-r--r--libs/androidfw/ResourceTypes.cpp37
-rw-r--r--libs/androidfw/ResourceUtils.cpp10
-rw-r--r--libs/androidfw/StringPool.cpp9
-rw-r--r--libs/androidfw/Util.cpp30
-rw-r--r--libs/androidfw/include/androidfw/AssetManager2.h20
-rw-r--r--libs/androidfw/include/androidfw/AssetsProvider.h31
-rw-r--r--libs/androidfw/include/androidfw/ByteBucketArray.h58
-rw-r--r--libs/androidfw/include/androidfw/ConfigDescription.h2
-rw-r--r--libs/androidfw/include/androidfw/IDiagnostics.h4
-rw-r--r--libs/androidfw/include/androidfw/Idmap.h7
-rw-r--r--libs/androidfw/include/androidfw/LoadedArsc.h10
-rw-r--r--libs/androidfw/include/androidfw/Locale.h6
-rw-r--r--libs/androidfw/include/androidfw/ResourceTypes.h20
-rw-r--r--libs/androidfw/include/androidfw/ResourceUtils.h4
-rw-r--r--libs/androidfw/include/androidfw/Source.h9
-rw-r--r--libs/androidfw/include/androidfw/StringPiece.h292
-rw-r--r--libs/androidfw/include/androidfw/StringPool.h6
-rw-r--r--libs/androidfw/include/androidfw/Util.h104
-rw-r--r--libs/androidfw/include/androidfw/misc.h4
-rw-r--r--libs/androidfw/misc.cpp44
-rw-r--r--libs/androidfw/tests/AttributeResolution_bench.cpp4
-rw-r--r--libs/androidfw/tests/ByteBucketArray_test.cpp53
-rw-r--r--libs/androidfw/tests/ConfigDescription_test.cpp6
-rw-r--r--libs/androidfw/tests/StringPiece_test.cpp32
-rw-r--r--libs/androidfw/tests/StringPool_test.cpp6
-rw-r--r--libs/hwui/Android.bp1
-rw-r--r--libs/hwui/CanvasTransform.cpp1
-rw-r--r--libs/hwui/DeferredLayerUpdater.h1
-rw-r--r--libs/hwui/Layer.cpp2
-rw-r--r--libs/hwui/Readback.cpp41
-rw-r--r--libs/hwui/RecordingCanvas.cpp1
-rw-r--r--libs/hwui/RecordingCanvas.h1
-rw-r--r--libs/hwui/SkiaCanvas.h1
-rw-r--r--libs/hwui/SkiaInterpolator.cpp2
-rw-r--r--libs/hwui/Tonemapper.cpp107
-rw-r--r--libs/hwui/Tonemapper.h28
-rw-r--r--libs/hwui/apex/android_paint.cpp1
-rw-r--r--libs/hwui/hwui/Canvas.h1
-rw-r--r--libs/hwui/jni/ColorFilter.cpp1
-rw-r--r--libs/hwui/jni/RenderEffect.cpp1
-rw-r--r--libs/hwui/pipeline/skia/LayerDrawable.cpp61
-rw-r--r--libs/hwui/pipeline/skia/RenderNodeDrawable.h1
-rw-r--r--libs/hwui/pipeline/skia/StretchMask.cpp4
-rw-r--r--libs/hwui/pipeline/skia/VkInteropFunctorDrawable.cpp2
-rw-r--r--libs/hwui/tests/common/CallCountingCanvas.h2
-rw-r--r--libs/hwui/tests/common/TestListViewSceneBase.cpp2
-rw-r--r--libs/hwui/tests/common/scenes/BitmapFillrate.cpp1
-rw-r--r--libs/hwui/tests/common/scenes/ClippingAnimation.cpp2
-rw-r--r--libs/hwui/tests/common/scenes/GlyphStressAnimation.cpp2
-rw-r--r--libs/hwui/tests/common/scenes/HwBitmapInCompositeShader.cpp1
-rw-r--r--libs/hwui/tests/common/scenes/HwLayerAnimation.cpp2
-rw-r--r--libs/hwui/tests/common/scenes/HwLayerSizeAnimation.cpp2
-rw-r--r--libs/hwui/tests/common/scenes/JankyScene.cpp2
-rw-r--r--libs/hwui/tests/common/scenes/ListOfFadedTextAnimation.cpp1
-rw-r--r--libs/hwui/tests/common/scenes/OvalAnimation.cpp2
-rw-r--r--libs/hwui/tests/common/scenes/PartialDamageAnimation.cpp2
-rw-r--r--libs/hwui/tests/common/scenes/PathClippingAnimation.cpp2
-rw-r--r--libs/hwui/tests/common/scenes/RectGridAnimation.cpp2
-rw-r--r--libs/hwui/tests/common/scenes/RoundRectClippingAnimation.cpp2
-rw-r--r--libs/hwui/tests/common/scenes/SaveLayer2Animation.cpp1
-rw-r--r--libs/hwui/tests/common/scenes/SaveLayerAnimation.cpp2
-rw-r--r--libs/hwui/tests/common/scenes/ShadowGrid2Animation.cpp2
-rw-r--r--libs/hwui/tests/common/scenes/ShadowGridAnimation.cpp2
-rw-r--r--libs/hwui/tests/common/scenes/ShadowShaderAnimation.cpp2
-rw-r--r--libs/hwui/tests/common/scenes/ShapeAnimation.cpp2
-rw-r--r--libs/hwui/tests/common/scenes/SimpleColorMatrixAnimation.cpp1
-rw-r--r--libs/hwui/tests/common/scenes/SimpleGradientAnimation.cpp1
-rw-r--r--libs/hwui/tests/common/scenes/StretchyListViewAnimation.cpp1
-rw-r--r--libs/hwui/tests/common/scenes/TextAnimation.cpp2
-rw-r--r--libs/hwui/tests/microbench/DisplayListCanvasBench.cpp2
-rw-r--r--libs/hwui/tests/microbench/RenderNodeBench.cpp2
-rw-r--r--libs/hwui/tests/unit/CanvasOpTests.cpp1
-rw-r--r--libs/hwui/tests/unit/RenderNodeDrawableTests.cpp1
-rw-r--r--libs/hwui/tests/unit/SkiaCanvasTests.cpp9
-rw-r--r--libs/hwui/tests/unit/SkiaPipelineTests.cpp1
-rw-r--r--libs/hwui/utils/PaintUtils.h1
101 files changed, 1314 insertions, 879 deletions
diff --git a/libs/WindowManager/Shell/src/com/android/wm/shell/bubbles/BubbleController.java b/libs/WindowManager/Shell/src/com/android/wm/shell/bubbles/BubbleController.java
index 1fd91debe3f6..9674b69baa00 100644
--- a/libs/WindowManager/Shell/src/com/android/wm/shell/bubbles/BubbleController.java
+++ b/libs/WindowManager/Shell/src/com/android/wm/shell/bubbles/BubbleController.java
@@ -475,8 +475,13 @@ public class BubbleController implements ConfigurationChangeListener {
@VisibleForTesting
public void onStatusBarStateChanged(boolean isShade) {
+ boolean didChange = mIsStatusBarShade != isShade;
+ if (DEBUG_BUBBLE_CONTROLLER) {
+ Log.d(TAG, "onStatusBarStateChanged isShade=" + isShade + " didChange=" + didChange);
+ }
mIsStatusBarShade = isShade;
- if (!mIsStatusBarShade) {
+ if (!mIsStatusBarShade && didChange) {
+ // Only collapse stack on change
collapseStack();
}
diff --git a/libs/WindowManager/Shell/src/com/android/wm/shell/common/split/DividerView.java b/libs/WindowManager/Shell/src/com/android/wm/shell/common/split/DividerView.java
index e8b0f0265394..214b304df07c 100644
--- a/libs/WindowManager/Shell/src/com/android/wm/shell/common/split/DividerView.java
+++ b/libs/WindowManager/Shell/src/com/android/wm/shell/common/split/DividerView.java
@@ -219,7 +219,11 @@ public class DividerView extends FrameLayout implements View.OnTouchListener {
insetsState.getSource(InsetsState.ITYPE_EXTRA_NAVIGATION_BAR);
// Only insets the divider bar with task bar when it's expanded so that the rounded corners
// will be drawn against task bar.
- if (taskBarInsetsSource.getFrame().height() >= mExpandedTaskBarHeight) {
+ // But there is no need to do it when IME showing because there are no rounded corners at
+ // the bottom. This also avoids the problem of task bar height not changing when IME
+ // floating.
+ if (!insetsState.getSourceOrDefaultVisibility(InsetsState.ITYPE_IME)
+ && taskBarInsetsSource.getFrame().height() >= mExpandedTaskBarHeight) {
mTempRect.inset(taskBarInsetsSource.calculateVisibleInsets(mTempRect));
}
diff --git a/libs/WindowManager/Shell/src/com/android/wm/shell/common/split/SplitLayout.java b/libs/WindowManager/Shell/src/com/android/wm/shell/common/split/SplitLayout.java
index ae496165028f..45b234a6398a 100644
--- a/libs/WindowManager/Shell/src/com/android/wm/shell/common/split/SplitLayout.java
+++ b/libs/WindowManager/Shell/src/com/android/wm/shell/common/split/SplitLayout.java
@@ -122,6 +122,7 @@ public final class SplitLayout implements DisplayInsetsController.OnInsetsChange
private int mDensity;
private final boolean mDimNonImeSide;
+ private ValueAnimator mDividerFlingAnimator;
public SplitLayout(String windowName, Context context, Configuration configuration,
SplitLayoutHandler splitLayoutHandler,
@@ -395,6 +396,10 @@ public final class SplitLayout implements DisplayInsetsController.OnInsetsChange
mSplitWindowManager.release(t);
mDisplayImeController.removePositionProcessor(mImePositionProcessor);
mImePositionProcessor.reset();
+ if (mDividerFlingAnimator != null) {
+ mDividerFlingAnimator.cancel();
+ }
+ resetDividerPosition();
}
public void release() {
@@ -577,13 +582,18 @@ public final class SplitLayout implements DisplayInsetsController.OnInsetsChange
CUJ_SPLIT_SCREEN_RESIZE);
return;
}
- ValueAnimator animator = ValueAnimator
+
+ if (mDividerFlingAnimator != null) {
+ mDividerFlingAnimator.cancel();
+ }
+
+ mDividerFlingAnimator = ValueAnimator
.ofInt(from, to)
.setDuration(duration);
- animator.setInterpolator(Interpolators.FAST_OUT_SLOW_IN);
- animator.addUpdateListener(
+ mDividerFlingAnimator.setInterpolator(Interpolators.FAST_OUT_SLOW_IN);
+ mDividerFlingAnimator.addUpdateListener(
animation -> updateDivideBounds((int) animation.getAnimatedValue()));
- animator.addListener(new AnimatorListenerAdapter() {
+ mDividerFlingAnimator.addListener(new AnimatorListenerAdapter() {
@Override
public void onAnimationEnd(Animator animation) {
if (flingFinishedCallback != null) {
@@ -591,14 +601,15 @@ public final class SplitLayout implements DisplayInsetsController.OnInsetsChange
}
InteractionJankMonitorUtils.endTracing(
CUJ_SPLIT_SCREEN_RESIZE);
+ mDividerFlingAnimator = null;
}
@Override
public void onAnimationCancel(Animator animation) {
- setDividePosition(to, true /* applyLayoutChange */);
+ mDividerFlingAnimator = null;
}
});
- animator.start();
+ mDividerFlingAnimator.start();
}
/** Switch both surface position with animation. */
diff --git a/libs/WindowManager/Shell/src/com/android/wm/shell/freeform/FreeformTaskListener.java b/libs/WindowManager/Shell/src/com/android/wm/shell/freeform/FreeformTaskListener.java
index 90b35a5a55e1..44bcdb2d5de5 100644
--- a/libs/WindowManager/Shell/src/com/android/wm/shell/freeform/FreeformTaskListener.java
+++ b/libs/WindowManager/Shell/src/com/android/wm/shell/freeform/FreeformTaskListener.java
@@ -82,7 +82,7 @@ public class FreeformTaskListener implements ShellTaskOrganizer.TaskListener {
mTasks.put(taskInfo.taskId, state);
if (!Transitions.ENABLE_SHELL_TRANSITIONS) {
SurfaceControl.Transaction t = new SurfaceControl.Transaction();
- mWindowDecorationViewModel.createWindowDecoration(taskInfo, leash, t, t);
+ mWindowDecorationViewModel.onTaskOpening(taskInfo, leash, t, t);
t.apply();
}
diff --git a/libs/WindowManager/Shell/src/com/android/wm/shell/freeform/FreeformTaskTransitionObserver.java b/libs/WindowManager/Shell/src/com/android/wm/shell/freeform/FreeformTaskTransitionObserver.java
index 168f6d79a390..6e710f7caeda 100644
--- a/libs/WindowManager/Shell/src/com/android/wm/shell/freeform/FreeformTaskTransitionObserver.java
+++ b/libs/WindowManager/Shell/src/com/android/wm/shell/freeform/FreeformTaskTransitionObserver.java
@@ -120,7 +120,7 @@ public class FreeformTaskTransitionObserver implements Transitions.TransitionObs
TransitionInfo.Change change,
SurfaceControl.Transaction startT,
SurfaceControl.Transaction finishT) {
- mWindowDecorViewModel.createWindowDecoration(
+ mWindowDecorViewModel.onTaskOpening(
change.getTaskInfo(), change.getLeash(), startT, finishT);
}
@@ -128,31 +128,23 @@ public class FreeformTaskTransitionObserver implements Transitions.TransitionObs
TransitionInfo.Change change,
SurfaceControl.Transaction startT,
SurfaceControl.Transaction finishT) {
- mWindowDecorViewModel.setupWindowDecorationForTransition(
- change.getTaskInfo(), startT, finishT);
+ mWindowDecorViewModel.onTaskClosing(change.getTaskInfo(), startT, finishT);
}
private void onChangeTransitionReady(
TransitionInfo.Change change,
SurfaceControl.Transaction startT,
SurfaceControl.Transaction finishT) {
- mWindowDecorViewModel.setupWindowDecorationForTransition(
- change.getTaskInfo(), startT, finishT);
+ mWindowDecorViewModel.onTaskChanging(
+ change.getTaskInfo(), change.getLeash(), startT, finishT);
}
private void onToFrontTransitionReady(
TransitionInfo.Change change,
SurfaceControl.Transaction startT,
SurfaceControl.Transaction finishT) {
- boolean exists = mWindowDecorViewModel.setupWindowDecorationForTransition(
- change.getTaskInfo(),
- startT,
- finishT);
- if (!exists) {
- // Window caption does not exist, create it
- mWindowDecorViewModel.createWindowDecoration(
- change.getTaskInfo(), change.getLeash(), startT, finishT);
- }
+ mWindowDecorViewModel.onTaskChanging(
+ change.getTaskInfo(), change.getLeash(), startT, finishT);
}
@Override
@@ -188,4 +180,4 @@ public class FreeformTaskTransitionObserver implements Transitions.TransitionObs
mWindowDecorViewModel.destroyWindowDecoration(taskInfo.get(i));
}
}
-}
+} \ No newline at end of file
diff --git a/libs/WindowManager/Shell/src/com/android/wm/shell/fullscreen/FullscreenTaskListener.java b/libs/WindowManager/Shell/src/com/android/wm/shell/fullscreen/FullscreenTaskListener.java
index 75a4091c7d78..6623f5ca84ee 100644
--- a/libs/WindowManager/Shell/src/com/android/wm/shell/fullscreen/FullscreenTaskListener.java
+++ b/libs/WindowManager/Shell/src/com/android/wm/shell/fullscreen/FullscreenTaskListener.java
@@ -103,7 +103,7 @@ public class FullscreenTaskListener implements ShellTaskOrganizer.TaskListener {
if (mWindowDecorViewModelOptional.isPresent()) {
SurfaceControl.Transaction t = new SurfaceControl.Transaction();
createdWindowDecor = mWindowDecorViewModelOptional.get()
- .createWindowDecoration(taskInfo, leash, t, t);
+ .onTaskOpening(taskInfo, leash, t, t);
t.apply();
}
if (!createdWindowDecor) {
diff --git a/libs/WindowManager/Shell/src/com/android/wm/shell/pip/PipTaskOrganizer.java b/libs/WindowManager/Shell/src/com/android/wm/shell/pip/PipTaskOrganizer.java
index f170e774739f..8ba2583757cd 100644
--- a/libs/WindowManager/Shell/src/com/android/wm/shell/pip/PipTaskOrganizer.java
+++ b/libs/WindowManager/Shell/src/com/android/wm/shell/pip/PipTaskOrganizer.java
@@ -63,6 +63,7 @@ import android.graphics.Rect;
import android.os.RemoteException;
import android.os.SystemClock;
import android.util.Log;
+import android.view.Choreographer;
import android.view.Display;
import android.view.Surface;
import android.view.SurfaceControl;
@@ -179,8 +180,10 @@ public class PipTaskOrganizer implements ShellTaskOrganizer.TaskListener,
// This is necessary in case there was a resize animation ongoing when exit PIP
// started, in which case the first resize will be skipped to let the exit
// operation handle the final resize out of PIP mode. See b/185306679.
- finishResize(tx, destinationBounds, direction, animationType);
- sendOnPipTransitionFinished(direction);
+ finishResizeDelayedIfNeeded(() -> {
+ finishResize(tx, destinationBounds, direction, animationType);
+ sendOnPipTransitionFinished(direction);
+ });
}
}
@@ -196,6 +199,39 @@ public class PipTaskOrganizer implements ShellTaskOrganizer.TaskListener,
}
};
+ /**
+ * Finishes resizing the PiP, delaying the operation if it has to be synced with the PiP menu.
+ *
+ * This is done to avoid a race condition between the last transaction applied in
+ * onPipAnimationUpdate and the finishResize in onPipAnimationEnd. The transaction in
+ * onPipAnimationUpdate is applied directly from WmShell, while onPipAnimationEnd creates a
+ * WindowContainerTransaction in finishResize, which is to be applied by WmCore later. Normally,
+ * the WCT should be the last transaction to finish the animation. However, it may happen that
+ * it gets applied *before* the transaction created by the last onPipAnimationUpdate. This
+ * happens only when the PiP surface transaction has to be synced with the PiP menu due to the
+ * necessity for a delay when syncing the PiP surface animation with the PiP menu surface
+ * animation and redrawing the PiP menu contents. As a result, the PiP surface gets scaled after
+ * the new bounds are applied by WmCore, which makes the PiP surface have unexpected bounds.
+ *
+ * To avoid this, we delay the finishResize operation until
+ * the next frame. This aligns the last onAnimationUpdate transaction with the WCT application.
+ */
+ private void finishResizeDelayedIfNeeded(Runnable finishResizeRunnable) {
+ if (!shouldSyncPipTransactionWithMenu()) {
+ finishResizeRunnable.run();
+ return;
+ }
+
+ // Delay the finishResize to the next frame
+ Choreographer.getInstance().postCallback(Choreographer.CALLBACK_COMMIT, () -> {
+ mMainExecutor.execute(finishResizeRunnable);
+ }, null);
+ }
+
+ private boolean shouldSyncPipTransactionWithMenu() {
+ return mPipMenuController.isMenuVisible();
+ }
+
@VisibleForTesting
final PipTransitionController.PipTransitionCallback mPipTransitionCallback =
new PipTransitionController.PipTransitionCallback() {
@@ -221,7 +257,7 @@ public class PipTaskOrganizer implements ShellTaskOrganizer.TaskListener,
@Override
public boolean handlePipTransaction(SurfaceControl leash,
SurfaceControl.Transaction tx, Rect destinationBounds) {
- if (mPipMenuController.isMenuVisible()) {
+ if (shouldSyncPipTransactionWithMenu()) {
mPipMenuController.movePipMenu(leash, tx, destinationBounds);
return true;
}
@@ -1223,7 +1259,7 @@ public class PipTaskOrganizer implements ShellTaskOrganizer.TaskListener,
mSurfaceTransactionHelper
.crop(tx, mLeash, toBounds)
.round(tx, mLeash, mPipTransitionState.isInPip());
- if (mPipMenuController.isMenuVisible()) {
+ if (shouldSyncPipTransactionWithMenu()) {
mPipMenuController.resizePipMenu(mLeash, tx, toBounds);
} else {
tx.apply();
@@ -1265,7 +1301,7 @@ public class PipTaskOrganizer implements ShellTaskOrganizer.TaskListener,
mSurfaceTransactionHelper
.scale(tx, mLeash, startBounds, toBounds, degrees)
.round(tx, mLeash, startBounds, toBounds);
- if (mPipMenuController.isMenuVisible()) {
+ if (shouldSyncPipTransactionWithMenu()) {
mPipMenuController.movePipMenu(mLeash, tx, toBounds);
} else {
tx.apply();
diff --git a/libs/WindowManager/Shell/src/com/android/wm/shell/pip/phone/PipController.java b/libs/WindowManager/Shell/src/com/android/wm/shell/pip/phone/PipController.java
index d28a9f3cf8ff..efe938f0a274 100644
--- a/libs/WindowManager/Shell/src/com/android/wm/shell/pip/phone/PipController.java
+++ b/libs/WindowManager/Shell/src/com/android/wm/shell/pip/phone/PipController.java
@@ -612,12 +612,21 @@ public class PipController implements PipTransitionController.PipTransitionCallb
new DisplayInsetsController.OnInsetsChangedListener() {
@Override
public void insetsChanged(InsetsState insetsState) {
+ DisplayLayout pendingLayout =
+ mDisplayController.getDisplayLayout(mPipBoundsState.getDisplayId());
+ if (mIsInFixedRotation
+ || pendingLayout.rotation()
+ != mPipBoundsState.getDisplayLayout().rotation()) {
+ // bail out if there is a pending rotation or fixed rotation change
+ return;
+ }
int oldMaxMovementBound = mPipBoundsState.getMovementBounds().bottom;
onDisplayChanged(
mDisplayController.getDisplayLayout(mPipBoundsState.getDisplayId()),
false /* saveRestoreSnapFraction */);
int newMaxMovementBound = mPipBoundsState.getMovementBounds().bottom;
if (!mEnablePipKeepClearAlgorithm) {
+ // offset PiP to adjust for bottom inset change
int pipTop = mPipBoundsState.getBounds().top;
int diff = newMaxMovementBound - oldMaxMovementBound;
if (diff < 0 && pipTop > newMaxMovementBound) {
diff --git a/libs/WindowManager/Shell/src/com/android/wm/shell/splitscreen/StageCoordinator.java b/libs/WindowManager/Shell/src/com/android/wm/shell/splitscreen/StageCoordinator.java
index acb71a80ee8a..4cb76230606f 100644
--- a/libs/WindowManager/Shell/src/com/android/wm/shell/splitscreen/StageCoordinator.java
+++ b/libs/WindowManager/Shell/src/com/android/wm/shell/splitscreen/StageCoordinator.java
@@ -669,6 +669,12 @@ public class StageCoordinator implements SplitLayout.SplitLayoutHandler,
mSplitLayout.init();
mSplitLayout.setDivideRatio(splitRatio);
+ // Apply surface bounds before animation start.
+ SurfaceControl.Transaction startT = mTransactionPool.acquire();
+ updateSurfaceBounds(mSplitLayout, startT, false /* applyResizingOffset */);
+ startT.apply();
+ mTransactionPool.release(startT);
+
// Set false to avoid record new bounds with old task still on top;
mShouldUpdateRecents = false;
mIsDividerRemoteAnimating = true;
@@ -742,7 +748,6 @@ public class StageCoordinator implements SplitLayout.SplitLayoutHandler,
mSyncQueue.queue(wct);
mSyncQueue.runInSync(t -> {
setDividerVisibility(true, t);
- updateSurfaceBounds(mSplitLayout, t, false /* applyResizingOffset */);
});
setEnterInstanceId(instanceId);
@@ -1035,7 +1040,7 @@ public class StageCoordinator implements SplitLayout.SplitLayoutHandler,
mIsDividerRemoteAnimating = false;
mSplitLayout.getInvisibleBounds(mTempRect1);
- if (childrenToTop == null) {
+ if (childrenToTop == null || childrenToTop.getTopVisibleChildTaskId() == INVALID_TASK_ID) {
mSideStage.removeAllTasks(wct, false /* toTop */);
mMainStage.deactivate(wct, false /* toTop */);
wct.reorder(mRootTaskInfo.token, false /* onTop */);
@@ -1294,13 +1299,6 @@ public class StageCoordinator implements SplitLayout.SplitLayoutHandler,
}
}
- private void onStageChildTaskEnterPip() {
- // When the exit split-screen is caused by one of the task enters auto pip,
- // we want both tasks to be put to bottom instead of top, otherwise it will end up
- // a fullscreen plus a pinned task instead of pinned only at the end of the transition.
- exitSplitScreen(null, EXIT_REASON_CHILD_TASK_ENTER_PIP);
- }
-
private void updateRecentTasksSplitPair() {
if (!mShouldUpdateRecents) {
return;
@@ -2063,7 +2061,6 @@ public class StageCoordinator implements SplitLayout.SplitLayoutHandler,
// Update divider state after animation so that it is still around and positioned
// properly for the animation itself.
mSplitLayout.release();
- mSplitLayout.resetDividerPosition();
mTopStageAfterFoldDismiss = STAGE_TYPE_UNDEFINED;
}
}
@@ -2340,11 +2337,6 @@ public class StageCoordinator implements SplitLayout.SplitLayoutHandler,
}
@Override
- public void onChildTaskEnterPip() {
- StageCoordinator.this.onStageChildTaskEnterPip();
- }
-
- @Override
public void onRootTaskVanished() {
reset();
StageCoordinator.this.onRootTaskVanished();
diff --git a/libs/WindowManager/Shell/src/com/android/wm/shell/splitscreen/StageTaskListener.java b/libs/WindowManager/Shell/src/com/android/wm/shell/splitscreen/StageTaskListener.java
index bcf900b99c69..358f712f76b5 100644
--- a/libs/WindowManager/Shell/src/com/android/wm/shell/splitscreen/StageTaskListener.java
+++ b/libs/WindowManager/Shell/src/com/android/wm/shell/splitscreen/StageTaskListener.java
@@ -18,7 +18,6 @@ package com.android.wm.shell.splitscreen;
import static android.app.ActivityTaskManager.INVALID_TASK_ID;
import static android.app.WindowConfiguration.WINDOWING_MODE_MULTI_WINDOW;
-import static android.app.WindowConfiguration.WINDOWING_MODE_PINNED;
import static android.app.WindowConfiguration.WINDOWING_MODE_UNDEFINED;
import static android.view.RemoteAnimationTarget.MODE_OPENING;
@@ -74,8 +73,6 @@ class StageTaskListener implements ShellTaskOrganizer.TaskListener {
void onChildTaskStatusChanged(int taskId, boolean present, boolean visible);
- void onChildTaskEnterPip();
-
void onRootTaskVanished();
void onNoLongerSupportMultiWindow();
@@ -257,9 +254,6 @@ class StageTaskListener implements ShellTaskOrganizer.TaskListener {
// Status is managed/synchronized by the transition lifecycle.
return;
}
- if (taskInfo.getWindowingMode() == WINDOWING_MODE_PINNED) {
- mCallbacks.onChildTaskEnterPip();
- }
sendStatusChanged();
} else {
throw new IllegalArgumentException(this + "\n Unknown task: " + taskInfo
diff --git a/libs/WindowManager/Shell/src/com/android/wm/shell/transition/OneShotRemoteHandler.java b/libs/WindowManager/Shell/src/com/android/wm/shell/transition/OneShotRemoteHandler.java
index 4e1fa290270d..485b400f458d 100644
--- a/libs/WindowManager/Shell/src/com/android/wm/shell/transition/OneShotRemoteHandler.java
+++ b/libs/WindowManager/Shell/src/com/android/wm/shell/transition/OneShotRemoteHandler.java
@@ -77,10 +77,10 @@ public class OneShotRemoteHandler implements Transitions.TransitionHandler {
if (mRemote.asBinder() != null) {
mRemote.asBinder().unlinkToDeath(remoteDied, 0 /* flags */);
}
+ if (sct != null) {
+ finishTransaction.merge(sct);
+ }
mMainExecutor.execute(() -> {
- if (sct != null) {
- finishTransaction.merge(sct);
- }
finishCallback.onTransitionFinished(wct, null /* wctCB */);
});
}
@@ -90,7 +90,13 @@ public class OneShotRemoteHandler implements Transitions.TransitionHandler {
if (mRemote.asBinder() != null) {
mRemote.asBinder().linkToDeath(remoteDied, 0 /* flags */);
}
- mRemote.getRemoteTransition().startAnimation(transition, info, startTransaction, cb);
+ // If the remote is actually in the same process, then make a copy of parameters since
+ // remote impls assume that they have to clean-up native references.
+ final SurfaceControl.Transaction remoteStartT = RemoteTransitionHandler.copyIfLocal(
+ startTransaction, mRemote.getRemoteTransition());
+ final TransitionInfo remoteInfo =
+ remoteStartT == startTransaction ? info : info.localRemoteCopy();
+ mRemote.getRemoteTransition().startAnimation(transition, remoteInfo, remoteStartT, cb);
// assume that remote will apply the start transaction.
startTransaction.clear();
} catch (RemoteException e) {
@@ -124,7 +130,13 @@ public class OneShotRemoteHandler implements Transitions.TransitionHandler {
}
};
try {
- mRemote.getRemoteTransition().mergeAnimation(transition, info, t, mergeTarget, cb);
+ // If the remote is actually in the same process, then make a copy of parameters since
+ // remote impls assume that they have to clean-up native references.
+ final SurfaceControl.Transaction remoteT =
+ RemoteTransitionHandler.copyIfLocal(t, mRemote.getRemoteTransition());
+ final TransitionInfo remoteInfo = remoteT == t ? info : info.localRemoteCopy();
+ mRemote.getRemoteTransition().mergeAnimation(
+ transition, remoteInfo, remoteT, mergeTarget, cb);
} catch (RemoteException e) {
Log.e(Transitions.TAG, "Error merging remote transition.", e);
}
diff --git a/libs/WindowManager/Shell/src/com/android/wm/shell/transition/RemoteTransitionHandler.java b/libs/WindowManager/Shell/src/com/android/wm/shell/transition/RemoteTransitionHandler.java
index 9469529de8f1..b4e05848882c 100644
--- a/libs/WindowManager/Shell/src/com/android/wm/shell/transition/RemoteTransitionHandler.java
+++ b/libs/WindowManager/Shell/src/com/android/wm/shell/transition/RemoteTransitionHandler.java
@@ -19,6 +19,7 @@ package com.android.wm.shell.transition;
import android.annotation.NonNull;
import android.annotation.Nullable;
import android.os.IBinder;
+import android.os.Parcel;
import android.os.RemoteException;
import android.util.ArrayMap;
import android.util.Log;
@@ -120,10 +121,10 @@ public class RemoteTransitionHandler implements Transitions.TransitionHandler {
public void onTransitionFinished(WindowContainerTransaction wct,
SurfaceControl.Transaction sct) {
unhandleDeath(remote.asBinder(), finishCallback);
+ if (sct != null) {
+ finishTransaction.merge(sct);
+ }
mMainExecutor.execute(() -> {
- if (sct != null) {
- finishTransaction.merge(sct);
- }
mRequestedRemotes.remove(transition);
finishCallback.onTransitionFinished(wct, null /* wctCB */);
});
@@ -131,8 +132,14 @@ public class RemoteTransitionHandler implements Transitions.TransitionHandler {
};
Transitions.setRunningRemoteTransitionDelegate(remote.getAppThread());
try {
+ // If the remote is actually in the same process, then make a copy of parameters since
+ // remote impls assume that they have to clean-up native references.
+ final SurfaceControl.Transaction remoteStartT =
+ copyIfLocal(startTransaction, remote.getRemoteTransition());
+ final TransitionInfo remoteInfo =
+ remoteStartT == startTransaction ? info : info.localRemoteCopy();
handleDeath(remote.asBinder(), finishCallback);
- remote.getRemoteTransition().startAnimation(transition, info, startTransaction, cb);
+ remote.getRemoteTransition().startAnimation(transition, remoteInfo, remoteStartT, cb);
// assume that remote will apply the start transaction.
startTransaction.clear();
} catch (RemoteException e) {
@@ -145,6 +152,28 @@ public class RemoteTransitionHandler implements Transitions.TransitionHandler {
return true;
}
+ static SurfaceControl.Transaction copyIfLocal(SurfaceControl.Transaction t,
+ IRemoteTransition remote) {
+ // We care more about parceling than local (though they should be the same); so, use
+ // queryLocalInterface since that's what Binder uses to decide if it needs to parcel.
+ if (remote.asBinder().queryLocalInterface(IRemoteTransition.DESCRIPTOR) == null) {
+ // No local interface, so binder itself will parcel and thus we don't need to.
+ return t;
+ }
+ // Binder won't be parceling; however, the remotes assume they have their own native
+ // objects (and don't know if caller is local or not), so we need to make a COPY here so
+ // that the remote can clean it up without clearing the original transaction.
+ // Since there's no direct `copy` for Transaction, we have to parcel/unparcel instead.
+ final Parcel p = Parcel.obtain();
+ try {
+ t.writeToParcel(p, 0);
+ p.setDataPosition(0);
+ return SurfaceControl.Transaction.CREATOR.createFromParcel(p);
+ } finally {
+ p.recycle();
+ }
+ }
+
@Override
public void mergeAnimation(@NonNull IBinder transition, @NonNull TransitionInfo info,
@NonNull SurfaceControl.Transaction t, @NonNull IBinder mergeTarget,
@@ -175,7 +204,11 @@ public class RemoteTransitionHandler implements Transitions.TransitionHandler {
}
};
try {
- remote.mergeAnimation(transition, info, t, mergeTarget, cb);
+ // If the remote is actually in the same process, then make a copy of parameters since
+ // remote impls assume that they have to clean-up native references.
+ final SurfaceControl.Transaction remoteT = copyIfLocal(t, remote);
+ final TransitionInfo remoteInfo = remoteT == t ? info : info.localRemoteCopy();
+ remote.mergeAnimation(transition, remoteInfo, remoteT, mergeTarget, cb);
} catch (RemoteException e) {
Log.e(Transitions.TAG, "Error attempting to merge remote transition.", e);
}
diff --git a/libs/WindowManager/Shell/src/com/android/wm/shell/transition/Transitions.java b/libs/WindowManager/Shell/src/com/android/wm/shell/transition/Transitions.java
index 56d51bda762f..c6935c054422 100644
--- a/libs/WindowManager/Shell/src/com/android/wm/shell/transition/Transitions.java
+++ b/libs/WindowManager/Shell/src/com/android/wm/shell/transition/Transitions.java
@@ -503,6 +503,7 @@ public class Transitions implements RemoteCallable<Transitions> {
// Treat this as an abort since we are bypassing any merge logic and effectively
// finishing immediately.
onAbort(transitionToken);
+ releaseSurfaces(info);
return;
}
@@ -607,6 +608,15 @@ public class Transitions implements RemoteCallable<Transitions> {
onFinish(transition, wct, wctCB, false /* abort */);
}
+ /**
+ * Releases an info's animation-surfaces. These don't need to persist and we need to release
+ * them asap so that SF can free memory sooner.
+ */
+ private void releaseSurfaces(@Nullable TransitionInfo info) {
+ if (info == null) return;
+ info.releaseAnimSurfaces();
+ }
+
private void onFinish(IBinder transition,
@Nullable WindowContainerTransaction wct,
@Nullable WindowContainerTransactionCallback wctCB,
@@ -645,6 +655,11 @@ public class Transitions implements RemoteCallable<Transitions> {
}
ProtoLog.v(ShellProtoLogGroup.WM_SHELL_TRANSITIONS,
"Transition animation finished (abort=%b), notifying core %s", abort, transition);
+ if (active.mStartT != null) {
+ // Applied by now, so close immediately. Do not set to null yet, though, since nullness
+ // is used later to disambiguate malformed transitions.
+ active.mStartT.close();
+ }
// Merge all relevant transactions together
SurfaceControl.Transaction fullFinish = active.mFinishT;
for (int iA = activeIdx + 1; iA < mActiveTransitions.size(); ++iA) {
@@ -664,12 +679,14 @@ public class Transitions implements RemoteCallable<Transitions> {
fullFinish.apply();
}
// Now perform all the finishes.
+ releaseSurfaces(active.mInfo);
mActiveTransitions.remove(activeIdx);
mOrganizer.finishTransition(transition, wct, wctCB);
while (activeIdx < mActiveTransitions.size()) {
if (!mActiveTransitions.get(activeIdx).mMerged) break;
ActiveTransition merged = mActiveTransitions.remove(activeIdx);
mOrganizer.finishTransition(merged.mToken, null /* wct */, null /* wctCB */);
+ releaseSurfaces(merged.mInfo);
}
// sift through aborted transitions
while (mActiveTransitions.size() > activeIdx
@@ -682,8 +699,9 @@ public class Transitions implements RemoteCallable<Transitions> {
}
mOrganizer.finishTransition(aborted.mToken, null /* wct */, null /* wctCB */);
for (int i = 0; i < mObservers.size(); ++i) {
- mObservers.get(i).onTransitionFinished(active.mToken, true);
+ mObservers.get(i).onTransitionFinished(aborted.mToken, true);
}
+ releaseSurfaces(aborted.mInfo);
}
if (mActiveTransitions.size() <= activeIdx) {
ProtoLog.v(ShellProtoLogGroup.WM_SHELL_TRANSITIONS, "All active transition animations "
diff --git a/libs/WindowManager/Shell/src/com/android/wm/shell/windowdecor/CaptionWindowDecorViewModel.java b/libs/WindowManager/Shell/src/com/android/wm/shell/windowdecor/CaptionWindowDecorViewModel.java
index 8369569b4163..e40db4e4dcf2 100644
--- a/libs/WindowManager/Shell/src/com/android/wm/shell/windowdecor/CaptionWindowDecorViewModel.java
+++ b/libs/WindowManager/Shell/src/com/android/wm/shell/windowdecor/CaptionWindowDecorViewModel.java
@@ -55,6 +55,8 @@ import com.android.wm.shell.desktopmode.DesktopModeStatus;
import com.android.wm.shell.freeform.FreeformTaskTransitionStarter;
import com.android.wm.shell.transition.Transitions;
+import java.util.function.Supplier;
+
/**
* View model for the window decoration with a caption and shadows. Works with
* {@link CaptionWindowDecoration}.
@@ -62,6 +64,8 @@ import com.android.wm.shell.transition.Transitions;
public class CaptionWindowDecorViewModel implements WindowDecorViewModel {
private static final String TAG = "CaptionViewModel";
+ private final CaptionWindowDecoration.Factory mCaptionWindowDecorFactory;
+ private final Supplier<InputManager> mInputManagerSupplier;
private final ActivityTaskManager mActivityTaskManager;
private final ShellTaskOrganizer mTaskOrganizer;
private final Context mContext;
@@ -77,6 +81,7 @@ public class CaptionWindowDecorViewModel implements WindowDecorViewModel {
private final SparseArray<CaptionWindowDecoration> mWindowDecorByTaskId = new SparseArray<>();
private final DragStartListenerImpl mDragStartListener = new DragStartListenerImpl();
+ private EventReceiverFactory mEventReceiverFactory = new EventReceiverFactory();
public CaptionWindowDecorViewModel(
Context context,
@@ -86,6 +91,29 @@ public class CaptionWindowDecorViewModel implements WindowDecorViewModel {
DisplayController displayController,
SyncTransactionQueue syncQueue,
DesktopModeController desktopModeController) {
+ this(
+ context,
+ mainHandler,
+ mainChoreographer,
+ taskOrganizer,
+ displayController,
+ syncQueue,
+ desktopModeController,
+ new CaptionWindowDecoration.Factory(),
+ InputManager::getInstance);
+ }
+
+ public CaptionWindowDecorViewModel(
+ Context context,
+ Handler mainHandler,
+ Choreographer mainChoreographer,
+ ShellTaskOrganizer taskOrganizer,
+ DisplayController displayController,
+ SyncTransactionQueue syncQueue,
+ DesktopModeController desktopModeController,
+ CaptionWindowDecoration.Factory captionWindowDecorFactory,
+ Supplier<InputManager> inputManagerSupplier) {
+
mContext = context;
mMainHandler = mainHandler;
mMainChoreographer = mainChoreographer;
@@ -94,7 +122,13 @@ public class CaptionWindowDecorViewModel implements WindowDecorViewModel {
mDisplayController = displayController;
mSyncQueue = syncQueue;
mDesktopModeController = desktopModeController;
- mTransitionDragActive = false;
+
+ mCaptionWindowDecorFactory = captionWindowDecorFactory;
+ mInputManagerSupplier = inputManagerSupplier;
+ }
+
+ void setEventReceiverFactory(EventReceiverFactory eventReceiverFactory) {
+ mEventReceiverFactory = eventReceiverFactory;
}
@Override
@@ -103,42 +137,13 @@ public class CaptionWindowDecorViewModel implements WindowDecorViewModel {
}
@Override
- public boolean createWindowDecoration(
+ public boolean onTaskOpening(
ActivityManager.RunningTaskInfo taskInfo,
SurfaceControl taskSurface,
SurfaceControl.Transaction startT,
SurfaceControl.Transaction finishT) {
if (!shouldShowWindowDecor(taskInfo)) return false;
- CaptionWindowDecoration oldDecoration = mWindowDecorByTaskId.get(taskInfo.taskId);
- if (oldDecoration != null) {
- // close the old decoration if it exists to avoid two window decorations being added
- oldDecoration.close();
- }
- final CaptionWindowDecoration windowDecoration = new CaptionWindowDecoration(
- mContext,
- mDisplayController,
- mTaskOrganizer,
- taskInfo,
- taskSurface,
- mMainHandler,
- mMainChoreographer,
- mSyncQueue);
- mWindowDecorByTaskId.put(taskInfo.taskId, windowDecoration);
-
- TaskPositioner taskPositioner = new TaskPositioner(mTaskOrganizer, windowDecoration,
- mDragStartListener);
- CaptionTouchEventListener touchEventListener =
- new CaptionTouchEventListener(taskInfo, taskPositioner,
- windowDecoration.getDragDetector());
- windowDecoration.setCaptionListeners(touchEventListener, touchEventListener);
- windowDecoration.setDragResizeCallback(taskPositioner);
- setupWindowDecorationForTransition(taskInfo, startT, finishT);
- if (mInputMonitor == null) {
- mInputMonitor = InputManager.getInstance().monitorGestureInput(
- "caption-touch", mContext.getDisplayId());
- mEventReceiver = new EventReceiver(
- mInputMonitor.getInputChannel(), Looper.myLooper());
- }
+ createWindowDecoration(taskInfo, taskSurface, startT, finishT);
return true;
}
@@ -151,25 +156,45 @@ public class CaptionWindowDecorViewModel implements WindowDecorViewModel {
}
@Override
- public boolean setupWindowDecorationForTransition(
+ public void onTaskChanging(
+ RunningTaskInfo taskInfo,
+ SurfaceControl taskSurface,
+ SurfaceControl.Transaction startT,
+ SurfaceControl.Transaction finishT) {
+ final CaptionWindowDecoration decoration = mWindowDecorByTaskId.get(taskInfo.taskId);
+
+ if (!shouldShowWindowDecor(taskInfo)) {
+ if (decoration != null) {
+ destroyWindowDecoration(taskInfo);
+ }
+ return;
+ }
+
+ if (decoration == null) {
+ createWindowDecoration(taskInfo, taskSurface, startT, finishT);
+ } else {
+ decoration.relayout(taskInfo, startT, finishT);
+ }
+ }
+
+ @Override
+ public void onTaskClosing(
RunningTaskInfo taskInfo,
SurfaceControl.Transaction startT,
SurfaceControl.Transaction finishT) {
final CaptionWindowDecoration decoration = mWindowDecorByTaskId.get(taskInfo.taskId);
- if (decoration == null) return false;
+ if (decoration == null) return;
decoration.relayout(taskInfo, startT, finishT);
- return true;
}
@Override
- public boolean destroyWindowDecoration(RunningTaskInfo taskInfo) {
+ public void destroyWindowDecoration(RunningTaskInfo taskInfo) {
final CaptionWindowDecoration decoration =
mWindowDecorByTaskId.removeReturnOld(taskInfo.taskId);
- if (decoration == null) return false;
+ if (decoration == null) return;
decoration.close();
- return true;
}
private class CaptionTouchEventListener implements
@@ -217,6 +242,7 @@ public class CaptionWindowDecorViewModel implements WindowDecorViewModel {
decoration.setButtonVisibility();
}
}
+
private void injectBackKey() {
sendBackEvent(KeyEvent.ACTION_DOWN);
sendBackEvent(KeyEvent.ACTION_UP);
@@ -266,7 +292,7 @@ public class CaptionWindowDecorViewModel implements WindowDecorViewModel {
*/
private void handleEventForMove(MotionEvent e) {
RunningTaskInfo taskInfo = mTaskOrganizer.getRunningTaskInfo(mTaskId);
- int windowingMode = mDesktopModeController
+ int windowingMode = mDesktopModeController
.getDisplayAreaWindowingMode(taskInfo.displayId);
if (windowingMode == WINDOWING_MODE_FULLSCREEN) {
return;
@@ -302,7 +328,7 @@ public class CaptionWindowDecorViewModel implements WindowDecorViewModel {
}
// InputEventReceiver to listen for touch input outside of caption bounds
- private class EventReceiver extends InputEventReceiver {
+ class EventReceiver extends InputEventReceiver {
EventReceiver(InputChannel channel, Looper looper) {
super(channel, looper);
}
@@ -318,8 +344,15 @@ public class CaptionWindowDecorViewModel implements WindowDecorViewModel {
}
}
+ class EventReceiverFactory {
+ EventReceiver create(InputChannel channel, Looper looper) {
+ return new EventReceiver(channel, looper);
+ }
+ }
+
/**
* Handle MotionEvents relevant to focused task's caption that don't directly touch it
+ *
* @param ev the {@link MotionEvent} received by {@link EventReceiver}
*/
private void handleReceivedMotionEvent(MotionEvent ev) {
@@ -401,7 +434,6 @@ public class CaptionWindowDecorViewModel implements WindowDecorViewModel {
return focusedDecor;
}
-
private boolean shouldShowWindowDecor(RunningTaskInfo taskInfo) {
if (taskInfo.getWindowingMode() == WINDOWING_MODE_FREEFORM) return true;
return DesktopModeStatus.IS_SUPPORTED
@@ -410,7 +442,47 @@ public class CaptionWindowDecorViewModel implements WindowDecorViewModel {
.getResources().getConfiguration().smallestScreenWidthDp >= 600;
}
- private class DragStartListenerImpl implements TaskPositioner.DragStartListener{
+ private void createWindowDecoration(
+ ActivityManager.RunningTaskInfo taskInfo,
+ SurfaceControl taskSurface,
+ SurfaceControl.Transaction startT,
+ SurfaceControl.Transaction finishT) {
+ CaptionWindowDecoration oldDecoration = mWindowDecorByTaskId.get(taskInfo.taskId);
+ if (oldDecoration != null) {
+ // close the old decoration if it exists to avoid two window decorations being added
+ oldDecoration.close();
+ }
+ final CaptionWindowDecoration windowDecoration =
+ mCaptionWindowDecorFactory.create(
+ mContext,
+ mDisplayController,
+ mTaskOrganizer,
+ taskInfo,
+ taskSurface,
+ mMainHandler,
+ mMainChoreographer,
+ mSyncQueue);
+ mWindowDecorByTaskId.put(taskInfo.taskId, windowDecoration);
+
+ TaskPositioner taskPositioner =
+ new TaskPositioner(mTaskOrganizer, windowDecoration, mDragStartListener);
+ CaptionTouchEventListener touchEventListener =
+ new CaptionTouchEventListener(
+ taskInfo, taskPositioner, windowDecoration.getDragDetector());
+ windowDecoration.setCaptionListeners(touchEventListener, touchEventListener);
+ windowDecoration.setDragResizeCallback(taskPositioner);
+ windowDecoration.relayout(taskInfo, startT, finishT);
+ if (mInputMonitor == null) {
+ InputManager inputManager = mInputManagerSupplier.get();
+ mInputMonitor =
+ inputManager.monitorGestureInput("caption-touch", mContext.getDisplayId());
+ mEventReceiver =
+ mEventReceiverFactory.create(
+ mInputMonitor.getInputChannel(), Looper.myLooper());
+ }
+ }
+
+ private class DragStartListenerImpl implements TaskPositioner.DragStartListener {
@Override
public void onDragStart(int taskId) {
mWindowDecorByTaskId.get(taskId).closeHandleMenu();
diff --git a/libs/WindowManager/Shell/src/com/android/wm/shell/windowdecor/CaptionWindowDecoration.java b/libs/WindowManager/Shell/src/com/android/wm/shell/windowdecor/CaptionWindowDecoration.java
index 59576cd3ec15..037ca2031254 100644
--- a/libs/WindowManager/Shell/src/com/android/wm/shell/windowdecor/CaptionWindowDecoration.java
+++ b/libs/WindowManager/Shell/src/com/android/wm/shell/windowdecor/CaptionWindowDecoration.java
@@ -42,7 +42,8 @@ import com.android.wm.shell.desktopmode.DesktopModeStatus;
/**
* Defines visuals and behaviors of a window decoration of a caption bar and shadows. It works with
- * {@link CaptionWindowDecorViewModel}. The caption bar contains a handle, back button, and close button.
+ * {@link CaptionWindowDecorViewModel}. The caption bar contains a handle, back button, and close
+ * button.
*
* The shadow's thickness is 20dp when the window is in focus and 5dp when the window isn't.
*/
@@ -181,12 +182,12 @@ public class CaptionWindowDecoration extends WindowDecoration<WindowDecorLinearL
if (oldDecorationSurface != mDecorationContainerSurface || mDragResizeListener == null) {
closeDragResizeListener();
mDragResizeListener = new DragResizeInputListener(
- mContext,
- mHandler,
- mChoreographer,
- mDisplay.getDisplayId(),
- mDecorationContainerSurface,
- mDragResizeCallback);
+ mContext,
+ mHandler,
+ mChoreographer,
+ mDisplay.getDisplayId(),
+ mDecorationContainerSurface,
+ mDragResizeCallback);
}
int touchSlop = ViewConfiguration.get(mResult.mRootView.getContext()).getScaledTouchSlop();
@@ -242,7 +243,6 @@ public class CaptionWindowDecoration extends WindowDecoration<WindowDecorLinearL
/**
* Sets the visibility of buttons and color of caption based on desktop mode status
- *
*/
void setButtonVisibility() {
mDesktopActive = DesktopModeStatus.isActive(mContext);
@@ -313,6 +313,7 @@ public class CaptionWindowDecoration extends WindowDecoration<WindowDecorLinearL
/**
* Close an open handle menu if input is outside of menu coordinates
+ *
* @param ev the tapped point to compare against
*/
void closeHandleMenuIfNeeded(MotionEvent ev) {
@@ -329,6 +330,7 @@ public class CaptionWindowDecoration extends WindowDecoration<WindowDecorLinearL
/**
* Offset the coordinates of a {@link MotionEvent} to be in the same coordinate space as caption
+ *
* @param ev the {@link MotionEvent} to offset
* @return the point of the input in local space
*/
@@ -343,7 +345,8 @@ public class CaptionWindowDecoration extends WindowDecoration<WindowDecorLinearL
/**
* Determine if a passed MotionEvent is in a view in caption
- * @param ev the {@link MotionEvent} to check
+ *
+ * @param ev the {@link MotionEvent} to check
* @param layoutId the id of the view
* @return {@code true} if event is inside the specified view, {@code false} if not
*/
@@ -363,6 +366,7 @@ public class CaptionWindowDecoration extends WindowDecoration<WindowDecorLinearL
* Check a passed MotionEvent if a click has occurred on any button on this caption
* Note this should only be called when a regular onClick is not possible
* (i.e. the button was clicked through status bar layer)
+ *
* @param ev the MotionEvent to compare
*/
void checkClickEvent(MotionEvent ev) {
@@ -399,4 +403,27 @@ public class CaptionWindowDecoration extends WindowDecoration<WindowDecorLinearL
closeHandleMenu();
super.close();
}
+
+ static class Factory {
+
+ CaptionWindowDecoration create(
+ Context context,
+ DisplayController displayController,
+ ShellTaskOrganizer taskOrganizer,
+ ActivityManager.RunningTaskInfo taskInfo,
+ SurfaceControl taskSurface,
+ Handler handler,
+ Choreographer choreographer,
+ SyncTransactionQueue syncQueue) {
+ return new CaptionWindowDecoration(
+ context,
+ displayController,
+ taskOrganizer,
+ taskInfo,
+ taskSurface,
+ handler,
+ choreographer,
+ syncQueue);
+ }
+ }
}
diff --git a/libs/WindowManager/Shell/src/com/android/wm/shell/windowdecor/WindowDecorViewModel.java b/libs/WindowManager/Shell/src/com/android/wm/shell/windowdecor/WindowDecorViewModel.java
index 2ce4d04377a1..907977c661f8 100644
--- a/libs/WindowManager/Shell/src/com/android/wm/shell/windowdecor/WindowDecorViewModel.java
+++ b/libs/WindowManager/Shell/src/com/android/wm/shell/windowdecor/WindowDecorViewModel.java
@@ -28,7 +28,6 @@ import com.android.wm.shell.freeform.FreeformTaskTransitionStarter;
* servers.
*/
public interface WindowDecorViewModel {
-
/**
* Sets the transition starter that starts freeform task transitions.
*
@@ -37,16 +36,16 @@ public interface WindowDecorViewModel {
void setFreeformTaskTransitionStarter(FreeformTaskTransitionStarter transitionStarter);
/**
- * Creates a window decoration for the given task.
- * Can be {@code null} for Fullscreen tasks but not Freeform ones.
+ * Creates a window decoration for the given task. Can be {@code null} for Fullscreen tasks but
+ * not Freeform ones.
*
- * @param taskInfo the initial task info of the task
+ * @param taskInfo the initial task info of the task
* @param taskSurface the surface of the task
- * @param startT the start transaction to be applied before the transition
- * @param finishT the finish transaction to restore states after the transition
+ * @param startT the start transaction to be applied before the transition
+ * @param finishT the finish transaction to restore states after the transition
* @return {@code true} if window decoration was created, {@code false} otherwise
*/
- boolean createWindowDecoration(
+ boolean onTaskOpening(
ActivityManager.RunningTaskInfo taskInfo,
SurfaceControl taskSurface,
SurfaceControl.Transaction startT,
@@ -54,7 +53,7 @@ public interface WindowDecorViewModel {
/**
* Notifies a task info update on the given task, with the window decoration created previously
- * for this task by {@link #createWindowDecoration}.
+ * for this task by {@link #onTaskOpening}.
*
* @param taskInfo the new task info of the task
*/
@@ -62,13 +61,29 @@ public interface WindowDecorViewModel {
/**
* Notifies a transition is about to start about the given task to give the window decoration a
- * chance to prepare for this transition.
+ * chance to prepare for this transition. Unlike {@link #onTaskInfoChanged}, this method creates
+ * a window decoration if one does not exist but is required.
+ *
+ * @param taskInfo the initial task info of the task
+ * @param taskSurface the surface of the task
+ * @param startT the start transaction to be applied before the transition
+ * @param finishT the finish transaction to restore states after the transition
+ */
+ void onTaskChanging(
+ ActivityManager.RunningTaskInfo taskInfo,
+ SurfaceControl taskSurface,
+ SurfaceControl.Transaction startT,
+ SurfaceControl.Transaction finishT);
+
+ /**
+ * Notifies that the given task is about to close to give the window decoration a chance to
+ * prepare for this transition.
*
- * @param startT the start transaction to be applied before the transition
- * @param finishT the finish transaction to restore states after the transition
- * @return {@code true} if window decoration exists, {@code false} otherwise
+ * @param taskInfo the initial task info of the task
+ * @param startT the start transaction to be applied before the transition
+ * @param finishT the finish transaction to restore states after the transition
*/
- boolean setupWindowDecorationForTransition(
+ void onTaskClosing(
ActivityManager.RunningTaskInfo taskInfo,
SurfaceControl.Transaction startT,
SurfaceControl.Transaction finishT);
@@ -77,7 +92,6 @@ public interface WindowDecorViewModel {
* Destroys the window decoration of the give task.
*
* @param taskInfo the info of the task
- * @return {@code true} if window decoration was destroyed, {@code false} otherwise
*/
- boolean destroyWindowDecoration(ActivityManager.RunningTaskInfo taskInfo);
-}
+ void destroyWindowDecoration(ActivityManager.RunningTaskInfo taskInfo);
+} \ No newline at end of file
diff --git a/libs/WindowManager/Shell/tests/unittest/src/com/android/wm/shell/freeform/FreeformTaskTransitionObserverTest.java b/libs/WindowManager/Shell/tests/unittest/src/com/android/wm/shell/freeform/FreeformTaskTransitionObserverTest.java
index 7068a84c3056..48415d47304c 100644
--- a/libs/WindowManager/Shell/tests/unittest/src/com/android/wm/shell/freeform/FreeformTaskTransitionObserverTest.java
+++ b/libs/WindowManager/Shell/tests/unittest/src/com/android/wm/shell/freeform/FreeformTaskTransitionObserverTest.java
@@ -103,7 +103,7 @@ public class FreeformTaskTransitionObserverTest {
mTransitionObserver.onTransitionReady(transition, info, startT, finishT);
mTransitionObserver.onTransitionStarting(transition);
- verify(mWindowDecorViewModel).createWindowDecoration(
+ verify(mWindowDecorViewModel).onTaskOpening(
change.getTaskInfo(), change.getLeash(), startT, finishT);
}
@@ -120,7 +120,7 @@ public class FreeformTaskTransitionObserverTest {
mTransitionObserver.onTransitionReady(transition, info, startT, finishT);
mTransitionObserver.onTransitionStarting(transition);
- verify(mWindowDecorViewModel).setupWindowDecorationForTransition(
+ verify(mWindowDecorViewModel).onTaskClosing(
change.getTaskInfo(), startT, finishT);
}
diff --git a/libs/WindowManager/Shell/tests/unittest/src/com/android/wm/shell/windowdecor/CaptionWindowDecorViewModelTests.java b/libs/WindowManager/Shell/tests/unittest/src/com/android/wm/shell/windowdecor/CaptionWindowDecorViewModelTests.java
new file mode 100644
index 000000000000..8b134ed1dfe4
--- /dev/null
+++ b/libs/WindowManager/Shell/tests/unittest/src/com/android/wm/shell/windowdecor/CaptionWindowDecorViewModelTests.java
@@ -0,0 +1,217 @@
+/*
+ * Copyright (C) 2022 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.wm.shell.windowdecor;
+
+import static android.app.WindowConfiguration.ACTIVITY_TYPE_STANDARD;
+import static android.app.WindowConfiguration.ACTIVITY_TYPE_UNDEFINED;
+import static android.app.WindowConfiguration.WINDOWING_MODE_FREEFORM;
+import static android.app.WindowConfiguration.WINDOWING_MODE_UNDEFINED;
+
+import static org.mockito.Mockito.any;
+import static org.mockito.Mockito.anyInt;
+import static org.mockito.Mockito.doReturn;
+import static org.mockito.Mockito.mock;
+import static org.mockito.Mockito.never;
+import static org.mockito.Mockito.verify;
+import static org.mockito.Mockito.when;
+
+import android.app.ActivityManager;
+import android.hardware.input.InputManager;
+import android.os.Handler;
+import android.os.Looper;
+import android.view.Choreographer;
+import android.view.Display;
+import android.view.InputChannel;
+import android.view.InputMonitor;
+import android.view.SurfaceControl;
+
+import androidx.test.filters.SmallTest;
+import androidx.test.rule.GrantPermissionRule;
+
+import com.android.wm.shell.ShellTaskOrganizer;
+import com.android.wm.shell.ShellTestCase;
+import com.android.wm.shell.TestRunningTaskInfoBuilder;
+import com.android.wm.shell.common.DisplayController;
+import com.android.wm.shell.common.SyncTransactionQueue;
+import com.android.wm.shell.desktopmode.DesktopModeController;
+
+import org.junit.Before;
+import org.junit.Test;
+import org.mockito.Mock;
+
+import java.util.ArrayList;
+import java.util.List;
+import java.util.function.Supplier;
+
+/** Tests of {@link CaptionWindowDecorViewModel} */
+@SmallTest
+public class CaptionWindowDecorViewModelTests extends ShellTestCase {
+ @Mock private CaptionWindowDecoration mCaptionWindowDecoration;
+
+ @Mock private CaptionWindowDecoration.Factory mCaptionWindowDecorFactory;
+
+ @Mock private Handler mMainHandler;
+
+ @Mock private Choreographer mMainChoreographer;
+
+ @Mock private ShellTaskOrganizer mTaskOrganizer;
+
+ @Mock private DisplayController mDisplayController;
+
+ @Mock private SyncTransactionQueue mSyncQueue;
+
+ @Mock private DesktopModeController mDesktopModeController;
+
+ @Mock private InputMonitor mInputMonitor;
+
+ @Mock private InputChannel mInputChannel;
+
+ @Mock private CaptionWindowDecorViewModel.EventReceiverFactory mEventReceiverFactory;
+
+ @Mock private CaptionWindowDecorViewModel.EventReceiver mEventReceiver;
+
+ @Mock private InputManager mInputManager;
+
+ private final List<InputManager> mMockInputManagers = new ArrayList<>();
+
+ private CaptionWindowDecorViewModel mCaptionWindowDecorViewModel;
+
+ @Before
+ public void setUp() {
+ mMockInputManagers.add(mInputManager);
+
+ mCaptionWindowDecorViewModel =
+ new CaptionWindowDecorViewModel(
+ mContext,
+ mMainHandler,
+ mMainChoreographer,
+ mTaskOrganizer,
+ mDisplayController,
+ mSyncQueue,
+ mDesktopModeController,
+ mCaptionWindowDecorFactory,
+ new MockObjectSupplier<>(mMockInputManagers, () -> mock(InputManager.class)));
+ mCaptionWindowDecorViewModel.setEventReceiverFactory(mEventReceiverFactory);
+
+ doReturn(mCaptionWindowDecoration)
+ .when(mCaptionWindowDecorFactory)
+ .create(any(), any(), any(), any(), any(), any(), any(), any());
+
+ when(mInputManager.monitorGestureInput(any(), anyInt())).thenReturn(mInputMonitor);
+ when(mEventReceiverFactory.create(any(), any())).thenReturn(mEventReceiver);
+ when(mInputMonitor.getInputChannel()).thenReturn(mInputChannel);
+ }
+
+ @Test
+ public void testDeleteCaptionOnChangeTransitionWhenNecessary() throws Exception {
+ Looper.prepare();
+ final int taskId = 1;
+ final ActivityManager.RunningTaskInfo taskInfo =
+ createTaskInfo(taskId, WINDOWING_MODE_FREEFORM);
+ SurfaceControl surfaceControl = mock(SurfaceControl.class);
+ final SurfaceControl.Transaction startT = mock(SurfaceControl.Transaction.class);
+ final SurfaceControl.Transaction finishT = mock(SurfaceControl.Transaction.class);
+ GrantPermissionRule.grant(android.Manifest.permission.MONITOR_INPUT);
+
+ mCaptionWindowDecorViewModel.onTaskOpening(taskInfo, surfaceControl, startT, finishT);
+ verify(mCaptionWindowDecorFactory)
+ .create(
+ mContext,
+ mDisplayController,
+ mTaskOrganizer,
+ taskInfo,
+ surfaceControl,
+ mMainHandler,
+ mMainChoreographer,
+ mSyncQueue);
+
+ taskInfo.configuration.windowConfiguration.setWindowingMode(WINDOWING_MODE_UNDEFINED);
+ taskInfo.configuration.windowConfiguration.setActivityType(ACTIVITY_TYPE_UNDEFINED);
+ mCaptionWindowDecorViewModel.onTaskChanging(taskInfo, surfaceControl, startT, finishT);
+ verify(mCaptionWindowDecoration).close();
+ }
+
+ @Test
+ public void testCreateCaptionOnChangeTransitionWhenNecessary() throws Exception {
+ final int taskId = 1;
+ final ActivityManager.RunningTaskInfo taskInfo =
+ createTaskInfo(taskId, WINDOWING_MODE_UNDEFINED);
+ SurfaceControl surfaceControl = mock(SurfaceControl.class);
+ final SurfaceControl.Transaction startT = mock(SurfaceControl.Transaction.class);
+ final SurfaceControl.Transaction finishT = mock(SurfaceControl.Transaction.class);
+ taskInfo.configuration.windowConfiguration.setActivityType(ACTIVITY_TYPE_UNDEFINED);
+
+ mCaptionWindowDecorViewModel.onTaskChanging(taskInfo, surfaceControl, startT, finishT);
+
+ verify(mCaptionWindowDecorFactory, never())
+ .create(
+ mContext,
+ mDisplayController,
+ mTaskOrganizer,
+ taskInfo,
+ surfaceControl,
+ mMainHandler,
+ mMainChoreographer,
+ mSyncQueue);
+
+ taskInfo.configuration.windowConfiguration.setWindowingMode(WINDOWING_MODE_FREEFORM);
+ taskInfo.configuration.windowConfiguration.setActivityType(ACTIVITY_TYPE_STANDARD);
+
+ mCaptionWindowDecorViewModel.onTaskChanging(taskInfo, surfaceControl, startT, finishT);
+
+ verify(mCaptionWindowDecorFactory)
+ .create(
+ mContext,
+ mDisplayController,
+ mTaskOrganizer,
+ taskInfo,
+ surfaceControl,
+ mMainHandler,
+ mMainChoreographer,
+ mSyncQueue);
+ }
+
+ private static ActivityManager.RunningTaskInfo createTaskInfo(int taskId, int windowingMode) {
+ ActivityManager.RunningTaskInfo taskInfo =
+ new TestRunningTaskInfoBuilder()
+ .setDisplayId(Display.DEFAULT_DISPLAY)
+ .setVisible(true)
+ .build();
+ taskInfo.taskId = taskId;
+ taskInfo.configuration.windowConfiguration.setWindowingMode(windowingMode);
+ return taskInfo;
+ }
+
+ private static class MockObjectSupplier<T> implements Supplier<T> {
+ private final List<T> mObjects;
+ private final Supplier<T> mDefaultSupplier;
+ private int mNumOfCalls = 0;
+
+ private MockObjectSupplier(List<T> objects, Supplier<T> defaultSupplier) {
+ mObjects = objects;
+ mDefaultSupplier = defaultSupplier;
+ }
+
+ @Override
+ public T get() {
+ final T mock =
+ mNumOfCalls < mObjects.size() ? mObjects.get(mNumOfCalls)
+ : mDefaultSupplier.get();
+ ++mNumOfCalls;
+ return mock;
+ }
+ }
+}
diff --git a/libs/androidfw/ApkAssets.cpp b/libs/androidfw/ApkAssets.cpp
index 9aa37872b8de..15aaae25f754 100755..100644
--- a/libs/androidfw/ApkAssets.cpp
+++ b/libs/androidfw/ApkAssets.cpp
@@ -18,6 +18,7 @@
#include "android-base/errors.h"
#include "android-base/logging.h"
+#include "android-base/utf8.h"
namespace android {
@@ -83,15 +84,16 @@ std::unique_ptr<ApkAssets> ApkAssets::LoadOverlay(const std::string& idmap_path,
return {};
}
+ std::string overlay_path(loaded_idmap->OverlayApkPath());
+ auto fd = unique_fd(base::utf8::open(overlay_path.c_str(), O_RDONLY | O_CLOEXEC));
std::unique_ptr<AssetsProvider> overlay_assets;
- const std::string overlay_path(loaded_idmap->OverlayApkPath());
- if (IsFabricatedOverlay(overlay_path)) {
+ if (IsFabricatedOverlay(fd)) {
// Fabricated overlays do not contain resource definitions. All of the overlay resource values
// are defined inline in the idmap.
- overlay_assets = EmptyAssetsProvider::Create(overlay_path);
+ overlay_assets = EmptyAssetsProvider::Create(std::move(overlay_path));
} else {
// The overlay should be an APK.
- overlay_assets = ZipAssetsProvider::Create(overlay_path, flags);
+ overlay_assets = ZipAssetsProvider::Create(std::move(overlay_path), flags, std::move(fd));
}
if (overlay_assets == nullptr) {
return {};
diff --git a/libs/androidfw/AssetManager2.cpp b/libs/androidfw/AssetManager2.cpp
index 3fa369d7ff4a..68f5e4a88c7e 100644
--- a/libs/androidfw/AssetManager2.cpp
+++ b/libs/androidfw/AssetManager2.cpp
@@ -22,6 +22,7 @@
#include <iterator>
#include <map>
#include <set>
+#include <span>
#include "android-base/logging.h"
#include "android-base/stringprintf.h"
@@ -111,7 +112,7 @@ void AssetManager2::BuildDynamicRefTable() {
// A mapping from path of apk assets that could be target packages of overlays to the runtime
// package id of its first loaded package. Overlays currently can only override resources in the
// first package in the target resource table.
- std::unordered_map<std::string, uint8_t> target_assets_package_ids;
+ std::unordered_map<std::string_view, uint8_t> target_assets_package_ids;
// Overlay resources are not directly referenced by an application so their resource ids
// can change throughout the application's lifetime. Assign overlay package ids last.
@@ -134,7 +135,7 @@ void AssetManager2::BuildDynamicRefTable() {
if (auto loaded_idmap = apk_assets->GetLoadedIdmap(); loaded_idmap != nullptr) {
// The target package must precede the overlay package in the apk assets paths in order
// to take effect.
- auto iter = target_assets_package_ids.find(std::string(loaded_idmap->TargetApkPath()));
+ auto iter = target_assets_package_ids.find(loaded_idmap->TargetApkPath());
if (iter == target_assets_package_ids.end()) {
LOG(INFO) << "failed to find target package for overlay "
<< loaded_idmap->OverlayApkPath();
@@ -179,7 +180,7 @@ void AssetManager2::BuildDynamicRefTable() {
if (overlay_ref_table != nullptr) {
// If this package is from an overlay, use a dynamic reference table that can rewrite
// overlay resource ids to their corresponding target resource ids.
- new_group.dynamic_ref_table = overlay_ref_table;
+ new_group.dynamic_ref_table = std::move(overlay_ref_table);
}
DynamicRefTable* ref_table = new_group.dynamic_ref_table.get();
@@ -187,9 +188,9 @@ void AssetManager2::BuildDynamicRefTable() {
ref_table->mAppAsLib = package->IsDynamic() && package->GetPackageId() == 0x7f;
}
- // Add the package and to the set of packages with the same ID.
+ // Add the package to the set of packages with the same ID.
PackageGroup* package_group = &package_groups_[idx];
- package_group->packages_.push_back(ConfiguredPackage{package.get(), {}});
+ package_group->packages_.emplace_back().loaded_package_ = package.get();
package_group->cookies_.push_back(apk_assets_cookies[apk_assets]);
// Add the package name -> build time ID mappings.
@@ -201,30 +202,39 @@ void AssetManager2::BuildDynamicRefTable() {
if (auto apk_assets_path = apk_assets->GetPath()) {
// Overlay target ApkAssets must have been created using path based load apis.
- target_assets_package_ids.insert(std::make_pair(std::string(*apk_assets_path), package_id));
+ target_assets_package_ids.emplace(*apk_assets_path, package_id);
}
}
}
// Now assign the runtime IDs so that we have a build-time to runtime ID map.
- const auto package_groups_end = package_groups_.end();
- for (auto iter = package_groups_.begin(); iter != package_groups_end; ++iter) {
- const std::string& package_name = iter->packages_[0].loaded_package_->GetPackageName();
- for (auto iter2 = package_groups_.begin(); iter2 != package_groups_end; ++iter2) {
- iter2->dynamic_ref_table->addMapping(String16(package_name.c_str(), package_name.size()),
- iter->dynamic_ref_table->mAssignedPackageId);
-
- // Add the alias resources to the dynamic reference table of every package group. Since
- // staging aliases can only be defined by the framework package (which is not a shared
- // library), the compile-time package id of the framework is the same across all packages
- // that compile against the framework.
- for (const auto& package : iter->packages_) {
- for (const auto& entry : package.loaded_package_->GetAliasResourceIdMap()) {
- iter2->dynamic_ref_table->addAlias(entry.first, entry.second);
- }
- }
+ DynamicRefTable::AliasMap aliases;
+ for (const auto& group : package_groups_) {
+ const std::string& package_name = group.packages_[0].loaded_package_->GetPackageName();
+ const auto name_16 = String16(package_name.c_str(), package_name.size());
+ for (auto&& inner_group : package_groups_) {
+ inner_group.dynamic_ref_table->addMapping(name_16,
+ group.dynamic_ref_table->mAssignedPackageId);
+ }
+
+ for (const auto& package : group.packages_) {
+ const auto& package_aliases = package.loaded_package_->GetAliasResourceIdMap();
+ aliases.insert(aliases.end(), package_aliases.begin(), package_aliases.end());
}
}
+
+ if (!aliases.empty()) {
+ std::sort(aliases.begin(), aliases.end(), [](auto&& l, auto&& r) { return l.first < r.first; });
+
+ // Add the alias resources to the dynamic reference table of every package group. Since
+ // staging aliases can only be defined by the framework package (which is not a shared
+ // library), the compile-time package id of the framework is the same across all packages
+ // that compile against the framework.
+ for (auto& group : std::span(package_groups_.data(), package_groups_.size() - 1)) {
+ group.dynamic_ref_table->setAliases(aliases);
+ }
+ package_groups_.back().dynamic_ref_table->setAliases(std::move(aliases));
+ }
}
void AssetManager2::DumpToLog() const {
@@ -317,7 +327,7 @@ const std::unordered_map<std::string, std::string>*
return &loaded_package->GetOverlayableMap();
}
-bool AssetManager2::GetOverlayablesToString(const android::StringPiece& package_name,
+bool AssetManager2::GetOverlayablesToString(android::StringPiece package_name,
std::string* out) const {
uint8_t package_id = 0U;
for (const auto& apk_assets : apk_assets_) {
@@ -364,7 +374,7 @@ bool AssetManager2::GetOverlayablesToString(const android::StringPiece& package_
const std::string name = ToFormattedResourceString(*res_name);
output.append(base::StringPrintf(
"resource='%s' overlayable='%s' actor='%s' policy='0x%08x'\n",
- name.c_str(), info->name.c_str(), info->actor.c_str(), info->policy_flags));
+ name.c_str(), info->name.data(), info->actor.data(), info->policy_flags));
}
}
}
@@ -492,7 +502,7 @@ std::unique_ptr<AssetDir> AssetManager2::OpenDir(const std::string& dirname) con
continue;
}
- auto func = [&](const StringPiece& name, FileType type) {
+ auto func = [&](StringPiece name, FileType type) {
AssetDir::FileInfo info;
info.setFileName(String8(name.data(), name.size()));
info.setFileType(type);
@@ -1271,7 +1281,7 @@ base::expected<const ResolvedBag*, NullOrIOError> AssetManager2::GetBag(
return result;
}
-static bool Utf8ToUtf16(const StringPiece& str, std::u16string* out) {
+static bool Utf8ToUtf16(StringPiece str, std::u16string* out) {
ssize_t len =
utf8_to_utf16_length(reinterpret_cast<const uint8_t*>(str.data()), str.size(), false);
if (len < 0) {
@@ -1346,22 +1356,22 @@ base::expected<uint32_t, NullOrIOError> AssetManager2::GetResourceId(
void AssetManager2::RebuildFilterList() {
for (PackageGroup& group : package_groups_) {
- for (ConfiguredPackage& impl : group.packages_) {
- // Destroy it.
- impl.filtered_configs_.~ByteBucketArray();
-
- // Re-create it.
- new (&impl.filtered_configs_) ByteBucketArray<FilteredConfigGroup>();
-
+ for (ConfiguredPackage& package : group.packages_) {
+ package.filtered_configs_.forEachItem([](auto, auto& fcg) { fcg.type_entries.clear(); });
// Create the filters here.
- impl.loaded_package_->ForEachTypeSpec([&](const TypeSpec& type_spec, uint8_t type_id) {
- FilteredConfigGroup& group = impl.filtered_configs_.editItemAt(type_id - 1);
+ package.loaded_package_->ForEachTypeSpec([&](const TypeSpec& type_spec, uint8_t type_id) {
+ FilteredConfigGroup* group = nullptr;
for (const auto& type_entry : type_spec.type_entries) {
if (type_entry.config.match(configuration_)) {
- group.type_entries.push_back(&type_entry);
+ if (!group) {
+ group = &package.filtered_configs_.editItemAt(type_id - 1);
+ }
+ group->type_entries.push_back(&type_entry);
}
}
});
+ package.filtered_configs_.trimBuckets(
+ [](const auto& fcg) { return fcg.type_entries.empty(); });
}
}
}
@@ -1402,30 +1412,34 @@ uint8_t AssetManager2::GetAssignedPackageId(const LoadedPackage* package) const
std::unique_ptr<Theme> AssetManager2::NewTheme() {
constexpr size_t kInitialReserveSize = 32;
auto theme = std::unique_ptr<Theme>(new Theme(this));
+ theme->keys_.reserve(kInitialReserveSize);
theme->entries_.reserve(kInitialReserveSize);
return theme;
}
+void AssetManager2::ForEachPackage(base::function_ref<bool(const std::string&, uint8_t)> func,
+ package_property_t excluded_property_flags) const {
+ for (const PackageGroup& package_group : package_groups_) {
+ const auto loaded_package = package_group.packages_.front().loaded_package_;
+ if ((loaded_package->GetPropertyFlags() & excluded_property_flags) == 0U
+ && !func(loaded_package->GetPackageName(),
+ package_group.dynamic_ref_table->mAssignedPackageId)) {
+ return;
+ }
+ }
+}
+
Theme::Theme(AssetManager2* asset_manager) : asset_manager_(asset_manager) {
}
Theme::~Theme() = default;
struct Theme::Entry {
- uint32_t attr_res_id;
ApkAssetsCookie cookie;
uint32_t type_spec_flags;
Res_value value;
};
-namespace {
-struct ThemeEntryKeyComparer {
- bool operator() (const Theme::Entry& entry, uint32_t attr_res_id) const noexcept {
- return entry.attr_res_id < attr_res_id;
- }
-};
-} // namespace
-
base::expected<std::monostate, NullOrIOError> Theme::ApplyStyle(uint32_t resid, bool force) {
ATRACE_NAME("Theme::ApplyStyle");
@@ -1454,18 +1468,20 @@ base::expected<std::monostate, NullOrIOError> Theme::ApplyStyle(uint32_t resid,
continue;
}
- auto entry_it = std::lower_bound(entries_.begin(), entries_.end(), attr_res_id,
- ThemeEntryKeyComparer{});
- if (entry_it != entries_.end() && entry_it->attr_res_id == attr_res_id) {
+ const auto key_it = std::lower_bound(keys_.begin(), keys_.end(), attr_res_id);
+ const auto entry_it = entries_.begin() + (key_it - keys_.begin());
+ if (key_it != keys_.end() && *key_it == attr_res_id) {
if (is_undefined) {
// DATA_NULL_UNDEFINED clears the value of the attribute in the theme only when `force` is
- /// true.
+ // true.
+ keys_.erase(key_it);
entries_.erase(entry_it);
} else if (force) {
- *entry_it = Entry{attr_res_id, it->cookie, (*bag)->type_spec_flags, it->value};
+ *entry_it = Entry{it->cookie, (*bag)->type_spec_flags, it->value};
}
} else {
- entries_.insert(entry_it, Entry{attr_res_id, it->cookie, (*bag)->type_spec_flags, it->value});
+ keys_.insert(key_it, attr_res_id);
+ entries_.insert(entry_it, Entry{it->cookie, (*bag)->type_spec_flags, it->value});
}
}
return {};
@@ -1476,6 +1492,7 @@ void Theme::Rebase(AssetManager2* am, const uint32_t* style_ids, const uint8_t*
ATRACE_NAME("Theme::Rebase");
// Reset the entries without changing the vector capacity to prevent reallocations during
// ApplyStyle.
+ keys_.clear();
entries_.clear();
asset_manager_ = am;
for (size_t i = 0; i < style_count; i++) {
@@ -1484,16 +1501,14 @@ void Theme::Rebase(AssetManager2* am, const uint32_t* style_ids, const uint8_t*
}
std::optional<AssetManager2::SelectedValue> Theme::GetAttribute(uint32_t resid) const {
-
constexpr const uint32_t kMaxIterations = 20;
uint32_t type_spec_flags = 0u;
for (uint32_t i = 0; i <= kMaxIterations; i++) {
- auto entry_it = std::lower_bound(entries_.begin(), entries_.end(), resid,
- ThemeEntryKeyComparer{});
- if (entry_it == entries_.end() || entry_it->attr_res_id != resid) {
+ const auto key_it = std::lower_bound(keys_.begin(), keys_.end(), resid);
+ if (key_it == keys_.end() || *key_it != resid) {
return std::nullopt;
}
-
+ const auto entry_it = entries_.begin() + (key_it - keys_.begin());
type_spec_flags |= entry_it->type_spec_flags;
if (entry_it->value.dataType == Res_value::TYPE_ATTRIBUTE) {
resid = entry_it->value.data;
@@ -1527,6 +1542,7 @@ base::expected<std::monostate, NullOrIOError> Theme::ResolveAttributeReference(
}
void Theme::Clear() {
+ keys_.clear();
entries_.clear();
}
@@ -1538,18 +1554,19 @@ base::expected<std::monostate, IOError> Theme::SetTo(const Theme& source) {
type_spec_flags_ = source.type_spec_flags_;
if (asset_manager_ == source.asset_manager_) {
+ keys_ = source.keys_;
entries_ = source.entries_;
} else {
- std::map<ApkAssetsCookie, ApkAssetsCookie> src_to_dest_asset_cookies;
- typedef std::map<int, int> SourceToDestinationRuntimePackageMap;
- std::map<ApkAssetsCookie, SourceToDestinationRuntimePackageMap> src_asset_cookie_id_map;
+ std::unordered_map<ApkAssetsCookie, ApkAssetsCookie> src_to_dest_asset_cookies;
+ using SourceToDestinationRuntimePackageMap = std::unordered_map<int, int>;
+ std::unordered_map<ApkAssetsCookie, SourceToDestinationRuntimePackageMap> src_asset_cookie_id_map;
// Determine which ApkAssets are loaded in both theme AssetManagers.
- const auto src_assets = source.asset_manager_->GetApkAssets();
+ const auto& src_assets = source.asset_manager_->GetApkAssets();
for (size_t i = 0; i < src_assets.size(); i++) {
const ApkAssets* src_asset = src_assets[i];
- const auto dest_assets = asset_manager_->GetApkAssets();
+ const auto& dest_assets = asset_manager_->GetApkAssets();
for (size_t j = 0; j < dest_assets.size(); j++) {
const ApkAssets* dest_asset = dest_assets[j];
if (src_asset != dest_asset) {
@@ -1570,15 +1587,17 @@ base::expected<std::monostate, IOError> Theme::SetTo(const Theme& source) {
}
src_to_dest_asset_cookies.insert(std::make_pair(i, j));
- src_asset_cookie_id_map.insert(std::make_pair(i, package_map));
+ src_asset_cookie_id_map.insert(std::make_pair(i, std::move(package_map)));
break;
}
}
// Reset the data in the destination theme.
+ keys_.clear();
entries_.clear();
- for (const auto& entry : source.entries_) {
+ for (size_t i = 0, size = source.entries_.size(); i != size; ++i) {
+ const auto& entry = source.entries_[i];
bool is_reference = (entry.value.dataType == Res_value::TYPE_ATTRIBUTE
|| entry.value.dataType == Res_value::TYPE_REFERENCE
|| entry.value.dataType == Res_value::TYPE_DYNAMIC_ATTRIBUTE
@@ -1618,13 +1637,15 @@ base::expected<std::monostate, IOError> Theme::SetTo(const Theme& source) {
}
}
+ const auto source_res_id = source.keys_[i];
+
// The package id of the attribute needs to be rewritten to the package id of the
// attribute in the destination.
- int attribute_dest_package_id = get_package_id(entry.attr_res_id);
+ int attribute_dest_package_id = get_package_id(source_res_id);
if (attribute_dest_package_id != 0x01) {
// Find the cookie of the attribute resource id in the source AssetManager
base::expected<FindEntryResult, NullOrIOError> attribute_entry_result =
- source.asset_manager_->FindEntry(entry.attr_res_id, 0 /* density_override */ ,
+ source.asset_manager_->FindEntry(source_res_id, 0 /* density_override */ ,
true /* stop_at_first_match */,
true /* ignore_configuration */);
if (UNLIKELY(IsIOError(attribute_entry_result))) {
@@ -1648,16 +1669,15 @@ base::expected<std::monostate, IOError> Theme::SetTo(const Theme& source) {
attribute_dest_package_id = attribute_dest_package->second;
}
- auto dest_attr_id = make_resid(attribute_dest_package_id, get_type_id(entry.attr_res_id),
- get_entry_id(entry.attr_res_id));
- Theme::Entry new_entry{dest_attr_id, data_dest_cookie, entry.type_spec_flags,
- Res_value{.dataType = entry.value.dataType,
- .data = attribute_data}};
-
+ auto dest_attr_id = make_resid(attribute_dest_package_id, get_type_id(source_res_id),
+ get_entry_id(source_res_id));
+ const auto key_it = std::lower_bound(keys_.begin(), keys_.end(), dest_attr_id);
+ const auto entry_it = entries_.begin() + (key_it - keys_.begin());
// Since the entries were cleared, the attribute resource id has yet been mapped to any value.
- auto entry_it = std::lower_bound(entries_.begin(), entries_.end(), dest_attr_id,
- ThemeEntryKeyComparer{});
- entries_.insert(entry_it, new_entry);
+ keys_.insert(key_it, dest_attr_id);
+ entries_.insert(entry_it, Entry{data_dest_cookie, entry.type_spec_flags,
+ Res_value{.dataType = entry.value.dataType,
+ .data = attribute_data}});
}
}
return {};
@@ -1665,9 +1685,11 @@ base::expected<std::monostate, IOError> Theme::SetTo(const Theme& source) {
void Theme::Dump() const {
LOG(INFO) << base::StringPrintf("Theme(this=%p, AssetManager2=%p)", this, asset_manager_);
- for (auto& entry : entries_) {
+ for (size_t i = 0, size = keys_.size(); i != size; ++i) {
+ auto res_id = keys_[i];
+ const auto& entry = entries_[i];
LOG(INFO) << base::StringPrintf(" entry(0x%08x)=(0x%08x) type=(0x%02x), cookie(%d)",
- entry.attr_res_id, entry.value.data, entry.value.dataType,
+ res_id, entry.value.data, entry.value.dataType,
entry.cookie);
}
}
diff --git a/libs/androidfw/AssetsProvider.cpp b/libs/androidfw/AssetsProvider.cpp
index bce34d37c90b..2d3c06506a1f 100644
--- a/libs/androidfw/AssetsProvider.cpp
+++ b/libs/androidfw/AssetsProvider.cpp
@@ -73,9 +73,6 @@ std::unique_ptr<Asset> AssetsProvider::CreateAssetFromFd(base::unique_fd fd,
(path != nullptr) ? base::unique_fd(-1) : std::move(fd));
}
-ZipAssetsProvider::PathOrDebugName::PathOrDebugName(std::string&& value, bool is_path)
- : value_(std::forward<std::string>(value)), is_path_(is_path) {}
-
const std::string* ZipAssetsProvider::PathOrDebugName::GetPath() const {
return is_path_ ? &value_ : nullptr;
}
@@ -84,34 +81,42 @@ const std::string& ZipAssetsProvider::PathOrDebugName::GetDebugName() const {
return value_;
}
+void ZipAssetsProvider::ZipCloser::operator()(ZipArchive* a) const {
+ ::CloseArchive(a);
+}
+
ZipAssetsProvider::ZipAssetsProvider(ZipArchiveHandle handle, PathOrDebugName&& path,
package_property_t flags, time_t last_mod_time)
- : zip_handle_(handle, ::CloseArchive),
- name_(std::forward<PathOrDebugName>(path)),
+ : zip_handle_(handle),
+ name_(std::move(path)),
flags_(flags),
last_mod_time_(last_mod_time) {}
std::unique_ptr<ZipAssetsProvider> ZipAssetsProvider::Create(std::string path,
- package_property_t flags) {
+ package_property_t flags,
+ base::unique_fd fd) {
+ const auto released_fd = fd.ok() ? fd.release() : -1;
ZipArchiveHandle handle;
- if (int32_t result = OpenArchive(path.c_str(), &handle); result != 0) {
+ if (int32_t result = released_fd < 0 ? OpenArchive(path.c_str(), &handle)
+ : OpenArchiveFd(released_fd, path.c_str(), &handle)) {
LOG(ERROR) << "Failed to open APK '" << path << "': " << ::ErrorCodeString(result);
CloseArchive(handle);
return {};
}
struct stat sb{.st_mtime = -1};
- if (stat(path.c_str(), &sb) < 0) {
- // Stat requires execute permissions on all directories path to the file. If the process does
- // not have execute permissions on this file, allow the zip to be opened but IsUpToDate() will
- // always have to return true.
- LOG(WARNING) << "Failed to stat file '" << path << "': "
- << base::SystemErrorCodeToString(errno);
+ // Skip all up-to-date checks if the file won't ever change.
+ if (!isReadonlyFilesystem(path.c_str())) {
+ if ((released_fd < 0 ? stat(path.c_str(), &sb) : fstat(released_fd, &sb)) < 0) {
+ // Stat requires execute permissions on all directories path to the file. If the process does
+ // not have execute permissions on this file, allow the zip to be opened but IsUpToDate() will
+ // always have to return true.
+ PLOG(WARNING) << "Failed to stat file '" << path << "'";
+ }
}
return std::unique_ptr<ZipAssetsProvider>(
- new ZipAssetsProvider(handle, PathOrDebugName{std::move(path),
- true /* is_path */}, flags, sb.st_mtime));
+ new ZipAssetsProvider(handle, PathOrDebugName::Path(std::move(path)), flags, sb.st_mtime));
}
std::unique_ptr<ZipAssetsProvider> ZipAssetsProvider::Create(base::unique_fd fd,
@@ -133,17 +138,19 @@ std::unique_ptr<ZipAssetsProvider> ZipAssetsProvider::Create(base::unique_fd fd,
}
struct stat sb{.st_mtime = -1};
- if (fstat(released_fd, &sb) < 0) {
- // Stat requires execute permissions on all directories path to the file. If the process does
- // not have execute permissions on this file, allow the zip to be opened but IsUpToDate() will
- // always have to return true.
- LOG(WARNING) << "Failed to fstat file '" << friendly_name << "': "
- << base::SystemErrorCodeToString(errno);
+ // Skip all up-to-date checks if the file won't ever change.
+ if (!isReadonlyFilesystem(released_fd)) {
+ if (fstat(released_fd, &sb) < 0) {
+ // Stat requires execute permissions on all directories path to the file. If the process does
+ // not have execute permissions on this file, allow the zip to be opened but IsUpToDate() will
+ // always have to return true.
+ LOG(WARNING) << "Failed to fstat file '" << friendly_name
+ << "': " << base::SystemErrorCodeToString(errno);
+ }
}
- return std::unique_ptr<ZipAssetsProvider>(
- new ZipAssetsProvider(handle, PathOrDebugName{std::move(friendly_name),
- false /* is_path */}, flags, sb.st_mtime));
+ return std::unique_ptr<ZipAssetsProvider>(new ZipAssetsProvider(
+ handle, PathOrDebugName::DebugName(std::move(friendly_name)), flags, sb.st_mtime));
}
std::unique_ptr<Asset> ZipAssetsProvider::OpenInternal(const std::string& path,
@@ -210,9 +217,9 @@ std::unique_ptr<Asset> ZipAssetsProvider::OpenInternal(const std::string& path,
return asset;
}
-bool ZipAssetsProvider::ForEachFile(const std::string& root_path,
- const std::function<void(const StringPiece&, FileType)>& f)
- const {
+bool ZipAssetsProvider::ForEachFile(
+ const std::string& root_path,
+ base::function_ref<void(StringPiece, FileType)> f) const {
std::string root_path_full = root_path;
if (root_path_full.back() != '/') {
root_path_full += '/';
@@ -238,8 +245,7 @@ bool ZipAssetsProvider::ForEachFile(const std::string& root_path,
if (!leaf_file_path.empty()) {
auto iter = std::find(leaf_file_path.begin(), leaf_file_path.end(), '/');
if (iter != leaf_file_path.end()) {
- std::string dir =
- leaf_file_path.substr(0, std::distance(leaf_file_path.begin(), iter)).to_string();
+ std::string dir(leaf_file_path.substr(0, std::distance(leaf_file_path.begin(), iter)));
dirs.insert(std::move(dir));
} else {
f(leaf_file_path, kFileTypeRegular);
@@ -277,6 +283,9 @@ const std::string& ZipAssetsProvider::GetDebugName() const {
}
bool ZipAssetsProvider::IsUpToDate() const {
+ if (last_mod_time_ == -1) {
+ return true;
+ }
struct stat sb{};
if (fstat(GetFileDescriptor(zip_handle_.get()), &sb) < 0) {
// If fstat fails on the zip archive, return true so the zip archive the resource system does
@@ -287,10 +296,10 @@ bool ZipAssetsProvider::IsUpToDate() const {
}
DirectoryAssetsProvider::DirectoryAssetsProvider(std::string&& path, time_t last_mod_time)
- : dir_(std::forward<std::string>(path)), last_mod_time_(last_mod_time) {}
+ : dir_(std::move(path)), last_mod_time_(last_mod_time) {}
std::unique_ptr<DirectoryAssetsProvider> DirectoryAssetsProvider::Create(std::string path) {
- struct stat sb{};
+ struct stat sb;
const int result = stat(path.c_str(), &sb);
if (result == -1) {
LOG(ERROR) << "Failed to find directory '" << path << "'.";
@@ -302,12 +311,13 @@ std::unique_ptr<DirectoryAssetsProvider> DirectoryAssetsProvider::Create(std::st
return nullptr;
}
- if (path[path.size() - 1] != OS_PATH_SEPARATOR) {
+ if (path.back() != OS_PATH_SEPARATOR) {
path += OS_PATH_SEPARATOR;
}
- return std::unique_ptr<DirectoryAssetsProvider>(new DirectoryAssetsProvider(std::move(path),
- sb.st_mtime));
+ const bool isReadonly = isReadonlyFilesystem(path.c_str());
+ return std::unique_ptr<DirectoryAssetsProvider>(
+ new DirectoryAssetsProvider(std::move(path), isReadonly ? -1 : sb.st_mtime));
}
std::unique_ptr<Asset> DirectoryAssetsProvider::OpenInternal(const std::string& path,
@@ -324,8 +334,7 @@ std::unique_ptr<Asset> DirectoryAssetsProvider::OpenInternal(const std::string&
bool DirectoryAssetsProvider::ForEachFile(
const std::string& /* root_path */,
- const std::function<void(const StringPiece&, FileType)>& /* f */)
- const {
+ base::function_ref<void(StringPiece, FileType)> /* f */) const {
return true;
}
@@ -338,7 +347,10 @@ const std::string& DirectoryAssetsProvider::GetDebugName() const {
}
bool DirectoryAssetsProvider::IsUpToDate() const {
- struct stat sb{};
+ if (last_mod_time_ == -1) {
+ return true;
+ }
+ struct stat sb;
if (stat(dir_.c_str(), &sb) < 0) {
// If stat fails on the zip archive, return true so the zip archive the resource system does
// attempt to refresh the ApkAsset.
@@ -349,8 +361,7 @@ bool DirectoryAssetsProvider::IsUpToDate() const {
MultiAssetsProvider::MultiAssetsProvider(std::unique_ptr<AssetsProvider>&& primary,
std::unique_ptr<AssetsProvider>&& secondary)
- : primary_(std::forward<std::unique_ptr<AssetsProvider>>(primary)),
- secondary_(std::forward<std::unique_ptr<AssetsProvider>>(secondary)) {
+ : primary_(std::move(primary)), secondary_(std::move(secondary)) {
debug_name_ = primary_->GetDebugName() + " and " + secondary_->GetDebugName();
path_ = (primary_->GetDebugName() != kEmptyDebugString) ? primary_->GetPath()
: secondary_->GetPath();
@@ -372,9 +383,9 @@ std::unique_ptr<Asset> MultiAssetsProvider::OpenInternal(const std::string& path
return (asset) ? std::move(asset) : secondary_->Open(path, mode, file_exists);
}
-bool MultiAssetsProvider::ForEachFile(const std::string& root_path,
- const std::function<void(const StringPiece&, FileType)>& f)
- const {
+bool MultiAssetsProvider::ForEachFile(
+ const std::string& root_path,
+ base::function_ref<void(StringPiece, FileType)> f) const {
return primary_->ForEachFile(root_path, f) && secondary_->ForEachFile(root_path, f);
}
@@ -397,8 +408,8 @@ std::unique_ptr<AssetsProvider> EmptyAssetsProvider::Create() {
return std::unique_ptr<EmptyAssetsProvider>(new EmptyAssetsProvider({}));
}
-std::unique_ptr<AssetsProvider> EmptyAssetsProvider::Create(const std::string& path) {
- return std::unique_ptr<EmptyAssetsProvider>(new EmptyAssetsProvider(path));
+std::unique_ptr<AssetsProvider> EmptyAssetsProvider::Create(std::string path) {
+ return std::unique_ptr<EmptyAssetsProvider>(new EmptyAssetsProvider(std::move(path)));
}
std::unique_ptr<Asset> EmptyAssetsProvider::OpenInternal(const std::string& /* path */,
@@ -412,7 +423,7 @@ std::unique_ptr<Asset> EmptyAssetsProvider::OpenInternal(const std::string& /* p
bool EmptyAssetsProvider::ForEachFile(
const std::string& /* root_path */,
- const std::function<void(const StringPiece&, FileType)>& /* f */) const {
+ base::function_ref<void(StringPiece, FileType)> /* f */) const {
return true;
}
@@ -435,4 +446,4 @@ bool EmptyAssetsProvider::IsUpToDate() const {
return true;
}
-} // namespace android \ No newline at end of file
+} // namespace android
diff --git a/libs/androidfw/ConfigDescription.cpp b/libs/androidfw/ConfigDescription.cpp
index 19ead9583eb2..93a7d173cb97 100644
--- a/libs/androidfw/ConfigDescription.cpp
+++ b/libs/androidfw/ConfigDescription.cpp
@@ -637,7 +637,7 @@ static bool parseVersion(const char* name, ResTable_config* out) {
return true;
}
-bool ConfigDescription::Parse(const StringPiece& str, ConfigDescription* out) {
+bool ConfigDescription::Parse(StringPiece str, ConfigDescription* out) {
std::vector<std::string> parts = util::SplitAndLowercase(str, '-');
ConfigDescription config;
diff --git a/libs/androidfw/Idmap.cpp b/libs/androidfw/Idmap.cpp
index e122d4844ee9..89835742c8ff 100644
--- a/libs/androidfw/Idmap.cpp
+++ b/libs/androidfw/Idmap.cpp
@@ -201,7 +201,7 @@ IdmapResMap::Result IdmapResMap::Lookup(uint32_t target_res_id) const {
const auto& config = configurations_[value.config_index];
values_map[config] = value.value;
}
- return Result(values_map);
+ return Result(std::move(values_map));
}
return {};
}
@@ -250,8 +250,7 @@ std::optional<std::string_view> ReadString(const uint8_t** in_out_data_ptr, size
}
} // namespace
-LoadedIdmap::LoadedIdmap(std::string&& idmap_path,
- const Idmap_header* header,
+LoadedIdmap::LoadedIdmap(std::string&& idmap_path, const Idmap_header* header,
const Idmap_data_header* data_header,
const Idmap_target_entry* target_entries,
const Idmap_target_entry_inline* target_inline_entries,
@@ -259,23 +258,21 @@ LoadedIdmap::LoadedIdmap(std::string&& idmap_path,
const ConfigDescription* configs,
const Idmap_overlay_entry* overlay_entries,
std::unique_ptr<ResStringPool>&& string_pool,
- std::string_view overlay_apk_path,
- std::string_view target_apk_path)
- : header_(header),
- data_header_(data_header),
- target_entries_(target_entries),
- target_inline_entries_(target_inline_entries),
- inline_entry_values_(inline_entry_values),
- configurations_(configs),
- overlay_entries_(overlay_entries),
- string_pool_(std::move(string_pool)),
- idmap_path_(std::move(idmap_path)),
- overlay_apk_path_(overlay_apk_path),
- target_apk_path_(target_apk_path),
- idmap_last_mod_time_(getFileModDate(idmap_path_.data())) {}
-
-std::unique_ptr<LoadedIdmap> LoadedIdmap::Load(const StringPiece& idmap_path,
- const StringPiece& idmap_data) {
+ std::string_view overlay_apk_path, std::string_view target_apk_path)
+ : header_(header),
+ data_header_(data_header),
+ target_entries_(target_entries),
+ target_inline_entries_(target_inline_entries),
+ inline_entry_values_(inline_entry_values),
+ configurations_(configs),
+ overlay_entries_(overlay_entries),
+ string_pool_(std::move(string_pool)),
+ idmap_path_(std::move(idmap_path)),
+ overlay_apk_path_(overlay_apk_path),
+ target_apk_path_(target_apk_path),
+ idmap_last_mod_time_(getFileModDate(idmap_path_.data())) {}
+
+std::unique_ptr<LoadedIdmap> LoadedIdmap::Load(StringPiece idmap_path, StringPiece idmap_data) {
ATRACE_CALL();
size_t data_size = idmap_data.size();
auto data_ptr = reinterpret_cast<const uint8_t*>(idmap_data.data());
@@ -365,7 +362,7 @@ std::unique_ptr<LoadedIdmap> LoadedIdmap::Load(const StringPiece& idmap_path,
// Can't use make_unique because LoadedIdmap constructor is private.
return std::unique_ptr<LoadedIdmap>(
- new LoadedIdmap(idmap_path.to_string(), header, data_header, target_entries,
+ new LoadedIdmap(std::string(idmap_path), header, data_header, target_entries,
target_inline_entries, target_inline_entry_values, configurations,
overlay_entries, std::move(idmap_string_pool), *target_path, *overlay_path));
}
diff --git a/libs/androidfw/LoadedArsc.cpp b/libs/androidfw/LoadedArsc.cpp
index 386f718208b3..c0fdfe25da21 100644
--- a/libs/androidfw/LoadedArsc.cpp
+++ b/libs/androidfw/LoadedArsc.cpp
@@ -645,16 +645,16 @@ std::unique_ptr<const LoadedPackage> LoadedPackage::Load(const Chunk& chunk,
}
std::string name;
- util::ReadUtf16StringFromDevice(overlayable->name, arraysize(overlayable->name), &name);
+ util::ReadUtf16StringFromDevice(overlayable->name, std::size(overlayable->name), &name);
std::string actor;
- util::ReadUtf16StringFromDevice(overlayable->actor, arraysize(overlayable->actor), &actor);
-
- if (loaded_package->overlayable_map_.find(name) !=
- loaded_package->overlayable_map_.end()) {
- LOG(ERROR) << "Multiple <overlayable> blocks with the same name '" << name << "'.";
+ util::ReadUtf16StringFromDevice(overlayable->actor, std::size(overlayable->actor), &actor);
+ auto [name_to_actor_it, inserted] =
+ loaded_package->overlayable_map_.emplace(std::move(name), std::move(actor));
+ if (!inserted) {
+ LOG(ERROR) << "Multiple <overlayable> blocks with the same name '"
+ << name_to_actor_it->first << "'.";
return {};
}
- loaded_package->overlayable_map_.emplace(name, actor);
// Iterate over the overlayable policy chunks contained within the overlayable chunk data
ChunkIterator overlayable_iter(child_chunk.data_ptr(), child_chunk.data_size());
@@ -669,7 +669,6 @@ std::unique_ptr<const LoadedPackage> LoadedPackage::Load(const Chunk& chunk,
LOG(ERROR) << "RES_TABLE_OVERLAYABLE_POLICY_TYPE too small.";
return {};
}
-
if ((overlayable_child_chunk.data_size() / sizeof(ResTable_ref))
< dtohl(policy_header->entry_count)) {
LOG(ERROR) << "RES_TABLE_OVERLAYABLE_POLICY_TYPE too small to hold entries.";
@@ -691,8 +690,8 @@ std::unique_ptr<const LoadedPackage> LoadedPackage::Load(const Chunk& chunk,
// Add the pairing of overlayable properties and resource ids to the package
OverlayableInfo overlayable_info {
- .name = name,
- .actor = actor,
+ .name = name_to_actor_it->first,
+ .actor = name_to_actor_it->second,
.policy_flags = policy_header->policy_flags
};
loaded_package->overlayable_infos_.emplace_back(std::move(overlayable_info), std::move(ids));
@@ -736,6 +735,7 @@ std::unique_ptr<const LoadedPackage> LoadedPackage::Load(const Chunk& chunk,
const auto entry_end = entry_begin + dtohl(lib_alias->count);
std::unordered_set<uint32_t> finalized_ids;
finalized_ids.reserve(entry_end - entry_begin);
+ loaded_package->alias_id_map_.reserve(entry_end - entry_begin);
for (auto entry_iter = entry_begin; entry_iter != entry_end; ++entry_iter) {
if (!entry_iter) {
LOG(ERROR) << "NULL ResTable_staged_alias_entry record??";
@@ -749,13 +749,20 @@ std::unique_ptr<const LoadedPackage> LoadedPackage::Load(const Chunk& chunk,
}
auto staged_id = dtohl(entry_iter->stagedResId);
- auto [_, success] = loaded_package->alias_id_map_.emplace(staged_id, finalized_id);
- if (!success) {
+ loaded_package->alias_id_map_.emplace_back(staged_id, finalized_id);
+ }
+
+ std::sort(loaded_package->alias_id_map_.begin(), loaded_package->alias_id_map_.end(),
+ [](auto&& l, auto&& r) { return l.first < r.first; });
+ const auto duplicate_it =
+ std::adjacent_find(loaded_package->alias_id_map_.begin(),
+ loaded_package->alias_id_map_.end(),
+ [](auto&& l, auto&& r) { return l.first == r.first; });
+ if (duplicate_it != loaded_package->alias_id_map_.end()) {
LOG(ERROR) << StringPrintf("Repeated staged resource id '%08x' in staged aliases.",
- staged_id);
+ duplicate_it->first);
return {};
}
- }
} break;
default:
diff --git a/libs/androidfw/Locale.cpp b/libs/androidfw/Locale.cpp
index d87a3ce72177..272a988ec55a 100644
--- a/libs/androidfw/Locale.cpp
+++ b/libs/androidfw/Locale.cpp
@@ -66,7 +66,7 @@ static inline bool is_number(const std::string& str) {
return std::all_of(std::begin(str), std::end(str), ::isdigit);
}
-bool LocaleValue::InitFromFilterString(const StringPiece& str) {
+bool LocaleValue::InitFromFilterString(StringPiece str) {
// A locale (as specified in the filter) is an underscore separated name such
// as "en_US", "en_Latn_US", or "en_US_POSIX".
std::vector<std::string> parts = util::SplitAndLowercase(str, '_');
@@ -132,11 +132,11 @@ bool LocaleValue::InitFromFilterString(const StringPiece& str) {
return true;
}
-bool LocaleValue::InitFromBcp47Tag(const StringPiece& bcp47tag) {
+bool LocaleValue::InitFromBcp47Tag(StringPiece bcp47tag) {
return InitFromBcp47TagImpl(bcp47tag, '-');
}
-bool LocaleValue::InitFromBcp47TagImpl(const StringPiece& bcp47tag, const char separator) {
+bool LocaleValue::InitFromBcp47TagImpl(StringPiece bcp47tag, const char separator) {
std::vector<std::string> subtags = util::SplitAndLowercase(bcp47tag, separator);
if (subtags.size() == 1) {
set_language(subtags[0].c_str());
diff --git a/libs/androidfw/ResourceTypes.cpp b/libs/androidfw/ResourceTypes.cpp
index 267190a54195..31516dc58035 100644
--- a/libs/androidfw/ResourceTypes.cpp
+++ b/libs/androidfw/ResourceTypes.cpp
@@ -33,7 +33,9 @@
#include <type_traits>
#include <vector>
+#include <android-base/file.h>
#include <android-base/macros.h>
+#include <android-base/utf8.h>
#include <androidfw/ByteBucketArray.h>
#include <androidfw/ResourceTypes.h>
#include <androidfw/TypeWrappers.h>
@@ -236,12 +238,23 @@ void Res_png_9patch::serialize(const Res_png_9patch& patch, const int32_t* xDivs
}
bool IsFabricatedOverlay(const std::string& path) {
- std::ifstream fin(path);
+ return IsFabricatedOverlay(path.c_str());
+}
+
+bool IsFabricatedOverlay(const char* path) {
+ auto fd = base::unique_fd(base::utf8::open(path, O_RDONLY|O_CLOEXEC));
+ if (fd < 0) {
+ return false;
+ }
+ return IsFabricatedOverlay(fd);
+}
+
+bool IsFabricatedOverlay(base::borrowed_fd fd) {
uint32_t magic;
- if (fin.read(reinterpret_cast<char*>(&magic), sizeof(uint32_t))) {
- return magic == kFabricatedOverlayMagic;
+ if (!base::ReadFullyAtOffset(fd, &magic, sizeof(magic), 0)) {
+ return false;
}
- return false;
+ return magic == kFabricatedOverlayMagic;
}
static bool assertIdmapHeader(const void* idmap, size_t size) {
@@ -6988,11 +7001,10 @@ status_t ResTable::parsePackage(const ResTable_package* const pkg,
DynamicRefTable::DynamicRefTable() : DynamicRefTable(0, false) {}
DynamicRefTable::DynamicRefTable(uint8_t packageId, bool appAsLib)
- : mAssignedPackageId(packageId)
+ : mLookupTable()
+ , mAssignedPackageId(packageId)
, mAppAsLib(appAsLib)
{
- memset(mLookupTable, 0, sizeof(mLookupTable));
-
// Reserved package ids
mLookupTable[APP_PACKAGE_ID] = APP_PACKAGE_ID;
mLookupTable[SYS_PACKAGE_ID] = SYS_PACKAGE_ID;
@@ -7073,10 +7085,6 @@ void DynamicRefTable::addMapping(uint8_t buildPackageId, uint8_t runtimePackageI
mLookupTable[buildPackageId] = runtimePackageId;
}
-void DynamicRefTable::addAlias(uint32_t stagedId, uint32_t finalizedId) {
- mAliasId[stagedId] = finalizedId;
-}
-
status_t DynamicRefTable::lookupResourceId(uint32_t* resId) const {
uint32_t res = *resId;
size_t packageId = Res_GETPACKAGE(res) + 1;
@@ -7086,11 +7094,12 @@ status_t DynamicRefTable::lookupResourceId(uint32_t* resId) const {
return NO_ERROR;
}
- auto alias_id = mAliasId.find(res);
- if (alias_id != mAliasId.end()) {
+ const auto alias_it = std::lower_bound(mAliasId.begin(), mAliasId.end(), res,
+ [](const AliasMap::value_type& pair, uint32_t val) { return pair.first < val; });
+ if (alias_it != mAliasId.end() && alias_it->first == res) {
// Rewrite the resource id to its alias resource id. Since the alias resource id is a
// compile-time id, it still needs to be resolved further.
- res = alias_id->second;
+ res = alias_it->second;
}
if (packageId == SYS_PACKAGE_ID || (packageId == APP_PACKAGE_ID && !mAppAsLib)) {
diff --git a/libs/androidfw/ResourceUtils.cpp b/libs/androidfw/ResourceUtils.cpp
index 87fb2c038c9f..ccb61561578f 100644
--- a/libs/androidfw/ResourceUtils.cpp
+++ b/libs/androidfw/ResourceUtils.cpp
@@ -18,7 +18,7 @@
namespace android {
-bool ExtractResourceName(const StringPiece& str, StringPiece* out_package, StringPiece* out_type,
+bool ExtractResourceName(StringPiece str, StringPiece* out_package, StringPiece* out_type,
StringPiece* out_entry) {
*out_package = "";
*out_type = "";
@@ -33,16 +33,16 @@ bool ExtractResourceName(const StringPiece& str, StringPiece* out_package, Strin
while (current != end) {
if (out_type->size() == 0 && *current == '/') {
has_type_separator = true;
- out_type->assign(start, current - start);
+ *out_type = StringPiece(start, current - start);
start = current + 1;
} else if (out_package->size() == 0 && *current == ':') {
has_package_separator = true;
- out_package->assign(start, current - start);
+ *out_package = StringPiece(start, current - start);
start = current + 1;
}
current++;
}
- out_entry->assign(start, end - start);
+ *out_entry = StringPiece(start, end - start);
return !(has_package_separator && out_package->empty()) &&
!(has_type_separator && out_type->empty());
@@ -50,7 +50,7 @@ bool ExtractResourceName(const StringPiece& str, StringPiece* out_package, Strin
base::expected<AssetManager2::ResourceName, NullOrIOError> ToResourceName(
const StringPoolRef& type_string_ref, const StringPoolRef& entry_string_ref,
- const StringPiece& package_name) {
+ StringPiece package_name) {
AssetManager2::ResourceName name{
.package = package_name.data(),
.package_len = package_name.size(),
diff --git a/libs/androidfw/StringPool.cpp b/libs/androidfw/StringPool.cpp
index b59e906f6281..1cb8df311c89 100644
--- a/libs/androidfw/StringPool.cpp
+++ b/libs/androidfw/StringPool.cpp
@@ -161,16 +161,15 @@ const StringPool::Context& StringPool::StyleRef::GetContext() const {
return entry_->context;
}
-StringPool::Ref StringPool::MakeRef(const StringPiece& str) {
+StringPool::Ref StringPool::MakeRef(StringPiece str) {
return MakeRefImpl(str, Context{}, true);
}
-StringPool::Ref StringPool::MakeRef(const StringPiece& str, const Context& context) {
+StringPool::Ref StringPool::MakeRef(StringPiece str, const Context& context) {
return MakeRefImpl(str, context, true);
}
-StringPool::Ref StringPool::MakeRefImpl(const StringPiece& str, const Context& context,
- bool unique) {
+StringPool::Ref StringPool::MakeRefImpl(StringPiece str, const Context& context, bool unique) {
if (unique) {
auto range = indexed_strings_.equal_range(str);
for (auto iter = range.first; iter != range.second; ++iter) {
@@ -181,7 +180,7 @@ StringPool::Ref StringPool::MakeRefImpl(const StringPiece& str, const Context& c
}
std::unique_ptr<Entry> entry(new Entry());
- entry->value = str.to_string();
+ entry->value = std::string(str);
entry->context = context;
entry->index_ = strings_.size();
entry->ref_ = 0;
diff --git a/libs/androidfw/Util.cpp b/libs/androidfw/Util.cpp
index 52ad0dce8187..be55fe8b4bb6 100644
--- a/libs/androidfw/Util.cpp
+++ b/libs/androidfw/Util.cpp
@@ -42,7 +42,7 @@ void ReadUtf16StringFromDevice(const uint16_t* src, size_t len, std::string* out
}
}
-std::u16string Utf8ToUtf16(const StringPiece& utf8) {
+std::u16string Utf8ToUtf16(StringPiece utf8) {
ssize_t utf16_length =
utf8_to_utf16_length(reinterpret_cast<const uint8_t*>(utf8.data()), utf8.length());
if (utf16_length <= 0) {
@@ -56,7 +56,7 @@ std::u16string Utf8ToUtf16(const StringPiece& utf8) {
return utf16;
}
-std::string Utf16ToUtf8(const StringPiece16& utf16) {
+std::string Utf16ToUtf8(StringPiece16 utf16) {
ssize_t utf8_length = utf16_to_utf8_length(utf16.data(), utf16.length());
if (utf8_length <= 0) {
return {};
@@ -68,7 +68,7 @@ std::string Utf16ToUtf8(const StringPiece16& utf16) {
return utf8;
}
-std::string Utf8ToModifiedUtf8(const std::string& utf8) {
+std::string Utf8ToModifiedUtf8(std::string_view utf8) {
// Java uses Modified UTF-8 which only supports the 1, 2, and 3 byte formats of UTF-8. To encode
// 4 byte UTF-8 codepoints, Modified UTF-8 allows the use of surrogate pairs in the same format
// of CESU-8 surrogate pairs. Calculate the size of the utf8 string with all 4 byte UTF-8
@@ -86,7 +86,7 @@ std::string Utf8ToModifiedUtf8(const std::string& utf8) {
// Early out if no 4 byte codepoints are found
if (size == modified_size) {
- return utf8;
+ return std::string(utf8);
}
std::string output;
@@ -115,7 +115,7 @@ std::string Utf8ToModifiedUtf8(const std::string& utf8) {
return output;
}
-std::string ModifiedUtf8ToUtf8(const std::string& modified_utf8) {
+std::string ModifiedUtf8ToUtf8(std::string_view modified_utf8) {
// The UTF-8 representation will have a byte length less than or equal to the Modified UTF-8
// representation.
std::string output;
@@ -170,30 +170,28 @@ std::string ModifiedUtf8ToUtf8(const std::string& modified_utf8) {
return output;
}
-static std::vector<std::string> SplitAndTransform(
- const StringPiece& str, char sep, const std::function<char(char)>& f) {
+template <class Func>
+static std::vector<std::string> SplitAndTransform(StringPiece str, char sep, Func&& f) {
std::vector<std::string> parts;
const StringPiece::const_iterator end = std::end(str);
StringPiece::const_iterator start = std::begin(str);
StringPiece::const_iterator current;
do {
current = std::find(start, end, sep);
- parts.emplace_back(str.substr(start, current).to_string());
- if (f) {
- std::string& part = parts.back();
- std::transform(part.begin(), part.end(), part.begin(), f);
- }
+ parts.emplace_back(StringPiece(start, current - start));
+ std::string& part = parts.back();
+ std::transform(part.begin(), part.end(), part.begin(), f);
start = current + 1;
} while (current != end);
return parts;
}
-std::vector<std::string> SplitAndLowercase(const StringPiece& str, char sep) {
- return SplitAndTransform(str, sep, ::tolower);
+std::vector<std::string> SplitAndLowercase(StringPiece str, char sep) {
+ return SplitAndTransform(str, sep, [](char c) { return ::tolower(c); });
}
std::unique_ptr<uint8_t[]> Copy(const BigBuffer& buffer) {
- std::unique_ptr<uint8_t[]> data = std::unique_ptr<uint8_t[]>(new uint8_t[buffer.size()]);
+ auto data = std::unique_ptr<uint8_t[]>(new uint8_t[buffer.size()]);
uint8_t* p = data.get();
for (const auto& block : buffer) {
memcpy(p, block.buffer.get(), block.size);
@@ -211,7 +209,7 @@ StringPiece16 GetString16(const android::ResStringPool& pool, size_t idx) {
std::string GetString(const android::ResStringPool& pool, size_t idx) {
if (auto str = pool.string8At(idx); str.ok()) {
- return ModifiedUtf8ToUtf8(str->to_string());
+ return ModifiedUtf8ToUtf8(*str);
}
return Utf16ToUtf8(GetString16(pool, idx));
}
diff --git a/libs/androidfw/include/androidfw/AssetManager2.h b/libs/androidfw/include/androidfw/AssetManager2.h
index 1bde792da2ba..f10cb9bf480a 100644
--- a/libs/androidfw/include/androidfw/AssetManager2.h
+++ b/libs/androidfw/include/androidfw/AssetManager2.h
@@ -17,6 +17,7 @@
#ifndef ANDROIDFW_ASSETMANAGER2_H_
#define ANDROIDFW_ASSETMANAGER2_H_
+#include "android-base/function_ref.h"
#include "android-base/macros.h"
#include <array>
@@ -104,7 +105,7 @@ class AssetManager2 {
// new resource IDs.
bool SetApkAssets(std::vector<const ApkAssets*> apk_assets, bool invalidate_caches = true);
- inline const std::vector<const ApkAssets*> GetApkAssets() const {
+ inline const std::vector<const ApkAssets*>& GetApkAssets() const {
return apk_assets_;
}
@@ -124,8 +125,7 @@ class AssetManager2 {
uint8_t GetAssignedPackageId(const LoadedPackage* package) const;
// Returns a string representation of the overlayable API of a package.
- bool GetOverlayablesToString(const android::StringPiece& package_name,
- std::string* out) const;
+ bool GetOverlayablesToString(android::StringPiece package_name, std::string* out) const;
const std::unordered_map<std::string, std::string>* GetOverlayableMapForPackage(
uint32_t package_id) const;
@@ -321,17 +321,8 @@ class AssetManager2 {
// Creates a new Theme from this AssetManager.
std::unique_ptr<Theme> NewTheme();
- void ForEachPackage(const std::function<bool(const std::string&, uint8_t)> func,
- package_property_t excluded_property_flags = 0U) const {
- for (const PackageGroup& package_group : package_groups_) {
- const auto loaded_package = package_group.packages_.front().loaded_package_;
- if ((loaded_package->GetPropertyFlags() & excluded_property_flags) == 0U
- && !func(loaded_package->GetPackageName(),
- package_group.dynamic_ref_table->mAssignedPackageId)) {
- return;
- }
- }
- }
+ void ForEachPackage(base::function_ref<bool(const std::string&, uint8_t)> func,
+ package_property_t excluded_property_flags = 0U) const;
void DumpToLog() const;
@@ -572,6 +563,7 @@ class Theme {
AssetManager2* asset_manager_ = nullptr;
uint32_t type_spec_flags_ = 0u;
+ std::vector<uint32_t> keys_;
std::vector<Entry> entries_;
};
diff --git a/libs/androidfw/include/androidfw/AssetsProvider.h b/libs/androidfw/include/androidfw/AssetsProvider.h
index 966ec74c1786..d33c325ff369 100644
--- a/libs/androidfw/include/androidfw/AssetsProvider.h
+++ b/libs/androidfw/include/androidfw/AssetsProvider.h
@@ -20,6 +20,7 @@
#include <memory>
#include <string>
+#include "android-base/function_ref.h"
#include "android-base/macros.h"
#include "android-base/unique_fd.h"
@@ -46,7 +47,7 @@ struct AssetsProvider {
// Iterate over all files and directories provided by the interface. The order of iteration is
// stable.
virtual bool ForEachFile(const std::string& path,
- const std::function<void(const StringPiece&, FileType)>& f) const = 0;
+ base::function_ref<void(StringPiece, FileType)> f) const = 0;
// Retrieves the path to the contents of the AssetsProvider on disk. The path could represent an
// APk, a directory, or some other file type.
@@ -80,8 +81,8 @@ struct AssetsProvider {
// Supplies assets from a zip archive.
struct ZipAssetsProvider : public AssetsProvider {
- static std::unique_ptr<ZipAssetsProvider> Create(std::string path,
- package_property_t flags);
+ static std::unique_ptr<ZipAssetsProvider> Create(std::string path, package_property_t flags,
+ base::unique_fd fd = {});
static std::unique_ptr<ZipAssetsProvider> Create(base::unique_fd fd,
std::string friendly_name,
@@ -90,7 +91,7 @@ struct ZipAssetsProvider : public AssetsProvider {
off64_t len = kUnknownLength);
bool ForEachFile(const std::string& root_path,
- const std::function<void(const StringPiece&, FileType)>& f) const override;
+ base::function_ref<void(StringPiece, FileType)> f) const override;
WARN_UNUSED std::optional<std::string_view> GetPath() const override;
WARN_UNUSED const std::string& GetDebugName() const override;
@@ -108,7 +109,12 @@ struct ZipAssetsProvider : public AssetsProvider {
time_t last_mod_time);
struct PathOrDebugName {
- PathOrDebugName(std::string&& value, bool is_path);
+ static PathOrDebugName Path(std::string value) {
+ return {std::move(value), true};
+ }
+ static PathOrDebugName DebugName(std::string value) {
+ return {std::move(value), false};
+ }
// Retrieves the path or null if this class represents a debug name.
WARN_UNUSED const std::string* GetPath() const;
@@ -117,11 +123,16 @@ struct ZipAssetsProvider : public AssetsProvider {
WARN_UNUSED const std::string& GetDebugName() const;
private:
+ PathOrDebugName(std::string value, bool is_path) : value_(std::move(value)), is_path_(is_path) {
+ }
std::string value_;
bool is_path_;
};
- std::unique_ptr<ZipArchive, void (*)(ZipArchive*)> zip_handle_;
+ struct ZipCloser {
+ void operator()(ZipArchive* a) const;
+ };
+ std::unique_ptr<ZipArchive, ZipCloser> zip_handle_;
PathOrDebugName name_;
package_property_t flags_;
time_t last_mod_time_;
@@ -132,7 +143,7 @@ struct DirectoryAssetsProvider : public AssetsProvider {
static std::unique_ptr<DirectoryAssetsProvider> Create(std::string root_dir);
bool ForEachFile(const std::string& path,
- const std::function<void(const StringPiece&, FileType)>& f) const override;
+ base::function_ref<void(StringPiece, FileType)> f) const override;
WARN_UNUSED std::optional<std::string_view> GetPath() const override;
WARN_UNUSED const std::string& GetDebugName() const override;
@@ -157,7 +168,7 @@ struct MultiAssetsProvider : public AssetsProvider {
std::unique_ptr<AssetsProvider>&& secondary);
bool ForEachFile(const std::string& root_path,
- const std::function<void(const StringPiece&, FileType)>& f) const override;
+ base::function_ref<void(StringPiece, FileType)> f) const override;
WARN_UNUSED std::optional<std::string_view> GetPath() const override;
WARN_UNUSED const std::string& GetDebugName() const override;
@@ -181,10 +192,10 @@ struct MultiAssetsProvider : public AssetsProvider {
// Does not provide any assets.
struct EmptyAssetsProvider : public AssetsProvider {
static std::unique_ptr<AssetsProvider> Create();
- static std::unique_ptr<AssetsProvider> Create(const std::string& path);
+ static std::unique_ptr<AssetsProvider> Create(std::string path);
bool ForEachFile(const std::string& path,
- const std::function<void(const StringPiece&, FileType)>& f) const override;
+ base::function_ref<void(StringPiece, FileType)> f) const override;
WARN_UNUSED std::optional<std::string_view> GetPath() const override;
WARN_UNUSED const std::string& GetDebugName() const override;
diff --git a/libs/androidfw/include/androidfw/ByteBucketArray.h b/libs/androidfw/include/androidfw/ByteBucketArray.h
index 949c9445b3e8..ca0a9eda9caa 100644
--- a/libs/androidfw/include/androidfw/ByteBucketArray.h
+++ b/libs/androidfw/include/androidfw/ByteBucketArray.h
@@ -17,6 +17,7 @@
#ifndef __BYTE_BUCKET_ARRAY_H
#define __BYTE_BUCKET_ARRAY_H
+#include <algorithm>
#include <cstdint>
#include <cstring>
@@ -31,14 +32,16 @@ namespace android {
template <typename T>
class ByteBucketArray {
public:
- ByteBucketArray() : default_() { memset(buckets_, 0, sizeof(buckets_)); }
+ ByteBucketArray() {
+ memset(buckets_, 0, sizeof(buckets_));
+ }
~ByteBucketArray() {
- for (size_t i = 0; i < kNumBuckets; i++) {
- if (buckets_[i] != NULL) {
- delete[] buckets_[i];
- }
- }
+ deleteBuckets();
+ }
+
+ void clear() {
+ deleteBuckets();
memset(buckets_, 0, sizeof(buckets_));
}
@@ -53,7 +56,7 @@ class ByteBucketArray {
uint8_t bucket_index = static_cast<uint8_t>(index) >> 4;
T* bucket = buckets_[bucket_index];
- if (bucket == NULL) {
+ if (bucket == nullptr) {
return default_;
}
return bucket[0x0f & static_cast<uint8_t>(index)];
@@ -64,9 +67,9 @@ class ByteBucketArray {
<< ") with size=" << size();
uint8_t bucket_index = static_cast<uint8_t>(index) >> 4;
- T* bucket = buckets_[bucket_index];
- if (bucket == NULL) {
- bucket = buckets_[bucket_index] = new T[kBucketSize]();
+ T*& bucket = buckets_[bucket_index];
+ if (bucket == nullptr) {
+ bucket = new T[kBucketSize]();
}
return bucket[0x0f & static_cast<uint8_t>(index)];
}
@@ -80,11 +83,44 @@ class ByteBucketArray {
return true;
}
+ template <class Func>
+ void forEachItem(Func f) {
+ for (size_t i = 0; i < kNumBuckets; i++) {
+ const auto bucket = buckets_[i];
+ if (bucket != nullptr) {
+ for (size_t j = 0; j < kBucketSize; j++) {
+ f((i << 4) | j, bucket[j]);
+ }
+ }
+ }
+ }
+
+ template <class Func>
+ void trimBuckets(Func isEmptyFunc) {
+ for (size_t i = 0; i < kNumBuckets; i++) {
+ const auto bucket = buckets_[i];
+ if (bucket != nullptr) {
+ if (std::all_of(bucket, bucket + kBucketSize, isEmptyFunc)) {
+ delete[] bucket;
+ buckets_[i] = nullptr;
+ }
+ }
+ }
+ }
+
private:
enum { kNumBuckets = 16, kBucketSize = 16 };
+ void deleteBuckets() {
+ for (size_t i = 0; i < kNumBuckets; i++) {
+ if (buckets_[i] != nullptr) {
+ delete[] buckets_[i];
+ }
+ }
+ }
+
T* buckets_[kNumBuckets];
- T default_;
+ static inline const T default_ = {};
};
} // namespace android
diff --git a/libs/androidfw/include/androidfw/ConfigDescription.h b/libs/androidfw/include/androidfw/ConfigDescription.h
index 61d10cd4e55b..71087cdfb6fa 100644
--- a/libs/androidfw/include/androidfw/ConfigDescription.h
+++ b/libs/androidfw/include/androidfw/ConfigDescription.h
@@ -72,7 +72,7 @@ struct ConfigDescription : public ResTable_config {
* The resulting configuration has the appropriate sdkVersion defined
* for backwards compatibility.
*/
- static bool Parse(const android::StringPiece& str, ConfigDescription* out = nullptr);
+ static bool Parse(android::StringPiece str, ConfigDescription* out = nullptr);
/**
* If the configuration uses an axis that was added after
diff --git a/libs/androidfw/include/androidfw/IDiagnostics.h b/libs/androidfw/include/androidfw/IDiagnostics.h
index 273b05ac7689..4d5844eaa069 100644
--- a/libs/androidfw/include/androidfw/IDiagnostics.h
+++ b/libs/androidfw/include/androidfw/IDiagnostics.h
@@ -35,7 +35,7 @@ struct DiagMessage {
public:
DiagMessage() = default;
- explicit DiagMessage(const android::StringPiece& src) : source_(src) {
+ explicit DiagMessage(android::StringPiece src) : source_(src) {
}
explicit DiagMessage(const Source& src) : source_(src) {
@@ -61,7 +61,7 @@ struct DiagMessage {
template <>
inline DiagMessage& DiagMessage::operator<<(const ::std::u16string& value) {
- message_ << android::StringPiece16(value);
+ message_ << value;
return *this;
}
diff --git a/libs/androidfw/include/androidfw/Idmap.h b/libs/androidfw/include/androidfw/Idmap.h
index a1cbbbf2271b..60689128dffb 100644
--- a/libs/androidfw/include/androidfw/Idmap.h
+++ b/libs/androidfw/include/androidfw/Idmap.h
@@ -93,8 +93,8 @@ class IdmapResMap {
public:
Result() = default;
explicit Result(uint32_t value) : data_(value) {};
- explicit Result(const std::map<ConfigDescription, Res_value> &value)
- : data_(value) { };
+ explicit Result(std::map<ConfigDescription, Res_value> value) : data_(std::move(value)) {
+ }
// Returns `true` if the resource is overlaid.
explicit operator bool() const {
@@ -157,8 +157,7 @@ class IdmapResMap {
class LoadedIdmap {
public:
// Loads an IDMAP from a chunk of memory. Returns nullptr if the IDMAP data was malformed.
- static std::unique_ptr<LoadedIdmap> Load(const StringPiece& idmap_path,
- const StringPiece& idmap_data);
+ static std::unique_ptr<LoadedIdmap> Load(StringPiece idmap_path, StringPiece idmap_data);
// Returns the path to the IDMAP.
std::string_view IdmapPath() const {
diff --git a/libs/androidfw/include/androidfw/LoadedArsc.h b/libs/androidfw/include/androidfw/LoadedArsc.h
index 79d962829046..4d12885ad291 100644
--- a/libs/androidfw/include/androidfw/LoadedArsc.h
+++ b/libs/androidfw/include/androidfw/LoadedArsc.h
@@ -99,8 +99,8 @@ enum : package_property_t {
};
struct OverlayableInfo {
- std::string name;
- std::string actor;
+ std::string_view name;
+ std::string_view actor;
uint32_t policy_flags;
};
@@ -275,7 +275,7 @@ class LoadedPackage {
return overlayable_map_;
}
- const std::map<uint32_t, uint32_t>& GetAliasResourceIdMap() const {
+ const std::vector<std::pair<uint32_t, uint32_t>>& GetAliasResourceIdMap() const {
return alias_id_map_;
}
@@ -295,8 +295,8 @@ class LoadedPackage {
std::unordered_map<uint8_t, TypeSpec> type_specs_;
ByteBucketArray<uint32_t> resource_ids_;
std::vector<DynamicPackageEntry> dynamic_package_map_;
- std::vector<const std::pair<OverlayableInfo, std::unordered_set<uint32_t>>> overlayable_infos_;
- std::map<uint32_t, uint32_t> alias_id_map_;
+ std::vector<std::pair<OverlayableInfo, std::unordered_set<uint32_t>>> overlayable_infos_;
+ std::vector<std::pair<uint32_t, uint32_t>> alias_id_map_;
// A map of overlayable name to actor
std::unordered_map<std::string, std::string> overlayable_map_;
diff --git a/libs/androidfw/include/androidfw/Locale.h b/libs/androidfw/include/androidfw/Locale.h
index 484ed79a8efd..8934bed098fe 100644
--- a/libs/androidfw/include/androidfw/Locale.h
+++ b/libs/androidfw/include/androidfw/Locale.h
@@ -39,10 +39,10 @@ struct LocaleValue {
/**
* Initialize this LocaleValue from a config string.
*/
- bool InitFromFilterString(const android::StringPiece& config);
+ bool InitFromFilterString(android::StringPiece config);
// Initializes this LocaleValue from a BCP-47 locale tag.
- bool InitFromBcp47Tag(const android::StringPiece& bcp47tag);
+ bool InitFromBcp47Tag(android::StringPiece bcp47tag);
/**
* Initialize this LocaleValue from parts of a vector.
@@ -70,7 +70,7 @@ struct LocaleValue {
inline bool operator>(const LocaleValue& o) const;
private:
- bool InitFromBcp47TagImpl(const android::StringPiece& bcp47tag, const char separator);
+ bool InitFromBcp47TagImpl(android::StringPiece bcp47tag, const char separator);
void set_language(const char* language);
void set_region(const char* language);
diff --git a/libs/androidfw/include/androidfw/ResourceTypes.h b/libs/androidfw/include/androidfw/ResourceTypes.h
index b2b95b72e0e0..52321dad8a5a 100644
--- a/libs/androidfw/include/androidfw/ResourceTypes.h
+++ b/libs/androidfw/include/androidfw/ResourceTypes.h
@@ -21,6 +21,7 @@
#define _LIBS_UTILS_RESOURCE_TYPES_H
#include <android-base/expected.h>
+#include <android-base/unique_fd.h>
#include <androidfw/Asset.h>
#include <androidfw/Errors.h>
@@ -58,6 +59,8 @@ constexpr const uint32_t kFabricatedOverlayCurrentVersion = 3;
// Returns whether or not the path represents a fabricated overlay.
bool IsFabricatedOverlay(const std::string& path);
+bool IsFabricatedOverlay(const char* path);
+bool IsFabricatedOverlay(android::base::borrowed_fd fd);
/**
* In C++11, char16_t is defined as *at least* 16 bits. We do a lot of
@@ -1882,7 +1885,10 @@ public:
void addMapping(uint8_t buildPackageId, uint8_t runtimePackageId);
- void addAlias(uint32_t stagedId, uint32_t finalizedId);
+ using AliasMap = std::vector<std::pair<uint32_t, uint32_t>>;
+ void setAliases(AliasMap aliases) {
+ mAliasId = std::move(aliases);
+ }
// Returns whether or not the value must be looked up.
bool requiresLookup(const Res_value* value) const;
@@ -1896,12 +1902,12 @@ public:
return mEntries;
}
-private:
- uint8_t mAssignedPackageId;
- uint8_t mLookupTable[256];
- KeyedVector<String16, uint8_t> mEntries;
- bool mAppAsLib;
- std::map<uint32_t, uint32_t> mAliasId;
+ private:
+ uint8_t mLookupTable[256];
+ uint8_t mAssignedPackageId;
+ bool mAppAsLib;
+ KeyedVector<String16, uint8_t> mEntries;
+ AliasMap mAliasId;
};
bool U16StringToInt(const char16_t* s, size_t len, Res_value* outValue);
diff --git a/libs/androidfw/include/androidfw/ResourceUtils.h b/libs/androidfw/include/androidfw/ResourceUtils.h
index bd1c44033b88..2d90a526dfbe 100644
--- a/libs/androidfw/include/androidfw/ResourceUtils.h
+++ b/libs/androidfw/include/androidfw/ResourceUtils.h
@@ -25,14 +25,14 @@ namespace android {
// Extracts the package, type, and name from a string of the format: [[package:]type/]name
// Validation must be performed on each extracted piece.
// Returns false if there was a syntax error.
-bool ExtractResourceName(const StringPiece& str, StringPiece* out_package, StringPiece* out_type,
+bool ExtractResourceName(StringPiece str, StringPiece* out_package, StringPiece* out_type,
StringPiece* out_entry);
// Convert a type_string_ref, entry_string_ref, and package to AssetManager2::ResourceName.
// Useful for getting resource name without re-running AssetManager2::FindEntry searches.
base::expected<AssetManager2::ResourceName, NullOrIOError> ToResourceName(
const StringPoolRef& type_string_ref, const StringPoolRef& entry_string_ref,
- const StringPiece& package_name);
+ StringPiece package_name);
// Formats a ResourceName to "package:type/entry_name".
std::string ToFormattedResourceString(const AssetManager2::ResourceName& resource_name);
diff --git a/libs/androidfw/include/androidfw/Source.h b/libs/androidfw/include/androidfw/Source.h
index 0421a91d3eba..ddc9ba421101 100644
--- a/libs/androidfw/include/androidfw/Source.h
+++ b/libs/androidfw/include/androidfw/Source.h
@@ -34,15 +34,14 @@ struct Source {
Source() = default;
- inline Source(const android::StringPiece& path) : path(path.to_string()) { // NOLINT(implicit)
+ inline Source(android::StringPiece path) : path(path) { // NOLINT(implicit)
}
- inline Source(const android::StringPiece& path, const android::StringPiece& archive)
- : path(path.to_string()), archive(archive.to_string()) {
+ inline Source(android::StringPiece path, android::StringPiece archive)
+ : path(path), archive(archive) {
}
- inline Source(const android::StringPiece& path, size_t line)
- : path(path.to_string()), line(line) {
+ inline Source(android::StringPiece path, size_t line) : path(path), line(line) {
}
inline Source WithLine(size_t line) const {
diff --git a/libs/androidfw/include/androidfw/StringPiece.h b/libs/androidfw/include/androidfw/StringPiece.h
index fac2fa4fa575..f6cc64edfb5a 100644
--- a/libs/androidfw/include/androidfw/StringPiece.h
+++ b/libs/androidfw/include/androidfw/StringPiece.h
@@ -19,307 +19,37 @@
#include <ostream>
#include <string>
+#include <string_view>
-#include "utils/JenkinsHash.h"
#include "utils/Unicode.h"
namespace android {
-// Read only wrapper around basic C strings. Prevents excessive copying.
-// StringPiece does not own the data it is wrapping. The lifetime of the underlying
-// data must outlive this StringPiece.
-//
-// WARNING: When creating from std::basic_string<>, moving the original
-// std::basic_string<> will invalidate the data held in a BasicStringPiece<>.
-// BasicStringPiece<> should only be used transitively.
-//
-// NOTE: When creating an std::pair<StringPiece, T> using std::make_pair(),
-// passing an std::string will first copy the string, then create a StringPiece
-// on the copy, which is then immediately destroyed.
-// Instead, create a StringPiece explicitly:
-//
-// std::string my_string = "foo";
-// std::make_pair<StringPiece, T>(StringPiece(my_string), ...);
-template <typename TChar>
-class BasicStringPiece {
- public:
- using const_iterator = const TChar*;
- using difference_type = size_t;
- using size_type = size_t;
-
- // End of string marker.
- constexpr static const size_t npos = static_cast<size_t>(-1);
-
- BasicStringPiece();
- BasicStringPiece(const BasicStringPiece<TChar>& str);
- BasicStringPiece(const std::basic_string<TChar>& str); // NOLINT(google-explicit-constructor)
- BasicStringPiece(const TChar* str); // NOLINT(google-explicit-constructor)
- BasicStringPiece(const TChar* str, size_t len);
-
- BasicStringPiece<TChar>& operator=(const BasicStringPiece<TChar>& rhs);
- BasicStringPiece<TChar>& assign(const TChar* str, size_t len);
-
- BasicStringPiece<TChar> substr(size_t start, size_t len = npos) const;
- BasicStringPiece<TChar> substr(BasicStringPiece<TChar>::const_iterator begin,
- BasicStringPiece<TChar>::const_iterator end) const;
-
- const TChar* data() const;
- size_t length() const;
- size_t size() const;
- bool empty() const;
- std::basic_string<TChar> to_string() const;
-
- bool contains(const BasicStringPiece<TChar>& rhs) const;
- int compare(const BasicStringPiece<TChar>& rhs) const;
- bool operator<(const BasicStringPiece<TChar>& rhs) const;
- bool operator>(const BasicStringPiece<TChar>& rhs) const;
- bool operator==(const BasicStringPiece<TChar>& rhs) const;
- bool operator!=(const BasicStringPiece<TChar>& rhs) const;
-
- const_iterator begin() const;
- const_iterator end() const;
-
- private:
- const TChar* data_;
- size_t length_;
-};
+template <class T>
+using BasicStringPiece = std::basic_string_view<T>;
using StringPiece = BasicStringPiece<char>;
using StringPiece16 = BasicStringPiece<char16_t>;
-//
-// BasicStringPiece implementation.
-//
-
-template <typename TChar>
-constexpr const size_t BasicStringPiece<TChar>::npos;
-
-template <typename TChar>
-inline BasicStringPiece<TChar>::BasicStringPiece() : data_(nullptr), length_(0) {}
-
-template <typename TChar>
-inline BasicStringPiece<TChar>::BasicStringPiece(const BasicStringPiece<TChar>& str)
- : data_(str.data_), length_(str.length_) {}
-
-template <typename TChar>
-inline BasicStringPiece<TChar>::BasicStringPiece(const std::basic_string<TChar>& str)
- : data_(str.data()), length_(str.length()) {}
-
-template <>
-inline BasicStringPiece<char>::BasicStringPiece(const char* str)
- : data_(str), length_(str != nullptr ? strlen(str) : 0) {}
-
-template <>
-inline BasicStringPiece<char16_t>::BasicStringPiece(const char16_t* str)
- : data_(str), length_(str != nullptr ? strlen16(str) : 0) {}
-
-template <typename TChar>
-inline BasicStringPiece<TChar>::BasicStringPiece(const TChar* str, size_t len)
- : data_(str), length_(len) {}
-
-template <typename TChar>
-inline BasicStringPiece<TChar>& BasicStringPiece<TChar>::operator=(
- const BasicStringPiece<TChar>& rhs) {
- data_ = rhs.data_;
- length_ = rhs.length_;
- return *this;
-}
-
-template <typename TChar>
-inline BasicStringPiece<TChar>& BasicStringPiece<TChar>::assign(const TChar* str, size_t len) {
- data_ = str;
- length_ = len;
- return *this;
-}
-
-template <typename TChar>
-inline BasicStringPiece<TChar> BasicStringPiece<TChar>::substr(size_t start, size_t len) const {
- if (len == npos) {
- len = length_ - start;
- }
-
- if (start > length_ || start + len > length_) {
- return BasicStringPiece<TChar>();
- }
- return BasicStringPiece<TChar>(data_ + start, len);
-}
-
-template <typename TChar>
-inline BasicStringPiece<TChar> BasicStringPiece<TChar>::substr(
- BasicStringPiece<TChar>::const_iterator begin,
- BasicStringPiece<TChar>::const_iterator end) const {
- return BasicStringPiece<TChar>(begin, end - begin);
-}
-
-template <typename TChar>
-inline const TChar* BasicStringPiece<TChar>::data() const {
- return data_;
-}
-
-template <typename TChar>
-inline size_t BasicStringPiece<TChar>::length() const {
- return length_;
-}
-
-template <typename TChar>
-inline size_t BasicStringPiece<TChar>::size() const {
- return length_;
-}
-
-template <typename TChar>
-inline bool BasicStringPiece<TChar>::empty() const {
- return length_ == 0;
-}
-
-template <typename TChar>
-inline std::basic_string<TChar> BasicStringPiece<TChar>::to_string() const {
- return std::basic_string<TChar>(data_, length_);
-}
-
-template <>
-inline bool BasicStringPiece<char>::contains(const BasicStringPiece<char>& rhs) const {
- if (!data_ || !rhs.data_) {
- return false;
- }
- if (rhs.length_ > length_) {
- return false;
- }
- return strstr(data_, rhs.data_) != nullptr;
-}
-
-template <>
-inline int BasicStringPiece<char>::compare(const BasicStringPiece<char>& rhs) const {
- const char nullStr = '\0';
- const char* b1 = data_ != nullptr ? data_ : &nullStr;
- const char* e1 = b1 + length_;
- const char* b2 = rhs.data_ != nullptr ? rhs.data_ : &nullStr;
- const char* e2 = b2 + rhs.length_;
-
- while (b1 < e1 && b2 < e2) {
- const int d = static_cast<int>(*b1++) - static_cast<int>(*b2++);
- if (d) {
- return d;
- }
- }
- return static_cast<int>(length_ - rhs.length_);
-}
-
-inline ::std::ostream& operator<<(::std::ostream& out, const BasicStringPiece<char16_t>& str) {
- const ssize_t result_len = utf16_to_utf8_length(str.data(), str.size());
- if (result_len < 0) {
- // Empty string.
- return out;
- }
-
- std::string result;
- result.resize(static_cast<size_t>(result_len));
- utf16_to_utf8(str.data(), str.length(), &*result.begin(), static_cast<size_t>(result_len) + 1);
- return out << result;
-}
-
-template <>
-inline bool BasicStringPiece<char16_t>::contains(const BasicStringPiece<char16_t>& rhs) const {
- if (!data_ || !rhs.data_) {
- return false;
- }
- if (rhs.length_ > length_) {
- return false;
- }
- return strstr16(data_, rhs.data_) != nullptr;
-}
-
-template <>
-inline int BasicStringPiece<char16_t>::compare(const BasicStringPiece<char16_t>& rhs) const {
- const char16_t nullStr = u'\0';
- const char16_t* b1 = data_ != nullptr ? data_ : &nullStr;
- const char16_t* b2 = rhs.data_ != nullptr ? rhs.data_ : &nullStr;
- return strzcmp16(b1, length_, b2, rhs.length_);
-}
-
-template <typename TChar>
-inline bool BasicStringPiece<TChar>::operator<(const BasicStringPiece<TChar>& rhs) const {
- return compare(rhs) < 0;
-}
-
-template <typename TChar>
-inline bool BasicStringPiece<TChar>::operator>(const BasicStringPiece<TChar>& rhs) const {
- return compare(rhs) > 0;
-}
-
-template <typename TChar>
-inline bool BasicStringPiece<TChar>::operator==(const BasicStringPiece<TChar>& rhs) const {
- return compare(rhs) == 0;
-}
-
-template <typename TChar>
-inline bool BasicStringPiece<TChar>::operator!=(const BasicStringPiece<TChar>& rhs) const {
- return compare(rhs) != 0;
-}
-
-template <typename TChar>
-inline typename BasicStringPiece<TChar>::const_iterator BasicStringPiece<TChar>::begin() const {
- return data_;
-}
-
-template <typename TChar>
-inline typename BasicStringPiece<TChar>::const_iterator BasicStringPiece<TChar>::end() const {
- return data_ + length_;
-}
-
-template <typename TChar>
-inline bool operator==(const TChar* lhs, const BasicStringPiece<TChar>& rhs) {
- return BasicStringPiece<TChar>(lhs) == rhs;
-}
-
-template <typename TChar>
-inline bool operator!=(const TChar* lhs, const BasicStringPiece<TChar>& rhs) {
- return BasicStringPiece<TChar>(lhs) != rhs;
-}
-
-inline ::std::ostream& operator<<(::std::ostream& out, const BasicStringPiece<char>& str) {
- return out.write(str.data(), str.size());
-}
-
-template <typename TChar>
-inline ::std::basic_string<TChar>& operator+=(::std::basic_string<TChar>& lhs,
- const BasicStringPiece<TChar>& rhs) {
- return lhs.append(rhs.data(), rhs.size());
-}
-
-template <typename TChar>
-inline bool operator==(const ::std::basic_string<TChar>& lhs, const BasicStringPiece<TChar>& rhs) {
- return BasicStringPiece<TChar>(lhs) == rhs;
-}
-
-template <typename TChar>
-inline bool operator!=(const ::std::basic_string<TChar>& lhs, const BasicStringPiece<TChar>& rhs) {
- return BasicStringPiece<TChar>(lhs) != rhs;
-}
-
} // namespace android
-inline ::std::ostream& operator<<(::std::ostream& out, const std::u16string& str) {
+namespace std {
+
+inline ::std::ostream& operator<<(::std::ostream& out, ::std::u16string_view str) {
ssize_t utf8_len = utf16_to_utf8_length(str.data(), str.size());
if (utf8_len < 0) {
- return out << "???";
+ return out; // empty
}
std::string utf8;
utf8.resize(static_cast<size_t>(utf8_len));
- utf16_to_utf8(str.data(), str.size(), &*utf8.begin(), utf8_len + 1);
+ utf16_to_utf8(str.data(), str.size(), utf8.data(), utf8_len + 1);
return out << utf8;
}
-namespace std {
-
-template <typename TChar>
-struct hash<android::BasicStringPiece<TChar>> {
- size_t operator()(const android::BasicStringPiece<TChar>& str) const {
- uint32_t hashCode = android::JenkinsHashMixBytes(
- 0, reinterpret_cast<const uint8_t*>(str.data()), sizeof(TChar) * str.size());
- return static_cast<size_t>(hashCode);
- }
-};
+inline ::std::ostream& operator<<(::std::ostream& out, const ::std::u16string& str) {
+ return out << std::u16string_view(str);
+}
} // namespace std
diff --git a/libs/androidfw/include/androidfw/StringPool.h b/libs/androidfw/include/androidfw/StringPool.h
index 25174d8fe3c8..0190ab57bf23 100644
--- a/libs/androidfw/include/androidfw/StringPool.h
+++ b/libs/androidfw/include/androidfw/StringPool.h
@@ -167,11 +167,11 @@ class StringPool {
// Adds a string to the pool, unless it already exists. Returns a reference to the string in the
// pool.
- Ref MakeRef(const android::StringPiece& str);
+ Ref MakeRef(android::StringPiece str);
// Adds a string to the pool, unless it already exists, with a context object that can be used
// when sorting the string pool. Returns a reference to the string in the pool.
- Ref MakeRef(const android::StringPiece& str, const Context& context);
+ Ref MakeRef(android::StringPiece str, const Context& context);
// Adds a string from another string pool. Returns a reference to the string in the string pool.
Ref MakeRef(const Ref& ref);
@@ -215,7 +215,7 @@ class StringPool {
static bool Flatten(BigBuffer* out, const StringPool& pool, bool utf8, IDiagnostics* diag);
- Ref MakeRefImpl(const android::StringPiece& str, const Context& context, bool unique);
+ Ref MakeRefImpl(android::StringPiece str, const Context& context, bool unique);
void ReAssignIndices();
std::vector<std::unique_ptr<Entry>> strings_;
diff --git a/libs/androidfw/include/androidfw/Util.h b/libs/androidfw/include/androidfw/Util.h
index 1bbc7f5716bc..a188abb7ecb5 100644
--- a/libs/androidfw/include/androidfw/Util.h
+++ b/libs/androidfw/include/androidfw/Util.h
@@ -22,7 +22,6 @@
#include <cstdlib>
#include <memory>
-#include <sstream>
#include <vector>
#include "androidfw/BigBuffer.h"
@@ -33,7 +32,14 @@
#ifdef __ANDROID__
#define ANDROID_LOG(x) LOG(x)
#else
-#define ANDROID_LOG(x) std::stringstream()
+namespace android {
+// No default logging for aapt2, as it's too noisy for a command line dev tool.
+struct NullLogger {
+ template <class T>
+ friend const NullLogger& operator<<(const NullLogger& l, const T&) { return l; }
+};
+}
+#define ANDROID_LOG(x) (android::NullLogger{})
#endif
namespace android {
@@ -49,90 +55,28 @@ std::unique_ptr<T> make_unique(Args&&... args) {
return std::unique_ptr<T>(new T{std::forward<Args>(args)...});
}
-// Based on std::unique_ptr, but uses free() to release malloc'ed memory
-// without incurring the size increase of holding on to a custom deleter.
-template <typename T>
-class unique_cptr {
- public:
- using pointer = typename std::add_pointer<T>::type;
-
- constexpr unique_cptr() : ptr_(nullptr) {}
- constexpr explicit unique_cptr(std::nullptr_t) : ptr_(nullptr) {}
- explicit unique_cptr(pointer ptr) : ptr_(ptr) {}
- unique_cptr(unique_cptr&& o) noexcept : ptr_(o.ptr_) { o.ptr_ = nullptr; }
-
- ~unique_cptr() { std::free(reinterpret_cast<void*>(ptr_)); }
-
- inline unique_cptr& operator=(unique_cptr&& o) noexcept {
- if (&o == this) {
- return *this;
- }
-
- std::free(reinterpret_cast<void*>(ptr_));
- ptr_ = o.ptr_;
- o.ptr_ = nullptr;
- return *this;
- }
-
- inline unique_cptr& operator=(std::nullptr_t) {
- std::free(reinterpret_cast<void*>(ptr_));
- ptr_ = nullptr;
- return *this;
- }
-
- pointer release() {
- pointer result = ptr_;
- ptr_ = nullptr;
- return result;
+// Based on std::unique_ptr, but uses free() to release malloc'ed memory.
+struct FreeDeleter {
+ void operator()(void* ptr) const {
+ ::free(ptr);
}
-
- inline pointer get() const { return ptr_; }
-
- void reset(pointer ptr = pointer()) {
- if (ptr == ptr_) {
- return;
- }
-
- pointer old_ptr = ptr_;
- ptr_ = ptr;
- std::free(reinterpret_cast<void*>(old_ptr));
- }
-
- inline void swap(unique_cptr& o) { std::swap(ptr_, o.ptr_); }
-
- inline explicit operator bool() const { return ptr_ != nullptr; }
-
- inline typename std::add_lvalue_reference<T>::type operator*() const { return *ptr_; }
-
- inline pointer operator->() const { return ptr_; }
-
- inline bool operator==(const unique_cptr& o) const { return ptr_ == o.ptr_; }
-
- inline bool operator!=(const unique_cptr& o) const { return ptr_ != o.ptr_; }
-
- inline bool operator==(std::nullptr_t) const { return ptr_ == nullptr; }
-
- inline bool operator!=(std::nullptr_t) const { return ptr_ != nullptr; }
-
- private:
- DISALLOW_COPY_AND_ASSIGN(unique_cptr);
-
- pointer ptr_;
};
+template <typename T>
+using unique_cptr = std::unique_ptr<T, FreeDeleter>;
void ReadUtf16StringFromDevice(const uint16_t* src, size_t len, std::string* out);
// Converts a UTF-8 string to a UTF-16 string.
-std::u16string Utf8ToUtf16(const StringPiece& utf8);
+std::u16string Utf8ToUtf16(StringPiece utf8);
// Converts a UTF-16 string to a UTF-8 string.
-std::string Utf16ToUtf8(const StringPiece16& utf16);
+std::string Utf16ToUtf8(StringPiece16 utf16);
// Converts a UTF8 string into Modified UTF8
-std::string Utf8ToModifiedUtf8(const std::string& utf8);
+std::string Utf8ToModifiedUtf8(std::string_view utf8);
// Converts a Modified UTF8 string into a UTF8 string
-std::string ModifiedUtf8ToUtf8(const std::string& modified_utf8);
+std::string ModifiedUtf8ToUtf8(std::string_view modified_utf8);
inline uint16_t HostToDevice16(uint16_t value) {
return htods(value);
@@ -150,15 +94,15 @@ inline uint32_t DeviceToHost32(uint32_t value) {
return dtohl(value);
}
-std::vector<std::string> SplitAndLowercase(const android::StringPiece& str, char sep);
+std::vector<std::string> SplitAndLowercase(android::StringPiece str, char sep);
-template <typename T>
-inline bool IsFourByteAligned(const incfs::map_ptr<T>& data) {
- return ((size_t)data.unsafe_ptr() & 0x3U) == 0;
+inline bool IsFourByteAligned(const void* data) {
+ return ((uintptr_t)data & 0x3U) == 0;
}
-inline bool IsFourByteAligned(const void* data) {
- return ((size_t)data & 0x3U) == 0;
+template <typename T>
+inline bool IsFourByteAligned(const incfs::map_ptr<T>& data) {
+ return IsFourByteAligned(data.unsafe_ptr());
}
// Helper method to extract a UTF-16 string from a StringPool. If the string is stored as UTF-8,
diff --git a/libs/androidfw/include/androidfw/misc.h b/libs/androidfw/include/androidfw/misc.h
index 5a5a0e29125d..d40d24ede769 100644
--- a/libs/androidfw/include/androidfw/misc.h
+++ b/libs/androidfw/include/androidfw/misc.h
@@ -44,6 +44,10 @@ FileType getFileType(const char* fileName);
/* get the file's modification date; returns -1 w/errno set on failure */
time_t getFileModDate(const char* fileName);
+// Check if |path| or |fd| resides on a readonly filesystem.
+bool isReadonlyFilesystem(const char* path);
+bool isReadonlyFilesystem(int fd);
+
}; // namespace android
#endif // _LIBS_ANDROID_FW_MISC_H
diff --git a/libs/androidfw/misc.cpp b/libs/androidfw/misc.cpp
index 52854205207c..d3949e9cf69f 100644
--- a/libs/androidfw/misc.cpp
+++ b/libs/androidfw/misc.cpp
@@ -21,12 +21,17 @@
//
#include <androidfw/misc.h>
-#include <sys/stat.h>
+#include "android-base/logging.h"
+
+#ifdef __linux__
+#include <sys/statvfs.h>
+#include <sys/vfs.h>
+#endif // __linux__
+
#include <cstring>
-#include <errno.h>
#include <cstdio>
-
-using namespace android;
+#include <errno.h>
+#include <sys/stat.h>
namespace android {
@@ -41,8 +46,7 @@ FileType getFileType(const char* fileName)
if (errno == ENOENT || errno == ENOTDIR)
return kFileTypeNonexistent;
else {
- fprintf(stderr, "getFileType got errno=%d on '%s'\n",
- errno, fileName);
+ PLOG(ERROR) << "getFileType(): stat(" << fileName << ") failed";
return kFileTypeUnknown;
}
} else {
@@ -82,4 +86,32 @@ time_t getFileModDate(const char* fileName)
return sb.st_mtime;
}
+#ifndef __linux__
+// No need to implement these on the host, the functions only matter on a device.
+bool isReadonlyFilesystem(const char*) {
+ return false;
+}
+bool isReadonlyFilesystem(int) {
+ return false;
+}
+#else // __linux__
+bool isReadonlyFilesystem(const char* path) {
+ struct statfs sfs;
+ if (::statfs(path, &sfs)) {
+ PLOG(ERROR) << "isReadonlyFilesystem(): statfs(" << path << ") failed";
+ return false;
+ }
+ return (sfs.f_flags & ST_RDONLY) != 0;
+}
+
+bool isReadonlyFilesystem(int fd) {
+ struct statfs sfs;
+ if (::fstatfs(fd, &sfs)) {
+ PLOG(ERROR) << "isReadonlyFilesystem(): fstatfs(" << fd << ") failed";
+ return false;
+ }
+ return (sfs.f_flags & ST_RDONLY) != 0;
+}
+#endif // __linux__
+
}; // namespace android
diff --git a/libs/androidfw/tests/AttributeResolution_bench.cpp b/libs/androidfw/tests/AttributeResolution_bench.cpp
index ddd8ab820cb1..1c89c61c8f78 100644
--- a/libs/androidfw/tests/AttributeResolution_bench.cpp
+++ b/libs/androidfw/tests/AttributeResolution_bench.cpp
@@ -120,8 +120,8 @@ static void BM_ApplyStyleFramework(benchmark::State& state) {
return;
}
- std::unique_ptr<Asset> asset = assetmanager.OpenNonAsset(layout_path->to_string(), value->cookie,
- Asset::ACCESS_BUFFER);
+ std::unique_ptr<Asset> asset =
+ assetmanager.OpenNonAsset(std::string(*layout_path), value->cookie, Asset::ACCESS_BUFFER);
if (asset == nullptr) {
state.SkipWithError("failed to load layout");
return;
diff --git a/libs/androidfw/tests/ByteBucketArray_test.cpp b/libs/androidfw/tests/ByteBucketArray_test.cpp
index 5d464c7dc0f7..9c36cfb212c5 100644
--- a/libs/androidfw/tests/ByteBucketArray_test.cpp
+++ b/libs/androidfw/tests/ByteBucketArray_test.cpp
@@ -52,4 +52,57 @@ TEST(ByteBucketArrayTest, TestSparseInsertion) {
}
}
+TEST(ByteBucketArrayTest, TestForEach) {
+ ByteBucketArray<int> bba;
+ ASSERT_TRUE(bba.set(0, 1));
+ ASSERT_TRUE(bba.set(10, 2));
+ ASSERT_TRUE(bba.set(26, 3));
+ ASSERT_TRUE(bba.set(129, 4));
+ ASSERT_TRUE(bba.set(234, 5));
+
+ int count = 0;
+ bba.forEachItem([&count](auto i, auto val) {
+ ++count;
+ switch (i) {
+ case 0:
+ EXPECT_EQ(1, val);
+ break;
+ case 10:
+ EXPECT_EQ(2, val);
+ break;
+ case 26:
+ EXPECT_EQ(3, val);
+ break;
+ case 129:
+ EXPECT_EQ(4, val);
+ break;
+ case 234:
+ EXPECT_EQ(5, val);
+ break;
+ default:
+ EXPECT_EQ(0, val);
+ break;
+ }
+ });
+ ASSERT_EQ(4 * 16, count);
+}
+
+TEST(ByteBucketArrayTest, TestTrimBuckets) {
+ ByteBucketArray<int> bba;
+ ASSERT_TRUE(bba.set(0, 1));
+ ASSERT_TRUE(bba.set(255, 2));
+ {
+ bba.trimBuckets([](auto val) { return val < 2; });
+ int count = 0;
+ bba.forEachItem([&count](auto, auto) { ++count; });
+ ASSERT_EQ(1 * 16, count);
+ }
+ {
+ bba.trimBuckets([](auto val) { return val < 3; });
+ int count = 0;
+ bba.forEachItem([&count](auto, auto) { ++count; });
+ ASSERT_EQ(0, count);
+ }
+}
+
} // namespace android
diff --git a/libs/androidfw/tests/ConfigDescription_test.cpp b/libs/androidfw/tests/ConfigDescription_test.cpp
index ce7f8054e2ca..8fed0a4d22fc 100644
--- a/libs/androidfw/tests/ConfigDescription_test.cpp
+++ b/libs/androidfw/tests/ConfigDescription_test.cpp
@@ -25,8 +25,8 @@
namespace android {
-static ::testing::AssertionResult TestParse(
- const StringPiece& input, ConfigDescription* config = nullptr) {
+static ::testing::AssertionResult TestParse(StringPiece input,
+ ConfigDescription* config = nullptr) {
if (ConfigDescription::Parse(input, config)) {
return ::testing::AssertionSuccess() << input << " was successfully parsed";
}
@@ -138,7 +138,7 @@ TEST(ConfigDescriptionTest, ParseVrAttribute) {
EXPECT_EQ(std::string("vrheadset-v26"), config.toString().string());
}
-static inline ConfigDescription ParseConfigOrDie(const android::StringPiece& str) {
+static inline ConfigDescription ParseConfigOrDie(android::StringPiece str) {
ConfigDescription config;
CHECK(ConfigDescription::Parse(str, &config)) << "invalid configuration: " << str;
return config;
diff --git a/libs/androidfw/tests/StringPiece_test.cpp b/libs/androidfw/tests/StringPiece_test.cpp
index 316a5c1bf40e..822e527253df 100644
--- a/libs/androidfw/tests/StringPiece_test.cpp
+++ b/libs/androidfw/tests/StringPiece_test.cpp
@@ -60,36 +60,4 @@ TEST(StringPieceTest, PiecesHaveCorrectSortOrderUtf8) {
EXPECT_TRUE(StringPiece(car) > banana);
}
-TEST(StringPieceTest, ContainsOtherStringPiece) {
- StringPiece text("I am a leaf on the wind.");
- StringPiece start_needle("I am");
- StringPiece end_needle("wind.");
- StringPiece middle_needle("leaf");
- StringPiece empty_needle("");
- StringPiece missing_needle("soar");
- StringPiece long_needle("This string is longer than the text.");
-
- EXPECT_TRUE(text.contains(start_needle));
- EXPECT_TRUE(text.contains(end_needle));
- EXPECT_TRUE(text.contains(middle_needle));
- EXPECT_TRUE(text.contains(empty_needle));
- EXPECT_FALSE(text.contains(missing_needle));
- EXPECT_FALSE(text.contains(long_needle));
-
- StringPiece16 text16(u"I am a leaf on the wind.");
- StringPiece16 start_needle16(u"I am");
- StringPiece16 end_needle16(u"wind.");
- StringPiece16 middle_needle16(u"leaf");
- StringPiece16 empty_needle16(u"");
- StringPiece16 missing_needle16(u"soar");
- StringPiece16 long_needle16(u"This string is longer than the text.");
-
- EXPECT_TRUE(text16.contains(start_needle16));
- EXPECT_TRUE(text16.contains(end_needle16));
- EXPECT_TRUE(text16.contains(middle_needle16));
- EXPECT_TRUE(text16.contains(empty_needle16));
- EXPECT_FALSE(text16.contains(missing_needle16));
- EXPECT_FALSE(text16.contains(long_needle16));
-}
-
} // namespace android
diff --git a/libs/androidfw/tests/StringPool_test.cpp b/libs/androidfw/tests/StringPool_test.cpp
index 047d45785409..0e0acae165d9 100644
--- a/libs/androidfw/tests/StringPool_test.cpp
+++ b/libs/androidfw/tests/StringPool_test.cpp
@@ -321,15 +321,15 @@ TEST(StringPoolTest, ModifiedUTF8) {
ASSERT_EQ(test.setTo(data.get(), buffer.size()), NO_ERROR);
auto str = test.string8At(0);
ASSERT_TRUE(str.has_value());
- EXPECT_THAT(str->to_string(), Eq("\xED\xA0\x81\xED\xB0\x80"));
+ EXPECT_THAT(*str, Eq("\xED\xA0\x81\xED\xB0\x80"));
str = test.string8At(1);
ASSERT_TRUE(str.has_value());
- EXPECT_THAT(str->to_string(), Eq("foo \xED\xA0\x81\xED\xB0\xB7 bar"));
+ EXPECT_THAT(*str, Eq("foo \xED\xA0\x81\xED\xB0\xB7 bar"));
str = test.string8At(2);
ASSERT_TRUE(str.has_value());
- EXPECT_THAT(str->to_string(), Eq("\xED\xA0\x81\xED\xB0\x80\xED\xA0\x81\xED\xB0\xB7"));
+ EXPECT_THAT(*str, Eq("\xED\xA0\x81\xED\xB0\x80\xED\xA0\x81\xED\xB0\xB7"));
// Check that retrieving the strings returns the original UTF-8 character bytes
EXPECT_THAT(android::util::GetString(test, 0), Eq("\xF0\x90\x90\x80"));
diff --git a/libs/hwui/Android.bp b/libs/hwui/Android.bp
index c0a4fdf5eb74..88cfed9357d8 100644
--- a/libs/hwui/Android.bp
+++ b/libs/hwui/Android.bp
@@ -589,6 +589,7 @@ cc_defaults {
"ProfileData.cpp",
"ProfileDataContainer.cpp",
"Readback.cpp",
+ "Tonemapper.cpp",
"TreeInfo.cpp",
"WebViewFunctorManager.cpp",
"protos/graphicsstats.proto",
diff --git a/libs/hwui/CanvasTransform.cpp b/libs/hwui/CanvasTransform.cpp
index 673041a661ce..cd4fae86aa52 100644
--- a/libs/hwui/CanvasTransform.cpp
+++ b/libs/hwui/CanvasTransform.cpp
@@ -17,6 +17,7 @@
#include "CanvasTransform.h"
#include <SkAndroidFrameworkUtils.h>
+#include <SkBlendMode.h>
#include <SkColorFilter.h>
#include <SkGradientShader.h>
#include <SkHighContrastFilter.h>
diff --git a/libs/hwui/DeferredLayerUpdater.h b/libs/hwui/DeferredLayerUpdater.h
index 9a4c5505fa35..a7f8f6189a8e 100644
--- a/libs/hwui/DeferredLayerUpdater.h
+++ b/libs/hwui/DeferredLayerUpdater.h
@@ -16,6 +16,7 @@
#pragma once
+#include <SkBlendMode.h>
#include <SkColorFilter.h>
#include <SkImage.h>
#include <SkMatrix.h>
diff --git a/libs/hwui/Layer.cpp b/libs/hwui/Layer.cpp
index 9053c1240957..fc3118ae32dd 100644
--- a/libs/hwui/Layer.cpp
+++ b/libs/hwui/Layer.cpp
@@ -20,6 +20,8 @@
#include "utils/Color.h"
#include "utils/MathUtils.h"
+#include <SkBlendMode.h>
+
#include <log/log.h>
namespace android {
diff --git a/libs/hwui/Readback.cpp b/libs/hwui/Readback.cpp
index 02c2e67a319b..8dcd6dbe6421 100644
--- a/libs/hwui/Readback.cpp
+++ b/libs/hwui/Readback.cpp
@@ -16,16 +16,6 @@
#include "Readback.h"
-#include <sync/sync.h>
-#include <system/window.h>
-
-#include <gui/TraceUtils.h>
-#include "DeferredLayerUpdater.h"
-#include "Properties.h"
-#include "hwui/Bitmap.h"
-#include "pipeline/skia/LayerDrawable.h"
-#include "renderthread/EglManager.h"
-#include "renderthread/VulkanManager.h"
#include <SkBitmap.h>
#include <SkBlendMode.h>
#include <SkCanvas.h>
@@ -38,6 +28,19 @@
#include <SkRefCnt.h>
#include <SkSamplingOptions.h>
#include <SkSurface.h>
+#include <gui/TraceUtils.h>
+#include <private/android/AHardwareBufferHelpers.h>
+#include <shaders/shaders.h>
+#include <sync/sync.h>
+#include <system/window.h>
+
+#include "DeferredLayerUpdater.h"
+#include "Properties.h"
+#include "Tonemapper.h"
+#include "hwui/Bitmap.h"
+#include "pipeline/skia/LayerDrawable.h"
+#include "renderthread/EglManager.h"
+#include "renderthread/VulkanManager.h"
#include "utils/Color.h"
#include "utils/MathUtils.h"
#include "utils/NdkUtils.h"
@@ -91,8 +94,18 @@ void Readback::copySurfaceInto(ANativeWindow* window, const std::shared_ptr<Copy
}
}
- sk_sp<SkColorSpace> colorSpace = DataSpaceToColorSpace(
- static_cast<android_dataspace>(ANativeWindow_getBuffersDataSpace(window)));
+ int32_t dataspace = ANativeWindow_getBuffersDataSpace(window);
+
+ // If the application is not updating the Surface themselves, e.g., another
+ // process is producing buffers for the application to display, then
+ // ANativeWindow_getBuffersDataSpace will return an unknown answer, so grab
+ // the dataspace from buffer metadata instead, if it exists.
+ if (dataspace == 0) {
+ dataspace = AHardwareBuffer_getDataSpace(sourceBuffer.get());
+ }
+
+ sk_sp<SkColorSpace> colorSpace =
+ DataSpaceToColorSpace(static_cast<android_dataspace>(dataspace));
sk_sp<SkImage> image =
SkImage::MakeFromAHardwareBuffer(sourceBuffer.get(), kPremul_SkAlphaType, colorSpace);
@@ -227,6 +240,10 @@ void Readback::copySurfaceInto(ANativeWindow* window, const std::shared_ptr<Copy
const bool hasBufferCrop = cropRect.left < cropRect.right && cropRect.top < cropRect.bottom;
auto constraint =
hasBufferCrop ? SkCanvas::kStrict_SrcRectConstraint : SkCanvas::kFast_SrcRectConstraint;
+
+ static constexpr float kMaxLuminanceNits = 4000.f;
+ tonemapPaint(image->imageInfo(), canvas->imageInfo(), kMaxLuminanceNits, paint);
+
canvas->drawImageRect(image, imageSrcRect, imageDstRect, sampling, &paint, constraint);
canvas->restore();
diff --git a/libs/hwui/RecordingCanvas.cpp b/libs/hwui/RecordingCanvas.cpp
index f5ebfd5d9e23..f070e97dff2a 100644
--- a/libs/hwui/RecordingCanvas.cpp
+++ b/libs/hwui/RecordingCanvas.cpp
@@ -22,6 +22,7 @@
#include <experimental/type_traits>
#include "SkAndroidFrameworkUtils.h"
+#include "SkBlendMode.h"
#include "SkCanvas.h"
#include "SkCanvasPriv.h"
#include "SkColor.h"
diff --git a/libs/hwui/RecordingCanvas.h b/libs/hwui/RecordingCanvas.h
index 35bec9335d7c..f37729ebb59c 100644
--- a/libs/hwui/RecordingCanvas.h
+++ b/libs/hwui/RecordingCanvas.h
@@ -34,6 +34,7 @@
#include <SkRuntimeEffect.h>
#include <vector>
+enum class SkBlendMode;
class SkRRect;
namespace android {
diff --git a/libs/hwui/SkiaCanvas.h b/libs/hwui/SkiaCanvas.h
index 51007c52260d..eece77e061b6 100644
--- a/libs/hwui/SkiaCanvas.h
+++ b/libs/hwui/SkiaCanvas.h
@@ -33,6 +33,7 @@
#include <cassert>
#include <optional>
+enum class SkBlendMode;
class SkRRect;
namespace android {
diff --git a/libs/hwui/SkiaInterpolator.cpp b/libs/hwui/SkiaInterpolator.cpp
index 0695dd1ab218..153c3b6f7e04 100644
--- a/libs/hwui/SkiaInterpolator.cpp
+++ b/libs/hwui/SkiaInterpolator.cpp
@@ -17,6 +17,8 @@
#include "SkiaInterpolator.h"
#include "include/core/SkMath.h"
+#include "include/core/SkScalar.h"
+#include "include/core/SkTypes.h"
#include "include/private/SkFixed.h"
#include "include/private/SkMalloc.h"
#include "include/private/SkTo.h"
diff --git a/libs/hwui/Tonemapper.cpp b/libs/hwui/Tonemapper.cpp
new file mode 100644
index 000000000000..a7e76b631140
--- /dev/null
+++ b/libs/hwui/Tonemapper.cpp
@@ -0,0 +1,107 @@
+/*
+ * Copyright 2022 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 "Tonemapper.h"
+
+#include <SkRuntimeEffect.h>
+#include <log/log.h>
+#include <shaders/shaders.h>
+
+#include "utils/Color.h"
+
+namespace android::uirenderer {
+
+namespace {
+
+class ColorFilterRuntimeEffectBuilder : public SkRuntimeEffectBuilder {
+public:
+ explicit ColorFilterRuntimeEffectBuilder(sk_sp<SkRuntimeEffect> effect)
+ : SkRuntimeEffectBuilder(std::move(effect)) {}
+
+ sk_sp<SkColorFilter> makeColorFilter() {
+ return this->effect()->makeColorFilter(this->uniforms());
+ }
+};
+
+static sk_sp<SkColorFilter> createLinearEffectColorFilter(const shaders::LinearEffect& linearEffect,
+ float maxDisplayLuminance,
+ float currentDisplayLuminanceNits,
+ float maxLuminance) {
+ auto shaderString = SkString(shaders::buildLinearEffectSkSL(linearEffect));
+ auto [runtimeEffect, error] = SkRuntimeEffect::MakeForColorFilter(std::move(shaderString));
+ if (!runtimeEffect) {
+ LOG_ALWAYS_FATAL("LinearColorFilter construction error: %s", error.c_str());
+ }
+
+ ColorFilterRuntimeEffectBuilder effectBuilder(std::move(runtimeEffect));
+
+ const auto uniforms =
+ shaders::buildLinearEffectUniforms(linearEffect, android::mat4(), maxDisplayLuminance,
+ currentDisplayLuminanceNits, maxLuminance);
+
+ for (const auto& uniform : uniforms) {
+ effectBuilder.uniform(uniform.name.c_str()).set(uniform.value.data(), uniform.value.size());
+ }
+
+ return effectBuilder.makeColorFilter();
+}
+
+static bool extractTransfer(ui::Dataspace dataspace) {
+ return dataspace & HAL_DATASPACE_TRANSFER_MASK;
+}
+
+static bool isHdrDataspace(ui::Dataspace dataspace) {
+ const auto transfer = extractTransfer(dataspace);
+
+ return transfer == HAL_DATASPACE_TRANSFER_ST2084 || transfer == HAL_DATASPACE_TRANSFER_HLG;
+}
+
+static ui::Dataspace getDataspace(const SkImageInfo& image) {
+ return static_cast<ui::Dataspace>(
+ ColorSpaceToADataSpace(image.colorSpace(), image.colorType()));
+}
+
+} // namespace
+
+// Given a source and destination image info, and the max content luminance, generate a tonemaping
+// shader and tag it on the supplied paint.
+void tonemapPaint(const SkImageInfo& source, const SkImageInfo& destination, float maxLuminanceNits,
+ SkPaint& paint) {
+ const auto sourceDataspace = getDataspace(source);
+ const auto destinationDataspace = getDataspace(destination);
+
+ if (extractTransfer(sourceDataspace) != extractTransfer(destinationDataspace) &&
+ (isHdrDataspace(sourceDataspace) || isHdrDataspace(destinationDataspace))) {
+ const auto effect = shaders::LinearEffect{
+ .inputDataspace = sourceDataspace,
+ .outputDataspace = destinationDataspace,
+ .undoPremultipliedAlpha = source.alphaType() == kPremul_SkAlphaType,
+ .fakeInputDataspace = destinationDataspace,
+ .type = shaders::LinearEffect::SkSLType::ColorFilter};
+ constexpr float kMaxDisplayBrightnessNits = 1000.f;
+ constexpr float kCurrentDisplayBrightnessNits = 500.f;
+ sk_sp<SkColorFilter> colorFilter = createLinearEffectColorFilter(
+ effect, kMaxDisplayBrightnessNits, kCurrentDisplayBrightnessNits, maxLuminanceNits);
+
+ if (paint.getColorFilter()) {
+ paint.setColorFilter(SkColorFilters::Compose(paint.refColorFilter(), colorFilter));
+ } else {
+ paint.setColorFilter(colorFilter);
+ }
+ }
+}
+
+} // namespace android::uirenderer
diff --git a/libs/hwui/Tonemapper.h b/libs/hwui/Tonemapper.h
new file mode 100644
index 000000000000..c0d5325fa9f8
--- /dev/null
+++ b/libs/hwui/Tonemapper.h
@@ -0,0 +1,28 @@
+/*
+ * Copyright 2022 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 <SkCanvas.h>
+
+namespace android::uirenderer {
+
+// Given a source and destination image info, and the max content luminance, generate a tonemaping
+// shader and tag it on the supplied paint.
+void tonemapPaint(const SkImageInfo& source, const SkImageInfo& destination, float maxLuminanceNits,
+ SkPaint& paint);
+
+} // namespace android::uirenderer
diff --git a/libs/hwui/apex/android_paint.cpp b/libs/hwui/apex/android_paint.cpp
index 70bd085343ce..cc79cba5e19c 100644
--- a/libs/hwui/apex/android_paint.cpp
+++ b/libs/hwui/apex/android_paint.cpp
@@ -19,6 +19,7 @@
#include "TypeCast.h"
#include <hwui/Paint.h>
+#include <SkBlendMode.h>
using namespace android;
diff --git a/libs/hwui/hwui/Canvas.h b/libs/hwui/hwui/Canvas.h
index 82d23b51b12a..4608088a7cd9 100644
--- a/libs/hwui/hwui/Canvas.h
+++ b/libs/hwui/hwui/Canvas.h
@@ -30,6 +30,7 @@
#include <SkMatrix.h>
class SkAnimatedImage;
+enum class SkBlendMode;
class SkCanvasState;
class SkRRect;
class SkRuntimeShaderBuilder;
diff --git a/libs/hwui/jni/ColorFilter.cpp b/libs/hwui/jni/ColorFilter.cpp
index cef21f91f3c1..4bd7ef47b871 100644
--- a/libs/hwui/jni/ColorFilter.cpp
+++ b/libs/hwui/jni/ColorFilter.cpp
@@ -17,6 +17,7 @@
#include "GraphicsJNI.h"
+#include "SkBlendMode.h"
#include "SkColorFilter.h"
#include "SkColorMatrixFilter.h"
diff --git a/libs/hwui/jni/RenderEffect.cpp b/libs/hwui/jni/RenderEffect.cpp
index 213f35a81b88..f3db1705e694 100644
--- a/libs/hwui/jni/RenderEffect.cpp
+++ b/libs/hwui/jni/RenderEffect.cpp
@@ -15,6 +15,7 @@
*/
#include "Bitmap.h"
#include "GraphicsJNI.h"
+#include "SkBlendMode.h"
#include "SkImageFilter.h"
#include "SkImageFilters.h"
#include "graphics_jni_helpers.h"
diff --git a/libs/hwui/pipeline/skia/LayerDrawable.cpp b/libs/hwui/pipeline/skia/LayerDrawable.cpp
index 3ba540921f64..99f54c19d2e5 100644
--- a/libs/hwui/pipeline/skia/LayerDrawable.cpp
+++ b/libs/hwui/pipeline/skia/LayerDrawable.cpp
@@ -25,6 +25,7 @@
#include "SkColorFilter.h"
#include "SkRuntimeEffect.h"
#include "SkSurface.h"
+#include "Tonemapper.h"
#include "gl/GrGLTypes.h"
#include "math/mat4.h"
#include "system/graphics-base-v1.0.h"
@@ -76,37 +77,6 @@ static bool shouldFilterRect(const SkMatrix& matrix, const SkRect& srcRect, cons
isIntegerAligned(dstDevRect.y()));
}
-static sk_sp<SkShader> createLinearEffectShader(sk_sp<SkShader> shader,
- const shaders::LinearEffect& linearEffect,
- float maxDisplayLuminance,
- float currentDisplayLuminanceNits,
- float maxLuminance) {
- auto shaderString = SkString(shaders::buildLinearEffectSkSL(linearEffect));
- auto [runtimeEffect, error] = SkRuntimeEffect::MakeForShader(std::move(shaderString));
- if (!runtimeEffect) {
- LOG_ALWAYS_FATAL("LinearColorFilter construction error: %s", error.c_str());
- }
-
- SkRuntimeShaderBuilder effectBuilder(std::move(runtimeEffect));
-
- effectBuilder.child("child") = std::move(shader);
-
- const auto uniforms = shaders::buildLinearEffectUniforms(
- linearEffect, mat4(), maxDisplayLuminance, currentDisplayLuminanceNits, maxLuminance);
-
- for (const auto& uniform : uniforms) {
- effectBuilder.uniform(uniform.name.c_str()).set(uniform.value.data(), uniform.value.size());
- }
-
- return effectBuilder.makeShader();
-}
-
-static bool isHdrDataspace(ui::Dataspace dataspace) {
- const auto transfer = dataspace & HAL_DATASPACE_TRANSFER_MASK;
-
- return transfer == HAL_DATASPACE_TRANSFER_ST2084 || transfer == HAL_DATASPACE_TRANSFER_HLG;
-}
-
static void adjustCropForYUV(uint32_t format, int bufferWidth, int bufferHeight, SkRect* cropRect) {
// Chroma channels of YUV420 images are subsampled we may need to shrink the crop region by
// a whole texel on each side. Since skia still adds its own 0.5 inset, we apply an
@@ -215,31 +185,10 @@ bool LayerDrawable::DrawLayer(GrRecordingContext* context,
sampling = SkSamplingOptions(SkFilterMode::kLinear);
}
- const auto sourceDataspace = static_cast<ui::Dataspace>(
- ColorSpaceToADataSpace(layerImage->colorSpace(), layerImage->colorType()));
- const SkImageInfo& imageInfo = canvas->imageInfo();
- const auto destinationDataspace = static_cast<ui::Dataspace>(
- ColorSpaceToADataSpace(imageInfo.colorSpace(), imageInfo.colorType()));
-
- if (isHdrDataspace(sourceDataspace) || isHdrDataspace(destinationDataspace)) {
- const auto effect = shaders::LinearEffect{
- .inputDataspace = sourceDataspace,
- .outputDataspace = destinationDataspace,
- .undoPremultipliedAlpha = layerImage->alphaType() == kPremul_SkAlphaType,
- .fakeInputDataspace = destinationDataspace};
- auto shader = layerImage->makeShader(sampling,
- SkMatrix::RectToRect(skiaSrcRect, skiaDestRect));
- constexpr float kMaxDisplayBrightess = 1000.f;
- constexpr float kCurrentDisplayBrightness = 500.f;
- shader = createLinearEffectShader(std::move(shader), effect, kMaxDisplayBrightess,
- kCurrentDisplayBrightness,
- layer->getMaxLuminanceNits());
- paint.setShader(shader);
- canvas->drawRect(skiaDestRect, paint);
- } else {
- canvas->drawImageRect(layerImage.get(), skiaSrcRect, skiaDestRect, sampling, &paint,
- constraint);
- }
+ tonemapPaint(layerImage->imageInfo(), canvas->imageInfo(), layer->getMaxLuminanceNits(),
+ paint);
+ canvas->drawImageRect(layerImage.get(), skiaSrcRect, skiaDestRect, sampling, &paint,
+ constraint);
canvas->restore();
// restore the original matrix
diff --git a/libs/hwui/pipeline/skia/RenderNodeDrawable.h b/libs/hwui/pipeline/skia/RenderNodeDrawable.h
index 6c390c3fce24..c7582e734009 100644
--- a/libs/hwui/pipeline/skia/RenderNodeDrawable.h
+++ b/libs/hwui/pipeline/skia/RenderNodeDrawable.h
@@ -18,6 +18,7 @@
#include "SkiaUtils.h"
+#include <SkBlendMode.h>
#include <SkCanvas.h>
#include <SkDrawable.h>
#include <SkMatrix.h>
diff --git a/libs/hwui/pipeline/skia/StretchMask.cpp b/libs/hwui/pipeline/skia/StretchMask.cpp
index 2dbeb3adfab3..b169c9200e88 100644
--- a/libs/hwui/pipeline/skia/StretchMask.cpp
+++ b/libs/hwui/pipeline/skia/StretchMask.cpp
@@ -14,8 +14,10 @@
* limitations under the License.
*/
#include "StretchMask.h"
-#include "SkSurface.h"
+
+#include "SkBlendMode.h"
#include "SkCanvas.h"
+#include "SkSurface.h"
#include "TransformCanvas.h"
#include "SkiaDisplayList.h"
diff --git a/libs/hwui/pipeline/skia/VkInteropFunctorDrawable.cpp b/libs/hwui/pipeline/skia/VkInteropFunctorDrawable.cpp
index 3c7617d35c7c..e168a7b9459a 100644
--- a/libs/hwui/pipeline/skia/VkInteropFunctorDrawable.cpp
+++ b/libs/hwui/pipeline/skia/VkInteropFunctorDrawable.cpp
@@ -33,6 +33,8 @@
#include "thread/ThreadBase.h"
#include "utils/TimeUtils.h"
+#include <SkBlendMode.h>
+
namespace android {
namespace uirenderer {
namespace skiapipeline {
diff --git a/libs/hwui/tests/common/CallCountingCanvas.h b/libs/hwui/tests/common/CallCountingCanvas.h
index d3c41191eef1..dc36a2e01815 100644
--- a/libs/hwui/tests/common/CallCountingCanvas.h
+++ b/libs/hwui/tests/common/CallCountingCanvas.h
@@ -19,6 +19,8 @@
#include <SkCanvasVirtualEnforcer.h>
#include <SkNoDrawCanvas.h>
+enum class SkBlendMode;
+
namespace android {
namespace uirenderer {
namespace test {
diff --git a/libs/hwui/tests/common/TestListViewSceneBase.cpp b/libs/hwui/tests/common/TestListViewSceneBase.cpp
index 43df4a0b1576..e70d44c9c60a 100644
--- a/libs/hwui/tests/common/TestListViewSceneBase.cpp
+++ b/libs/hwui/tests/common/TestListViewSceneBase.cpp
@@ -19,6 +19,8 @@
#include "TestContext.h"
#include "TestUtils.h"
+#include <SkBlendMode.h>
+
#include <utils/Color.h>
namespace android {
diff --git a/libs/hwui/tests/common/scenes/BitmapFillrate.cpp b/libs/hwui/tests/common/scenes/BitmapFillrate.cpp
index 5af7d43d7f66..19e87f851827 100644
--- a/libs/hwui/tests/common/scenes/BitmapFillrate.cpp
+++ b/libs/hwui/tests/common/scenes/BitmapFillrate.cpp
@@ -19,6 +19,7 @@
#include "utils/Color.h"
#include <SkBitmap.h>
+#include <SkBlendMode.h>
using namespace android;
using namespace android::uirenderer;
diff --git a/libs/hwui/tests/common/scenes/ClippingAnimation.cpp b/libs/hwui/tests/common/scenes/ClippingAnimation.cpp
index 2a016ac1b5bc..3a1ea8c29963 100644
--- a/libs/hwui/tests/common/scenes/ClippingAnimation.cpp
+++ b/libs/hwui/tests/common/scenes/ClippingAnimation.cpp
@@ -16,6 +16,8 @@
#include "TestSceneBase.h"
+#include <SkBlendMode.h>
+
class ClippingAnimation;
static TestScene::Registrar _RectGrid(TestScene::Info{
diff --git a/libs/hwui/tests/common/scenes/GlyphStressAnimation.cpp b/libs/hwui/tests/common/scenes/GlyphStressAnimation.cpp
index 4271d2f04b88..484289a8ef1d 100644
--- a/libs/hwui/tests/common/scenes/GlyphStressAnimation.cpp
+++ b/libs/hwui/tests/common/scenes/GlyphStressAnimation.cpp
@@ -20,6 +20,8 @@
#include <hwui/Paint.h>
#include <minikin/Layout.h>
+#include <SkBlendMode.h>
+
#include <cstdio>
class GlyphStressAnimation;
diff --git a/libs/hwui/tests/common/scenes/HwBitmapInCompositeShader.cpp b/libs/hwui/tests/common/scenes/HwBitmapInCompositeShader.cpp
index 0d5ca6df9ff3..dfdd0d8727b9 100644
--- a/libs/hwui/tests/common/scenes/HwBitmapInCompositeShader.cpp
+++ b/libs/hwui/tests/common/scenes/HwBitmapInCompositeShader.cpp
@@ -17,6 +17,7 @@
#include "TestSceneBase.h"
#include "utils/Color.h"
+#include <SkBlendMode.h>
#include <SkColorSpace.h>
#include <SkGradientShader.h>
#include <SkImagePriv.h>
diff --git a/libs/hwui/tests/common/scenes/HwLayerAnimation.cpp b/libs/hwui/tests/common/scenes/HwLayerAnimation.cpp
index cac2fb3d8d5c..2955fb25ec2c 100644
--- a/libs/hwui/tests/common/scenes/HwLayerAnimation.cpp
+++ b/libs/hwui/tests/common/scenes/HwLayerAnimation.cpp
@@ -16,6 +16,8 @@
#include "TestSceneBase.h"
+#include <SkBlendMode.h>
+
class HwLayerAnimation;
static TestScene::Registrar _HwLayer(TestScene::Info{
diff --git a/libs/hwui/tests/common/scenes/HwLayerSizeAnimation.cpp b/libs/hwui/tests/common/scenes/HwLayerSizeAnimation.cpp
index 77a59dfe6ba5..8c9a6147f47d 100644
--- a/libs/hwui/tests/common/scenes/HwLayerSizeAnimation.cpp
+++ b/libs/hwui/tests/common/scenes/HwLayerSizeAnimation.cpp
@@ -16,6 +16,8 @@
#include "TestSceneBase.h"
+#include <SkBlendMode.h>
+
class HwLayerSizeAnimation;
static TestScene::Registrar _HwLayerSize(TestScene::Info{
diff --git a/libs/hwui/tests/common/scenes/JankyScene.cpp b/libs/hwui/tests/common/scenes/JankyScene.cpp
index f5e6b317529a..250b986e7e73 100644
--- a/libs/hwui/tests/common/scenes/JankyScene.cpp
+++ b/libs/hwui/tests/common/scenes/JankyScene.cpp
@@ -16,6 +16,8 @@
#include "TestSceneBase.h"
+#include <SkBlendMode.h>
+
#include <unistd.h>
class JankyScene;
diff --git a/libs/hwui/tests/common/scenes/ListOfFadedTextAnimation.cpp b/libs/hwui/tests/common/scenes/ListOfFadedTextAnimation.cpp
index 5eaf1853233a..f669dbc9323e 100644
--- a/libs/hwui/tests/common/scenes/ListOfFadedTextAnimation.cpp
+++ b/libs/hwui/tests/common/scenes/ListOfFadedTextAnimation.cpp
@@ -17,6 +17,7 @@
#include "TestSceneBase.h"
#include "tests/common/TestListViewSceneBase.h"
#include "hwui/Paint.h"
+#include <SkBlendMode.h>
#include <SkGradientShader.h>
class ListOfFadedTextAnimation;
diff --git a/libs/hwui/tests/common/scenes/OvalAnimation.cpp b/libs/hwui/tests/common/scenes/OvalAnimation.cpp
index 402c1ece2146..1a2af8382ad7 100644
--- a/libs/hwui/tests/common/scenes/OvalAnimation.cpp
+++ b/libs/hwui/tests/common/scenes/OvalAnimation.cpp
@@ -17,6 +17,8 @@
#include "TestSceneBase.h"
#include "utils/Color.h"
+#include <SkBlendMode.h>
+
class OvalAnimation;
static TestScene::Registrar _Oval(TestScene::Info{"oval", "Draws 1 oval.",
diff --git a/libs/hwui/tests/common/scenes/PartialDamageAnimation.cpp b/libs/hwui/tests/common/scenes/PartialDamageAnimation.cpp
index fb1b000a995e..25cf4d61bf9d 100644
--- a/libs/hwui/tests/common/scenes/PartialDamageAnimation.cpp
+++ b/libs/hwui/tests/common/scenes/PartialDamageAnimation.cpp
@@ -16,6 +16,8 @@
#include "TestSceneBase.h"
+#include <SkBlendMode.h>
+
class PartialDamageAnimation;
static TestScene::Registrar _PartialDamage(TestScene::Info{
diff --git a/libs/hwui/tests/common/scenes/PathClippingAnimation.cpp b/libs/hwui/tests/common/scenes/PathClippingAnimation.cpp
index 1e343c1dd283..969514c50d14 100644
--- a/libs/hwui/tests/common/scenes/PathClippingAnimation.cpp
+++ b/libs/hwui/tests/common/scenes/PathClippingAnimation.cpp
@@ -16,6 +16,8 @@
#include <vector>
+#include <SkBlendMode.h>
+
#include "TestSceneBase.h"
class PathClippingAnimation : public TestScene {
diff --git a/libs/hwui/tests/common/scenes/RectGridAnimation.cpp b/libs/hwui/tests/common/scenes/RectGridAnimation.cpp
index f37bcbc3ee1b..99e785887b16 100644
--- a/libs/hwui/tests/common/scenes/RectGridAnimation.cpp
+++ b/libs/hwui/tests/common/scenes/RectGridAnimation.cpp
@@ -16,6 +16,8 @@
#include "TestSceneBase.h"
+#include <SkBlendMode.h>
+
class RectGridAnimation;
static TestScene::Registrar _RectGrid(TestScene::Info{
diff --git a/libs/hwui/tests/common/scenes/RoundRectClippingAnimation.cpp b/libs/hwui/tests/common/scenes/RoundRectClippingAnimation.cpp
index e9f353d887f2..2c27969487d3 100644
--- a/libs/hwui/tests/common/scenes/RoundRectClippingAnimation.cpp
+++ b/libs/hwui/tests/common/scenes/RoundRectClippingAnimation.cpp
@@ -16,6 +16,8 @@
#include "TestSceneBase.h"
+#include <SkBlendMode.h>
+
#include <vector>
class RoundRectClippingAnimation : public TestScene {
diff --git a/libs/hwui/tests/common/scenes/SaveLayer2Animation.cpp b/libs/hwui/tests/common/scenes/SaveLayer2Animation.cpp
index 252f539ffca9..ee30c131efbd 100644
--- a/libs/hwui/tests/common/scenes/SaveLayer2Animation.cpp
+++ b/libs/hwui/tests/common/scenes/SaveLayer2Animation.cpp
@@ -16,6 +16,7 @@
#include <hwui/Paint.h>
#include <minikin/Layout.h>
+#include <SkBlendMode.h>
#include <string>
#include "TestSceneBase.h"
diff --git a/libs/hwui/tests/common/scenes/SaveLayerAnimation.cpp b/libs/hwui/tests/common/scenes/SaveLayerAnimation.cpp
index 31a8ae1d38cd..d5060c758f93 100644
--- a/libs/hwui/tests/common/scenes/SaveLayerAnimation.cpp
+++ b/libs/hwui/tests/common/scenes/SaveLayerAnimation.cpp
@@ -16,6 +16,8 @@
#include "TestSceneBase.h"
+#include <SkBlendMode.h>
+
class SaveLayerAnimation;
static TestScene::Registrar _SaveLayer(TestScene::Info{
diff --git a/libs/hwui/tests/common/scenes/ShadowGrid2Animation.cpp b/libs/hwui/tests/common/scenes/ShadowGrid2Animation.cpp
index c13e80e8c204..827ddab118d9 100644
--- a/libs/hwui/tests/common/scenes/ShadowGrid2Animation.cpp
+++ b/libs/hwui/tests/common/scenes/ShadowGrid2Animation.cpp
@@ -16,6 +16,8 @@
#include "TestSceneBase.h"
+#include <SkBlendMode.h>
+
class ShadowGrid2Animation;
static TestScene::Registrar _ShadowGrid2(TestScene::Info{
diff --git a/libs/hwui/tests/common/scenes/ShadowGridAnimation.cpp b/libs/hwui/tests/common/scenes/ShadowGridAnimation.cpp
index 772b98e32220..a4fb10c5081e 100644
--- a/libs/hwui/tests/common/scenes/ShadowGridAnimation.cpp
+++ b/libs/hwui/tests/common/scenes/ShadowGridAnimation.cpp
@@ -16,6 +16,8 @@
#include "TestSceneBase.h"
+#include <SkBlendMode.h>
+
class ShadowGridAnimation;
static TestScene::Registrar _ShadowGrid(TestScene::Info{
diff --git a/libs/hwui/tests/common/scenes/ShadowShaderAnimation.cpp b/libs/hwui/tests/common/scenes/ShadowShaderAnimation.cpp
index 0019da5fd80b..58c03727bc29 100644
--- a/libs/hwui/tests/common/scenes/ShadowShaderAnimation.cpp
+++ b/libs/hwui/tests/common/scenes/ShadowShaderAnimation.cpp
@@ -16,6 +16,8 @@
#include "TestSceneBase.h"
+#include <SkBlendMode.h>
+
class ShadowShaderAnimation;
static TestScene::Registrar _ShadowShader(TestScene::Info{
diff --git a/libs/hwui/tests/common/scenes/ShapeAnimation.cpp b/libs/hwui/tests/common/scenes/ShapeAnimation.cpp
index 70a1557dcf6a..c0c3dfd9a8c4 100644
--- a/libs/hwui/tests/common/scenes/ShapeAnimation.cpp
+++ b/libs/hwui/tests/common/scenes/ShapeAnimation.cpp
@@ -17,6 +17,8 @@
#include "TestSceneBase.h"
#include "utils/Color.h"
+#include <SkBlendMode.h>
+
#include <cstdio>
class ShapeAnimation;
diff --git a/libs/hwui/tests/common/scenes/SimpleColorMatrixAnimation.cpp b/libs/hwui/tests/common/scenes/SimpleColorMatrixAnimation.cpp
index 2aeb42cc0e20..40f2ed081626 100644
--- a/libs/hwui/tests/common/scenes/SimpleColorMatrixAnimation.cpp
+++ b/libs/hwui/tests/common/scenes/SimpleColorMatrixAnimation.cpp
@@ -16,6 +16,7 @@
#include "TestSceneBase.h"
+#include <SkBlendMode.h>
#include <SkColorFilter.h>
#include <SkColorMatrix.h>
#include <SkGradientShader.h>
diff --git a/libs/hwui/tests/common/scenes/SimpleGradientAnimation.cpp b/libs/hwui/tests/common/scenes/SimpleGradientAnimation.cpp
index 57a260c8d234..a9e7a34b5b3f 100644
--- a/libs/hwui/tests/common/scenes/SimpleGradientAnimation.cpp
+++ b/libs/hwui/tests/common/scenes/SimpleGradientAnimation.cpp
@@ -16,6 +16,7 @@
#include "TestSceneBase.h"
+#include <SkBlendMode.h>
#include <SkGradientShader.h>
class SimpleGradientAnimation;
diff --git a/libs/hwui/tests/common/scenes/StretchyListViewAnimation.cpp b/libs/hwui/tests/common/scenes/StretchyListViewAnimation.cpp
index 7d3ca9642458..bb95490c1d39 100644
--- a/libs/hwui/tests/common/scenes/StretchyListViewAnimation.cpp
+++ b/libs/hwui/tests/common/scenes/StretchyListViewAnimation.cpp
@@ -15,6 +15,7 @@
*/
#include <SkBitmap.h>
+#include <SkBlendMode.h>
#include <SkCanvas.h>
#include <SkColor.h>
#include <SkFont.h>
diff --git a/libs/hwui/tests/common/scenes/TextAnimation.cpp b/libs/hwui/tests/common/scenes/TextAnimation.cpp
index d30903679bce..78146b8cabf2 100644
--- a/libs/hwui/tests/common/scenes/TextAnimation.cpp
+++ b/libs/hwui/tests/common/scenes/TextAnimation.cpp
@@ -17,6 +17,8 @@
#include "TestSceneBase.h"
#include "hwui/Paint.h"
+#include <SkBlendMode.h>
+
class TextAnimation;
static TestScene::Registrar _Text(TestScene::Info{"text", "Draws a bunch of text.",
diff --git a/libs/hwui/tests/microbench/DisplayListCanvasBench.cpp b/libs/hwui/tests/microbench/DisplayListCanvasBench.cpp
index 9cd10759a834..a55b72534924 100644
--- a/libs/hwui/tests/microbench/DisplayListCanvasBench.cpp
+++ b/libs/hwui/tests/microbench/DisplayListCanvasBench.cpp
@@ -22,6 +22,8 @@
#include "pipeline/skia/SkiaDisplayList.h"
#include "tests/common/TestUtils.h"
+#include <SkBlendMode.h>
+
using namespace android;
using namespace android::uirenderer;
using namespace android::uirenderer::skiapipeline;
diff --git a/libs/hwui/tests/microbench/RenderNodeBench.cpp b/libs/hwui/tests/microbench/RenderNodeBench.cpp
index 6aed251481bf..72946c4abdf0 100644
--- a/libs/hwui/tests/microbench/RenderNodeBench.cpp
+++ b/libs/hwui/tests/microbench/RenderNodeBench.cpp
@@ -19,6 +19,8 @@
#include "hwui/Canvas.h"
#include "RenderNode.h"
+#include <SkBlendMode.h>
+
using namespace android;
using namespace android::uirenderer;
diff --git a/libs/hwui/tests/unit/CanvasOpTests.cpp b/libs/hwui/tests/unit/CanvasOpTests.cpp
index d2b1ef91a898..1f6edf36af25 100644
--- a/libs/hwui/tests/unit/CanvasOpTests.cpp
+++ b/libs/hwui/tests/unit/CanvasOpTests.cpp
@@ -23,6 +23,7 @@
#include <tests/common/CallCountingCanvas.h>
+#include "SkBlendMode.h"
#include "SkBitmap.h"
#include "SkCanvas.h"
#include "SkColor.h"
diff --git a/libs/hwui/tests/unit/RenderNodeDrawableTests.cpp b/libs/hwui/tests/unit/RenderNodeDrawableTests.cpp
index ec949b80ea55..3caba2d410bd 100644
--- a/libs/hwui/tests/unit/RenderNodeDrawableTests.cpp
+++ b/libs/hwui/tests/unit/RenderNodeDrawableTests.cpp
@@ -17,6 +17,7 @@
#include <VectorDrawable.h>
#include <gtest/gtest.h>
+#include <SkBlendMode.h>
#include <SkClipStack.h>
#include <SkSurface_Base.h>
#include <string.h>
diff --git a/libs/hwui/tests/unit/SkiaCanvasTests.cpp b/libs/hwui/tests/unit/SkiaCanvasTests.cpp
index 50d9f5683a8b..87c52161d68e 100644
--- a/libs/hwui/tests/unit/SkiaCanvasTests.cpp
+++ b/libs/hwui/tests/unit/SkiaCanvasTests.cpp
@@ -17,10 +17,19 @@
#include "tests/common/TestUtils.h"
#include <hwui/Paint.h>
+#include <SkAlphaType.h>
+#include <SkBitmap.h>
+#include <SkBlendMode.h>
+#include <SkCanvas.h>
#include <SkCanvasStateUtils.h>
+#include <SkColor.h>
#include <SkColorSpace.h>
+#include <SkColorType.h>
+#include <SkImageInfo.h>
#include <SkPicture.h>
#include <SkPictureRecorder.h>
+#include <SkRefCnt.h>
+#include <SkSurface.h>
#include <gtest/gtest.h>
using namespace android;
diff --git a/libs/hwui/tests/unit/SkiaPipelineTests.cpp b/libs/hwui/tests/unit/SkiaPipelineTests.cpp
index 7419f8fd89f1..4d0595e03da6 100644
--- a/libs/hwui/tests/unit/SkiaPipelineTests.cpp
+++ b/libs/hwui/tests/unit/SkiaPipelineTests.cpp
@@ -17,6 +17,7 @@
#include <VectorDrawable.h>
#include <gtest/gtest.h>
+#include <SkBlendMode.h>
#include <SkClipStack.h>
#include <SkSurface_Base.h>
#include <string.h>
diff --git a/libs/hwui/utils/PaintUtils.h b/libs/hwui/utils/PaintUtils.h
index 94bcb1110e05..f44f9d0fe2d4 100644
--- a/libs/hwui/utils/PaintUtils.h
+++ b/libs/hwui/utils/PaintUtils.h
@@ -19,6 +19,7 @@
#include <GLES2/gl2.h>
#include <utils/Blur.h>
+#include <SkBlendMode.h>
#include <SkColorFilter.h>
#include <SkPaint.h>
#include <SkShader.h>