summaryrefslogtreecommitdiff
diff options
context:
space:
mode:
authorAdrian Roos <roosa@google.com>2018-08-22 20:19:49 +0200
committerMichael Wright <michaelwr@google.com>2018-08-22 21:37:39 +0100
commit9ee5dff83c1cae93dff281735b60134dbd26dd8a (patch)
tree149228b8475292809b26cc01f0d132567c1cb680
parent2c9fd5fbbb43541ce4291c07bf61bdbe5dc70252 (diff)
downloadbase-9ee5dff83c1cae93dff281735b60134dbd26dd8a.tar.gz
BootAnimation: Fix boot animation with hidden cutout
We do this by storing the masking inset in a persistent property. The boot animation then animates itself to where it would be if that masking inset were applied, then changes the viewport. For this to work, we also need to make sure the DisplayManagerService has the right overlay right at the start. Bug: 112876936 Test: Hide cutout, then reboot. Verify boot animation is smooth. Change-Id: I3e988b2340b2e0d2be3939bdc6878704c234ccc8
-rw-r--r--cmds/bootanimation/BootAnimation.cpp49
-rw-r--r--cmds/bootanimation/BootAnimation.h4
-rw-r--r--core/java/android/app/ActivityThread.java11
-rw-r--r--services/core/java/com/android/server/display/DisplayManagerService.java45
-rw-r--r--services/core/java/com/android/server/display/LogicalDisplay.java14
-rw-r--r--services/core/java/com/android/server/om/OverlayManagerService.java23
-rw-r--r--services/java/com/android/server/SystemServer.java11
7 files changed, 152 insertions, 5 deletions
diff --git a/cmds/bootanimation/BootAnimation.cpp b/cmds/bootanimation/BootAnimation.cpp
index 8ffe5bf59315..ed6c25dc49c3 100644
--- a/cmds/bootanimation/BootAnimation.cpp
+++ b/cmds/bootanimation/BootAnimation.cpp
@@ -302,6 +302,7 @@ status_t BootAnimation::readyToRun() {
mHeight = h;
mFlingerSurfaceControl = control;
mFlingerSurface = s;
+ mTargetInset = -1;
// If the device has encryption turned on or is in process
// of being encrypted we show the encrypted boot animation.
@@ -942,6 +943,7 @@ bool BootAnimation::playAnimation(const Animation& animation)
if (mClockEnabled && mTimeIsAccurate && validClock(part)) {
drawClock(animation.clockFont, part.clockPosX, part.clockPosY);
}
+ handleViewport(frameDuration);
eglSwapBuffers(mDisplay, mSurface);
@@ -966,7 +968,7 @@ bool BootAnimation::playAnimation(const Animation& animation)
usleep(part.pause * ns2us(frameDuration));
// For infinite parts, we've now played them at least once, so perhaps exit
- if(exitPending() && !part.count)
+ if(exitPending() && !part.count && mCurrentInset >= mTargetInset)
break;
}
@@ -986,6 +988,51 @@ bool BootAnimation::playAnimation(const Animation& animation)
return true;
}
+void BootAnimation::handleViewport(nsecs_t timestep) {
+ if (mShuttingDown || !mFlingerSurfaceControl || mTargetInset == 0) {
+ return;
+ }
+ if (mTargetInset < 0) {
+ // Poll the amount for the top display inset. This will return -1 until persistent properties
+ // have been loaded.
+ mTargetInset = android::base::GetIntProperty("persist.sys.displayinset.top",
+ -1 /* default */, -1 /* min */, mHeight / 2 /* max */);
+ }
+ if (mTargetInset <= 0) {
+ return;
+ }
+
+ if (mCurrentInset < mTargetInset) {
+ // After the device boots, the inset will effectively be cropped away. We animate this here.
+ float fraction = static_cast<float>(mCurrentInset) / mTargetInset;
+ int interpolatedInset = (cosf((fraction + 1) * M_PI) / 2.0f + 0.5f) * mTargetInset;
+
+ SurfaceComposerClient::Transaction()
+ .setCrop(mFlingerSurfaceControl, Rect(0, interpolatedInset, mWidth, mHeight))
+ .apply();
+ } else {
+ // At the end of the animation, we switch to the viewport that DisplayManager will apply
+ // later. This changes the coordinate system, and means we must move the surface up by
+ // the inset amount.
+ sp<IBinder> dtoken(SurfaceComposerClient::getBuiltInDisplay(
+ ISurfaceComposer::eDisplayIdMain));
+
+ Rect layerStackRect(0, 0, mWidth, mHeight - mTargetInset);
+ Rect displayRect(0, mTargetInset, mWidth, mHeight);
+
+ SurfaceComposerClient::Transaction t;
+ t.setPosition(mFlingerSurfaceControl, 0, -mTargetInset)
+ .setCrop(mFlingerSurfaceControl, Rect(0, mTargetInset, mWidth, mHeight));
+ t.setDisplayProjection(dtoken, 0 /* orientation */, layerStackRect, displayRect);
+ t.apply();
+
+ mTargetInset = mCurrentInset = 0;
+ }
+
+ int delta = timestep * mTargetInset / ms2ns(200);
+ mCurrentInset += delta;
+}
+
void BootAnimation::releaseAnimation(Animation* animation) const
{
for (Vector<Animation::Part>::iterator it = animation->parts.begin(),
diff --git a/cmds/bootanimation/BootAnimation.h b/cmds/bootanimation/BootAnimation.h
index 56e131523bcb..b4699d884681 100644
--- a/cmds/bootanimation/BootAnimation.h
+++ b/cmds/bootanimation/BootAnimation.h
@@ -157,11 +157,15 @@ private:
void checkExit();
+ void handleViewport(nsecs_t timestep);
+
sp<SurfaceComposerClient> mSession;
AssetManager mAssets;
Texture mAndroid[2];
int mWidth;
int mHeight;
+ int mCurrentInset;
+ int mTargetInset;
bool mUseNpotTextures = false;
EGLDisplay mDisplay;
EGLDisplay mContext;
diff --git a/core/java/android/app/ActivityThread.java b/core/java/android/app/ActivityThread.java
index 87b334992d2d..ab0b88c22d28 100644
--- a/core/java/android/app/ActivityThread.java
+++ b/core/java/android/app/ActivityThread.java
@@ -146,6 +146,7 @@ import com.android.internal.os.RuntimeInit;
import com.android.internal.os.SomeArgs;
import com.android.internal.util.ArrayUtils;
import com.android.internal.util.FastPrintWriter;
+import com.android.internal.util.Preconditions;
import com.android.internal.util.function.pooled.PooledLambda;
import com.android.org.conscrypt.OpenSSLSocketImpl;
import com.android.org.conscrypt.TrustedCertificateStore;
@@ -5144,6 +5145,16 @@ public final class ActivityThread extends ClientTransactionHandler {
}
}
+ /**
+ * Updates the application info.
+ *
+ * This only works in the system process. Must be called on the main thread.
+ */
+ public void handleSystemApplicationInfoChanged(@NonNull ApplicationInfo ai) {
+ Preconditions.checkState(mSystemThread, "Must only be called in the system process");
+ handleApplicationInfoChanged(ai);
+ }
+
void handleApplicationInfoChanged(@NonNull final ApplicationInfo ai) {
// Updates triggered by package installation go through a package update
// receiver. Here we try to capture ApplicationInfo changes that are
diff --git a/services/core/java/com/android/server/display/DisplayManagerService.java b/services/core/java/com/android/server/display/DisplayManagerService.java
index ddd8855f3199..cc5a8271eb5f 100644
--- a/services/core/java/com/android/server/display/DisplayManagerService.java
+++ b/services/core/java/com/android/server/display/DisplayManagerService.java
@@ -151,6 +151,8 @@ public final class DisplayManagerService extends SystemService {
// Otherwise WFD is enabled according to the value of config_enableWifiDisplay.
private static final String FORCE_WIFI_DISPLAY_ENABLE = "persist.debug.wfd.enable";
+ private static final String PROP_DEFAULT_DISPLAY_TOP_INSET = "persist.sys.displayinset.top";
+
private static final long WAIT_FOR_DEFAULT_DISPLAY_TIMEOUT = 10000;
private static final int MSG_REGISTER_DEFAULT_DISPLAY_ADAPTERS = 1;
@@ -243,6 +245,15 @@ public final class DisplayManagerService extends SystemService {
// device).
private Point mStableDisplaySize = new Point();
+ // Whether the system has finished booting or not.
+ private boolean mSystemReady;
+
+ // The top inset of the default display.
+ // This gets persisted so that the boot animation knows how to transition from the display's
+ // full size to the size configured by the user. Right now we only persist and animate the top
+ // inset, but theoretically we could do it for all of them.
+ private int mDefaultDisplayTopInset;
+
// Viewports of the default display and the display that should receive touch
// input from an external source. Used by the input system.
private final DisplayViewport mDefaultViewport = new DisplayViewport();
@@ -301,6 +312,7 @@ public final class DisplayManagerService extends SystemService {
Resources resources = mContext.getResources();
mDefaultDisplayDefaultColorMode = mContext.getResources().getInteger(
com.android.internal.R.integer.config_defaultDisplayDefaultColorMode);
+ mDefaultDisplayTopInset = SystemProperties.getInt(PROP_DEFAULT_DISPLAY_TOP_INSET, -1);
float[] lux = getFloatArray(resources.obtainTypedArray(
com.android.internal.R.array.config_minimumBrightnessCurveLux));
float[] nits = getFloatArray(resources.obtainTypedArray(
@@ -311,6 +323,8 @@ public final class DisplayManagerService extends SystemService {
PowerManager pm = (PowerManager) mContext.getSystemService(Context.POWER_SERVICE);
mGlobalDisplayBrightness = pm.getDefaultScreenBrightnessSetting();
mCurrentUserId = UserHandle.USER_SYSTEM;
+
+ mSystemReady = false;
}
public void setupSchedulerPolicies() {
@@ -400,6 +414,10 @@ public final class DisplayManagerService extends SystemService {
synchronized (mSyncRoot) {
mSafeMode = safeMode;
mOnlyCore = onlyCore;
+ mSystemReady = true;
+ // Just in case the top inset changed before the system was ready. At this point, any
+ // relevant configuration should be in place.
+ recordTopInsetLocked(mLogicalDisplays.get(Display.DEFAULT_DISPLAY));
}
mHandler.sendEmptyMessage(MSG_REGISTER_ADDITIONAL_DISPLAY_ADAPTERS);
@@ -457,7 +475,7 @@ public final class DisplayManagerService extends SystemService {
LogicalDisplay display = mLogicalDisplays.get(displayId);
if (display != null) {
if (display.setDisplayInfoOverrideFromWindowManagerLocked(info)) {
- sendDisplayEventLocked(displayId, DisplayManagerGlobal.EVENT_DISPLAY_CHANGED);
+ handleLogicalDisplayChanged(displayId, display);
scheduleTraversalLocked(false);
}
}
@@ -938,6 +956,13 @@ public final class DisplayManagerService extends SystemService {
scheduleTraversalLocked(false);
}
+ private void handleLogicalDisplayChanged(int displayId, @NonNull LogicalDisplay display) {
+ if (displayId == Display.DEFAULT_DISPLAY) {
+ recordTopInsetLocked(display);
+ }
+ sendDisplayEventLocked(displayId, DisplayManagerGlobal.EVENT_DISPLAY_CHANGED);
+ }
+
private void applyGlobalDisplayStateLocked(List<Runnable> workQueue) {
final int count = mDisplayDevices.size();
for (int i = 0; i < count; i++) {
@@ -991,6 +1016,7 @@ public final class DisplayManagerService extends SystemService {
configureColorModeLocked(display, device);
if (isDefault) {
recordStableDisplayStatsIfNeededLocked(display);
+ recordTopInsetLocked(display);
}
mLogicalDisplays.put(displayId, display);
@@ -1039,6 +1065,21 @@ public final class DisplayManagerService extends SystemService {
}
}
+ private void recordTopInsetLocked(@Nullable LogicalDisplay d) {
+ // We must only persist the inset after boot has completed, otherwise we will end up
+ // overwriting the persisted value before the masking flag has been loaded from the
+ // resource overlay.
+ if (!mSystemReady || d == null) {
+ return;
+ }
+ int topInset = d.getInsets().top;
+ if (topInset == mDefaultDisplayTopInset) {
+ return;
+ }
+ mDefaultDisplayTopInset = topInset;
+ SystemProperties.set(PROP_DEFAULT_DISPLAY_TOP_INSET, Integer.toString(topInset));
+ }
+
private void setStableDisplaySizeLocked(int width, int height) {
mStableDisplaySize = new Point(width, height);
try {
@@ -1118,7 +1159,7 @@ public final class DisplayManagerService extends SystemService {
sendDisplayEventLocked(displayId, DisplayManagerGlobal.EVENT_DISPLAY_REMOVED);
changed = true;
} else if (!mTempDisplayInfo.equals(display.getDisplayInfoLocked())) {
- sendDisplayEventLocked(displayId, DisplayManagerGlobal.EVENT_DISPLAY_CHANGED);
+ handleLogicalDisplayChanged(displayId, display);
changed = true;
}
}
diff --git a/services/core/java/com/android/server/display/LogicalDisplay.java b/services/core/java/com/android/server/display/LogicalDisplay.java
index 373de63c0ec9..5b7c5205ce3a 100644
--- a/services/core/java/com/android/server/display/LogicalDisplay.java
+++ b/services/core/java/com/android/server/display/LogicalDisplay.java
@@ -18,6 +18,7 @@ package com.android.server.display;
import android.graphics.Rect;
import android.hardware.display.DisplayManagerInternal;
+import android.os.SystemProperties;
import android.view.Display;
import android.view.DisplayInfo;
import android.view.Surface;
@@ -57,6 +58,8 @@ import java.util.Objects;
* </p>
*/
final class LogicalDisplay {
+ private static final String PROP_MASKING_INSET_TOP = "persist.sys.displayinset.top";
+
private final DisplayInfo mBaseDisplayInfo = new DisplayInfo();
// The layer stack we use when the display has been blanked to prevent any
@@ -297,6 +300,17 @@ final class LogicalDisplay {
}
/**
+ * Return the insets currently applied to the display.
+ *
+ * Note that the base DisplayInfo already takes these insets into account, so if you want to
+ * find out the <b>true</b> size of the display, you need to add them back to the logical
+ * dimensions.
+ */
+ public Rect getInsets() {
+ return getMaskingInsets(mPrimaryDisplayDeviceInfo);
+ }
+
+ /**
* Returns insets in ROTATION_0 for areas that are masked.
*/
private static Rect getMaskingInsets(DisplayDeviceInfo deviceInfo) {
diff --git a/services/core/java/com/android/server/om/OverlayManagerService.java b/services/core/java/com/android/server/om/OverlayManagerService.java
index f082271ab094..c73870189002 100644
--- a/services/core/java/com/android/server/om/OverlayManagerService.java
+++ b/services/core/java/com/android/server/om/OverlayManagerService.java
@@ -22,11 +22,14 @@ import static android.content.Intent.ACTION_PACKAGE_CHANGED;
import static android.content.Intent.ACTION_PACKAGE_REMOVED;
import static android.content.Intent.ACTION_USER_ADDED;
import static android.content.Intent.ACTION_USER_REMOVED;
+import static android.content.pm.PackageManager.GET_SHARED_LIBRARY_FILES;
+import static android.content.pm.PackageManager.MATCH_SYSTEM_ONLY;
import static android.content.pm.PackageManager.SIGNATURE_MATCH;
import android.annotation.NonNull;
import android.annotation.Nullable;
import android.app.ActivityManager;
+import android.app.ActivityThread;
import android.app.IActivityManager;
import android.content.BroadcastReceiver;
import android.content.Context;
@@ -34,6 +37,7 @@ import android.content.Intent;
import android.content.IntentFilter;
import android.content.om.IOverlayManager;
import android.content.om.OverlayInfo;
+import android.content.pm.ApplicationInfo;
import android.content.pm.IPackageManager;
import android.content.pm.PackageInfo;
import android.content.pm.PackageManagerInternal;
@@ -269,13 +273,30 @@ public final class OverlayManagerService extends SystemService {
@Override
public void onBootPhase(int phase) {
- if (phase == PHASE_SYSTEM_SERVICES_READY) {
+ if (phase == PHASE_SYSTEM_SERVICES_READY && mInitCompleteSignal != null) {
ConcurrentUtils.waitForFutureNoInterrupt(mInitCompleteSignal,
"Wait for OverlayManagerService init");
mInitCompleteSignal = null;
}
}
+ public void updateSystemUiContext() {
+ if (mInitCompleteSignal != null) {
+ ConcurrentUtils.waitForFutureNoInterrupt(mInitCompleteSignal,
+ "Wait for OverlayManagerService init");
+ mInitCompleteSignal = null;
+ }
+
+ final ApplicationInfo ai;
+ try {
+ ai = mPackageManager.mPackageManager.getApplicationInfo("android",
+ GET_SHARED_LIBRARY_FILES, UserHandle.USER_SYSTEM);
+ } catch (RemoteException e) {
+ throw e.rethrowAsRuntimeException();
+ }
+ ActivityThread.currentActivityThread().handleSystemApplicationInfoChanged(ai);
+ }
+
private void initIfNeeded() {
final UserManager um = getContext().getSystemService(UserManager.class);
final List<UserInfo> users = um.getUsers(true /*excludeDying*/);
diff --git a/services/java/com/android/server/SystemServer.java b/services/java/com/android/server/SystemServer.java
index 3c833d862d9c..a61970e063bb 100644
--- a/services/java/com/android/server/SystemServer.java
+++ b/services/java/com/android/server/SystemServer.java
@@ -29,6 +29,7 @@ import android.content.res.Configuration;
import android.content.res.Resources.Theme;
import android.database.sqlite.SQLiteCompatibilityWalFlags;
import android.database.sqlite.SQLiteGlobal;
+import android.hardware.display.DisplayManagerInternal;
import android.os.BaseBundle;
import android.os.Binder;
import android.os.Build;
@@ -677,9 +678,17 @@ public final class SystemServer {
// Manages Overlay packages
traceBeginAndSlog("StartOverlayManagerService");
- mSystemServiceManager.startService(new OverlayManagerService(mSystemContext, installer));
+ OverlayManagerService overlayManagerService = new OverlayManagerService(
+ mSystemContext, installer);
+ mSystemServiceManager.startService(overlayManagerService);
traceEnd();
+ if (SystemProperties.getInt("persist.sys.displayinset.top", 0) > 0) {
+ // DisplayManager needs the overlay immediately.
+ overlayManagerService.updateSystemUiContext();
+ LocalServices.getService(DisplayManagerInternal.class).onOverlayChanged();
+ }
+
// The sensor service needs access to package manager service, app ops
// service, and permissions service, therefore we start it after them.
// Start sensor service in a separate thread. Completion should be checked