Android 10.0 锁屏壁纸 LockscreenWallpaper

前言

一、设置壁纸

通过系统设置进行锁屏壁纸和桌面壁纸的设置。
Setting 部分的代码:
packages/apps/WallpaperPicker2/src/com/android/wallpaper/module/DefaultWallpaperPersister.java

    private int setStreamToWallpaperManagerCompat(InputStream inputStream, boolean allowBackup,
            int whichWallpaper) {
   
        try {
   
            // whichWallpaper  // 壁纸类型
            return mWallpaperManagerCompat.setStream(inputStream, null, allowBackup,
                    whichWallpaper);
        } catch (IOException e) {
   
            return 0;
        }
    }
  ...
//    int whichWallpaper;    // 壁纸类型
//    if (mDestination == DEST_HOME_SCREEN) {    // 桌面壁纸
//        whichWallpaper = WallpaperManagerCompat.FLAG_SYSTEM;
//    } else if (mDestination == DEST_LOCK_SCREEN) {  // 锁屏壁纸
//        whichWallpaper = WallpaperManagerCompat.FLAG_LOCK;
//    } else { // DEST_BOTH    // 桌面壁纸 和 锁屏壁纸
 //       whichWallpaper = WallpaperManagerCompat.FLAG_SYSTEM
//                | WallpaperManagerCompat.FLAG_LOCK;
//    }
  ...

mWallpaperManagerCompat 其实就是 WallpaperManagerCompatV16 的对象。
packages/apps/WallpaperPicker2/src/com/android/wallpaper/compat/WallpaperManagerCompatV16.java

    @Override
    public int setStream(InputStream data, Rect visibleCropHint, boolean allowBackup,
                         int whichWallpaper) throws IOException {
   
        mWallpaperManager.setStream(data);
        // Return a value greater than zero to indicate success.
        return 1;
    }

由此可知,壁纸的设置是通过 WallpaperManager 类来进行的。

二、锁屏壁纸的显示

锁屏壁纸显示流程图:

上面应用程序设置完成了,下面就该进行壁纸显示了。
WallpaperManager#setStream()
frameworks/base/core/java/android/app/WallpaperManager.java

    public int setStream(InputStream bitmapData, Rect visibleCropHint,
            boolean allowBackup, @SetWallpaperFlags int which)
                    throws IOException {
   
        // 省略部分代码......
        try {
   
            //sGlobals.mService即 WallpaperManagerService。
            // WallpaperManager 在 SystemServiceRegistry 实例化,
            // 过程中传入 WallpaperManagerService 的 binder 对象。   
            ParcelFileDescriptor fd = sGlobals.mService.setWallpaper(null,
                    mContext.getOpPackageName(), visibleCropHint, allowBackup,
                    result, which, completion, mContext.getUserId());
            if (fd != null) {
   
                FileOutputStream fos = null;
                try {
   
                    // 将壁纸copy一份并存储到对应目录,
                    // 默认是/data/system/users/0/wallpaper(或wallpaper_lock),
                    // 其中0是主用户的userId,支持多用户
                    fos = new ParcelFileDescriptor.AutoCloseOutputStream(fd);
                    copyStreamToWallpaperFile(bitmapData, fos);
                    fos.close();
                    completion.waitForCompletion();
                } finally {
   
                    IoUtils.closeQuietly(fos);
                }
            }
        } catch (RemoteException e) {
   
            throw e.rethrowFromSystemServer();
        }
        return result.getInt(EXTRA_NEW_WALLPAPER_ID, 0);
    }

这里注意两个方法:sGlobals.mService.setWallpaper()和fos.close()。

先看第一个 WallpaperManagerService#setWallpaper() 方法:
frameworks/base/services/core/java/com/android/server/wallpaper/WallpaperManagerService.java

    public ParcelFileDescriptor setWallpaper(String name, String callingPackage,
            Rect cropHint, boolean allowBackup, Bundle extras, int which,
            IWallpaperManagerCallback completion, int userId) {
   
        userId = ActivityManager.handleIncomingUser(getCallingPid(), getCallingUid(), userId,
                false /* all */, true /* full */, "changing wallpaper", null /* pkg */);
        // 检查有没有设置壁纸的权限
       checkPermission(android.Manifest.permission.SET_WALLPAPER);
        
         //调用setStream方法的时候参数which必须是正确的
        if ((which & (FLAG_LOCK|FLAG_SYSTEM)) == 0) {
   
            final String msg = "Must specify a valid wallpaper category to set";
            Slog.e(TAG, msg);
            throw new IllegalArgumentException(msg);
        }
        // 省略部分代码......
        synchronized (mLock) {
   
            if (DEBUG) Slog.v(TAG, "setWallpaper which=0x" + Integer.toHexString(which));
            WallpaperData wallpaper;
  
            //如果当前没有锁屏壁纸的话,并且是设置桌面壁纸即which == FLAG_SYSTEM,那么同时设置为锁屏壁纸
            if (which == FLAG_SYSTEM && mLockWallpaperMap.get(userId) == null) {
   
                migrateSystemToLockWallpaperLocked(userId);
            }
            wallpaper = getWallpaperSafeLocked(userId, which);
            final long ident = Binder.clearCallingIdentity();
            try {
   
                // updateWallpaperBitmapLocked() 将创建一个文件描述符
                ParcelFileDescriptor pfd = updateWallpaperBitmapLocked(name, wallpaper, extras);
                if (pfd != null) {
   
                    wallpaper.imageWallpaperPending = true;
                    wallpaper.whichPending = which;
                    wallpaper.setComplete = completion;
                    wallpaper.cropHint.set(cropHint);
                    wallpaper.allowBackup = allowBackup;
                }
                return pfd;
            } finally {
   
                Binder.restoreCallingIdentity(ident);
            }
        }
    }

这里再跟进一步,看下 updateWallpaperBitmapLocked() 方法:
frameworks/base/services/core/java/com/android/server/wallpaper/WallpaperManagerService.java

    ParcelFileDescriptor updateWallpaperBitmapLocked(String name, WallpaperData wallpaper,
            Bundle extras) {
   
        if (name == null) name = "";
        try {
   
            // 通过getWallpaperDir() 获取文件路径;这个方法值得注意:后面会讲到。
            File dir = getWallpaperDir(wallpaper.userId);
            if (!dir.exists()) {
   
                dir.mkdir();
                FileUtils.setPermissions(
                        dir.getPath(),
                        FileUtils.S_IRWXU|FileUtils.S_IRWXG|FileUtils.S_IXOTH,
                        -1, -1);
            }
            // 创建一个文件描述符,并返回。
            ParcelFileDescriptor fd = ParcelFileDescriptor.open(wallpaper.wallpaperFile,
                    MODE_CREATE|MODE_READ_WRITE|MODE_TRUNCATE);
            // 省略部分代码......
            return fd;
        } catch (FileNotFoundException e) {
   
            Slog.w(TAG, "Error setting wallpaper", e);
        }
        return null;
    }

这里再看 fos.close() ,这个方法本身没什么可以看的,就是 FileOutputStream 文件字节输出流结束。但是这里涉及到了 WallpaperManagerService 的一个内部类 WallpaperObserver,通过名字我们就能知道它是一个观察者。
WallpaperObserver 初始化:在 WallpaperManagerService 初始化时,会调用 systemReady() 通过getWallpaperSafeLocked()方法初始化 WallpaperData,而这个 WallpaperData 中有个变量 wallpaperObserver ,也在开机时服务初始化, systemReady() 中调用 switchUser() 执行了 wallpaperObserver.startWatching()。
WallpaperObserver 这个内部类的作用:观察壁纸的变化并通知所有 IWallpaperServiceCallbacks 壁纸已经改变。 CREATE 在没有设置壁纸时触发,并且是第一次创建。每次更改壁纸时都会触发 CLOSE_WRITE,这也是关注 fos.close() 的原因。
所以文件的变化触发 WallpaperObserver 的 onEvent() :
frameworks/base/services/core/java/com/android/server/wallpaper/WallpaperManagerService.java

        @Override
        public void onEvent(int event, String path) {
   
            if (path == null) {
   
                return;
            }
            final boolean moved = (event == MOVED_TO);
            final boolean written = (event == CLOSE_WRITE || moved);
            // 获取发生了 CLOSE_WRITE 事件的文件路径
            final File changedFile = new File(mWallpaperDir, path);
            // System and system+lock changes happen on the system wallpaper input file;
            // lock-only changes happen on the dedicated lock wallpaper input file
            // 用于判断事件是不是这个事件发生的。
            final boolean sysWallpaperChanged = (mWallpaperFile.equals(changedFile));
            final boolean lockWallpaperChanged = (mWallpaperLockFile.equals(changedFile));
            int notifyColorsWhich = 0;
            WallpaperData wallpaper = dataForEvent(sysWallpaperChanged, lockWallpaperChanged);
            // 如果是锁屏壁纸更新
            if (moved && lockWallpaperChanged) {
   
                SELinux.restorecon(changedFile);
                notifyLockWallpaperChanged();
                notifyWallpaperColorsChanged(wallpaper, FLAG_LOCK);
                return;
            }
            synchronized (mLock) {
   
                if (sysWallpaperChanged || lockWallpaperChanged) {
   
                    notifyCallbacksLocked(wallpaper);
                    if (wallpaper.wallpaperComponent == null
                            || event != CLOSE_WRITE // includes the MOVED_TO case
                            || wallpaper.imageWallpaperPending) {
   
                        if (written) {
   
                  
                            SELinux.restorecon(changedFile);
                            if (moved) {
   
                                loadSettingsLocked(wallpaper.userId, true);
                            }
                            generateCrop(wallpaper);
                          
                            wallpaper.imageWallpaperPending = false;
                            if (sysWallpaperChanged) {
   
                                // 桌面壁纸变化,那么bind ImageWallpaper,ImageWallpaper是负责显示静态桌面壁纸的
                               bindWallpaperComponentLocked(mImageWallpaper, true,
                                        false, wallpaper, null);
                                notifyColorsWhich |= FLAG_SYSTEM;
                            }
                            if (lockWallpaperChanged
                                    || (wallpaper.whichPending & FLAG_LOCK) != 0) {
   
                                if (DEBUG) {
   
                                    Slog.i(TAG, "Lock-relevant wallpaper changed");
                                }
                            
                                if (!lockWallpaperChanged) {
   
                                    //如果参数which是system+lock,也就是同时设置锁屏和桌面壁纸,那么remove锁屏壁纸,因为已经是同一张壁纸了
                                    mLockWallpaperMap.remove(wallpaper.userId);
                                }
                                // and in any case, tell keyguard about it
                                notifyLockWallpaperChanged();
                                notifyColorsWhich |= FLAG_LOCK;
                            }
                            saveSettingsLocked(wallpaper.userId);
                            // Publish completion *after* we've persisted the changes
                            if (wallpaper.setComplete != null) {
   
                                try {
   
                                    wallpaper.setComplete.onWallpaperChanged();
                                } catch (RemoteException e) {
   
                                    // if this fails we don't really care; the setting app may just
                                    // have crashed and that sort of thing is a fact of life.
                                }
                            }
                        }
                    }
                }
            }
            // Outside of the lock since it will synchronize itself
            if (notifyColorsWhich != 0) {
   
                notifyWallpaperColorsChanged(wallpaper, notifyColorsWhich);
            }
        }

先看锁屏壁纸更新这一部分 notifyLockWallpaperChanged()
frameworks/base/services/core/java/com/android/server/wallpaper/WallpaperManagerService.java

    private void notifyLockWallpaperChanged() {
   
        final IWallpaperManagerCallback cb = mKeyguardListener;
        if (cb != null) {
   
            try {
   
                cb.onWallpaperChanged();
            } catch (RemoteException e) {
   
                // Oh well it went away; no big deal
            }
        }
    }
    @Override
    public boolean setLockWallpaperCallback(IWallpaperManagerCallback cb) {
   
        checkPermission(android.Manifest.permission.INTERNAL_SYSTEM_WINDOW);
        synchronized (mLock) {
   
            mKeyguardListener = cb;
        }
        return true;
    }

notifyLockWallpaperChanged 中执行 cb.onWallpaperChanged();这里的 cb = mKeyguardListener,而 mKeyguardListener 在 setLockWallpaperCallback() 方法中得到。 跟进我们发现 cb 其实就是 LockscreenWallpaper 引用,在 LockscreenWallpaper 的构造方法里赋值调用:
frameworks/base/packages/SystemUI/src/com/android/systemui/statusbar/phone/LockscreenWallpaper.java

    @Inject
    public LockscreenWallpaper(WallpaperManager wallpaperManager,
            @Nullable IWallpaperManager iWallpaperManager,
            KeyguardUpdateMonitor keyguardUpdateMonitor,
            DumpManager dumpManager,
            NotificationMediaManager mediaManager,
            @Main Handler mainHandler) {
   
        // 省略部分代码......
        if (iWallpaperManager != null) {
   
            // Service is disabled on some devices like Automotive
            try {
   
                // iWallpaperManager 是 WallpaperManagerService 的 binder对象,
                // 通过 dagger 在 SystemServicesModule 实例化。
                iWallpaperManager.setLockWallpaperCallback(this);
            } catch (RemoteException e) {
   
                Log.e(TAG, "System dead?" + e);
            }
        }
    }

所以当锁屏壁纸更新时,就会回调到 LockscreenWallpaper#onWallpaperChanged()
frameworks/base/packages/SystemUI/src/com/android/systemui/statusbar/phone/LockscreenWallpaper.java

    @Override
    public void onWallpaperChanged() {
   
        // Called on Binder thread.
        postUpdateWallpaper();
    }
    private void postUpdateWallpaper() {
   
        if (mH == null) {
   
            Log.wtfStack(TAG, "Trying to use LockscreenWallpaper before initialization.");
            return;
        }
        mH.removeCallbacks(this);
        mH.post(this);
    }

而 LockscreenWallpaper 类实现了 Runnable 接口的,所以看下它的 run() 方法; LockscreenWallpaper#run()
frameworks/base/packages/SystemUI/src/com/android/systemui/statusbar/phone/LockscreenWallpaper.java

    @Override
    public void run() {
   
        // Called in response to onWallpaperChanged on the main thread.
        if (mLoader != null) {
   
            mLoader.cancel(false /* interrupt */);
        }
        final int currentUser = mCurrentUserId;
        final UserHandle selectedUser = mSelectedUser;
        mLoader = new AsyncTask<Void, Void, LoaderResult>() {
   
            @Override
            protected LoaderResult doInBackground(Void... params) {
   
                return loadBitmap(currentUser, selectedUser);
            }
            @Override
            protected void onPostExecute(LoaderResult result) {
   
                super.onPostExecute(result);
                if (isCancelled()) {
   
                    return;
                }
                if (result.success) {
   
                    mCached = true;
                    mCache = result.bitmap;
                    mUpdateMonitor.setHasLockscreenWallpaper(result.bitmap != null);
                    // 通知StatusBar更新壁纸
                    mMediaManager.updateMediaMetaData(
                            true /* metaDataChanged */, true /* allowEnterAnimation */);
                }
                mLoader = null;
            }
        }.executeOnExecutor(AsyncTask.THREAD_POOL_EXECUTOR);
    }

异步获取壁纸,并通知StatusBar去更新壁纸。
NotificationMediaManager#updateMediaMetaData()
frameworks/base/packages/SystemUI/src/com/android/systemui/statusbar/NotificationMediaManager.java

    public void updateMediaMetaData(boolean metaDataChanged, boolean allowEnterAnimation) {
   
        Trace.beginSection("StatusBar#updateMediaMetaData");
  
        // 省略部分代码......
        Bitmap artworkBitmap = null;
        if (mediaMetadata != null && !mKeyguardBypassController.getBypassEnabled()) {
   
            artworkBitmap = mediaMetadata.getBitmap(MediaMetadata.METADATA_KEY_ART);
            if (artworkBitmap == null) {
   
                artworkBitmap = mediaMetadata.getBitmap(MediaMetadata.METADATA_KEY_ALBUM_ART);
            }
        }
 
        //在后台线程上处理图稿并将生成的位图发送到finishUpdateMediaMetaData。
        if (metaDataChanged) {
   
            for (AsyncTask<?, ?, ?> task : mProcessArtworkTasks) {
   
                task.cancel(true);
            }
            mProcessArtworkTasks.clear();
        }
        if (artworkBitmap != null && !Utils.useQsMediaPlayer(mContext)) {
   
            mProcessArtworkTasks.add(new ProcessArtworkTask(this, metaDataChanged,
                    allowEnterAnimation).execute(artworkBitmap));
        } else {
   
            finishUpdateMediaMetaData(metaDataChanged, allowEnterAnimation, null);
        }
        Trace.endSection();
    }

对锁屏壁纸所在 view 做 setImageBitmap。
frameworks/base/packages/SystemUI/src/com/android/systemui/statusbar/NotificationMediaManager.java

    private void finishUpdateMediaMetaData(boolean metaDataChanged, boolean allowEnterAnimation,
            @Nullable Bitmap bmp) {
   
        Drawable artworkDrawable = null;
        if (bmp != null) {
   
            artworkDrawable = new BitmapDrawable(mBackdropBack.getResources(), bmp);
        }
        // 省略部分代码......
        if ((hasArtwork || DEBUG_MEDIA_FAKE_ARTWORK)
                && (mStatusBarStateController.getState() != StatusBarState.SHADE || allowWhenShade)
                &&  mBiometricUnlockController != null && mBiometricUnlockController.getMode()
                        != BiometricUnlockController.MODE_WAKE_AND_UNLOCK_PULSING
                && !hideBecauseOccluded) {
   
             // 省略部分代码......
            if (metaDataChanged) {
   
                if (mBackdropBack.getDrawable() != null) {
   
                    Drawable drawable =
                            mBackdropBack.getDrawable().getConstantState()
                                    .newDrawable(mBackdropFront.getResources()).mutate();
                    // 设置壁纸 setImageDrawable()
                    mBackdropFront.setImageDrawable(drawable);
                    mBackdropFront.setAlpha(1f);
                    mBackdropFront.setVisibility(View.VISIBLE);
                } else {
   
                    mBackdropFront.setVisibility(View.INVISIBLE);
                }
 
                if (DEBUG_MEDIA_FAKE_ARTWORK) {
   
                    final int c = 0xFF000000 | (int)(Math.random() * 0xFFFFFF);
                    Log.v(TAG, String.format("DEBUG_MEDIA: setting new color: 0x%08x", c));
                    mBackdropBack.setBackgroundColor(0xFFFFFFFF);
                    mBackdropBack.setImageDrawable(new ColorDrawable(c));
                } else {
   
                    mBackdropBack.setImageDrawable(artworkDrawable);
                }
 
                if (mBackdropFront.getVisibility() == View.VISIBLE) {
   
                    if (DEBUG_MEDIA) {
   
                        Log.v(TAG, "DEBUG_MEDIA: Crossfading album artwork from "
                                + mBackdropFront.getDrawable()
                                + " to "
                                + mBackdropBack.getDrawable());
                    }
                    mBackdropFront.animate()
                            .setDuration(250)
                            .alpha(0f).withEndAction(mHideBackdropFront);
                }
            }
        } else {
   
            // 省略部分代码......
        }
    }

通过 mBackdropFront.setImageDrawable(drawable) 方法将图片设置进去,完成锁屏壁纸的更新。
mBackdropFront 在 NotificationMediaManager的 setup() 方法被赋值,而 setup() 方法在 StatusBar 的 makeStatusBarView() 中被调用初始化。
StatusBar#makeStatusBarView()
frameworks/layoutlib/bridge/src/com/android/layoutlib/bridge/bars/StatusBar.java

    protected void makeStatusBarView(@Nullable RegisterStatusBarResult result) {
   
        // 省略部分代码......
        mMediaManager.setup(backdrop, backdrop.findViewById(R.id.backdrop_front),
                backdrop.findViewById(R.id.backdrop_back), mScrimController, mLockscreenWallpaper);
        // 省略部分代码......
    }

相关推荐

  1. Android 界面启动流程

    2024-02-14 19:30:01       43 阅读
  2. Android 设置系统桌面壁纸

    2024-02-14 19:30:01       54 阅读

最近更新

  1. docker php8.1+nginx base 镜像 dockerfile 配置

    2024-02-14 19:30:01       94 阅读
  2. Could not load dynamic library ‘cudart64_100.dll‘

    2024-02-14 19:30:01       101 阅读
  3. 在Django里面运行非项目文件

    2024-02-14 19:30:01       82 阅读
  4. Python语言-面向对象

    2024-02-14 19:30:01       91 阅读

热门阅读

  1. Oracle数据库

    2024-02-14 19:30:01       47 阅读
  2. 深入解析MySQL 8:事务数据字典的变革

    2024-02-14 19:30:01       57 阅读
  3. 2.12作业

    2024-02-14 19:30:01       45 阅读
  4. 计算机如何搭建存储服务器

    2024-02-14 19:30:01       52 阅读
  5. awk的内置数值函数

    2024-02-14 19:30:01       50 阅读
  6. vue-cil的watch函数详解

    2024-02-14 19:30:01       51 阅读
  7. 假期day9(2024/2/14)

    2024-02-14 19:30:01       53 阅读