diff options
Diffstat (limited to 'libs')
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> |