AOSP Android 14 壁纸架构深度分析

AOSP Android 14 壁纸架构深度分析


一、整体架构概览

scss 复制代码
┌─────────────────────────────────────────────────────────────────┐
│                    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 最终合成到显示器                       │          │
│  └────────────────────────────────────────────────────┘          │
└─────────────────────────────────────────────────────────────────┘

二、源码目录结构

ruby 复制代码
═══════ 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

java 复制代码
// 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 --- 壁纸颜色描述

java 复制代码
// 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 服务注册和初始化

java 复制代码
// 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 --- 壁纸数据模型

java 复制代码
// 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 设置壁纸完整流程

java 复制代码
// 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 新增)

java 复制代码
// 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 壁纸服务绑定

java 复制代码
// 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 --- 与壁纸服务的连接

java 复制代码
// 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 核心架构

java 复制代码
// 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 整体结构

java 复制代码
// 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 渲染

java 复制代码
// 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 核心职责

java 复制代码
// 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 壁纸窗口层级关系

ini 复制代码
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 完整调用链

scss 复制代码
用户在 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 偏移计算细节

java 复制代码
// 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 双壁纸架构

scss 复制代码
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 锁屏壁纸处理

java 复制代码
// 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 颜色提取流程

scss 复制代码
壁纸设置/变化
│
▼
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 中的颜色处理

java 复制代码
// 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 壁纸切换动画

java 复制代码
// 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)

java 复制代码
// 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 --- 壁纸上层遮罩

java 复制代码
// 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 联动

java 复制代码
// 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 动态壁纸声明

xml 复制代码
<!-- 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>
xml 复制代码
<!-- 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 动态壁纸实现示例

java 复制代码
// 动态壁纸服务示例

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);
        }
    }
}

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

java 复制代码
// 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

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 加载和保存

java 复制代码
// 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 设置壁纸的完整路径

scss 复制代码
用户在壁纸选择器中选择一张图片
│
▼
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 壁纸显示/渲染路径

scss 复制代码
┌─────────────────────────────────────────────────────────┐
│                    渲染管线                                │
│                                                         │
│  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/...)                       │
│                                                         │
└─────────────────────────────────────────────────────────┘

十七、关键类关系图

scss 复制代码
┌───────────────────────────────────────────────────────────────────┐
│                                                                   │
│  ┌──────────────┐        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 客户端 API WallpaperManager.java
WallpaperManagerService 壁纸管理核心服务 WallpaperManagerService.java
WallpaperData 壁纸数据模型 WallpaperData.java
WallpaperCropper 壁纸裁剪 (Android 14) WallpaperCropper.java
WallpaperService.Engine 壁纸渲染引擎基类 WallpaperService.java
ImageWallpaper 默认静态壁纸实现 ImageWallpaper.java (SystemUI)
WallpaperController WMS壁纸窗口管理 WallpaperController.java
WallpaperColors 壁纸颜色描述 WallpaperColors.java
ThemeOverlayController Material You颜色 ThemeOverlayController.java (SystemUI)
ScrimController 壁纸遮罩管理 ScrimController.java (SystemUI)
WallpaperPicker2 壁纸选择器 App WallpaperPickerActivity.java
相关推荐
木子予彤4 小时前
直破 Android 17 大屏困局:Navigation 3 架构深度解析
android·android jetpack
用户41659673693554 小时前
记一次 Compose 文本排版填坑:为什么阿拉伯文案明明空间足够却强行换行?
android
九天轩辕4 小时前
Android CI/CD 编译 AIDL 报错分析与解决
android·java·ci/cd
人民的石头4 小时前
android AI 规则匹配引擎接入
android
小手智联老徐4 小时前
Windows 下 ADB 无线调试与系统级操作指南
android·windows·adb
叶羽西5 小时前
Android15 Media框架JNI Interface调试
android
spencer_tseng5 小时前
anti-screenshot (Android + iOS)
android·ios
程序员Android5 小时前
Android 相机MFNR 拍照trace 分析
android·数码相机
2501_915918415 小时前
基于Mach-O文件的动态库与静态库归属方案及API扫描实践
android·ios·小程序·https·uni-app·iphone·webview