BootAnimation+SE+开机MP4动画播放

先看效果

编译

m libbootanimation -j8

手动播放 验证编码器可用

bash 复制代码
PS C:\Users\HiMaq> adb push C:\Users\HiMaq\Videos\bootanimation.mp4 /system/media/bootanimation.mp4
C:\Users\HiMaq\Videos\bootanimation.mp4: 1 file pushed, 0 skipped. 8.9 MB/s (37097541 bytes in 3.994s)

adb push C:\Users\HiMaq\Videos\bootanimation.mp4 /sdcard/video.mp4
adb shell am start -a android.intent.action.VIEW -d file:///sdcard/video.mp4 -t video/mp4
Starting: Intent { act=android.intent.action.VIEW dat=file:///sdcard/video.mp4 typ=video/mp4 }

强行启动开机动画

修改思路,修改属性为1

bash 复制代码
  void BootAnimation::checkExit() {                                                                                                                                                                          
          char value[PROPERTY_VALUE_MAX];                                                                                                                                                                        
          property_get("service.bootanim.exit", value, "0");  // 读系统属性                                                                                                                                      
          int exitnow = atoi(value);                                                                                                                                                                             
          if (exitnow) {                                                                                                                                                                                         
              requestExit();    // 设置 Thread::mExitPending = true                                                                                                                                              
          }                                                                                                                                                                                                      
      }                                                                                                                                                                                                                        

手动启动bootanmation 可执行文件

bash 复制代码
PS C:\Users\HiMaq\Desktop> adb shell setprop service.bootanim.exit 0
PS C:\Users\HiMaq\Desktop> adb shell getprop service.bootanim.exit
0
PS C:\Users\HiMaq\Desktop> adb logcat -b main -s "BootAnimation"  
PS C:\Users\HiMaq\Desktop> adb shell system/bin/bootanimation

或者

bash 复制代码
emulator64_x86_64:/ #  setprop ctl.start bootanim
emulator64_x86_64:/ #  setprop ctl.stop bootanim

手动直接启动日志

./system/bin/bootanimation

bash 复制代码
行 17207: 06-09 12:45:50.302  2195  2199 D BootAnimation: Found MP4 boot animation: /system/media/bootanimation.mp4
	行 17208: 06-09 12:45:50.302  2195  2199 D BootAnimation: Playing MP4 boot animation: /system/media/bootanimation.mp4
	行 17209: 06-09 12:45:50.302  2195  2199 D BootAnimation: Emulator detected, skipping MP4 boot animation
	行 17210: 06-09 12:45:50.302  2195  2199 D BootAnimation: media.player service available after 0ms
	行 17211: 06-09 12:45:50.805   514  1993 D NuPlayerDriver: NuPlayerDriver(0x707b9355cfb0) created, clientPid(2195)
	行 17215: 06-09 12:45:50.808   514  2201 D NuPlayer: onSetVideoSurface(0x707c53563640, no video decoder)
	行 17218: 06-09 12:45:50.815  2195  2199 D BootAnimation: MP4 boot animation started (attempt 1)
	行 17484: 06-09 12:45:50.978   514  1993 D NuPlayerDriver: stop(0x707b9355cfb0)
	行 17485: 06-09 12:45:50.979   514  1993 D NuPlayerDriver: reset(0x707b9355cfb0) at state 8
	行 17504: 06-09 12:45:51.027   514  2201 D NuPlayerDriver: notifyResetComplete(0x707b9355cfb0)
	行 17505: 06-09 12:45:51.027   514  1993 D NuPlayerDriver: reset(0x707b9355cfb0) at state 0
	行 17509: 06-09 12:45:51.028  2195  2199 D BootAnimation: MP4 boot animation finished
	行 17510: 06-09 12:45:51.043  2195  2199 D BootAnimation: BootAnimationStopTiming start time: 103220ms
	行 17521: 06-09 12:45:52.578  2217  2217 D BootAnimation: BootAnimationStartTiming start time: 104755ms
	行 17522: 06-09 12:45:52.578  2217  2217 D BootAnimation: BootAnimationPreloadTiming start time: 104755ms
	行 17523: 06-09 12:45:52.578  2217  2217 D BootAnimation: BootAnimationPreloadStopTiming start time: 104755ms

获取属性

bash 复制代码
130|emulator64_x86_64:/ $ su
emulator64_x86_64:/ # getprop | grep exit
[service.bootanim.exit]: [1]

杀进程

bash 复制代码
maqi@QiMa:~$ ps -A |grep java
 124655 pts/0    00:01:24 java
maqi@QiMa:~$ kill -9 124655

检查生成时间

bash 复制代码
maqi@QiMa:~/android13-r44$ ls -l out/target/product/emulator_car_x86_64/system/lib64/libbootanimation.so 
-rwxr-xr-x 1 maqi maqi 112040  6月  9 20:38 out/target/product/emulator_car_x86_64/system/lib64/libbootanimation.so
bash 复制代码
maqi@QiMa:~/android13-r44$ ls -l out/target/product/emulator_car_x86_64/system/bin/bootanimation 
-rwxr-xr-x 1 maqi maqi 17104  6月  9 17:35 out/target/product/emulator_car_x86_64/system/bin/bootanimation

md5sum检查

bash 复制代码
maqi@QiMa:~/android13-r44$ md5sum out/target/product/emulator_car_x86_64/system/lib64/libbootanimation.so 
050059780cb5b71aba61b362496c954c  out/target/product/emulator_car_x86_64/system/lib64/libbootanimation.so

推包

bash 复制代码
maqi@QiMa:~/android13-r44$ adb root
restarting adbd as root
maqi@QiMa:~/android13-r44$ adb remount
remount succeeded
maqi@QiMa:~/android13-r44$ adb push out/target/product/emulator_car_x86_64/system/lib64/libbootanimation.so system/lib64/
out/target/product/emulator_car_x86_64/system/lib64/libbootanimation.so: 1 file pushed, 0 skipped. 147.6 MB/s (112040 bytes in 0.001s)

重启模拟器

这里我使用的是谷歌官方下载的模拟因为,我自己编译的模拟器不支持解码器

emulator -avd QmX86Api33 -writable-system

因为根本没有mediaserver NuPlayerDriver 报错无法找到解码器

bash 复制代码
# 1. 检查 mediaserver 进程是否存在                                                                                                                                                                         
ps -A | grep mediaserver                                                                                                                                                                                   
# 2. 检查 "media.player" 是否已注册                                                                                                                                                                        
service list | grep media.player                                                                                                                                                                           
# 3. 查看 mediaserver 启动日志                                                                                                                                                                             
adb logcat -b system -s "mediaserver" -t 100                                                                                                                                                                   
# 4. 看 BootAnimation 的等待输出                                                                                                                                                                           
adb logcat -b main -s "BootAnimation" -t 200     
# se 权限检查
adb logcat -b all -d | grep "avc: denied" | grep -E "bootanim|mediaserver"    

权限问题 拒绝访问 media.player

bash 复制代码
06-10 03:33:03.057   482   486 D BootAnimation: media.player service available after 27000ms
06-10 03:33:03.560   482   482 I BootAnimation: type=1400 audit(0.0:17): avc: denied { call } for scontext=u:r:bootanim:s0 tcontext=u:r:mediaserver:s0 tclass=binder permissive=1
06-10 03:33:03.560   482   482 I BootAnimation: type=1400 audit(0.0:18): avc: denied { transfer } for scontext=u:r:bootanim:s0 tcontext=u:r:mediaserver:s0 tclass=binder permissive=1
06-10 03:33:03.568   482   482 I BootAnimation: type=1400 audit(0.0:20): avc: denied { use } for path="/system/media/bootanimation.mp4" dev="overlay" ino=50 scontext=u:r:mediaserver:s0 tcontext=u:r:bootanim:s0 tclass=fd permissive=1
06-10 03:33:03.606   482   486 D BootAnimation: MP4 boot animation started (attempt 1)
06-10 03:33:06.858   482   486 D BootAnimation: MP4 boot animation finished
06-10 03:33:06.866   482   486 D BootAnimation: BootAnimationStopTiming start time: 40025ms

***手动验证 SE ***

启动途中 强行关闭se

bash 复制代码
PS C:\Users\HiMaq\Desktop> adb root
restarting adbd as root
PS C:\Users\HiMaq\Desktop> adb shell setenforce 0

验证

bash 复制代码
adb logcat | grep "avc: denied"

编译和推送SE

bash 复制代码
#!/bin/bash
set -e

cd /home/maqi/android13-r44
source build/envsetup.sh
lunch emulator_car_x86_64

# 编译
m selinux_policy

setools 权限检查工具

bash 复制代码
sudo apt-get install setools

查找precompiled_sepolicy

java 复制代码
maqi@QiMa:~/android13-r44$ tree -L 1  out/target/product/emulator_car_x86_64/vendor/etc/selinux/
out/target/product/emulator_car_x86_64/vendor/etc/selinux/
├── plat_pub_versioned.cil
├── plat_sepolicy_vers.txt
├── precompiled_sepolicy
├── precompiled_sepolicy.plat_sepolicy_and_mapping.sha256
├── precompiled_sepolicy.product_sepolicy_and_mapping.sha256
├── selinux_denial_metadata
├── vendor_file_contexts
├── vendor_hwservice_contexts
├── vendor_mac_permissions.xml
├── vendor_property_contexts
├── vendor_seapp_contexts
├── vendor_sepolicy.cil
├── vendor_service_contexts
└── vndservice_contexts

0 directories, 14 files

sesearch 产物权限检查

bash 复制代码
# 设置路径变量
POLICY_PATH="out/target/product/emulator_car_x86_64/vendor/etc/selinux/precompiled_sepolicy"

# 1. 查询 service_manager 权限
sesearch --allow -s bootanim -t mediaserver_service -c service_manager $POLICY_PATH

# 2. 查询 binder 权限
sesearch --allow -s bootanim -t mediaserver -c binder $POLICY_PATH

# 3. 查询 file 权限
sesearch --allow -s bootanim -t mnt_product_file -c file $POLICY_PATH

SE检查结果

bash 复制代码
maqi@QiMa:~/android13-r44$ # 设置路径变量
maqi@QiMa:~/android13-r44$ POLICY_PATH="out/target/product/emulator_car_x86_64/vendor/etc/selinux/precompiled_sepolicy"

maqi@QiMa:~/android13-r44$ # 1. 查询 service_manager 权限
maqi@QiMa:~/android13-r44$ sesearch --allow -s bootanim -t mediaserver_service -c service_manager $POLICY_PATH
allow bootanim mediaserver_service:service_manager find;

maqi@QiMa:~/android13-r44$ # 2. 查询 binder 权限
maqi@QiMa:~/android13-r44$ sesearch --allow -s bootanim -t mediaserver -c binder $POLICY_PATH
allow bootanim mediaserver:binder { call transfer };

maqi@QiMa:~/android13-r44$ # 3. 查询 file 权限
maqi@QiMa:~/android13-r44$ sesearch --allow -s bootanim -t mnt_product_file -c file $POLICY_PATH
allow bootanim mnt_product_file:file { getattr ioctl lock map open read watch watch_reads };

好的! SE检查完毕

推送precompiled_sepolicy到设备

bash 复制代码
adb root
adb remount

adb push out/target/product/emulator_car_x86_64/vendor/etc/selinux/. /vendor/etc/selinux/
adb push out/target/product/emulator_car_x86_64/system/etc/selinux/. /system/etc/selinux/
adb push out/target/product/emulator_car_x86_64/product/etc/selinux/. /product/etc/selinux/
adb reboot

附件代码diff

附件SE配置 说明

MP4 开机动画 SELinux 权限清单

Android 13 (AOSP) 主线自带,共 3 条规则 ,分布在 2 套策略副本


涉及的策略文件(2 套副本)

策略副本 文件路径
当前策略 system/sepolicy/public/bootanim.te
system/sepolicy/private/bootanim.te
API 33 冻结 system/sepolicy/prebuilts/api/33.0/public/bootanim.te
system/sepolicy/prebuilts/api/33.0/private/bootanim.te

每份副本的内容完全一致(同一笔 commit 同步写入)。修改时必须同步更新所有 2 套副本,否则 m selinux_policy 编译时会因 API 冻结校验失败。


规则 1:文件系统权限

文件 行号 规则
private/bootanim.te 22-23 r_dir_file(bootanim, mnt_product_file)
prebuilts/api/33.0/private/bootanim.te 22-23 同上

含义: 允许 bootanim 读取 /product/media/ 目录及下面的 bootanimation.mp4

说明:

  • r_dir_file 是 SELinux 宏,展开等价于:
    • allow bootanim mnt_product_file:dir r_dir_perms; --- 目录搜索/读
    • allow bootanim mnt_product_file:file r_file_perms; --- 文件读取
  • MP4 文件路径固定为 /product/media/bootanimation.mp4/oem/media/bootanimation.mp4/system/media/bootanimation.mp4,当前策略只覆盖了 /product/media/(第一优先级路径)。

规则 2:Service Manager 权限

文件 行号 规则
public/bootanim.te 49 allow bootanim mediaserver_service:service_manager find;
prebuilts/api/33.0/public/bootanim.te 49 同上

含义: 允许 bootanim 在 servicemanager 中查找 media.player 服务。

说明:

  • 代码中通过 defaultServiceManager()->checkService(String16("media.player")) 调用。
  • 这是 waitForMediaCodecs() 中轮询检测的服务名。

规则 3:Binder IPC 权限

文件 行号 规则
public/bootanim.te 48 binder_call(bootanim, mediaserver)
prebuilts/api/33.0/public/bootanim.te 48 同上

含义: 允许 bootanim 通过 Binder 向 mediaserver 进程发起 IPC 调用。

说明:

  • binder_call 是宏,展开为:
    • allow bootanim mediaserver:fd use;
    • allow bootanim mediaserver:binder { call transfer };
  • 这是 MediaPlayer 所有 API(setDataSourcepreparestartstop 等)的底层通信基础。

规则 4:mediaserver → bootanim 反向权限

Android 13 主线不包含以下规则,需要手动添加。

system/sepolicy/public/mediaserver.te 及对应的 API 冻结副本中添加:

文件 规则 含义
system/sepolicy/public/mediaserver.te allow mediaserver bootanim:fd use; 允许 mediaserver 使用 bootanim 传过来的文件描述符
allow mediaserver bootanim:binder call; 允许 mediaserver 调用 bootanim 的 Binder 接口

说明:

  • 这两条是规则3的反向补充 。完整通信需要双向权限:
    • 规则3:binder_call(bootanim, mediaserver) → bootanim 可调 mediaserver
    • 规则4:allow mediaserver bootanim:binder call → mediaserver 可调 bootanim
  • fd use 允许 mediaserver 接收 bootanim 通过 Binder 传递的 ParcelFileDescriptor
  • 可以添加到 mediaserver.te 文件末尾的 neverallow 规则之前,与其他 fd use 规则(hal_graphics_allocator:fd usesystem_server:fd usevold:fd use 等)放在一起。

涉及的策略文件(2 套副本):

策略副本 文件路径
当前策略 system/sepolicy/public/mediaserver.te
API 33 冻结 system/sepolicy/prebuilts/api/33.0/public/mediaserver.te

如果 API 28-32 的冻结副本也需要支持 MP4 boot animation,应同步添加。


汇总

复制代码
-----------------------------------------------------------------------------------
 策略副本                       文件           权限                    用途
-----------------------------------------------------------------------------------
 system/sepolicy/               public/        binder_call(bootanim,   Binder IPC
                                 bootanim.te    mediaserver)
 
                                public/        allow bootanim          查找 media.player
                                 bootanim.te    mediaserver_service:    服务
                                                service_manager find;

                                private/       r_dir_file(bootanim,    读 /product/media/
                                 bootanim.te    mnt_product_file)       *.mp4

 prebuilts/api/33.0/            public/        同上 (行号相同)
                                 bootanim.te
                                private/        同上 (行号相同)
                                 bootanim.te
-----------------------------------------------------------------------------------

⚠️ 注意:这 3 条规则已经是 不需要手动添加。 如果模拟器上 waitForMediaCodecs 等 90 秒才超时,是SELinux 权限问题, 或模拟器根本没有注册 media.player 这个服务名。

附件源码

Android.bp

加入libmedia

bash 复制代码
cc_library_shared {
    name: "libbootanimation",
    defaults: ["bootanimation_defaults"],

    srcs: ["BootAnimation.cpp"],

    shared_libs: [
        "libui",
        "libjnigraphics",
        "libEGL",
        "libGLESv2",
        "libgui",
        "libmedia",
    ],
}

Animation.h

bash 复制代码
    // MP4 boot animation support
    static const char* findBootAnimationMp4();
    bool playMp4(const char* filePath);

Animation.cpp

c 复制代码
// MP4 boot animation paths (checked before the traditional zip paths)
    static const char *BOOTANIMATION_MP4_FILES[] = {
            "/product/media/bootanimation.mp4",
            "/oem/media/bootanimation.mp4",
            "/system/media/bootanimation.mp4",
            nullptr,
    };

// Simple listener to track MediaPlayer playback completion and errors.
    class Mp4PlaybackListener : public MediaPlayerListener {
    public:
        bool completed = false;
        bool errored = false;
        int errorCode = 0;

        virtual void notify(int msg, int /*ext1*/, int /*ext2*/, const Parcel * /*obj*/) {
            if (msg == MEDIA_PLAYBACK_COMPLETE) {
                completed = true;
            } else if (msg == MEDIA_ERROR) {
                errored = true;
                errorCode = -100;
            }
        }
    };

    const char *BootAnimation::findBootAnimationMp4() {
        for (int i = 0; BOOTANIMATION_MP4_FILES[i] != nullptr; i++) {
            if (access(BOOTANIMATION_MP4_FILES[i], R_OK) == 0) {
                ALOGD("Found MP4 boot animation: %s", BOOTANIMATION_MP4_FILES[i]);
                return BOOTANIMATION_MP4_FILES[i];
            }
        }
        return nullptr;
    }
    static bool isEmulator() {
        char value[PROPERTY_VALUE_MAX];
        property_get("ro.kernel.qemu", value, "0");
        return atoi(value) == 1;
    }

    static bool waitForMediaCodecs(int maxWaitMs) {
        // 模拟器没有硬件 AVC decoder,直接跳过 MP4
        if (isEmulator()) {
            ALOGD("Emulator detected, skipping MP4 boot animation");
//            return false;
        }
        sp<IServiceManager> sm = defaultServiceManager();
        for (int waited = 0; waited < maxWaitMs; waited += 200) {
            sp<IBinder> binder = sm->checkService(String16("media.player"));
            if (binder != nullptr) {
                ALOGD("media.player service available after %dms", waited);
                usleep(500000);
                return true;
            }
            ALOGD("Waiting for media.player service... %dms", waited);
            usleep(200000);
        }
        SLOGE("media.player service not available after %dms", maxWaitMs);
        return false;
    }

    // ============================================================
    // playMp4: 使用 MediaPlayer 播放 MP4 开机动画
    // 参数: filePath - MP4 文件的绝对路径
    // 返回值: false = 播放完成或失败(需要回退到 ZIP 动画)
    // ============================================================
    bool BootAnimation::playMp4(const char* filePath) {
        ALOGD("Playing MP4 boot animation: %s", filePath);  // 日志:开始播放 MP4

        // 1. 销毁旧的 EGL 上下文/表面(之前的 ZIP 动画使用 GLES 渲染)
        eglMakeCurrent(mDisplay, EGL_NO_SURFACE, EGL_NO_SURFACE, EGL_NO_CONTEXT); // 解除当前 EGL 绑定
        eglDestroySurface(mDisplay, mSurface);      // 销毁 EGL 窗口表面
        eglDestroyContext(mDisplay, mContext);       // 销毁 EGL 上下文

        // 2. 等待 mediaserver 的 media.player 服务就绪(最多等 180 秒)
        //    注意:media.player 可能启动较慢(如模拟器上需 90 秒),
        //    这期间 service.bootanim.exit 可能已被 WMS 置为 "1",
        //    但我们必须在播放开始后再响应退出,否则 MP4 永远放不出来。
        if (!waitForMediaCodecs(10000*18)) {
            SLOGE("Media not available, falling back to ZIP");  // 超时,回退到 ZIP
            goto fallback_to_zip;                               // 跳转到 GLES 环境恢复逻辑
        }

        // 3. 最多重试 5 次(prepare/解码可能暂时失败)
        for (int attempt = 0; attempt < 5; attempt++) {
            sp<MediaPlayer> mp = new MediaPlayer();             // 创建 MediaPlayer 实例
            sp<Mp4PlaybackListener> listener = new Mp4PlaybackListener(); // 创建播放状态监听器
            mp->setListener(listener);                          // 绑定监听器

            // 4. 打开 MP4 文件并设置为 MediaPlayer 的数据源
            int fd = open(filePath, O_RDONLY);                  // 以只读方式打开文件
            if (fd < 0) {
                SLOGE("Failed to open MP4: %s", filePath);      // 文件不存在或无法读取
                goto fallback_to_zip;
            }
            struct stat sb;                                     // 获取文件大小
            fstat(fd, &sb);
            status_t err = mp->setDataSource(fd, 0, sb.st_size); // 设置数据源(文件描述符 + 偏移 + 大小)
            close(fd);                                          // 关闭 fd(MediaPlayer 内部已复制)

            if (err != NO_ERROR) {
                SLOGE("setDataSource failed: %d", err);         // MediaPlayer 不接受该文件
                goto fallback_to_zip;
            }

            // 5. 关联 SurfaceFlinger 的 BufferQueue,让视频渲染到启动动画图层
            err = mp->setVideoSurfaceTexture(
                    mFlingerSurface->getIGraphicBufferProducer());
            if (err != NO_ERROR) {
                SLOGE("setVideoSurfaceTexture failed: %d", err); // Surface 绑定失败
                goto fallback_to_zip;
            }

            // 6. 准备播放(setDataSource -> prepare 是异步的准备流程)
            mp->setLooping(true);                               // 设置循环播放
            err = mp->prepare();                                // 同步准备(解析文件头、初始化解码器)
            if (err != NO_ERROR) {
                ALOGW("prepare() failed (attempt %d/5): %d", attempt + 1, err); // 准备失败,可能解码器未就绪
                mp->reset();                                    // 重置 MediaPlayer 状态
                usleep(1000000);                                // 等待 1 秒后重试
                continue;
            }

            // 7. 开始播放
            mCallbacks->init({});                               // 通知回调初始化(清除之前的动画状态)
            mp->start();                                        // 启动 MP4 播放
            ALOGD("MP4 boot animation started (attempt %d)", attempt + 1); // 播放成功

            // 8. 短等 100ms 后检查解码器是否立刻报错(异步初始化失败检测)
            usleep(100000);
            if (listener->errored) {
                ALOGW("Async decoder failed immediately (attempt %d/5), retrying in 1s",
                      attempt + 1);                             // 解码器瞬间报错,重试
                mp->stop();
                mp->reset();
                usleep(1000000);                                // 等 1 秒
                continue;
            }

            // 9. 主循环:播放 MP4,同时轮询退出信号
            //    设计要点:
            //    - media.player 可能启动很慢(如 90 秒),导致进入此循环时
            //      service.bootanim.exit 早已被置 "1"。
            //    - 如果不给最低播放时间,checkExit() 会在第一个 50ms 周期
            //      就触发 requestExit(),MP4 几乎无机会渲染。
            //    - 因此加入 kMinPlayMs 保障:至少播放 N 毫秒后才响应退出。
            //    - 改为直接用 true 做 while 条件,手动控制 break 逻辑。
            {
                const int kMinPlayMs = 3000;  // 最低保障播放 3 秒
                int elapsedMs = 0;
                while (true) {
                    if (listener->errored) break;               // 播放出错则跳出
                    usleep(50000);                              // 每 50ms 检查一次
                    elapsedMs += 50;
                    // 超过最低保障时间后才开始响应退出请求
                    if (elapsedMs >= kMinPlayMs) {
                        checkExit();
                        if (exitPending()) break;
                    }
                }
            }

            // 10. 停止播放
            mp->stop();                                         // 停止播放
            mp->reset();                                        // 重置(释放解码器资源)

            // 11. 如果是正常退出(动画播放完毕),清理并结束进程
            if (exitPending()) {
                ALOGD("MP4 boot animation finished");           // 日志:播放结束
                mCallbacks->shutdown();                         // 通知回调关闭
                mFlingerSurface.clear();                        // 释放 Surface
                mFlingerSurfaceControl.clear();                 // 释放 SurfaceControl
                eglTerminate(mDisplay);                         // 终止 EGL 显示连接
                eglReleaseThread();                             // 释放线程 EGL 资源
                IPCThreadState::self()->stopProcess();          // 停止本进程(bootanim 退出)
                return false;
            }

            // 12. 如果不是退出(可能是出错循环重试),等 1 秒后进入下一次尝试
            usleep(1000000);
        }

        // 13. 5 次重试全部失败,记录错误并回退到 ZIP
        SLOGE("MP4 playback failed after all attempts, falling back to ZIP");

        fallback_to_zip:
        {
            // 14. 恢复 GLES 渲染环境,用于回退到 ZIP 动画播放
            //     必须指定 GLES2,否则 glCreateShader 等函数指针为 null 导致 SIGSEGV
            EGLConfig config = getEglConfig(mDisplay);                     // 获取 EGL 配置
            EGLint contextAttributes[] = {EGL_CONTEXT_CLIENT_VERSION, 2, EGL_NONE}; // GLES 2.0
            mContext = eglCreateContext(mDisplay, config, nullptr, contextAttributes); // 重建 EGL 上下文
            mSurface = eglCreateWindowSurface(mDisplay, config,            // 重建 EGL 窗口表面
                                              mFlingerSurface.get(), nullptr);
            eglMakeCurrent(mDisplay, mSurface, mSurface, mContext);        // 绑定到当前线程
        }
        return false;   // 返回 false,告知调用方需要回退到 ZIP 动画
    }
相关推荐
千里马学框架2 天前
一起学 Android 14:ShellTransition 屏幕旋转过程深度剖析
android·智能手机·性能优化·framework·性能·屏幕旋转·rotation
美狐美颜SDK开放平台2 天前
开发直播APP时如何接入视频美颜SDK?开发流程与注意事项
android·人工智能·计算机视觉·音视频·直播美颜sdk
AFinalStone2 天前
Android7 SystemUI源码解析(七)Keyguard锁屏模块深度解析
android·systemui
致远ccc2 天前
Google Play 上架前如何测试 App?多国家 Android 环境测试
android·app测试·googleplay·多国家应用测试
ttyyttemo2 天前
Kotlin 协程中的 Job 结构化并发与取消
android
sun0077002 天前
tbox 4g/5g切换,导致wan ip 改变,导致车机旧网络不可用。需要重启车机才行
android
其实防守也摸鱼2 天前
内网穿透与反向代理:原理、工具与实战指南
android·大数据·运维·安全·网络安全·自动化·渗透
AFinalStone3 天前
Android7 SystemUI 源码解析(四)NavigationBar 导航栏与 SystemBars
android·systemui
JMchen3 天前
属性动画原理与高级动画实现
android·kotlin·canvas
AFinalStone3 天前
Android7 SystemUI 源码解析(二)启动流程深度解析
android·systemui