Android 7.1 解决开机动画旋转问题

本文详细解析了Android系统中开机动画在屏幕旋转时出现的问题,包括启动界面方向固定及绘制过程中的方向跳变。通过对BootAnimation.cpp和WindowManagerService.java等核心文件的深入分析,提出了解决方案。

说明

本文所述解决方案具有 特殊性, 针对性, 需结合系统当前实现的屏幕方向设置方案.
仅供参考 请勿生搬硬套

*本文无动画流程解读说明, 请移步
*本文无固定屏幕方向方案解读说明, 请移步

平台

RK3288 + Android 7.1

概述

固定了屏幕方向后, 默认不旋转时, 所有的显示都正常, 但是, 在旋转后, 开机动画显示的方向要么不准确, 要么会跳变.

前提

屏幕旋转方向有四个值, 分别为
Surface.ROTATION_0 (默认)
Surface.ROTATION_90
Surface.ROTATION_180
Surface.ROTATION_270

分析

|-- frameworks/base/cmds/bootanimation/BootAnimation.cpp

BootAnimation::BootAnimation(bool shutdown) : Thread(false), mClockEnabled(true), mTimeIsAccurate(false),
        mTimeFormat12Hour(false), mTimeCheckThread(NULL) {
    mSession = new SurfaceComposerClient();

    // If the system has already booted, the animation is not being used for a boot.
    mSystemBoot = !property_get_bool(BOOT_COMPLETED_PROP_NAME, 0);
    mShutdown = shutdown;
    mReverseAxis = false;
    mVideoFile = NULL;
    mVideoAnimation = false;
    if(mShutdown){
        sp<IBinder> dtoken(SurfaceComposerClient::getBuiltInDisplay(
                                        ISurfaceComposer::eDisplayIdMain)); // primary_display_token
        DisplayInfo dinfo;
        status_t status = SurfaceComposerClient::getDisplayInfo(dtoken, &dinfo);
        if (status == OK) {
            ALOGD("DISPLAY,W-H: %d-%d, ori: %d", dinfo.w, dinfo.h, dinfo.orientation);
            if(dinfo.orientation==1 || dinfo.orientation==3 )
                mReverseAxis=true;
            else
                mReverseAxis=false;
        }
	}
}

初步怀疑是屏幕旋转导致了问题存在, 于是, 从上面的代码中, 抽取了读取屏幕信息的方法, 并打印出信息.
代码到动画绘制的循环中

bool BootAnimation::android()
{
    //...
    glBlendFunc(GL_SRC_ALPHA, GL_ONE_MINUS_SRC_ALPHA);
    glTexEnvx(GL_TEXTURE_ENV, GL_TEXTURE_ENV_MODE, GL_REPLACE);

//加入打印显示信息代码
	 sp<IBinder> dtoken(SurfaceComposerClient::getBuiltInDisplay(
                                        ISurfaceComposer::eDisplayIdMain)); // primary_display_token
     DisplayInfo dinfo;

    const nsecs_t startTime = systemTime();
    do {
//加入打印显示信息代码.
		status_t status = SurfaceComposerClient::getDisplayInfo(dtoken, &dinfo);
		if (status == OK) {
            ALOGD("DISPLAY,W-H: %d-%d, ori: %d", dinfo.w, dinfo.h, dinfo.orientation);
		}
		//...
        checkExit();
    } while (!exitPending());
}

LOG内容如下:

01-03 01:17:31.965 327-376/? D/BootAnimation: DISPLAY,W-H: 1920-1080, ori: 0

手上的设备是 1920x1080 15.6寸屏, 默认为横屏

当固定屏幕方向旋转90度(竖屏)时, 输出的LOG为:

2019-09-09 09:31:39.731 ? D/BootAnimation: DISPLAY,W-H: 1920-1080, ori: 0
//还有很多
2019-09-09 09:31:47.471 ? D/BootAnimation: DISPLAY,W-H: 1920-1080, ori: 1
//还有很多

通过观察, 旋转问题出现时确实是在屏幕方向变化时, 另外, 还有一个问题, 第一个动画的界面, 方向依然是默认的0度横屏方向.

总结下问题有两个:

  1. 动画启动界面方向为默认零度, 不随系统设置变化.
  2. 动画启动过程中, 会出来旋转并导致显示内容不完整, 如图:
    在这里插入图片描述

解决动画启动界面方向:

|-- frameworks/base/cmds/bootanimation/BootAnimation.cpp

BootAnimation::BootAnimation(bool shutdown) : Thread(false), mClockEnabled(true), mTimeIsAccurate(false),
        mTimeFormat12Hour(false), mTimeCheckThread(NULL) {
    mSession = new SurfaceComposerClient();

    // If the system has already booted, the animation is not being used for a boot.
    mSystemBoot = !property_get_bool(BOOT_COMPLETED_PROP_NAME, 0);
    mShutdown = shutdown;
    mReverseAxis = false;
    mVideoFile = NULL;
    mVideoAnimation = false;
//注释mShotdown判断, 启动时, 方向有可能设置为0以外的数值
    //if(mShutdown){
        sp<IBinder> dtoken(SurfaceComposerClient::getBuiltInDisplay(
                                        ISurfaceComposer::eDisplayIdMain)); // primary_display_token
        DisplayInfo dinfo;
        status_t status = SurfaceComposerClient::getDisplayInfo(dtoken, &dinfo);
        if (status == OK) {
            ALOGD("DISPLAY,W-H: %d-%d, ori: %d", dinfo.w, dinfo.h, dinfo.orientation);

//解决启动时方向错误, 其中, 变量 rot根据需要的方向设置[0, 3];			
			int rot = 1;//90度
			if (rot >= 0) {
				int w = dinfo.w;
				int h = dinfo.h;
				if(rot == 1 || rot == 3){//竖屏时, xy轴需要对调
					mReverseAxis=true;
					h = dinfo.w;
					w = dinfo.h;
				}else{
					mReverseAxis=false;
				}

				Rect layerStackRect(w, h);
				Rect displayRect(0, 0, w, h);
				SurfaceComposerClient::setDisplayProjection(dtoken, rot, layerStackRect, displayRect);
			}

        }
    //}
}

status_t BootAnimation::readyToRun() {
    mAssets.addDefaultAssets();

    sp<IBinder> dtoken(SurfaceComposerClient::getBuiltInDisplay(
            ISurfaceComposer::eDisplayIdMain));
    DisplayInfo dinfo;
    status_t status = SurfaceComposerClient::getDisplayInfo(dtoken, &dinfo);
    if (status)
        return -1;

    // create the native surface
    int curWidth = dinfo.w;
    int curHeight = dinfo.h;
//注释 mShutdown, 屏幕旋转不只发生在关机动画时.
    if(/*mShutdown && */mReverseAxis){
        curWidth = dinfo.h;
        curHeight = dinfo.w;
    }
//...省略代码
}

解决动画绘制过程中方向跳变.

动画中打印的LOG如下:

//启动正常
2019-09-09 10:41:20.662 ? D/BootAnimation: DISPLAY,W-H: 1920-1080, ori: 0
2019-09-09 10:41:20.818 ? D/BootAnimation: DISPLAY,W-H: 1920-1080, ori: 1
//省略

//到这时转回默认方向, 
2019-09-09 10:41:25.101 ? D/BootAnimation: DISPLAY,W-H: 1920-1080, ori: 0
//省略

//最后转回正常方向
2019-09-09 10:41:29.438 ? D/BootAnimation: DISPLAY,W-H: 1920-1080, ori: 1

|–frameworks/base/services/core/java/com/android/server/wm/WindowManagerService.java

    private WindowManagerService(Context context, InputManagerService inputManager,
            boolean haveInputMethods, boolean showBootMsgs, boolean onlyCore) {
		//... 省略代码
        // Add ourself to the Watchdog monitors.
        Watchdog.getInstance().addMonitor(this);

//设置默认旋转方向, 与动画的默认方向保持一致
		mRotation = Surface.ROTATION_90;

        SurfaceControl.openTransaction();
        try {
            createWatermarkInTransaction();
        } finally {
            SurfaceControl.closeTransaction();
        }

        showEmulatorDisplayOverlayIfNeeded();
    }

    /**
     * Updates the current rotation.
     *
     * Returns true if the rotation has been changed.  In this case YOU
     * MUST CALL sendNewConfiguration() TO UNFREEZE THE SCREEN.
     */
    public boolean updateRotationUncheckedLocked(boolean inTransaction) {
//省略大量代码
        if (DEBUG_ORIENTATION) {
            Slog.v(TAG_WM, "Selected orientation "
                    + mLastOrientation + ", got rotation " + rotation
                    + " which has " + (altOrientation ? "incompatible" : "compatible")
                    + " metrics");
        }
//保持默认方向
        /*if (mRotateOnBoot) {
             mRotation = Surface.ROTATION_0;
             rotation = Surface.ROTATION_90;
        }*/

        if (mRotation == rotation && mAltOrientation == altOrientation) {
            // No change.
             return false;
        }
//省略大量代码
	}

转屏相关代码记录:

以下流程存在多次调用, 分析时需仔细

     com.android.server.wm.WindowManagerService.displayReady(WindowManagerService.java:8781)
     com.android.server.am.ActivityManagerService.updateConfiguration(ActivityManagerService.java:19241)
     com.android.server.am.ActivityManagerService.updateConfigurationLocked(ActivityManagerService.java:19255)
     com.android.server.am.ActivityManagerService.updateConfigurationLocked(ActivityManagerService.java:19261)
     com.android.server.am.ActivityManagerService.updateConfigurationLocked(ActivityManagerService.java:19393)
     com.android.server.wm.WindowManagerService.setNewConfiguration(WindowManagerService.java:4413)
     com.android.server.wm.WindowManagerService.onConfigurationChanged(WindowManagerService.java:4448)
     com.android.server.wm.WindowManagerService.reconfigureDisplayLocked(WindowManagerService.java:9952)
     com.android.server.wm.WindowManagerService.sendNewConfiguration(WindowManagerService.java:8035)
     com.android.server.am.ActivityManagerService.updateConfiguration(ActivityManagerService.java:19241)
     com.android.server.am.ActivityManagerService.updateConfigurationLocked(ActivityManagerService.java:19255)
     com.android.server.am.ActivityManagerService.updateConfigurationLocked(ActivityManagerService.java:19261)
     com.android.server.am.ActivityManagerService.updateConfigurationLocked(ActivityManagerService.java:19424)
     com.android.server.wm.WindowManagerService.continueSurfaceLayout(WindowManagerService.java:5957)
     com.android.server.wm.WindowSurfacePlacer.continueLayout(WindowSurfacePlacer.java:173)
     com.android.server.wm.WindowSurfacePlacer.performSurfacePlacement(WindowSurfacePlacer.java:184)
     com.android.server.wm.WindowSurfacePlacer.performSurfacePlacementLoop(WindowSurfacePlacer.java:235)
     com.android.server.wm.WindowSurfacePlacer.performSurfacePlacementInner(WindowSurfacePlacer.java:320)
     com.android.server.wm.WindowSurfacePlacer.applySurfaceChangesTransaction(WindowSurfacePlacer.java:884)
     com.android.server.display.DisplayManagerService$LocalService.performTraversalInTransactionFromWindowManager(DisplayManagerService.java:1671)
     com.android.server.display.DisplayManagerService.performTraversalInTransactionFromWindowManagerInternal(DisplayManagerService.java:338)
     com.android.server.display.DisplayManagerService.performTraversalInTransactionLocked(DisplayManagerService.java:892)
     com.android.server.display.DisplayManagerService.configureDisplayInTransactionLocked(DisplayManagerService.java:983)
     com.android.server.display.LogicalDisplay.configureDisplayInTransactionLocked(LogicalDisplay.java:426)
     com.android.server.display.DisplayDevice.setProjectionInTransactionLocked(DisplayDevice.java:194)
     com.android.server.display.DisplayDevice.setDisplayProjection()

最终更新屏幕显示:
frameworks/base/services/core/java/com/android/server/display/DisplayDevice.java

    /**
     * Sets the display projection while in a transaction.
     *
     * @param orientation defines the display's orientation
     * @param layerStackRect defines which area of the window manager coordinate
     *            space will be used
     * @param displayRect defines where on the display will layerStackRect be
     *            mapped to. displayRect is specified post-orientation, that is
     *            it uses the orientation seen by the end-user
     */
    public final void setProjectionInTransactionLocked(int orientation,
            Rect layerStackRect, Rect displayRect) {
        if (mCurrentOrientation != orientation
                || mCurrentLayerStackRect == null
                || !mCurrentLayerStackRect.equals(layerStackRect)
                || mCurrentDisplayRect == null
                || !mCurrentDisplayRect.equals(displayRect)) {
            mCurrentOrientation = orientation;

            if (mCurrentLayerStackRect == null) {
                mCurrentLayerStackRect = new Rect();
            }
            mCurrentLayerStackRect.set(layerStackRect);

            if (mCurrentDisplayRect == null) {
                mCurrentDisplayRect = new Rect();
            }
            mCurrentDisplayRect.set(displayRect);
//与Bootanimation中的函数类似
            SurfaceControl.setDisplayProjection(mDisplayToken,
                    orientation, layerStackRect, displayRect);
        }
    }
<think>嗯,用户问的是如何去掉Android 7.1开机铃声。首先,我需要确认用户的需求是什么。可能他们觉得开机铃声太吵,或者想在特定场合下静音启动。Android系统不同版本的操作可能不同,所以得针对7.1版本来找方法。 接下来,我得回忆一下Android系统的启动流程。开机铃声通常是在系统启动时由系统服务加载并播放的,可能是在启动动画期间触发的。修改系统文件可能是必要的,比如找到相关的配置文件或者音频文件。 然后,可能的步骤包括:找到存放开机铃声的音频文件,通常是Ogg格式,位于/system/media目录下。用户需要root权限才能修改系统文件,这点很重要,得提醒用户风险,比如可能变砖或者失去保修。 另外,可能需要修改系统属性或者配置文件,比如build.prop,看看有没有相关的参数控制开机声音。或者禁用启动音效的服务,比如通过adb命令停用相关的服务组件。 还要考虑是否有第三方ROM或者工具可以辅助完成这个操作,比如使用Magisk模块来替换静音文件,这样可能更安全一些。 不过,用户可能没有root权限,这时候是否有其他方法?比如在用户空间设置静音,但通常开机铃声是在系统初始化阶段播放的,用户设置可能无法影响那个阶段。所以可能需要root。 另外,需要提醒用户备份原始文件,以防万一修改后出现问题,可以恢复。还要指导具体的操作步骤,比如使用Root Explorer或者ADB来挂载系统分区为可写,替换或删除音频文件。 最后,确保步骤清晰,分点说明,并强调风险,让用户自行决定是否继续操作。可能还要建议如果对系统不熟悉的话,谨慎操作,或者寻求专业人士帮助。</think>在Android 7.1系统中禁用开机铃声需要通过修改系统文件实现,以下是具体步骤和注意事项: 1. **原理说明** - 开机铃声由文件`PowerOn.ogg`控制,路径为:`/system/media/audio/ui/PowerOn.ogg` - 需拥有Root权限才能修改系统分区文件 2. **操作步骤** ① 获取Root权限 - 通过Magisk或SuperSU等工具解锁 - 警告:Root可能导致设备失去保修 ② 挂载系统分区为可写 ```bash adb shell su mount -o remount,rw /system ``` ③ 删除或重命名原文件 ```bash mv /system/media/audio/ui/PowerOn.ogg /sdcard/PowerOn.bak # 或直接删除 rm /system/media/audio/ui/PowerOn.ogg ``` ④ 创建静音文件替代(可选) - 生成0字节文件: ```bash touch /system/media/audio/ui/PowerOn.ogg ``` 3. **替代方案(无需Root)** - 通过ADB禁用系统音效服务: ```bash adb shell pm disable-user --user 0 com.android.systemui.audiofx ``` 注:可能影响其他系统音效 4. **风险提示** - 错误操作可能导致系统无法启动 - 建议先备份原始文件 - 部分厂商(如华为、三星)可能采用自定义实现路径 - 修改后需执行: ```bash chmod 644 /system/media/audio/ui/PowerOn.ogg mount -o remount,ro /system ``` 5. **验证方法** 重启设备时: - 观察是否仍有开机音 - 通过logcat检查音频服务日志: ```bash adb logcat | grep -i 'audio' ``` 建议操作前完整备份设备数据,修改系统文件存在风险,请谨慎操作。对于未解锁Bootloader的设备,建议优先尝试系统设置中的"启动音效"选项(如果有提供)。
评论 9
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值