AOSP Android 14 壁纸架构深度分析

11 阅读30分钟

AOSP Android 14 壁纸架构深度分析


一、整体架构概览

┌─────────────────────────────────────────────────────────────────┐
│                    Android 14 壁纸架构                            │
│                                                                 │
│  ┌──────────────────────────────────────────────────┐           │
│  │                  应用层 (Apps)                     │           │
│  │  ┌──────────────┐    ┌────────────────────────┐  │           │
│  │  │ WallpaperPicker│   │ 第三方壁纸 App          │  │           │
│  │  │ (壁纸选择器)   │   │ (Live Wallpaper等)     │  │           │
│  │  └──────┬───────┘    └──────────┬─────────────┘  │           │
│  │         │ WallpaperManager API   │                │           │
│  └─────────┼────────────────────────┼────────────────┘           │
│            │                        │                            │
│  ┌─────────▼────────────────────────▼────────────────┐           │
│  │              Framework 层                          │           │
│  │                                                    │           │
│  │  ┌─────────────────────┐  ┌──────────────────────┐│           │
│  │  │ WallpaperManager    │  │ WallpaperManager     ││           │
│  │  │ (客户端 API)        │  │ Service              ││           │
│  │  └─────────┬───────────┘  │ (系统服务)            ││           │
│  │            │               │                      ││           │
│  │            │ Binder IPC    │  ┌────────────────┐  ││           │
│  │            └──────────────►│  │WallpaperData   │  ││           │
│  │                            │  │(壁纸数据管理)    │  ││           │
│  │                            │  └────────────────┘  ││           │
│  │                            │  ┌────────────────┐  ││           │
│  │                            │  │WallpaperCropper│  ││           │
│  │                            │  │(裁剪处理)       │  ││           │
│  │                            │  └────────────────┘  ││           │
│  │                            └──────────┬───────────┘│           │
│  │                                       │            │           │
│  │  ┌────────────────────────────────────▼──────────┐│           │
│  │  │            WallpaperService                    ││           │
│  │  │  ┌──────────────────┐ ┌──────────────────────┐││           │
│  │  │  │ ImageWallpaper   │ │ LiveWallpaperService ││ │          │
│  │  │  │ (静态壁纸)       │ │ (动态壁纸)            │││           │
│  │  │  │  └─Engine        │ │  └─Engine             │││          │
│  │  │  │    └─GLEngine    │ │    └─自定义渲染        │││          │
│  │  │  └──────────────────┘ └──────────────────────┘││           │
│  │  └───────────────────────────────────────────────┘│           │
│  │                                                    │           │
│  │  ┌───────────────────────────────────────────────┐│           │
│  │  │         WindowManagerService                   ││           │
│  │  │  ┌──────────────────────────────────────────┐ ││           │
│  │  │  │ WallpaperController                      │ ││           │
│  │  │  │  ├── 壁纸窗口(TYPE_WALLPAPER)管理        │ ││           │
│  │  │  │  ├── 壁纸偏移/视差计算                    │ ││           │
│  │  │  │  ├── 壁纸可见性控制                       │ ││           │
│  │  │  │  └── 壁纸过渡动画                         │ ││           │
│  │  │  └──────────────────────────────────────────┘ ││           │
│  │  └───────────────────────────────────────────────┘│           │
│  └────────────────────────────────────────────────────┘           │
│                                                                 │
│  ┌────────────────────────────────────────────────────┐          │
│  │                SystemUI 层                          │          │
│  │  ┌─────────────────┐  ┌─────────────────────────┐ │          │
│  │  │ ThemeOverlay     │  │ Scrim / Shade           │ │          │
│  │  │ Controller       │  │ (壁纸上层遮罩)          │ │          │
│  │  │ (Material You    │  │                         │ │          │
│  │  │  颜色提取)       │  │ NotificationShade       │ │          │
│  │  └─────────────────┘  └─────────────────────────┘ │          │
│  └────────────────────────────────────────────────────┘          │
│                                                                 │
│  ┌────────────────────────────────────────────────────┐          │
│  │              SurfaceFlinger / HWComposer            │          │
│  │  壁纸 Surface 最终合成到显示器                       │          │
│  └────────────────────────────────────────────────────┘          │
└─────────────────────────────────────────────────────────────────┘

二、源码目录结构

═══════ Framework 层 ═══════

frameworks/base/
├── core/java/android/app/
│   ├── WallpaperManager.java               // ★ 客户端 API
│   ├── WallpaperInfo.java                  // 壁纸元数据
│   ├── WallpaperColors.java                // 壁纸颜色描述
│   └── IWallpaperManager.aidl              // 系统服务 AIDL
│
├── core/java/android/service/wallpaper/
│   ├── WallpaperService.java               // ★ 壁纸服务基类
│   │   └── Engine                          // 壁纸渲染引擎
│   └── IWallpaperEngine.aidl               // 引擎 AIDL
│
├── core/java/com/android/internal/view/
│   └── IWallpaperConnection.aidl
│
├── services/core/java/com/android/server/wallpaper/
│   ├── WallpaperManagerService.java         // ★★★ 系统服务
│   ├── WallpaperData.java                  // 壁纸数据
│   ├── WallpaperCropper.java               // 壁纸裁剪 (Android 14新增)
│   ├── WallpaperDisplayHelper.java         // 多显示器辅助
│   └── GLHelper.java                       // EGL 辅助
│
└── services/core/java/com/android/server/wm/
    ├── WallpaperController.java             // ★★ WMS 壁纸控制
    ├── WallpaperWindowToken.java            // 壁纸窗口令牌
    ├── WallpaperAnimationAdapter.java       // 壁纸动画适配
    └── WallpaperVisibilityListeners.java    // 可见性监听

═══════ 默认壁纸实现 ═══════

frameworks/base/packages/SystemUI/
└── src/com/android/systemui/wallpapers/
    ├── ImageWallpaper.java                  // ★ 静态壁纸实现
    └── gl/
        ├── ImageWallpaperRenderer.java      // OpenGL 渲染器
        ├── ImageRevealWallpaperRenderer.java // 揭示动画渲染 (Android 14)
        ├── EglHelper.java                   // EGL 辅助
        └── ...

═══════ 壁纸选择器 ═══════

packages/apps/WallpaperPicker2/
├── src/com/android/wallpaper/
│   ├── picker/                              // 选择器 UI
│   │   ├── WallpaperPickerActivity.java
│   │   ├── preview/                         // 预览
│   │   │   ├── WallpaperPreviewFragment.java
│   │   │   └── LiveWallpaperPreviewFragment.java
│   │   └── customization/                   // 自定义
│   ├── model/                               // 数据模型
│   │   ├── WallpaperInfo.java
│   │   ├── LiveWallpaperInfo.java
│   │   └── ImageWallpaperInfo.java
│   ├── module/                              // Dagger 模块
│   └── util/
│       └── WallpaperConnection.java         // 壁纸连接管理
│
└── res/

═══════ SystemUI 壁纸相关 ═══════

packages/SystemUI/src/com/android/systemui/
├── wallpapers/                              // 壁纸渲染
│   ├── ImageWallpaper.java
│   └── gl/
├── statusbar/phone/
│   ├── ScrimController.java                 // 遮罩控制
│   └── LockscreenWallpaper.java             // 锁屏壁纸
├── theme/
│   └── ThemeOverlayController.java          // 主题/颜色
└── keyguard/
    └── KeyguardSliceProvider.java           // 锁屏信息

三、WallpaperManager — 客户端 API

3.1 核心 API

// frameworks/base/core/java/android/app/WallpaperManager.java

public class WallpaperManager {
    
    // ═══════ 壁纸目标 (Android 7.0+ 支持分别设置) ═══════
    
    /** 主屏幕壁纸 */
    public static final int FLAG_SYSTEM = 1 << 0;  // 1
    
    /** 锁屏壁纸 */
    public static final int FLAG_LOCK = 1 << 1;    // 2
    
    // 两者都设置: FLAG_SYSTEM | FLAG_LOCK = 3
    
    // ═══════ 获取壁纸 ═══════
    
    /** 获取当前壁纸 Drawable */
    public Drawable getDrawable() { ... }
    
    /** 获取当前壁纸 Drawable (指定 flag) */
    public Drawable getDrawable(@SetWallpaperFlags int which) { ... }
    
    /** 获取壁纸文件描述符 */
    public ParcelFileDescriptor getWallpaperFile(
            @SetWallpaperFlags int which) { ... }
    
    /** 获取壁纸颜色 (Material You 使用) */
    public WallpaperColors getWallpaperColors(
            @SetWallpaperFlags int which) { ... }
    
    // ═══════ 设置壁纸 ═══════
    
    /** 设置静态壁纸 (Bitmap) */
    public int setBitmap(Bitmap bitmap) throws IOException { ... }
    
    /** 设置静态壁纸 (带裁剪区域) */
    public int setBitmap(Bitmap fullImage, Rect visibleCropHint,
            boolean allowBackup, @SetWallpaperFlags int which)
            throws IOException { ... }
    
    /** 设置静态壁纸 (InputStream) */
    public void setStream(InputStream bitmapData) throws IOException { ... }
    
    /** 设置静态壁纸 (带裁剪区域 + 返回 ID) */
    public int setStream(InputStream bitmapData, Rect visibleCropHint,
            boolean allowBackup, @SetWallpaperFlags int which)
            throws IOException { ... }
    
    /** 设置静态壁纸 (资源) */
    public void setResource(@RawRes int resid) throws IOException { ... }
    
    /** 设置动态壁纸 */
    public void setWallpaperComponent(ComponentName name) 
            throws IOException { ... }
    
    // ═══════ Android 14 新增: 多维度壁纸 ═══════
    
    /**
     * ★ Android 14: 设置壁纸带多维度信息
     * 支持壁纸暗化版本、模糊版本等
     */
    public int setBitmapWithCrops(
            Bitmap fullImage,
            Map<Point, Rect> cropHints,  // 不同屏幕尺寸的裁剪区域
            boolean allowBackup,
            @SetWallpaperFlags int which) throws IOException { ... }
    
    // ═══════ 壁纸偏移 (视差效果) ═══════
    
    /** 设置壁纸水平偏移步数 */
    public void setWallpaperOffsetSteps(float xStep, float yStep) { ... }
    
    /** 设置壁纸当前偏移 */
    public void setWallpaperOffsets(IBinder windowToken, 
            float xOffset, float yOffset) { ... }
    
    // ═══════ 颜色/主题 ═══════
    
    /** 注册壁纸颜色变化回调 */
    public void addOnColorsChangedListener(
            OnColorsChangedListener listener, Handler handler) { ... }
    
    /** 移除壁纸颜色变化回调 */
    public void removeOnColorsChangedListener(
            OnColorsChangedListener listener) { ... }
    
    // ═══════ 壁纸信息 ═══════
    
    /** 获取当前壁纸信息 (动态壁纸时有效) */
    public WallpaperInfo getWallpaperInfo() { ... }
    
    /** 获取指定 flag 的壁纸信息 */
    public WallpaperInfo getWallpaperInfo(@SetWallpaperFlags int which) { ... }
    
    /** 壁纸是否设置了锁屏壁纸 */
    public boolean isLockscreenLiveWallpaperEnabled() { ... }
    
    /** 是否支持壁纸 */
    public boolean isWallpaperSupported() { ... }
    
    /** 是否允许设置壁纸 */
    public boolean isSetWallpaperAllowed() { ... }
    
    // ═══════ 壁纸尺寸 ═══════
    
    /** 获取期望壁纸最小宽度 */
    public int getDesiredMinimumWidth() { ... }
    
    /** 获取期望壁纸最小高度 */
    public int getDesiredMinimumHeight() { ... }
    
    /** 设置壁纸期望尺寸 */
    public void suggestDesiredDimensions(int minimumWidth, 
            int minimumHeight) { ... }
    
    // ═══════ 颜色变化回调接口 ═══════
    
    public interface OnColorsChangedListener {
        /**
         * @param colors 新的壁纸颜色
         * @param which FLAG_SYSTEM 或 FLAG_LOCK
         */
        void onColorsChanged(WallpaperColors colors, int which);
    }
}

3.2 WallpaperColors — 壁纸颜色描述

// frameworks/base/core/java/android/app/WallpaperColors.java

public final class WallpaperColors implements Parcelable {
    
    // ═══════ 颜色 ═══════
    
    private final Color mPrimaryColor;      // 主色
    private final Color mSecondaryColor;    // 副色 (可选)
    private final Color mTertiaryColor;     // 第三色 (可选)
    
    // ═══════ 颜色提示标志 ═══════
    
    /** 壁纸主色偏暗 → 适合浅色文字/图标 */
    public static final int HINT_SUPPORTS_DARK_TEXT = 1 << 0;
    
    /** 壁纸主色偏亮 → 适合深色文字/图标 */
    public static final int HINT_SUPPORTS_DARK_THEME = 1 << 1;
    
    /** 壁纸颜色从 Bitmap 分析得到 (而非手动指定) */
    public static final int HINT_FROM_BITMAP = 1 << 2;
    
    private int mColorHints;
    
    // ═══════ 从 Bitmap 提取颜色 ═══════
    
    /**
     * ★ 从 Bitmap 自动提取壁纸颜色
     * 使用 Palette API + 颜色量化算法
     */
    public static WallpaperColors fromBitmap(@NonNull Bitmap bitmap) {
        return fromBitmap(bitmap, false /* darkTheme */);
    }
    
    public static WallpaperColors fromBitmap(@NonNull Bitmap bitmap,
            boolean darkTheme) {
        // 1. 缩小 Bitmap (性能优化)
        Bitmap scaledBitmap = Bitmap.createScaledBitmap(
            bitmap, 
            Math.min(bitmap.getWidth(), 112),
            Math.min(bitmap.getHeight(), 112),
            true /* filter */);
        
        // 2. 使用 Palette 提取颜色
        Palette palette = new Palette.Builder(scaledBitmap)
            .setQuantizer(new VariationalKMeansQuantizer())
            .maximumColorCount(128)
            .generate();
        
        // 3. 获取主要颜色
        List<Palette.Swatch> swatches = palette.getSwatches();
        // 按面积排序
        swatches.sort((a, b) -> b.getPopulation() - a.getPopulation());
        
        Color primaryColor = null;
        Color secondaryColor = null;
        Color tertiaryColor = null;
        
        if (swatches.size() >= 1) {
            primaryColor = Color.valueOf(swatches.get(0).getRgb());
        }
        if (swatches.size() >= 2) {
            secondaryColor = Color.valueOf(swatches.get(1).getRgb());
        }
        if (swatches.size() >= 3) {
            tertiaryColor = Color.valueOf(swatches.get(2).getRgb());
        }
        
        // 4. 计算颜色提示
        int hints = HINT_FROM_BITMAP;
        float luminance = calculateLuminance(primaryColor);
        if (luminance > 0.5f) {
            hints |= HINT_SUPPORTS_DARK_TEXT;
        }
        if (luminance < 0.3f) {
            hints |= HINT_SUPPORTS_DARK_THEME;
        }
        
        return new WallpaperColors(
            primaryColor, secondaryColor, tertiaryColor, hints);
    }
    
    /** 创建自定义颜色 (动态壁纸使用) */
    public WallpaperColors(@NonNull Color primaryColor,
            @Nullable Color secondaryColor,
            @Nullable Color tertiaryColor) {
        this(primaryColor, secondaryColor, tertiaryColor, 0);
    }
}

四、WallpaperManagerService — 核心系统服务 ★★★

4.1 服务注册和初始化

// frameworks/base/services/core/java/com/android/server/wallpaper/
// WallpaperManagerService.java

public class WallpaperManagerService extends IWallpaperManager.Stub
        implements IWallpaperManagerService {
    
    // ═══════ 壁纸文件路径 ═══════
    
    // /data/system/users/{userId}/
    private static final String WALLPAPER = "wallpaper_orig";        // 原始壁纸
    private static final String WALLPAPER_CROP = "wallpaper";        // 裁剪后壁纸
    private static final String WALLPAPER_LOCK_ORIG = "wallpaper_lock_orig";
    private static final String WALLPAPER_LOCK_CROP = "wallpaper_lock";
    private static final String WALLPAPER_INFO = "wallpaper_info.xml"; // 壁纸元信息
    
    // ═══════ 壁纸数据管理 ═══════
    
    // 每个用户每个显示器的壁纸数据
    // SparseArray<WallpaperData>: key = userId
    // WallpaperData 包含 system 和 lock 两套壁纸信息
    private final SparseArray<WallpaperData> mWallpaperMap = new SparseArray<>();
    private final SparseArray<WallpaperData> mLockWallpaperMap = new SparseArray<>();
    
    // 壁纸连接
    private final SparseArray<WallpaperConnection> mWallpaperConnections = 
        new SparseArray<>();
    
    // ═══════ 颜色回调 ═══════
    
    private final SparseArray<RemoteCallbackList<IWallpaperManagerCallback>>
        mColorsChangedListeners = new SparseArray<>();
    
    // ═══════ 初始化 ═══════
    
    public WallpaperManagerService(Context context) {
        mContext = context;
        mImageWallpaper = ComponentName.unflattenFromString(
            context.getResources().getString(
                R.string.image_wallpaper_component));
        // 通常是: com.android.systemui/.wallpapers.ImageWallpaper
        
        // 默认壁纸资源
        mDefaultWallpaperComponent = WallpaperManager.getDefaultWallpaperComponent(
            context);
    }
    
    /**
     * SystemServer 调用的初始化
     */
    public void systemReady() {
        // 1. 初始化默认显示器上的壁纸
        initializeFallbackWallpaper();
        
        // 2. 加载已保存的壁纸设置
        loadSettingsLocked(UserHandle.USER_SYSTEM, false);
        
        // 3. 绑定壁纸服务
        switchWallpaper(mWallpaperMap.get(UserHandle.USER_SYSTEM), null);
        
        // 4. 注册用户切换接收器
        registerUserSwitchReceiver();
        
        // 5. 注册包变化接收器(壁纸 App 更新/卸载)
        registerPackageMonitor();
    }
}

4.2 WallpaperData — 壁纸数据模型

// frameworks/base/services/core/java/com/android/server/wallpaper/
// WallpaperData.java

class WallpaperData {
    
    // ═══════ 基本信息 ═══════
    
    int userId;
    
    // 壁纸文件
    File wallpaperFile;           // 原始壁纸文件 (wallpaper_orig)
    File cropFile;                // 裁剪后壁纸文件 (wallpaper)
    
    // 壁纸组件 (动态壁纸时有效)
    ComponentName wallpaperComponent;     // 当前壁纸服务组件
    ComponentName nextWallpaperComponent; // 正在切换到的壁纸
    
    // 壁纸 ID
    int wallpaperId;              // 唯一标识
    
    // ═══════ 壁纸显示参数 ═══════
    
    // 裁剪区域
    Rect cropHint = new Rect(0, 0, 0, 0);
    
    // Android 14: 多维度裁剪
    SparseArray<Rect> mCropHints; // key = 屏幕方向/尺寸
    
    // 壁纸尺寸
    int width;                    // 原始宽度
    int height;                   // 原始高度
    
    // 填充颜色 (壁纸不够大时)
    int primaryColors;
    
    // ═══════ 壁纸颜色 (Material You) ═══════
    
    WallpaperColors mWallpaperColors;  // 提取的壁纸颜色
    boolean mIsColorExtractedFromDim;  // 颜色是否从暗化版本提取
    
    // ═══════ 壁纸偏移 ═══════
    
    float mWallpaperXOffset;      // 水平偏移 0.0-1.0
    float mWallpaperYOffset;      // 垂直偏移 0.0-1.0
    float mWallpaperXStep;        // 水平步进
    float mWallpaperYStep;        // 垂直步进
    int mWallpaperDisplayOffsetX; // 显示器偏移 X
    int mWallpaperDisplayOffsetY; // 显示器偏移 Y
    
    // ═══════ 连接状态 ═══════
    
    WallpaperConnection connection;    // 壁纸服务连接
    long lastDiedTime;                 // 上次崩溃时间
    boolean wallpaperUpdating;         // 正在更新中
    boolean mIsLockscreenLiveWallpaperEnabled;
    
    // ═══════ 标志 ═══════
    
    boolean allowBackup = true;        // 允许备份
    
    /**
     * Android 14: 壁纸是否支持暗模式
     */
    boolean mShouldDimByDefault = true;
    float mWallpaperDimAmount = 0.0f;  // 壁纸暗化程度 0.0-1.0
}

4.3 设置壁纸完整流程

// WallpaperManagerService.java

/**
 * ★★★ 设置静态壁纸的完整流程
 */
@Override
public int setWallpaper(String name, String callingPackage,
        @SetWallpaperFlags int which, Rect cropHint,
        boolean allowBackup, Bundle extras,
        int wallpaperId) {
    
    // 1. 权限检查
    checkCallingOrSelfPermission(SET_WALLPAPER);
    
    // 确认是否允许设置壁纸
    if (!isWallpaperSettingAllowed(callingPackage)) {
        throw new SecurityException("Not allowed to set wallpaper");
    }
    
    int userId = UserHandle.getCallingUserId();
    
    synchronized (mLock) {
        // 2. 获取或创建 WallpaperData
        WallpaperData wallpaper;
        if ((which & FLAG_LOCK) != 0) {
            wallpaper = getOrCreateLockWallpaperDataLocked(userId);
        } else {
            wallpaper = mWallpaperMap.get(userId);
        }
        
        if (wallpaper == null) {
            throw new IllegalStateException("No wallpaper data");
        }
        
        // 3. 生成新的壁纸 ID
        wallpaper.wallpaperId = makeWallpaperIdLocked();
        
        // 4. 设置裁剪参数
        if (cropHint != null && !cropHint.isEmpty()) {
            wallpaper.cropHint.set(cropHint);
        } else {
            wallpaper.cropHint.set(0, 0, 0, 0);
        }
        
        wallpaper.allowBackup = allowBackup;
        
        // 5. ★ 创建 ParcelFileDescriptor 供调用者写入壁纸数据
        ParcelFileDescriptor pfd = updateWallpaperBitmapLocked(
            name, wallpaper, extras);
        
        if (pfd != null) {
            // 壁纸文件准备好了
            wallpaper.imageWallpaperPending = true;
            wallpaper.whichPending = which;
        }
        
        return wallpaper.wallpaperId;
    }
}

/**
 * 创建壁纸文件的 ParcelFileDescriptor
 */
private ParcelFileDescriptor updateWallpaperBitmapLocked(
        String name, WallpaperData wallpaper, Bundle extras) {
    
    try {
        // 创建壁纸原始文件
        File dir = getWallpaperDir(wallpaper.userId);
        if (!dir.exists()) {
            dir.mkdirs();
        }
        
        File file = new File(dir, WALLPAPER);
        
        ParcelFileDescriptor pfd = ParcelFileDescriptor.open(file,
            MODE_CREATE | MODE_READ_WRITE | MODE_TRUNCATE);
        
        // 调用者会通过这个 pfd 写入壁纸数据
        // 写入完成后会触发 settingsRestored() 或 onCloseReceived()
        
        return pfd;
    } catch (FileNotFoundException e) {
        return null;
    }
}

/**
 * ★ 壁纸数据写入完成后的处理
 */
private void onWallpaperWriteComplete(WallpaperData wallpaper) {
    
    // 1. 裁剪壁纸
    generateCrop(wallpaper);
    
    // 2. 提取壁纸颜色
    extractColors(wallpaper);
    
    // 3. 保存壁纸设置到 XML
    saveSettingsLocked(wallpaper.userId);
    
    // 4. 如果是静态壁纸,绑定到 ImageWallpaper 服务
    if (wallpaper.wallpaperComponent == null
            || wallpaper.wallpaperComponent.equals(mImageWallpaper)) {
        // 静态壁纸 → 绑定 ImageWallpaper
        bindWallpaperComponentLocked(mImageWallpaper, 
            true /* force */, false /* fromUser */, wallpaper, null);
    }
    
    // 5. 通知壁纸变化
    notifyWallpaperChanged(wallpaper);
    
    // 6. 通知颜色变化 (Material You)
    notifyWallpaperColorsChanged(wallpaper, wallpaper.mWhich);
    
    // 7. 通知 WMS
    notifyWallpaperChangedToWms(wallpaper);
}

4.4 壁纸裁剪 — WallpaperCropper (Android 14 新增)

// frameworks/base/services/core/java/com/android/server/wallpaper/
// WallpaperCropper.java

/**
 * Android 14 新增的壁纸裁剪器
 * 支持多种屏幕尺寸/方向的智能裁剪
 */
public class WallpaperCropper {
    
    /**
     * ★ 生成裁剪后的壁纸
     */
    static void generateCrop(WallpaperData wallpaper) {
        
        // 1. 读取原始壁纸
        BitmapFactory.Options options = new BitmapFactory.Options();
        options.inJustDecodeBounds = true;
        BitmapFactory.decodeFile(
            wallpaper.wallpaperFile.getAbsolutePath(), options);
        
        int origWidth = options.outWidth;
        int origHeight = options.outHeight;
        
        // 2. 确定裁剪区域
        Rect cropHint = wallpaper.cropHint;
        if (cropHint.isEmpty()) {
            // 无裁剪 → 使用整张图
            cropHint = new Rect(0, 0, origWidth, origHeight);
        }
        
        // 3. 确定目标尺寸
        // Android 14: 支持多维度
        DisplayInfo displayInfo = getDefaultDisplayInfo();
        int targetWidth = displayInfo.logicalWidth;
        int targetHeight = displayInfo.logicalHeight;
        
        // 壁纸通常比屏幕宽 (用于滑动视差)
        float parallaxRatio = getParallaxRatio();
        targetWidth = (int) (targetWidth * parallaxRatio);
        // parallaxRatio 通常为 1.0 ~ 2.0
        
        // 4. 计算采样率 (inSampleSize)
        int sampleSize = calculateSampleSize(
            cropHint.width(), cropHint.height(),
            targetWidth, targetHeight);
        
        // 5. 解码裁剪区域
        options.inJustDecodeBounds = false;
        options.inSampleSize = sampleSize;
        
        BitmapRegionDecoder decoder = BitmapRegionDecoder.newInstance(
            wallpaper.wallpaperFile.getAbsolutePath(), false);
        Bitmap croppedBitmap = decoder.decodeRegion(cropHint, options);
        
        // 6. 缩放到目标尺寸
        if (croppedBitmap.getWidth() != targetWidth
                || croppedBitmap.getHeight() != targetHeight) {
            croppedBitmap = Bitmap.createScaledBitmap(
                croppedBitmap, targetWidth, targetHeight, true);
        }
        
        // 7. 保存裁剪后的壁纸
        FileOutputStream fos = new FileOutputStream(wallpaper.cropFile);
        croppedBitmap.compress(Bitmap.CompressFormat.PNG, 100, fos);
        fos.close();
        
        // 8. 释放 Bitmap
        croppedBitmap.recycle();
    }
    
    /**
     * Android 14: 多屏幕尺寸裁剪
     */
    static void generateMultiCrop(WallpaperData wallpaper,
            Map<Point, Rect> cropHints) {
        
        for (Map.Entry<Point, Rect> entry : cropHints.entrySet()) {
            Point screenSize = entry.getKey();
            Rect crop = entry.getValue();
            
            // 为每种屏幕尺寸生成一个裁剪版本
            // 存储在不同文件中
            File cropFile = getCropFileForSize(
                wallpaper, screenSize);
            
            generateCropForSize(wallpaper, crop, screenSize, cropFile);
        }
    }
}

4.5 壁纸服务绑定

// WallpaperManagerService.java

/**
 * ★★★ 绑定壁纸服务组件
 * 
 * 静态壁纸 → 绑定 ImageWallpaper
 * 动态壁纸 → 绑定第三方 LiveWallpaper 服务
 */
boolean bindWallpaperComponentLocked(ComponentName componentName,
        boolean force, boolean fromUser, WallpaperData wallpaper,
        IRemoteCallback reply) {
    
    // 1. 验证壁纸服务
    if (componentName == null) {
        componentName = mDefaultWallpaperComponent;
        if (componentName == null) {
            componentName = mImageWallpaper;
        }
    }
    
    // 2. 查询壁纸服务信息
    Intent intent = new Intent(WallpaperService.SERVICE_INTERFACE);
    intent.setComponent(componentName);
    
    ServiceInfo si = mIPackageManager.getServiceInfo(
        componentName, PackageManager.GET_META_DATA, 
        wallpaper.userId);
    
    if (si == null) {
        throw new SecurityException("Wallpaper service not found");
    }
    
    // 3. 检查权限
    if (!si.permission.equals(BIND_WALLPAPER)) {
        throw new SecurityException(
            "Wallpaper service must require BIND_WALLPAPER permission");
    }
    
    // 4. 解析壁纸元数据 (动态壁纸的 XML 配置)
    WallpaperInfo wi = null;
    if (!componentName.equals(mImageWallpaper)) {
        wi = new WallpaperInfo(mContext, 
            mIPackageManager.resolveService(intent, null, 0, wallpaper.userId));
    }
    
    // 5. 断开旧连接
    WallpaperConnection oldConn = wallpaper.connection;
    if (oldConn != null) {
        detachWallpaperLocked(oldConn);
    }
    
    // 6. 创建新连接
    WallpaperConnection newConn = new WallpaperConnection(
        wi, wallpaper, componentName);
    wallpaper.connection = newConn;
    
    // 7. ★ 绑定服务
    intent.setComponent(componentName);
    boolean bound = mContext.bindServiceAsUser(
        intent,
        newConn,                           // ServiceConnection
        Context.BIND_AUTO_CREATE 
            | Context.BIND_SHOWING_UI
            | Context.BIND_FOREGROUND_SERVICE_WHILE_AWAKE
            | Context.BIND_INCLUDE_CAPABILITIES,
        new UserHandle(wallpaper.userId));
    
    if (!bound) {
        // 绑定失败 → 回退到默认壁纸
        if (!componentName.equals(mImageWallpaper)) {
            return bindWallpaperComponentLocked(
                mImageWallpaper, true, false, wallpaper, reply);
        }
        return false;
    }
    
    // 8. 更新壁纸组件信息
    wallpaper.wallpaperComponent = componentName;
    wallpaper.connection = newConn;
    
    return true;
}

4.6 WallpaperConnection — 与壁纸服务的连接

// WallpaperManagerService 内部类

class WallpaperConnection extends IWallpaperConnection.Stub
        implements ServiceConnection {
    
    // 壁纸信息
    final WallpaperInfo mInfo;
    final WallpaperData mWallpaper;
    final ComponentName mComponent;
    
    // 壁纸引擎
    IWallpaperService mService;           // 壁纸服务接口
    IWallpaperEngine mEngine;             // 壁纸引擎接口
    
    // 连接状态
    boolean mConnected;
    boolean mDimensionsChanged;
    boolean mPaddingChanged;
    
    // ═══════ ServiceConnection 回调 ═══════
    
    @Override
    public void onServiceConnected(ComponentName name, IBinder service) {
        synchronized (mLock) {
            mService = IWallpaperService.Stub.asInterface(service);
            
            // ★ 连接壁纸引擎
            attachServiceLocked(this, mWallpaper);
        }
    }
    
    @Override
    public void onServiceDisconnected(ComponentName name) {
        synchronized (mLock) {
            mService = null;
            mEngine = null;
            
            // 壁纸服务断开 → 重试连接或回退
            if (mWallpaper.wallpaperUpdating) {
                // 正在更新,不重连
                return;
            }
            
            // 延迟重连
            if (!mWallpaper.wallpaperComponent.equals(mImageWallpaper)) {
                // 动态壁纸崩溃 → 回退到静态壁纸
                bindWallpaperComponentLocked(mImageWallpaper, 
                    true, false, mWallpaper, null);
            }
        }
    }
    
    // ═══════ IWallpaperConnection 接口 ═══════
    
    /** 壁纸引擎连接就绪 */
    @Override
    public void attachEngine(IWallpaperEngine engine, int displayId) {
        synchronized (mLock) {
            mEngine = engine;
            mConnected = true;
            
            // 设置壁纸引擎参数
            try {
                engine.setDesiredSize(
                    mWallpaper.width, mWallpaper.height);
                engine.setDisplayPadding(mWallpaper.padding);
                
                // 发送当前偏移
                engine.setWallpaperOffsets(
                    mWallpaper.mWallpaperXOffset,
                    mWallpaper.mWallpaperYOffset);
                
                // ★ 设置壁纸暗化 (Android 14)
                engine.applyDimming(mWallpaper.mWallpaperDimAmount);
                
            } catch (RemoteException e) { }
            
            // 通知 WMS 壁纸已就绪
            notifyCallbacksLocked(mWallpaper);
        }
    }
    
    /** 壁纸引擎报告颜色 */
    @Override
    public void onWallpaperColorsChanged(WallpaperColors colors, 
            int displayId) {
        synchronized (mLock) {
            mWallpaper.mWallpaperColors = colors;
            
            // ★ 通知所有颜色监听器 (Material You)
            notifyWallpaperColorsChangedOnDisplay(
                mWallpaper, mWallpaper.mWhich, displayId);
        }
    }
    
    /** 壁纸引擎报告尺寸 */
    @Override
    public void engineShown(IWallpaperEngine engine) {
        // 壁纸渲染完成,首帧已显示
    }
}

/**
 * 连接壁纸引擎
 */
void attachServiceLocked(WallpaperConnection conn, 
        WallpaperData wallpaper) {
    try {
        // ★ 调用壁纸服务创建引擎
        conn.mService.attach(
            conn,                              // IWallpaperConnection
            conn.mToken,                       // 窗口令牌
            TYPE_WALLPAPER,                    // 窗口类型
            false /* isPreview */,
            wallpaper.width,                   // 期望宽度
            wallpaper.height,                  // 期望高度
            wallpaper.padding,                 // 内边距
            wallpaper.mWhich                   // FLAG_SYSTEM / FLAG_LOCK
        );
    } catch (RemoteException e) {
        // 连接失败
    }
}

五、WallpaperService — 壁纸服务基类

5.1 核心架构

// frameworks/base/core/java/android/service/wallpaper/WallpaperService.java

public abstract class WallpaperService extends Service {
    
    // ═══════ 服务接口 ═══════
    
    static final String SERVICE_INTERFACE = 
        "android.service.wallpaper.WallpaperService";
    
    @Override
    public final IBinder onBind(Intent intent) {
        return new IWallpaperServiceWrapper(this);
    }
    
    /**
     * ★ 子类必须实现 — 创建壁纸引擎
     */
    public abstract Engine onCreateEngine();
    
    // ═══════ 内部服务包装 ═══════
    
    class IWallpaperServiceWrapper extends IWallpaperService.Stub {
        
        private final WallpaperService mTarget;
        
        @Override
        public void attach(IWallpaperConnection conn,
                IBinder windowToken,
                int windowType, boolean isPreview,
                int reqWidth, int reqHeight,
                Rect padding, int displayId) {
            
            // ★ 创建引擎实例
            Engine engine = mTarget.onCreateEngine();
            
            // 初始化引擎
            engine.attach(
                IWallpaperEngineWrapper.wrap(engine),
                conn, windowToken, windowType,
                isPreview, reqWidth, reqHeight, 
                padding, displayId);
        }
    }
    
    // ═══════ Engine — 壁纸渲染引擎 ★★★ ═══════
    
    public class Engine {
        
        // 窗口相关
        final BaseSurfaceHolder mSurfaceHolder = new WallpaperSurfaceHolder();
        SurfaceControl mSurfaceControl;
        
        // 窗口 Session (与 WMS 通信)
        IWindowSession mSession;
        
        // 可见性状态
        boolean mVisible;
        boolean mReportedVisible;
        
        // 壁纸偏移
        float mPendingXOffset;
        float mPendingYOffset;
        float mPendingXOffsetStep;
        float mPendingYOffsetStep;
        boolean mOffsetsChanged;
        
        // 尺寸
        int mWidth;           // Surface 宽度
        int mHeight;          // Surface 高度
        int mReqWidth;        // 请求宽度
        int mReqHeight;       // 请求高度
        
        // 壁纸标志
        int mWallpaperFlags;  // FLAG_SYSTEM / FLAG_LOCK
        
        // ══════ 生命周期回调 ══════
        
        /** 引擎创建时 */
        public void onCreate(SurfaceHolder surfaceHolder) { }
        
        /** 引擎销毁时 */
        public void onDestroy() { }
        
        /** 可见性变化 */
        public void onVisibilityChanged(boolean visible) { }
        
        /** Surface 创建 */
        public void onSurfaceCreated(SurfaceHolder holder) { }
        
        /** Surface 尺寸变化 */
        public void onSurfaceChanged(SurfaceHolder holder, 
                int format, int width, int height) { }
        
        /** Surface 销毁 */
        public void onSurfaceDestroyed(SurfaceHolder holder) { }
        
        /** 壁纸偏移变化 (滑动桌面时) */
        public void onOffsetsChanged(float xOffset, float yOffset,
                float xOffsetStep, float yOffsetStep,
                int xPixelOffset, int yPixelOffset) { }
        
        /** 触摸事件 */
        public void onTouchEvent(MotionEvent event) { }
        
        /** ★ 提供壁纸颜色 */
        public void notifyColorsChanged() {
            // 通知 WallpaperManagerService 颜色变化
            WallpaperColors colors = onComputeColors();
            if (colors != null) {
                try {
                    mConnection.onWallpaperColorsChanged(colors, mDisplayId);
                } catch (RemoteException e) { }
            }
        }
        
        /** 子类可覆盖 — 计算壁纸颜色 */
        public @Nullable WallpaperColors onComputeColors() {
            return null;
        }
        
        // ══════ Surface 管理 ══════
        
        /**
         * 壁纸引擎内部使用 SurfaceHolder 来渲染壁纸
         */
        
        /** 获取 SurfaceHolder */
        public SurfaceHolder getSurfaceHolder() {
            return mSurfaceHolder;
        }
        
        /** 设置触摸事件是否可接收 */
        public void setTouchEventsEnabled(boolean enabled) {
            mTouchEventsEnabled = enabled;
        }
        
        // ══════ ★ attach — 引擎初始化核心 ══════
        
        void attach(IWallpaperEngineWrapper wrapper,
                IWallpaperConnection conn,
                IBinder windowToken,
                int windowType, boolean isPreview,
                int reqWidth, int reqHeight,
                Rect padding, int displayId) {
            
            mConnection = conn;
            mWindowToken = windowToken;
            mIsPreview = isPreview;
            mReqWidth = reqWidth;
            mReqHeight = reqHeight;
            mDisplayId = displayId;
            
            // 1. 创建与 WMS 的窗口 Session
            mSession = WindowManagerGlobal.getWindowSession();
            
            // 2. ★ 添加壁纸窗口到 WMS
            mWindow = new W(this);  // IWindow 实现
            
            WindowManager.LayoutParams lp = 
                new WindowManager.LayoutParams();
            lp.type = WindowManager.LayoutParams.TYPE_WALLPAPER;
            lp.token = windowToken;
            lp.width = ViewGroup.LayoutParams.MATCH_PARENT;
            lp.height = ViewGroup.LayoutParams.MATCH_PARENT;
            lp.format = PixelFormat.RGBX_8888;
            lp.flags = WindowManager.LayoutParams.FLAG_NOT_FOCUSABLE
                | WindowManager.LayoutParams.FLAG_NOT_TOUCHABLE;
            lp.setTitle("Wallpaper{" + 
                Integer.toHexString(System.identityHashCode(this)) + "}");
            lp.inputFeatures = INPUT_FEATURE_NO_INPUT_CHANNEL;
            
            // 添加窗口
            int result = mSession.addToDisplayAsUser(
                mWindow, lp, View.VISIBLE,
                displayId, userId, mInsetsState,
                mInputChannel, mTempInsets, mTempControls);
            
            // 3. 获取 SurfaceControl
            mSurfaceControl = new SurfaceControl.Builder()
                .setName("WallpaperSurface")
                .setCallsite("WallpaperService.Engine.attach")
                .build();
            
            // 4. 调用 onCreate 回调
            onCreate(mSurfaceHolder);
            
            // 5. 设置为可见
            updateSurface(true, false, false);
        }
        
        /**
         * ★ 更新 Surface
         */
        void updateSurface(boolean forceRelayout, 
                boolean forceReport, boolean redrawNeeded) {
            
            // 与 WMS 协商 Surface 参数
            mSession.relayout(mWindow, mLayout, 
                mWidth, mHeight, View.VISIBLE, 0,
                mWinFrame, mOverscanInsets, mContentInsets,
                mVisibleInsets, mStableInsets,
                mBackdropFrame, mDisplayCutout,
                mMergedConfiguration, mSurfaceControl,
                mInsetsState, mTempControls, mSurfaceSize);
            
            // Surface 创建/变化回调
            if (surfaceCreated) {
                onSurfaceCreated(mSurfaceHolder);
            }
            if (sizeChanged) {
                onSurfaceChanged(mSurfaceHolder, 
                    mFormat, mWidth, mHeight);
            }
        }
        
        /**
         * ★ Android 14: 暗化壁纸
         */
        public void applyDimming(float dimAmount) {
            // 0.0 = 不暗化, 1.0 = 完全暗化
            mWallpaperDimAmount = dimAmount;
            
            // 通过 SurfaceControl 设置 Color filter
            SurfaceControl.Transaction t = new SurfaceControl.Transaction();
            if (dimAmount > 0) {
                // 添加暗化图层
                float[] colorMatrix = new float[]{
                    1-dimAmount, 0, 0, 0, 0,
                    0, 1-dimAmount, 0, 0, 0,
                    0, 0, 1-dimAmount, 0, 0,
                    0, 0, 0, 1, 0
                };
                t.setColorTransform(mSurfaceControl, colorMatrix, 
                    new float[3]);
            } else {
                t.clearColorTransform(mSurfaceControl);
            }
            t.apply();
        }
    }
}

六、ImageWallpaper — 默认静态壁纸实现

6.1 整体结构

// packages/SystemUI/src/com/android/systemui/wallpapers/ImageWallpaper.java

public class ImageWallpaper extends WallpaperService {
    
    @Override
    public Engine onCreateEngine() {
        return new GLEngine();
    }
    
    /**
     * ★ GLEngine — 使用 OpenGL ES 渲染壁纸
     * 
     * 为什么用 OpenGL 而不是 Canvas?
     * 1. GPU 渲染性能更好
     * 2. 支持高分辨率壁纸的高效渲染
     * 3. 支持硬件加速特效(模糊、暗化等)
     * 4. 与 SurfaceFlinger 的 Layer 直接对接
     */
    class GLEngine extends Engine {
        
        // EGL 上下文
        private EglHelper mEglHelper;
        
        // 渲染器
        private ImageWallpaperRenderer mRenderer;
        
        // 壁纸 Bitmap
        private Bitmap mBitmap;
        
        // 显示参数
        private int mDisplayWidth;
        private int mDisplayHeight;
        
        @Override
        public void onCreate(SurfaceHolder surfaceHolder) {
            super.onCreate(surfaceHolder);
            
            // 设置 Surface 类型
            surfaceHolder.setType(SurfaceHolder.SURFACE_TYPE_GPU);
            surfaceHolder.setFormat(PixelFormat.RGBA_8888);
            
            // 初始化 EGL
            mEglHelper = new EglHelper();
            
            // 创建渲染器
            mRenderer = new ImageWallpaperRenderer(getApplicationContext());
        }
        
        @Override
        public void onSurfaceCreated(SurfaceHolder holder) {
            super.onSurfaceCreated(holder);
            
            // 初始化 EGL Surface
            mEglHelper.init(holder, needSupportWideColorGamut());
            
            // 加载壁纸 Bitmap
            loadWallpaperBitmap();
        }
        
        @Override
        public void onSurfaceChanged(SurfaceHolder holder, 
                int format, int width, int height) {
            
            mDisplayWidth = width;
            mDisplayHeight = height;
            
            // 更新渲染器尺寸
            mRenderer.setDisplaySize(width, height);
            
            // 重新渲染
            drawFrame();
        }
        
        @Override
        public void onVisibilityChanged(boolean visible) {
            super.onVisibilityChanged(visible);
            
            if (visible) {
                drawFrame();
            }
        }
        
        @Override
        public void onOffsetsChanged(float xOffset, float yOffset,
                float xOffsetStep, float yOffsetStep,
                int xPixelOffset, int yPixelOffset) {
            
            // ★ 壁纸偏移变化(用户滑动桌面)
            mRenderer.setOffsets(xOffset, yOffset);
            drawFrame();
        }
        
        /**
         * ★ 渲染一帧壁纸
         */
        private void drawFrame() {
            if (!mEglHelper.hasEglContext()) return;
            
            // 1. 设置 EGL 为当前上下文
            mEglHelper.makeCurrent();
            
            // 2. 渲染壁纸
            mRenderer.draw(
                mDisplayWidth, mDisplayHeight,
                mBitmap,
                mPendingXOffset, mPendingYOffset);
            
            // 3. 交换缓冲区 (显示到屏幕)
            mEglHelper.swapBuffers();
            
            // 4. 报告帧已渲染
            reportEngineShown(true);
        }
        
        /**
         * ★ 加载壁纸 Bitmap
         */
        private void loadWallpaperBitmap() {
            // 从 WallpaperManagerService 获取壁纸文件
            WallpaperManager wm = WallpaperManager.getInstance(
                getApplicationContext());
            
            // 使用 BitmapFactory 解码
            // 针对大壁纸做下采样
            BitmapFactory.Options options = new BitmapFactory.Options();
            options.inPreferredConfig = Bitmap.Config.HARDWARE;
            // HARDWARE Config: Bitmap 直接存储在 GPU 内存中
            // 避免 CPU → GPU 的数据传输
            
            ParcelFileDescriptor pfd = wm.getWallpaperFile(
                WallpaperManager.FLAG_SYSTEM);
            
            if (pfd != null) {
                mBitmap = BitmapFactory.decodeFileDescriptor(
                    pfd.getFileDescriptor(), null, options);
                pfd.close();
            } else {
                // 使用默认壁纸
                mBitmap = BitmapFactory.decodeResource(
                    getResources(), 
                    com.android.internal.R.drawable.default_wallpaper,
                    options);
            }
            
            // 通知颜色
            notifyColorsChanged();
        }
        
        @Override
        public WallpaperColors onComputeColors() {
            if (mBitmap != null) {
                return WallpaperColors.fromBitmap(mBitmap);
            }
            return null;
        }
        
        @Override
        public void onDestroy() {
            super.onDestroy();
            mEglHelper.finish();
            if (mBitmap != null) {
                mBitmap.recycle();
            }
        }
    }
}

6.2 ImageWallpaperRenderer — OpenGL 渲染

// packages/SystemUI/src/com/android/systemui/wallpapers/gl/
// ImageWallpaperRenderer.java

public class ImageWallpaperRenderer {
    
    // OpenGL 纹理
    private int mTextureId;
    
    // 着色器程序
    private int mProgram;
    
    // 顶点/纹理坐标缓冲
    private FloatBuffer mVertexBuffer;
    private FloatBuffer mTexCoordBuffer;
    
    // 壁纸偏移
    private float mXOffset = 0.5f;
    private float mYOffset = 0.5f;
    
    // 壁纸/屏幕比例
    private float mBitmapAspectRatio;
    private float mScreenAspectRatio;
    
    /**
     * ★ 渲染壁纸
     */
    public void draw(int screenWidth, int screenHeight,
            Bitmap bitmap, float xOffset, float yOffset) {
        
        // 1. 设置视口
        GLES20.glViewport(0, 0, screenWidth, screenHeight);
        
        // 2. 清除颜色
        GLES20.glClear(GLES20.GL_COLOR_BUFFER_BIT);
        
        // 3. 使用着色器程序
        GLES20.glUseProgram(mProgram);
        
        // 4. 绑定壁纸纹理
        GLES20.glActiveTexture(GLES20.GL_TEXTURE0);
        GLES20.glBindTexture(GLES20.GL_TEXTURE_2D, mTextureId);
        
        // 5. ★ 计算纹理坐标(实现视差偏移)
        float[] texCoords = calculateTexCoords(
            bitmap.getWidth(), bitmap.getHeight(),
            screenWidth, screenHeight,
            xOffset, yOffset);
        
        // 6. 设置顶点和纹理坐标
        int positionHandle = GLES20.glGetAttribLocation(mProgram, "aPosition");
        int texCoordHandle = GLES20.glGetAttribLocation(mProgram, "aTexCoord");
        
        GLES20.glVertexAttribPointer(positionHandle, 2, 
            GLES20.GL_FLOAT, false, 0, mVertexBuffer);
        GLES20.glEnableVertexAttribArray(positionHandle);
        
        mTexCoordBuffer.put(texCoords).position(0);
        GLES20.glVertexAttribPointer(texCoordHandle, 2,
            GLES20.GL_FLOAT, false, 0, mTexCoordBuffer);
        GLES20.glEnableVertexAttribArray(texCoordHandle);
        
        // 7. 绘制
        GLES20.glDrawArrays(GLES20.GL_TRIANGLE_STRIP, 0, 4);
    }
    
    /**
     * ★★ 计算视差纹理坐标
     * 
     * 壁纸通常比屏幕宽,通过偏移显示不同区域实现视差效果
     * 
     * 壁纸图片:
     * ┌──────────────────────────────────────┐
     * │                                      │
     * │     ┌──────────┐                     │
     * │     │ 可见区域  │  ← 随 xOffset 移动  │
     * │     │ (屏幕大小)│                     │
     * │     └──────────┘                     │
     * │                                      │
     * └──────────────────────────────────────┘
     * 
     * xOffset = 0.0 → 显示最左边
     * xOffset = 0.5 → 显示中间
     * xOffset = 1.0 → 显示最右边
     */
    private float[] calculateTexCoords(
            int bitmapWidth, int bitmapHeight,
            int screenWidth, int screenHeight,
            float xOffset, float yOffset) {
        
        // 计算壁纸中可见区域的纹理坐标
        float visibleWidth = (float) screenWidth / bitmapWidth;
        float visibleHeight = (float) screenHeight / bitmapHeight;
        
        // 如果壁纸比屏幕大,计算偏移
        float maxOffsetX = 1.0f - visibleWidth;
        float maxOffsetY = 1.0f - visibleHeight;
        
        float texLeft = xOffset * maxOffsetX;
        float texRight = texLeft + visibleWidth;
        float texTop = yOffset * maxOffsetY;
        float texBottom = texTop + visibleHeight;
        
        // 限制在 [0, 1] 范围
        texLeft = Math.max(0, Math.min(1, texLeft));
        texRight = Math.max(0, Math.min(1, texRight));
        texTop = Math.max(0, Math.min(1, texTop));
        texBottom = Math.max(0, Math.min(1, texBottom));
        
        return new float[]{
            texLeft, texTop,     // 左上
            texRight, texTop,    // 右上
            texLeft, texBottom,  // 左下
            texRight, texBottom  // 右下
        };
    }
    
    /**
     * 上传 Bitmap 到 GPU 纹理
     */
    private void uploadBitmapTexture(Bitmap bitmap) {
        int[] textures = new int[1];
        GLES20.glGenTextures(1, textures, 0);
        mTextureId = textures[0];
        
        GLES20.glBindTexture(GLES20.GL_TEXTURE_2D, mTextureId);
        
        // 纹理参数
        GLES20.glTexParameteri(GLES20.GL_TEXTURE_2D,
            GLES20.GL_TEXTURE_MIN_FILTER, GLES20.GL_LINEAR);
        GLES20.glTexParameteri(GLES20.GL_TEXTURE_2D,
            GLES20.GL_TEXTURE_MAG_FILTER, GLES20.GL_LINEAR);
        GLES20.glTexParameteri(GLES20.GL_TEXTURE_2D,
            GLES20.GL_TEXTURE_WRAP_S, GLES20.GL_CLAMP_TO_EDGE);
        GLES20.glTexParameteri(GLES20.GL_TEXTURE_2D,
            GLES20.GL_TEXTURE_WRAP_T, GLES20.GL_CLAMP_TO_EDGE);
        
        // 上传 Bitmap 数据
        GLUtils.texImage2D(GLES20.GL_TEXTURE_2D, 0, bitmap, 0);
    }
    
    // 顶点着色器
    private static final String VERTEX_SHADER =
        "attribute vec4 aPosition;\n" +
        "attribute vec2 aTexCoord;\n" +
        "varying vec2 vTexCoord;\n" +
        "void main() {\n" +
        "    gl_Position = aPosition;\n" +
        "    vTexCoord = aTexCoord;\n" +
        "}\n";
    
    // 片段着色器
    private static final String FRAGMENT_SHADER =
        "precision mediump float;\n" +
        "varying vec2 vTexCoord;\n" +
        "uniform sampler2D uTexture;\n" +
        "void main() {\n" +
        "    gl_FragColor = texture2D(uTexture, vTexCoord);\n" +
        "}\n";
}

七、WallpaperController (WMS) — 壁纸窗口管理

7.1 核心职责

// frameworks/base/services/core/java/com/android/server/wm/
// WallpaperController.java

/**
 * WMS 中的壁纸控制器
 * 
 * 职责:
 * 1. 管理壁纸窗口 (TYPE_WALLPAPER) 的位置/大小/可见性
 * 2. 确定哪个窗口"需要壁纸"(哪个 Activity 显示壁纸背景)
 * 3. 控制壁纸偏移(视差效果)
 * 4. 壁纸过渡动画
 */
class WallpaperController {
    
    private final WindowManagerService mService;
    private final DisplayContent mDisplayContent;
    
    // ═══════ 壁纸窗口列表 ═══════
    
    // 当前活跃的壁纸窗口令牌
    private WallpaperWindowToken mWallpaperToken;
    
    // 壁纸目标窗口 (显示壁纸背景的窗口)
    private WindowState mWallpaperTarget;
    private WindowState mPrevWallpaperTarget;
    
    // ═══════ 壁纸偏移 ═══════
    
    float mLastWallpaperX = -1;
    float mLastWallpaperY = -1;
    float mLastWallpaperXStep = -1;
    float mLastWallpaperYStep = -1;
    int mLastWallpaperDisplayOffsetX = Integer.MIN_VALUE;
    int mLastWallpaperDisplayOffsetY = Integer.MIN_VALUE;
    
    // ═══════ 壁纸可见性 ═══════
    
    private boolean mWallpaperDrawing;
    private boolean mWallpaperIsTarget;
    
    // 可见性监听器
    private final WallpaperVisibilityListeners mWallpaperVisibilityListeners;
    
    // ═══════ 核心方法 ═══════
    
    /**
     * ★ 查找壁纸目标窗口
     * 
     * 壁纸目标 = 当前需要显示壁纸的窗口
     * 例如: Launcher 的窗口 (FLAG_SHOW_WALLPAPER)
     */
    void findWallpaperTarget() {
        WindowState newTarget = null;
        
        // 从上到下遍历所有窗口
        mDisplayContent.forAllWindows(w -> {
            // 检查窗口是否请求显示壁纸
            if ((w.mAttrs.flags & FLAG_SHOW_WALLPAPER) != 0) {
                // 这个窗口需要壁纸
                
                // 检查可见性
                if (w.isVisible() || w.isDrawn()) {
                    mFoundWallpaperTarget = true;
                    return true; // 找到了,停止遍历
                }
            }
            return false;
        }, true /* traverseTopToBottom */);
        
        // 更新壁纸目标
        if (newTarget != mWallpaperTarget) {
            WindowState oldTarget = mWallpaperTarget;
            mWallpaperTarget = newTarget;
            
            // 壁纸目标变化 → 需要更新壁纸位置
            updateWallpaperWindowsTarget(newTarget);
            
            // 壁纸目标变化 → 可能需要播放过渡动画
            if (mWallpaperTarget != null && oldTarget != null) {
                handleWallpaperTargetChange(oldTarget, mWallpaperTarget);
            }
        }
    }
    
    /**
     * ★ 更新壁纸窗口位置
     * 壁纸窗口总是放在壁纸目标窗口的正后面
     */
    void updateWallpaperWindowsTarget(WindowState target) {
        if (mWallpaperToken == null) return;
        
        // 壁纸窗口应该紧贴在目标窗口后面
        // 这样当目标窗口是透明/半透明时,壁纸可见
        
        for (int i = mWallpaperToken.getChildCount() - 1; i >= 0; i--) {
            WindowState wallpaper = mWallpaperToken.getChildAt(i);
            
            if (target != null) {
                // 将壁纸放在目标窗口后面
                wallpaper.mToken.getParent().positionChildAt(
                    target.getParent().mChildren.indexOf(target),
                    mWallpaperToken,
                    false /* includingParents */);
            }
        }
    }
    
    /**
     * ★ 更新壁纸偏移(视差效果)
     * 
     * 当 Launcher 滑动页面时调用
     */
    boolean updateWallpaperOffset(WindowState wallpaperWin,
            boolean sync) {
        
        // 1. 获取当前偏移
        float wpx = mWallpaperTarget != null 
            ? mWallpaperTarget.mWallpaperX : 0.5f;
        float wpy = mWallpaperTarget != null 
            ? mWallpaperTarget.mWallpaperY : 0.5f;
        
        // 2. 计算像素偏移
        int availableWidth = wallpaperWin.mRequestedWidth 
            - mDisplayContent.getDefaultDisplayInfo().logicalWidth;
        int availableHeight = wallpaperWin.mRequestedHeight 
            - mDisplayContent.getDefaultDisplayInfo().logicalHeight;
        
        int offsetX = availableWidth > 0 
            ? -(int) (availableWidth * wpx + 0.5f) : 0;
        int offsetY = availableHeight > 0
            ? -(int) (availableHeight * wpy + 0.5f) : 0;
        
        // 3. 应用偏移
        boolean changed = false;
        if (wallpaperWin.mXOffset != offsetX 
                || wallpaperWin.mYOffset != offsetY) {
            wallpaperWin.mXOffset = offsetX;
            wallpaperWin.mYOffset = offsetY;
            changed = true;
        }
        
        if (changed) {
            // 更新壁纸 Surface 位置
            SurfaceControl.Transaction t = 
                wallpaperWin.getPendingTransaction();
            t.setPosition(
                wallpaperWin.getSurfaceControl(),
                offsetX + wallpaperWin.mFrame.left,
                offsetY + wallpaperWin.mFrame.top);
            
            if (sync) {
                t.apply();
            }
        }
        
        return changed;
    }
    
    /**
     * ★ 壁纸可见性控制
     */
    void updateWallpaperVisibility() {
        if (mWallpaperToken == null) return;
        
        boolean visible = isWallpaperVisible();
        
        for (int i = mWallpaperToken.getChildCount() - 1; i >= 0; i--) {
            WindowState wallpaper = mWallpaperToken.getChildAt(i);
            
            if (visible) {
                wallpaper.show(false /* animate */);
            } else {
                wallpaper.hide(false /* animate */);
            }
        }
        
        // 通知壁纸可见性监听器
        mWallpaperVisibilityListeners.notifyWallpaperVisibilityChanged(
            mDisplayContent, visible);
    }
    
    /**
     * 判断壁纸是否应该可见
     */
    boolean isWallpaperVisible() {
        // 有壁纸目标且目标窗口可见
        if (mWallpaperTarget != null && mWallpaperTarget.isVisible()) {
            return true;
        }
        
        // 正在执行壁纸过渡动画
        if (isWallpaperTransitionAnimating()) {
            return true;
        }
        
        return false;
    }
}

7.2 壁纸窗口层级关系

WMS 窗口层级示意:

  (顶部)
  ┌────────────────────────────┐
  │ TYPE_STATUS_BAR            │  z=最高
  │ TYPE_NAVIGATION_BAR        │
  ├────────────────────────────┤
  │ TYPE_APPLICATION           │  ← 前台 App
  │ (FLAG_SHOW_WALLPAPER)      │  ← 如果有此 flag, 壁纸可见
  ├────────────────────────────┤
  │ TYPE_WALLPAPER             │  ← 壁纸窗口 ★
  │ (紧贴在壁纸目标窗口后面)     │
  ├────────────────────────────┤
  │ TYPE_APPLICATION (其他App)  │
  │ (不可见)                    │
  ├────────────────────────────┤
  │ 桌面壁纸底层                │  z=最低
  └────────────────────────────┘
  (底部)

当 Launcher 是前台时:
  Launcher 设置了 FLAG_SHOW_WALLPAPER
  → WallpaperController 找到 Launcher 作为壁纸目标
  → 壁纸窗口放在 Launcher 后面
  → Launcher 背景透明 → 壁纸可见

当普通 App 是前台时 (不透明):
  App 没有 FLAG_SHOW_WALLPAPER
  → 壁纸目标 = null
  → 壁纸窗口隐藏 (节省 GPU 资源)

八、壁纸偏移/视差效果

8.1 完整调用链

用户在 Launcher 左右滑动页面
│
▼
Launcher (Workspace.java):
├── computeScrollX()
│   └── 计算当前页面位置 → xOffset (0.0 ~ 1.0)
│
├── WallpaperManager.setWallpaperOffsets(windowToken, xOffset, yOffset)
│   │
│   └── → Binder → WallpaperManagerService.setWallpaperOffsets()
│       │
│       └── → 更新 WallpaperData.mWallpaperXOffset
│           │
│           └── → 通知 WallpaperConnection
│               │
│               └── → IWallpaperEngine.setWallpaperOffsets()
│                   │
│                   └── → WallpaperService.Engine.onOffsetsChanged()
│                       │
│                       ├── 静态壁纸 (ImageWallpaper):
│                       │   └── 重新计算纹理坐标 → drawFrame()
│                       │       └── 壁纸图片在屏幕上"滑动"
│                       │
│                       └── 动态壁纸 (Live Wallpaper):
│                           └── 自定义逻辑 (例如粒子效果跟随偏移)
│
└── 同时通知 WMS:
    WallpaperController.updateWallpaperOffset()
    └── 移动壁纸 Surface 的位置
        └── SurfaceControl.Transaction.setPosition()

视觉效果:
  
  页面1          页面2          页面3
  ┌──────┐      ┌──────┐      ┌──────┐
  │      │      │      │      │      │
  │ App1 │      │ App2 │      │ App3 │
  │      │      │      │      │      │
  └──────┘      └──────┘      └──────┘
  xOffset=0.0   xOffset=0.5   xOffset=1.0
  
  壁纸 (比屏幕宽):
  ┌─────────────────────────────────────────┐
  │                                         │
  │  ▲可见区域在此位置  ▲这里        ▲这里   │
  │                                         │
  └─────────────────────────────────────────┘
  
  → 用户滑动页面时,壁纸以更慢的速度跟随移动
  → 产生"视差"效果

8.2 偏移计算细节

// Launcher3 中的壁纸偏移设置

// packages/apps/Launcher3/src/com/android/launcher3/Workspace.java

public class Workspace extends PagedView {
    
    @Override
    protected void onScrollChanged(int l, int t, int oldl, int oldt) {
        super.onScrollChanged(l, t, oldl, oldt);
        
        updateWallpaperOffset();
    }
    
    private void updateWallpaperOffset() {
        int pageCount = getChildCount();
        if (pageCount <= 1) return;  // 只有一页不需要偏移
        
        // 当前滚动位置
        int scrollX = getScrollX();
        int maxScrollX = getMaxScrollX();
        
        // 计算偏移 (0.0 ~ 1.0)
        float offset = maxScrollX > 0 
            ? (float) scrollX / maxScrollX 
            : 0.5f;
        
        // 偏移步进 (每页对应的偏移增量)
        float step = 1.0f / (pageCount - 1);
        
        // 设置壁纸偏移
        WallpaperManager wm = WallpaperManager.getInstance(getContext());
        wm.setWallpaperOffsetSteps(step, 1.0f);
        wm.setWallpaperOffsets(getWindowToken(), offset, 0.5f);
        
        // 示例:
        // 5 个页面: step = 0.25
        // 页面0: offset = 0.0
        // 页面1: offset = 0.25
        // 页面2: offset = 0.5
        // 页面3: offset = 0.75
        // 页面4: offset = 1.0
    }
}

九、主屏幕壁纸 vs 锁屏壁纸

9.1 双壁纸架构

Android 7.0+ 支持独立的主屏幕和锁屏壁纸:

┌─────────────────────────────────────────────┐
│               壁纸存储                        │
│                                             │
│  /data/system/users/{userId}/               │
│  ├── wallpaper_orig     (主屏幕原始壁纸)      │
│  ├── wallpaper          (主屏幕裁剪后壁纸)     │
│  ├── wallpaper_lock_orig(锁屏原始壁纸)        │
│  ├── wallpaper_lock     (锁屏裁剪后壁纸)      │
│  └── wallpaper_info.xml (壁纸配置信息)        │
│                                             │
│  如果没有单独设置锁屏壁纸,                    │
│  则锁屏使用主屏幕壁纸                         │
└─────────────────────────────────────────────┘

┌─────────────────────────────────────────────┐
│               壁纸服务                        │
│                                             │
│  主屏幕:                                     │
│  ├── WallpaperData (FLAG_SYSTEM)             │
│  └── WallpaperConnection → ImageWallpaper    │
│      或 LiveWallpaperService                 │
│                                             │
│  锁屏:                                       │
│  ├── WallpaperData (FLAG_LOCK)               │
│  └── WallpaperConnection → ImageWallpaper    │
│      或 LiveWallpaperService                 │
│      (可以是不同的壁纸服务)                    │
│                                             │
│  Android 14: 锁屏也支持动态壁纸!             │
└─────────────────────────────────────────────┘

9.2 锁屏壁纸处理

// SystemUI 中的锁屏壁纸处理
// packages/SystemUI/src/com/android/systemui/statusbar/phone/
// LockscreenWallpaper.java (概念性代码)

/**
 * 处理锁屏壁纸的显示
 * 
 * 锁屏时:
 * 1. 如果有单独的锁屏壁纸 → 显示锁屏壁纸
 * 2. 如果没有 → 显示主屏幕壁纸 (可能加暗化效果)
 * 
 * Android 14:
 * 3. 锁屏也支持动态壁纸
 * 4. 壁纸暗化 (dim) 控制
 */

// WallpaperManagerService.java 中的锁屏壁纸管理

/**
 * 获取锁屏壁纸
 */
@Override
public ParcelFileDescriptor getWallpaperWithFeature(
        String callingPkg, String callingFeatureId,
        IWallpaperManagerCallback cb, 
        @SetWallpaperFlags int which,
        Bundle outParams, int wallpaperId, int userId) {
    
    synchronized (mLock) {
        WallpaperData wallpaper;
        
        if ((which & FLAG_LOCK) != 0) {
            // 请求锁屏壁纸
            wallpaper = mLockWallpaperMap.get(userId);
            
            if (wallpaper == null) {
                // 没有单独的锁屏壁纸 → 返回主屏幕壁纸
                wallpaper = mWallpaperMap.get(userId);
            }
        } else {
            // 请求主屏幕壁纸
            wallpaper = mWallpaperMap.get(userId);
        }
        
        if (wallpaper == null) return null;
        
        // 返回壁纸文件
        return ParcelFileDescriptor.open(wallpaper.cropFile, 
            MODE_READ_ONLY);
    }
}

/**
 * ★ Android 14: 壁纸暗化控制
 * 
 * 锁屏时壁纸可以被暗化,使文字更清晰
 */
@Override
public void setWallpaperDimAmount(float dimAmount) {
    // dimAmount: 0.0 = 不暗化, 1.0 = 完全暗
    
    int userId = UserHandle.getCallingUserId();
    
    synchronized (mLock) {
        WallpaperData systemWallpaper = mWallpaperMap.get(userId);
        WallpaperData lockWallpaper = mLockWallpaperMap.get(userId);
        
        if (systemWallpaper != null) {
            systemWallpaper.mWallpaperDimAmount = dimAmount;
            
            // 通知壁纸引擎应用暗化
            if (systemWallpaper.connection != null
                    && systemWallpaper.connection.mEngine != null) {
                try {
                    systemWallpaper.connection.mEngine
                        .applyDimming(dimAmount);
                } catch (RemoteException e) { }
            }
        }
        
        // 锁屏壁纸同理
        if (lockWallpaper != null) {
            lockWallpaper.mWallpaperDimAmount = dimAmount;
            // ...
        }
    }
}

十、Material You — 壁纸颜色系统 ★★★

10.1 颜色提取流程

壁纸设置/变化
│
▼
WallpaperManagerService.onWallpaperWriteComplete()
│
├── extractColors(wallpaper)
│   │
│   ├── 解码壁纸 Bitmap
│   ├── WallpaperColors.fromBitmap(bitmap)
│   │   ├── Palette API 颜色量化
│   │   ├── 提取主色 / 副色 / 第三色
│   │   └── 计算颜色提示 (DARK_TEXT / DARK_THEME)
│   │
│   └── wallpaper.mWallpaperColors = colors
│
├── notifyWallpaperColorsChanged(wallpaper, FLAG_SYSTEM)
│   │
│   └── for (IWallpaperManagerCallback cb : mColorsChangedListeners) {
│           cb.onWallpaperColorsChanged(colors, which, userId);
│       }
│       │
│       ├── ★ SystemUI: ThemeOverlayController 收到通知
│       │   │
│       │   └── reevaluateSystemTheme()
│       │       │
│       │       ├── 1. 从壁纸颜色中选择种子色 (seed color)
│       │       │   └── primaryColor → seed
│       │       │
│       │       ├── 2. 生成 Material You 调色板
│       │       │   └── ColorScheme(seed)
│       │       │       ├── accent1[13]   (主强调色)
│       │       │       ├── accent2[13]   (次强调色)
│       │       │       ├── accent3[13]   (第三强调色)
│       │       │       ├── neutral1[13]  (中性色1)
│       │       │       └── neutral2[13]  (中性色2)
│       │       │       // 每种 13 个色调级别 (0,10,50,100,...,900,1000)
│       │       │
│       │       ├── 3. 创建 FabricatedOverlay
│       │       │   └── 动态生成主题资源覆盖
│       │       │       ├── system_accent1_0 = accent1[0]
│       │       │       ├── system_accent1_10 = accent1[1]
│       │       │       ├── ...
│       │       │       └── 覆盖整个系统调色板
│       │       │
│       │       └── 4. 应用 Overlay
│       │           └── OverlayManagerService.setEnabled()
│       │               → 所有 App 的主题颜色随壁纸变化
│       │
│       ├── Settings: 主题颜色预览更新
│       │
│       └── Launcher: 图标着色更新
│
└── 或由壁纸引擎主动报告:
    WallpaperService.Engine.notifyColorsChanged()
    └── onComputeColors() → WallpaperColors
        └── 通过 IWallpaperConnection.onWallpaperColorsChanged()
            └── 同上流程

10.2 ThemeOverlayController 中的颜色处理

// packages/SystemUI/src/com/android/systemui/theme/
// ThemeOverlayController.java

@SysUISingleton
public class ThemeOverlayController extends CoreStartable {
    
    private final WallpaperManager mWallpaperManager;
    private WallpaperColors mCurrentColors;
    
    @Override
    public void start() {
        // 监听壁纸颜色变化
        mWallpaperManager.addOnColorsChangedListener(
            (colors, which) -> {
                if ((which & WallpaperManager.FLAG_SYSTEM) != 0) {
                    handleWallpaperColors(colors);
                }
            },
            null /* handler — 使用主线程 */
        );
        
        // 获取初始壁纸颜色
        WallpaperColors colors = mWallpaperManager.getWallpaperColors(
            WallpaperManager.FLAG_SYSTEM);
        if (colors != null) {
            handleWallpaperColors(colors);
        }
    }
    
    private void handleWallpaperColors(WallpaperColors colors) {
        if (colors == null) return;
        if (colors.equals(mCurrentColors)) return;
        
        mCurrentColors = colors;
        
        // ★ 重新评估系统主题
        reevaluateSystemTheme(false /* forceReload */);
    }
    
    /**
     * ★★★ 从壁纸颜色生成系统主题
     */
    void reevaluateSystemTheme(boolean forceReload) {
        WallpaperColors colors = mCurrentColors;
        if (colors == null) return;
        
        // 1. 获取种子色
        int seedColor = getSeedColor(colors);
        
        // 2. 检查用户是否手动选择了颜色
        String storedColors = Settings.Secure.getStringForUser(
            mContext.getContentResolver(),
            Settings.Secure.THEME_CUSTOMIZATION_OVERLAY_PACKAGES,
            mCurrentUser);
        
        // 3. 生成 ColorScheme
        ColorScheme scheme = new ColorScheme(seedColor, isDarkTheme());
        
        // 4. 构建 Overlay
        FabricatedOverlay overlay = createOverlay(scheme);
        
        // 5. 应用 Overlay
        mOverlayManager.commit(new OverlayManagerTransaction.Builder()
            .setEnabled(overlay, true, mCurrentUser)
            .build());
    }
    
    /**
     * 从壁纸颜色提取种子色
     */
    private int getSeedColor(WallpaperColors colors) {
        // 使用 Material Color Utilities 库
        // 从壁纸颜色中选择最适合的种子色
        
        Color primary = colors.getPrimaryColor();
        
        // 转换到 CAM16 色彩空间
        int argb = primary.toArgb();
        
        // 使用 Material You 的种子色算法
        // 考虑色相、饱和度、亮度
        return Score.score(
            ColorUtils.colorToCAM(argb)).get(0);
    }
    
    /**
     * 从 ColorScheme 创建 FabricatedOverlay
     */
    private FabricatedOverlay createOverlay(ColorScheme scheme) {
        FabricatedOverlay.Builder builder = new FabricatedOverlay.Builder(
            "com.android.systemui", "wallpaper_theme", "android");
        
        // accent1 (主强调色系列)
        builder.setResourceValue(
            "android:color/system_accent1_0", 
            TypedValue.TYPE_INT_COLOR_ARGB8,
            scheme.getAccent1().get(0));
        builder.setResourceValue(
            "android:color/system_accent1_10",
            TypedValue.TYPE_INT_COLOR_ARGB8,
            scheme.getAccent1().get(1));
        // ... 13 个色调级别
        
        // accent2, accent3, neutral1, neutral2 同理
        // 共 5 × 13 = 65 个颜色值
        
        return builder.build();
    }
}

十一、壁纸过渡动画

11.1 壁纸切换动画

// WallpaperController.java 中的动画处理

/**
 * 壁纸过渡动画场景:
 * 
 * 1. App → Launcher (返回桌面时壁纸出现)
 * 2. Launcher → App (离开桌面时壁纸消失)
 * 3. 锁屏解锁 (壁纸可能变化)
 * 4. 壁纸更换 (新壁纸替换旧壁纸)
 */

// 场景1: App 退出,壁纸出现
// WallpaperAnimationAdapter.java

class WallpaperAnimationAdapter {
    
    /**
     * 创建壁纸动画目标
     * 用于 App 过渡动画中控制壁纸 Surface
     */
    static RemoteAnimationTarget createWallpaperAnimationTarget(
            WallpaperWindowToken wallpaperToken) {
        
        WindowState wallpaperWin = wallpaperToken.getTopChild();
        if (wallpaperWin == null) return null;
        
        SurfaceControl animLeash = wallpaperWin.createAnimationLeash();
        
        return new RemoteAnimationTarget(
            wallpaperWin.mWallpaperToken.hashCode(),
            RemoteAnimationTarget.MODE_OPENING,  // 壁纸正在"打开"
            animLeash,
            false /* isTranslucent */,
            null /* clipRect */,
            null /* contentInsets */,
            wallpaperWin.getPrefixOrderIndex(),
            new Point(0, 0),
            wallpaperWin.getBounds(),
            wallpaperWin.getWindowConfiguration(),
            false /* isNotInRecents */
        );
    }
}

/**
 * Launcher 在 App 过渡动画中处理壁纸
 */
// Launcher3 QuickStep:
// 当从 App 返回 Home 时
private void animateWallpaperForAppClose(
        RemoteAnimationTarget[] wallpaperTargets) {
    
    for (RemoteAnimationTarget wallpaper : wallpaperTargets) {
        SurfaceControl surface = wallpaper.leash;
        
        SurfaceControl.Transaction t = new SurfaceControl.Transaction();
        
        // 壁纸从缩小/模糊 → 正常
        ValueAnimator animator = ValueAnimator.ofFloat(0f, 1f);
        animator.setDuration(350);
        animator.setInterpolator(DECELERATE);
        
        animator.addUpdateListener(a -> {
            float progress = (float) a.getAnimatedValue();
            
            SurfaceControl.Transaction frameT = 
                new SurfaceControl.Transaction();
            
            // 壁纸缩放: 0.95 → 1.0 (轻微放大效果)
            float scale = lerp(0.95f, 1.0f, progress);
            frameT.setScale(surface, scale, scale);
            
            // 壁纸透明度: 0.0 → 1.0 (渐显)
            frameT.setAlpha(surface, progress);
            
            frameT.apply();
        });
        
        animator.start();
    }
}

11.2 Android 14 壁纸揭示动画 (Reveal)

// packages/SystemUI/src/com/android/systemui/wallpapers/gl/
// ImageRevealWallpaperRenderer.java

/**
 * ★ Android 14 新增: 壁纸切换时的揭示动画
 * 
 * 效果: 新壁纸从中心或某个点向外扩展,
 *       像"揭开"一样替换旧壁纸
 * 
 * ┌──────────────────┐    ┌──────────────────┐
 * │   旧壁纸          │    │   旧壁  ┌──┐纸   │
 * │                  │    │       │新│      │
 * │                  │ →  │       │壁│      │
 * │                  │    │       │纸│      │
 * │                  │    │       └──┘      │
 * └──────────────────┘    └──────────────────┘
 *                                ↓
 *                         ┌──────────────────┐
 *                         │  ┌────────────┐  │
 *                         │  │            │  │
 *                         │  │   新壁纸    │  │
 *                         │  │            │  │
 *                         │  └────────────┘  │
 *                         └──────────────────┘
 *                                ↓
 *                         ┌──────────────────┐
 *                         │                  │
 *                         │     新壁纸        │
 *                         │     (全屏)        │
 *                         │                  │
 *                         └──────────────────┘
 */
public class ImageRevealWallpaperRenderer {
    
    // 动画参数
    private float mRevealProgress = 0f;     // 0=旧壁纸, 1=新壁纸
    private float mRevealCenterX;           // 揭示中心 X
    private float mRevealCenterY;           // 揭示中心 Y
    
    // 两张壁纸纹理
    private int mOldTextureId;              // 旧壁纸
    private int mNewTextureId;              // 新壁纸
    
    // ★ 揭示着色器 (使用圆形遮罩)
    private static final String REVEAL_FRAGMENT_SHADER =
        "precision mediump float;\n" +
        "varying vec2 vTexCoord;\n" +
        "uniform sampler2D uOldTexture;\n" +
        "uniform sampler2D uNewTexture;\n" +
        "uniform float uRevealProgress;\n" +
        "uniform vec2 uRevealCenter;\n" +
        "void main() {\n" +
        "    vec2 uv = vTexCoord;\n" +
        "    float dist = distance(uv, uRevealCenter);\n" +
        "    float maxDist = 1.5;\n" +   // 对角线距离
        "    float radius = uRevealProgress * maxDist;\n" +
        "    if (dist < radius) {\n" +
        "        gl_FragColor = texture2D(uNewTexture, uv);\n" +
        "    } else {\n" +
        "        gl_FragColor = texture2D(uOldTexture, uv);\n" +
        "    }\n" +
        "}\n";
    
    /**
     * 渲染带揭示效果的壁纸
     */
    public void draw(float progress) {
        mRevealProgress = progress;
        
        GLES20.glUseProgram(mRevealProgram);
        
        // 绑定两张纹理
        GLES20.glActiveTexture(GLES20.GL_TEXTURE0);
        GLES20.glBindTexture(GLES20.GL_TEXTURE_2D, mOldTextureId);
        GLES20.glUniform1i(mOldTextureHandle, 0);
        
        GLES20.glActiveTexture(GLES20.GL_TEXTURE1);
        GLES20.glBindTexture(GLES20.GL_TEXTURE_2D, mNewTextureId);
        GLES20.glUniform1i(mNewTextureHandle, 1);
        
        // 设置揭示参数
        GLES20.glUniform1f(mRevealProgressHandle, progress);
        GLES20.glUniform2f(mRevealCenterHandle, 
            mRevealCenterX, mRevealCenterY);
        
        // 绘制
        GLES20.glDrawArrays(GLES20.GL_TRIANGLE_STRIP, 0, 4);
    }
}

十二、壁纸与 SystemUI 交互

12.1 ScrimController — 壁纸上层遮罩

// packages/SystemUI/src/com/android/systemui/statusbar/phone/
// ScrimController.java

/**
 * 管理壁纸上方的遮罩层
 * 
 * 遮罩层级 (从底到顶):
 * 
 *  壁纸 Surface (WallpaperService 渲染)
 *     ↑
 *  ScrimBehind (后遮罩 — 壁纸暗化)
 *     ↑
 *  App 内容 / Launcher
 *     ↑
 *  ScrimInFront (前遮罩 — AOD 使用)
 *     ↑
 *  Notification Shade
 *     ↑
 *  Status Bar
 */

@SysUISingleton
public class ScrimController {
    
    // 遮罩视图
    private ScrimView mScrimBehind;     // 壁纸后面的遮罩
    private ScrimView mScrimInFront;    // 所有内容前面的遮罩
    private ScrimView mNotificationsScrim; // 通知背后的遮罩
    
    // 各场景的遮罩透明度
    
    /** 锁屏状态 */
    // ScrimBehind: 有一定暗度 (让时钟/通知更清晰)
    // 壁纸可见但被暗化
    
    /** 通知面板展开 */
    // ScrimBehind: 随展开程度加深
    // 壁纸逐渐被遮挡
    
    /** AOD (Always-on Display) */
    // ScrimBehind: 几乎全黑
    // ScrimInFront: 完全黑 (只露出AOD内容)
    
    /** 正常解锁状态 */
    // ScrimBehind: 透明
    // 壁纸完全可见 (通过 Launcher 的透明背景)
    
    /**
     * ★ 根据状态更新遮罩
     */
    private void applyState() {
        switch (mState) {
            case KEYGUARD:
                // 锁屏: 壁纸后方有暗化遮罩
                mScrimBehind.setAlpha(0.25f);  // 25% 暗化
                mNotificationsScrim.setAlpha(calculateNotifAlpha());
                mScrimInFront.setAlpha(0f);
                break;
                
            case SHADE_LOCKED:
                // 锁屏下拉: 壁纸暗化加深
                mScrimBehind.setAlpha(0.6f);   // 60% 暗化
                break;
                
            case BOUNCER:
                // 密码界面: 壁纸大幅暗化
                mScrimBehind.setAlpha(0.8f);
                break;
                
            case AOD:
                // Always-on: 几乎全黑
                mScrimBehind.setAlpha(1.0f);   // 完全遮挡壁纸
                mScrimInFront.setAlpha(
                    mDozeParameters.getAlwaysOnAlpha());
                break;
                
            case UNLOCKED:
                // 解锁: 壁纸完全可见
                mScrimBehind.setAlpha(0f);
                mScrimInFront.setAlpha(0f);
                break;
                
            case PULSING:
                // 脉冲通知: 轻微暗化
                mScrimBehind.setAlpha(0.5f);
                break;
        }
    }
    
    /**
     * 通知面板展开时壁纸暗化过渡
     */
    public void setNotificationPanelExpansion(float fraction) {
        // fraction: 0.0 = 收起, 1.0 = 完全展开
        
        if (mState == ScrimState.KEYGUARD) {
            // 从锁屏下拉通知面板
            float scrimAlpha = lerp(0.25f, 0.6f, fraction);
            mScrimBehind.setAlpha(scrimAlpha);
        }
    }
}

12.2 壁纸可见性与 SystemUI 联动

// SystemUI 中监听壁纸可见性

// WallpaperVisibilityListeners.java (WMS 内部)

class WallpaperVisibilityListeners {
    
    // 注册的监听器 (SystemUI 等)
    private final ArrayMap<IBinder, WallpaperVisibilityListener> 
        mListeners = new ArrayMap<>();
    
    void notifyWallpaperVisibilityChanged(
            DisplayContent displayContent, boolean visible) {
        for (WallpaperVisibilityListener listener : mListeners.values()) {
            listener.onWallpaperVisibilityChanged(visible, 
                displayContent.getDisplayId());
        }
    }
}

// SystemUI 中注册监听
// CentralSurfacesImpl.java (简化)

private void registerWallpaperVisibilityListener() {
    IWindowManager wms = WindowManagerGlobal.getWindowManagerService();
    
    wms.registerWallpaperVisibilityListener(
        new IWallpaperVisibilityListener.Stub() {
            @Override
            public void onWallpaperVisibilityChanged(
                    boolean visible, int displayId) {
                
                // 壁纸可见性变化
                // 用于决定状态栏/导航栏图标颜色
                mMainExecutor.execute(() -> {
                    mWallpaperVisible = visible;
                    updateScrimController();
                    updateDarkIconStatus();
                });
            }
        },
        DEFAULT_DISPLAY);
}

十三、动态壁纸 (Live Wallpaper)

13.1 动态壁纸声明

<!-- AndroidManifest.xml -->
<service
    android:name=".MyLiveWallpaperService"
    android:label="My Live Wallpaper"
    android:permission="android.permission.BIND_WALLPAPER">
    
    <intent-filter>
        <action android:name="android.service.wallpaper.WallpaperService" />
    </intent-filter>
    
    <meta-data
        android:name="android.service.wallpaper"
        android:resource="@xml/wallpaper" />
</service>
<!-- res/xml/wallpaper.xml -->
<wallpaper
    xmlns:android="http://schemas.android.com/apk/res/android"
    android:thumbnail="@drawable/preview"
    android:description="@string/description"
    android:settingsActivity=".WallpaperSettingsActivity"
    android:author="@string/author"
    android:contextUri="https://example.com"
    android:contextDescription="@string/context_desc"
    android:showMetadataInPreview="true"
    android:supportsMultipleDisplays="false" />

13.2 动态壁纸实现示例

// 动态壁纸服务示例

public class ParticleWallpaperService extends WallpaperService {
    
    @Override
    public Engine onCreateEngine() {
        return new ParticleEngine();
    }
    
    class ParticleEngine extends Engine {
        
        private final Handler mHandler = new Handler();
        private boolean mVisible;
        private float mXOffset = 0.5f;
        
        // 粒子系统
        private List<Particle> mParticles = new ArrayList<>();
        
        @Override
        public void onCreate(SurfaceHolder surfaceHolder) {
            super.onCreate(surfaceHolder);
            
            // 允许触摸事件
            setTouchEventsEnabled(true);
            
            // 初始化粒子
            initParticles();
        }
        
        @Override
        public void onVisibilityChanged(boolean visible) {
            mVisible = visible;
            if (visible) {
                // 开始渲染循环
                mHandler.post(mDrawRunner);
            } else {
                // 停止渲染(节省电量)
                mHandler.removeCallbacks(mDrawRunner);
            }
        }
        
        @Override
        public void onSurfaceChanged(SurfaceHolder holder, 
                int format, int width, int height) {
            super.onSurfaceChanged(holder, format, width, height);
            // 更新粒子系统尺寸
            updateParticleBounds(width, height);
        }
        
        @Override
        public void onOffsetsChanged(float xOffset, float yOffset,
                float xOffsetStep, float yOffsetStep,
                int xPixelOffset, int yPixelOffset) {
            
            mXOffset = xOffset;
            // 粒子跟随偏移移动
            drawFrame();
        }
        
        @Override
        public void onTouchEvent(MotionEvent event) {
            if (event.getAction() == MotionEvent.ACTION_DOWN) {
                // 触摸时产生粒子爆炸效果
                spawnParticleBurst(event.getX(), event.getY());
            }
        }
        
        /**
         * 渲染循环
         */
        private final Runnable mDrawRunner = new Runnable() {
            @Override
            public void run() {
                drawFrame();
                if (mVisible) {
                    mHandler.postDelayed(this, 16); // ~60fps
                }
            }
        };
        
        /**
         * ★ 渲染一帧
         */
        private void drawFrame() {
            SurfaceHolder holder = getSurfaceHolder();
            Canvas canvas = null;
            
            try {
                canvas = holder.lockCanvas();
                if (canvas != null) {
                    // 清除背景
                    canvas.drawColor(Color.BLACK);
                    
                    // 更新和绘制粒子
                    for (Particle p : mParticles) {
                        p.update(mXOffset);
                        p.draw(canvas);
                    }
                }
            } finally {
                if (canvas != null) {
                    holder.unlockCanvasAndPost(canvas);
                }
            }
        }
        
        @Override
        public WallpaperColors onComputeColors() {
            // ★ 报告壁纸颜色给系统 (Material You)
            return new WallpaperColors(
                Color.valueOf(Color.BLUE),      // 主色
                Color.valueOf(Color.CYAN),       // 副色
                Color.valueOf(Color.WHITE)       // 第三色
            );
        }
        
        @Override
        public void onDestroy() {
            super.onDestroy();
            mHandler.removeCallbacks(mDrawRunner);
        }
    }
}

十四、多用户/多显示器支持

// WallpaperManagerService.java

/**
 * 多用户壁纸管理
 */

// 每个用户有独立的壁纸数据
private final SparseArray<WallpaperData> mWallpaperMap;    // userId → data
private final SparseArray<WallpaperData> mLockWallpaperMap; // userId → data

/**
 * 用户切换时的壁纸处理
 */
void switchUser(int userId) {
    synchronized (mLock) {
        // 1. 断开旧用户的壁纸连接
        WallpaperData oldWallpaper = mWallpaperMap.get(mCurrentUserId);
        if (oldWallpaper != null && oldWallpaper.connection != null) {
            detachWallpaperLocked(oldWallpaper.connection);
        }
        
        mCurrentUserId = userId;
        
        // 2. 加载新用户的壁纸设置
        if (!mWallpaperMap.contains(userId)) {
            loadSettingsLocked(userId, false);
        }
        
        // 3. 绑定新用户的壁纸
        WallpaperData newWallpaper = mWallpaperMap.get(userId);
        switchWallpaper(newWallpaper, null);
        
        // 4. 通知壁纸颜色变化 (Material You 需要刷新)
        notifyWallpaperColorsChanged(newWallpaper, FLAG_SYSTEM);
        
        WallpaperData lockWallpaper = mLockWallpaperMap.get(userId);
        if (lockWallpaper != null) {
            notifyWallpaperColorsChanged(lockWallpaper, FLAG_LOCK);
        }
    }
}

/**
 * 多显示器壁纸管理 (Android 14)
 */
// WallpaperDisplayHelper.java

class WallpaperDisplayHelper {
    
    /**
     * 获取指定显示器的壁纸尺寸
     */
    Point getDesiredWallpaperSize(int displayId) {
        DisplayInfo di = getDisplayInfo(displayId);
        
        // 壁纸宽度 = 屏幕宽度 × 视差比率
        int width = (int) (Math.max(di.logicalWidth, di.logicalHeight) 
            * getParallaxRatio());
        int height = Math.max(di.logicalWidth, di.logicalHeight);
        
        return new Point(width, height);
    }
    
    /**
     * 获取视差比率
     * 1.0 = 壁纸与屏幕等宽 (无视差)
     * 1.3 = 壁纸比屏幕宽 30% (有视差)
     */
    float getParallaxRatio() {
        // 可由配置或 OEM 定制
        return mContext.getResources().getFloat(
            R.dimen.config_wallpaperParallaxRatio);
    }
}

十五、壁纸设置持久化

15.1 wallpaper_info.xml

<!-- /data/system/users/{userId}/wallpaper_info.xml -->

<?xml version='1.0' encoding='utf-8' standalone='yes' ?>
<wp xmlns:wp="http://schemas.android.com/apk/res/android"
    wp:width="2160"
    wp:height="2400"
    wp:cropLeft="0"
    wp:cropTop="0"
    wp:cropRight="2160"
    wp:cropBottom="2400"
    wp:name="wallpaper_orig"
    wp:id="42"
    wp:allowBackup="true"
    wp:wallpaperComponent="com.android.systemui/.wallpapers.ImageWallpaper">
    
    <!-- 壁纸颜色 -->
    <colors
        wp:colorValue="-14374589"
        wp:colorValue2="-12236860"
        wp:colorValue3="-6243049"
        wp:colorHints="4" />
    
    <!-- Android 14: 暗化参数 -->
    <dimAmount wp:value="0.0" />
    
</wp>

15.2 加载和保存

// WallpaperManagerService.java

/**
 * 加载壁纸设置
 */
private void loadSettingsLocked(int userId, boolean keepDimensionHints) {
    File wallpaperDir = getWallpaperDir(userId);
    File infoFile = new File(wallpaperDir, WALLPAPER_INFO);
    
    if (!infoFile.exists()) {
        // 没有设置过壁纸,使用默认
        migrateFromOld(userId);
        return;
    }
    
    // 解析 XML
    FileInputStream fis = new FileInputStream(infoFile);
    XmlPullParser parser = Xml.newPullParser();
    parser.setInput(fis, StandardCharsets.UTF_8.name());
    
    // 读取壁纸参数
    WallpaperData wallpaper = new WallpaperData(userId);
    
    while (parser.next() != XmlPullParser.END_DOCUMENT) {
        if (parser.getName().equals("wp")) {
            wallpaper.width = getAttributeInt(parser, "width", 0);
            wallpaper.height = getAttributeInt(parser, "height", 0);
            wallpaper.cropHint.left = getAttributeInt(parser, "cropLeft", 0);
            wallpaper.cropHint.top = getAttributeInt(parser, "cropTop", 0);
            wallpaper.cropHint.right = getAttributeInt(parser, "cropRight", 0);
            wallpaper.cropHint.bottom = getAttributeInt(parser, "cropBottom", 0);
            wallpaper.wallpaperId = getAttributeInt(parser, "id", -1);
            
            String component = parser.getAttributeValue(null, 
                "wallpaperComponent");
            wallpaper.wallpaperComponent = 
                ComponentName.unflattenFromString(component);
            
            wallpaper.mWallpaperDimAmount = getAttributeFloat(
                parser, "dimAmount", 0f);
        }
        
        if (parser.getName().equals("colors")) {
            // 读取壁纸颜色
            int primary = getAttributeInt(parser, "colorValue", 0);
            int secondary = getAttributeInt(parser, "colorValue2", 0);
            int tertiary = getAttributeInt(parser, "colorValue3", 0);
            int hints = getAttributeInt(parser, "colorHints", 0);
            
            wallpaper.mWallpaperColors = new WallpaperColors(
                Color.valueOf(primary),
                secondary != 0 ? Color.valueOf(secondary) : null,
                tertiary != 0 ? Color.valueOf(tertiary) : null,
                hints);
        }
    }
    
    mWallpaperMap.put(userId, wallpaper);
}

/**
 * 保存壁纸设置
 */
private void saveSettingsLocked(int userId) {
    WallpaperData wallpaper = mWallpaperMap.get(userId);
    WallpaperData lockWallpaper = mLockWallpaperMap.get(userId);
    
    File infoFile = new File(getWallpaperDir(userId), WALLPAPER_INFO);
    
    FileOutputStream fos = new FileOutputStream(infoFile);
    XmlSerializer out = new FastXmlSerializer();
    out.setOutput(fos, StandardCharsets.UTF_8.name());
    
    out.startDocument(null, true);
    out.startTag(null, "wp");
    
    // 写入壁纸参数
    out.attribute(null, "width", String.valueOf(wallpaper.width));
    out.attribute(null, "height", String.valueOf(wallpaper.height));
    out.attribute(null, "cropLeft", String.valueOf(wallpaper.cropHint.left));
    // ... 其他属性
    out.attribute(null, "wallpaperComponent", 
        wallpaper.wallpaperComponent.flattenToString());
    out.attribute(null, "id", String.valueOf(wallpaper.wallpaperId));
    out.attribute(null, "dimAmount", 
        String.valueOf(wallpaper.mWallpaperDimAmount));
    
    // 写入颜色
    if (wallpaper.mWallpaperColors != null) {
        out.startTag(null, "colors");
        out.attribute(null, "colorValue", 
            String.valueOf(wallpaper.mWallpaperColors
                .getPrimaryColor().toArgb()));
        // ... secondary, tertiary, hints
        out.endTag(null, "colors");
    }
    
    out.endTag(null, "wp");
    
    // 锁屏壁纸
    if (lockWallpaper != null) {
        out.startTag(null, "lockWp");
        // ... 类似参数
        out.endTag(null, "lockWp");
    }
    
    out.endDocument();
    fos.close();
}

十六、完整数据流总结

16.1 设置壁纸的完整路径

用户在壁纸选择器中选择一张图片
│
▼
WallpaperPicker2
├── 显示预览
├── 用户确认
└── 调用 WallpaperManager.setBitmap(bitmap, cropHint, true, FLAG_SYSTEM)
    │
    ▼
WallpaperManager (客户端)
├── 调用 IWallpaperManager.setWallpaper(...)
│   │
│   └── Binder IPC
│       │
│       ▼
WallpaperManagerService (系统服务)
├── 权限检查
├── 生成壁纸 ID
├── 创建 ParcelFileDescriptor
│   └── 指向 /data/system/users/{userId}/wallpaper_orig
│
│   ← 返回 pfd 给客户端
│   │
│   ▼
WallpaperManager (客户端)
├── 通过 pfd 写入 Bitmap 数据
│   └── bitmap.compress(PNG, 100, pfd.getOutputStream())
├── 关闭 pfd
│   │
│   └── → 触发 WallpaperManagerService.onWallpaperWriteComplete()
│       │
│       ▼
WallpaperManagerService
├── 1. generateCrop(wallpaper)
│   └── 裁剪壁纸 → 保存到 wallpaper (cropFile)
│
├── 2. extractColors(wallpaper)
│   └── WallpaperColors.fromBitmap() → 提取主色/副色/第三色
│
├── 3. saveSettingsLocked()
│   └── 保存到 wallpaper_info.xml
│
├── 4. bindWallpaperComponentLocked(ImageWallpaper, ...)
│   │   └── 绑定壁纸服务
│   │       │
│   │       ▼
│   │   ImageWallpaper (SystemUI)
│   │   ├── onCreateEngine() → GLEngine
│   │   ├── Engine.attach() → 创建 TYPE_WALLPAPER 窗口
│   │   ├── Engine.onSurfaceCreated() → 初始化 EGL
│   │   ├── loadWallpaperBitmap() → 解码裁剪后壁纸
│   │   ├── uploadBitmapTexture() → 上传 GPU 纹理
│   │   ├── drawFrame() → OpenGL 渲染壁纸
│   │   └── reportEngineShown() → 通知首帧已显示
│   │
│   └── WMS:
│       └── WallpaperController
│           ├── 注册壁纸窗口
│           ├── findWallpaperTarget()
│           ├── updateWallpaperWindowsTarget()
│           └── updateWallpaperVisibility()
│
├── 5. notifyWallpaperChanged()
│   └── 通知所有注册的回调
│
└── 6. notifyWallpaperColorsChanged()
    └── 通知颜色变化
        │
        ├── SystemUI: ThemeOverlayController
        │   └── reevaluateSystemTheme()
        │       └── Material You 颜色更新
        │           └── 全系统主题颜色变化
        │
        ├── Launcher: 图标/Widget 颜色更新
        │
        └── Settings: 主题预览更新

16.2 壁纸显示/渲染路径

┌─────────────────────────────────────────────────────────┐
│                    渲染管线                                │
│                                                         │
│  WallpaperService.Engine                                 │
│  (ImageWallpaper.GLEngine)                               │
│       │                                                  │
│       ├── lockCanvas() 或 EGL makeCurrent()              │
│       ├── 渲染壁纸内容 (Canvas 或 OpenGL)                  │
│       └── unlockCanvasAndPost() 或 eglSwapBuffers()      │
│                    │                                     │
│                    ▼                                     │
│  SurfaceFlinger                                          │
│       │                                                  │
│       ├── 壁纸 Layer (TYPE_WALLPAPER)                    │
│       │   └── z-order: 在壁纸目标窗口后面                  │
│       │                                                  │
│       ├── 合成:                                          │
│       │   ┌──────────────────────────────┐              │
│       │   │  NavigationBar Layer (顶层)   │              │
│       │   ├──────────────────────────────┤              │
│       │   │  StatusBar Layer             │              │
│       │   ├──────────────────────────────┤              │
│       │   │  App / Launcher Layer         │              │
│       │   │  (可能半透明/透明,露出壁纸)    │              │
│       │   ├──────────────────────────────┤              │
│       │   │  ★ Wallpaper Layer           │ ← 壁纸在这里 │
│       │   ├──────────────────────────────┤              │
│       │   │  底层                         │              │
│       │   └──────────────────────────────┘              │
│       │                                                  │
│       └── → Display (HDMI/DSI/...)                       │
│                                                         │
└─────────────────────────────────────────────────────────┘

十七、关键类关系图

┌───────────────────────────────────────────────────────────────────┐
│                                                                   │
│  ┌──────────────┐        Binder         ┌──────────────────────┐ │
│  │WallpaperManager│ ←──────────────────→ │WallpaperManager     │ │
│  │(App 客户端 API)│                      │Service (系统服务)    │ │
│  └──────┬───────┘                       └─────────┬────────────┘ │
│         │                                         │              │
│         │ setWallpaper()                          │              │
│         │ getWallpaperColors()                    │              │
│         │ setWallpaperOffsets()                   │              │
│         │ addOnColorsChangedListener()            │              │
│         │                                         │              │
│         │                    ┌─────────────────────┤              │
│         │                    │                     │              │
│         │           ┌────────▼────────┐   ┌────────▼────────┐   │
│         │           │  WallpaperData  │   │  WallpaperData  │   │
│         │           │  (FLAG_SYSTEM)  │   │  (FLAG_LOCK)    │   │
│         │           │  ├─wallpaperFile│   │  ├─wallpaperFile│   │
│         │           │  ├─cropFile     │   │  ├─cropFile     │   │
│         │           │  ├─colors       │   │  ├─colors       │   │
│         │           │  └─connection───│───│──└─connection    │   │
│         │           └────────┬────────┘   └─────────────────┘   │
│         │                    │                                   │
│         │          ┌─────────▼──────────┐                       │
│         │          │WallpaperConnection │                       │
│         │          │(ServiceConnection) │                       │
│         │          └────────┬───────────┘                       │
│         │                   │ bindService()                      │
│         │                   ▼                                    │
│         │          ┌──────────────────┐                          │
│         │          │ WallpaperService │ (抽象基类)               │
│         │          │  ├─onCreateEngine()                        │
│         │          │  └─Engine        │                          │
│         │          └────────┬─────────┘                          │
│         │                   │                                    │
│         │       ┌───────────┼───────────────┐                   │
│         │       ▼                           ▼                   │
│         │  ┌──────────────┐         ┌──────────────────┐        │
│         │  │ImageWallpaper│         │LiveWallpaperXxx  │        │
│         │  │(SystemUI)    │         │(第三方)           │        │
│         │  │ └─GLEngine   │         │ └─CustomEngine   │        │
│         │  │   ├─EGL      │         │   ├─Canvas/GL    │        │
│         │  │   ├─Texture  │         │   └─自定义渲染    │        │
│         │  │   └─drawFrame│         │                  │        │
│         │  └──────────────┘         └──────────────────┘        │
│         │                                                       │
│         │  ┌──────────────────────────────────────────────┐     │
│         │  │              WMS (WindowManagerService)       │     │
│         │  │                                              │     │
│         │  │  ┌───────────────────────────────────────┐   │     │
│         │  │  │ WallpaperController                    │   │     │
│         │  │  │ ├─findWallpaperTarget()               │   │     │
│         │  │  │ │  └─遍历窗口找 FLAG_SHOW_WALLPAPER   │   │     │
│         │  │  │ ├─updateWallpaperOffset()             │   │     │
│         │  │  │ │  └─SurfaceControl.setPosition()     │   │     │
│         │  │  │ ├─updateWallpaperVisibility()          │   │     │
│         │  │  │ └─handleWallpaperTargetChange()       │   │     │
│         │  │  └───────────────────────────────────────┘   │     │
│         │  │                                              │     │
│         │  │  ┌───────────────────────────────────────┐   │     │
│         │  │  │ WallpaperWindowToken                   │   │     │
│         │  │  │ └─TYPE_WALLPAPER 窗口令牌              │   │     │
│         │  │  └───────────────────────────────────────┘   │     │
│         │  └──────────────────────────────────────────────┘     │
│         │                                                       │
│  ┌──────▼──────────────────────────────────────────────────┐    │
│  │                    SystemUI                              │    │
│  │                                                          │    │
│  │  ┌────────────────────┐  ┌───────────────────────────┐  │    │
│  │  │ThemeOverlayController│ │ScrimController            │  │    │
│  │  │                    │  │├─ScrimBehind (壁纸暗化)    │  │    │
│  │  │ onColorsChanged()  │  │├─ScrimInFront (AOD遮罩)   │  │    │
│  │  │ → ColorScheme      │  │└─NotificationsScrim       │  │    │
│  │  │ → FabricatedOverlay│  │                           │  │    │
│  │  │ → 全系统主题更新    │  └───────────────────────────┘  │    │
│  │  └────────────────────┘                                  │    │
│  └──────────────────────────────────────────────────────────┘    │
│                                                                   │
│  ┌────────────────────────────────────────────────────────────┐  │
│  │                    SurfaceFlinger                           │  │
│  │  壁纸 Surface Layer → 合成 → Display                       │  │
│  └────────────────────────────────────────────────────────────┘  │
│                                                                   │
└───────────────────────────────────────────────────────────────────┘

十八、总结

组件职责关键类
WallpaperManager客户端 APIWallpaperManager.java
WallpaperManagerService壁纸管理核心服务WallpaperManagerService.java
WallpaperData壁纸数据模型WallpaperData.java
WallpaperCropper壁纸裁剪 (Android 14)WallpaperCropper.java
WallpaperService.Engine壁纸渲染引擎基类WallpaperService.java
ImageWallpaper默认静态壁纸实现ImageWallpaper.java (SystemUI)
WallpaperControllerWMS壁纸窗口管理WallpaperController.java
WallpaperColors壁纸颜色描述WallpaperColors.java
ThemeOverlayControllerMaterial You颜色ThemeOverlayController.java (SystemUI)
ScrimController壁纸遮罩管理ScrimController.java (SystemUI)
WallpaperPicker2壁纸选择器 AppWallpaperPickerActivity.java