前言 一、设置壁纸 通过系统设置进行锁屏壁纸和桌面壁纸的设置。 Setting 部分的代码:packages/apps/WallpaperPicker2/src/com/android/wallpaper/module/DefaultWallpaperPersister.java
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 private int setStreamToWallpaperManagerCompat (InputStream inputStream, boolean allowBackup, int whichWallpaper) { try { return mWallpaperManagerCompat.setStream(inputStream, null , allowBackup, whichWallpaper); } catch (IOException e) { return 0 ; } } ... ...
mWallpaperManagerCompat 其实就是 WallpaperManagerCompatV16 的对象。packages/apps/WallpaperPicker2/src/com/android/wallpaper/compat/WallpaperManagerCompatV16.java
1 2 3 4 5 6 7 @Override public int setStream (InputStream data, Rect visibleCropHint, boolean allowBackup, int whichWallpaper) throws IOException { mWallpaperManager.setStream(data); return 1 ; }
由此可知,壁纸的设置是通过 WallpaperManager 类来进行的。
二、锁屏壁纸的显示 锁屏壁纸显示流程图:
上面应用程序设置完成了,下面就该进行壁纸显示了。 WallpaperManager#setStream()frameworks/base/core/java/android/app/WallpaperManager.java
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 public int setStream (InputStream bitmapData, Rect visibleCropHint, boolean allowBackup, @SetWallpaperFlags int which) throws IOException { try { ParcelFileDescriptor fd = sGlobals.mService.setWallpaper(null , mContext.getOpPackageName(), visibleCropHint, allowBackup, result, which, completion, mContext.getUserId()); if (fd != null ) { FileOutputStream fos = null ; try { 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
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 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 , true , "changing wallpaper" , null ); checkPermission(android.Manifest.permission.SET_WALLPAPER); 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; if (which == FLAG_SYSTEM && mLockWallpaperMap.get(userId) == null ) { migrateSystemToLockWallpaperLocked(userId); } wallpaper = getWallpaperSafeLocked(userId, which); final long ident = Binder.clearCallingIdentity(); try { 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
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 ParcelFileDescriptor updateWallpaperBitmapLocked (String name, WallpaperData wallpaper, Bundle extras) { if (name == null ) name = "" ; try { 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
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 67 68 69 70 71 72 73 74 75 76 77 @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); final File changedFile = new File (mWallpaperDir, path); 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 || wallpaper.imageWallpaperPending) { if (written) { SELinux.restorecon(changedFile); if (moved) { loadSettingsLocked(wallpaper.userId, true ); } generateCrop(wallpaper); wallpaper.imageWallpaperPending = false ; if (sysWallpaperChanged) { 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) { mLockWallpaperMap.remove(wallpaper.userId); } notifyLockWallpaperChanged(); notifyColorsWhich |= FLAG_LOCK; } saveSettingsLocked(wallpaper.userId); if (wallpaper.setComplete != null ) { try { wallpaper.setComplete.onWallpaperChanged(); } catch (RemoteException e) { } } } } } } if (notifyColorsWhich != 0 ) { notifyWallpaperColorsChanged(wallpaper, notifyColorsWhich); } }
先看锁屏壁纸更新这一部分 notifyLockWallpaperChanged() :frameworks/base/services/core/java/com/android/server/wallpaper/WallpaperManagerService.java
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 private void notifyLockWallpaperChanged () { final IWallpaperManagerCallback cb = mKeyguardListener; if (cb != null ) { try { cb.onWallpaperChanged(); } catch (RemoteException e) { } } } @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
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 @Inject public LockscreenWallpaper (WallpaperManager wallpaperManager, @Nullable IWallpaperManager iWallpaperManager, KeyguardUpdateMonitor keyguardUpdateMonitor, DumpManager dumpManager, NotificationMediaManager mediaManager, @Main Handler mainHandler) { if (iWallpaperManager != null ) { try { 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
1 2 3 4 5 6 7 8 9 10 11 12 13 @Override public void onWallpaperChanged () { 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
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 @Override public void run () { if (mLoader != null ) { mLoader.cancel(false ); } 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 ); mMediaManager.updateMediaMetaData( true , true ); } mLoader = null ; } }.executeOnExecutor(AsyncTask.THREAD_POOL_EXECUTOR); }
异步获取壁纸,并通知StatusBar去更新壁纸。NotificationMediaManager#updateMediaMetaData() frameworks/base/packages/SystemUI/src/com/android/systemui/statusbar/NotificationMediaManager.java
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 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); } } 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
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 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(); 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
1 2 3 4 5 6 protected void makeStatusBarView (@Nullable RegisterStatusBarResult result) { mMediaManager.setup(backdrop, backdrop.findViewById(R.id.backdrop_front), backdrop.findViewById(R.id.backdrop_back), mScrimController, mLockscreenWallpaper); }
本文链接: http://longzhiye.top/2024/01/01/2024-01-01/