Bootstrap

Android 10 设置壁纸流程

-----------------------------------------------------------------------------------------------------------------------------------------------------

android10的壁纸显示流程已经和之前的不一样了,我也查了一些资料,然后结合自己看代码,最后找到了显示壁纸的地方了。只是静态壁纸。

packages/SystemUI/src/com/android/systemui/glwallpaper/ImageWallpaperRenderer.java

不管是去设置壁纸还是当你休眠唤醒,都会走这里壁纸。

ImageWallpaperRenderer这个在ImageWallpaper中有调用,ImageWallpaper也需要一起看看。

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

-----------------------------------------------------------忽略,这个是壁纸显示相关的-----------------------------------------------------------------------

 

 

Wallpapers

长按桌面,选择wallpapers去设置壁纸,可以设置主屏,也可以设置锁屏,也可以同时设置。

/packages/apps/WallpaperPicker2/ 代码在这里。这里分析一下点了设置后的流程。

 

从桌面长按,然后选择wallpapers,然后选择一张图片,然后点击设置,这时候会有个对话框出来,让你选择设置的类型。Home screen / Lock screen / Home and Lock screen

这个avtivity代码在

/packages/apps/WallpaperPicker2/src/com/android/wallpaper/picker/PreviewActivity.java

我们主要看这个对话框的代码,这个对话框的代码在

/packages/apps/WallpaperPicker2/src/com/android/wallpaper/picker/SetWallpaperDialogFragment.java

    public Dialog onCreateDialog(Bundle savedInstanceState) {
        super.onCreateDialog(savedInstanceState);

        Context context = getContext();

        @SuppressWarnings("RestrictTo")
        View layout =
                View.inflate(
                        new ContextThemeWrapper(getActivity(), R.style.LightDialogTheme),
                        R.layout.dialog_set_wallpaper,
                        null);

        AlertDialog dialog = new AlertDialog.Builder(context, R.style.LightDialogTheme)
                .setTitle(mTitleResId)
                .setView(layout)
                .create();

        mSetHomeWallpaperButton = layout.findViewById(R.id.set_home_wallpaper_button);
        mSetHomeWallpaperButton.setOnClickListener(v -> {
            mListener.onSet(WallpaperPersister.DEST_HOME_SCREEN);
            dismiss();
        });
        ButtonDrawableSetterCompat.setDrawableToButtonStart(
                mSetHomeWallpaperButton,
                context.getDrawable(R.drawable.ic_home_24px));

        mSetLockWallpaperButton = layout.findViewById(R.id.set_lock_wallpaper_button);
        mSetLockWallpaperButton.setOnClickListener(v -> {
            mListener.onSet(WallpaperPersister.DEST_LOCK_SCREEN);
            dismiss();
        });
        ButtonDrawableSetterCompat.setDrawableToButtonStart(
                mSetLockWallpaperButton,
                context.getDrawable(R.drawable.ic_lock_outline_24px));

        mSetBothWallpaperButton = layout.findViewById(R.id.set_both_wallpaper_button);
        mSetBothWallpaperButton.setOnClickListener(v -> {
            mListener.onSet(WallpaperPersister.DEST_BOTH);
            dismiss();
        });
        ButtonDrawableSetterCompat.setDrawableToButtonStart(
                mSetBothWallpaperButton,
                context.getDrawable(R.drawable.ic_smartphone_24px));

        updateButtonsVisibility();

        return dialog;
    }

可以看到这里面就有3个button,对应的就是screen / Lock screen / Home and Lock screen,点击后就会调用onSet这个接口,下面就去追踪这个接口的实现。

 

这个接口实现的地方在

/packages/apps/WallpaperPicker2/src/com/android/wallpaper/picker/PreviewFragment.java

    @Override
    public void onSet(int destination) {
        setCurrentWallpaper(destination);
    }


    /**
     * Sets current wallpaper to the device based on current zoom and scroll state.
     *
     * @param destination The wallpaper destination i.e. home vs. lockscreen vs. both.
     */
    private void setCurrentWallpaper(@Destination final int destination) {
        mWallpaperSetter.setCurrentWallpaper(getActivity(), mWallpaper, mWallpaperAsset,
                destination, mFullResImageView.getScale(), calculateCropRect(),
                new SetWallpaperCallback() {
                    @Override
                    public void onSuccess() {
                        finishActivityWithResultOk();
                    }

                    @Override
                    public void onError(@Nullable Throwable throwable) {
                        showSetWallpaperErrorDialog(destination);
                    }
                });
    }

 

然后调用setCurrentWallpaper去设置壁纸。代码在

/packages/apps/WallpaperPicker2/src/com/android/module/WallpaperSetter.java


    /**
     * Sets current wallpaper to the device based on current zoom and scroll state.
     *
     * @param containerActivity main Activity that owns the current fragment
     * @param wallpaper info for the actual wallpaper to set
     * @param wallpaperAsset  Wallpaper asset from which to retrieve image data.
     * @param destination The wallpaper destination i.e. home vs. lockscreen vs. both.
     * @param wallpaperScale Scaling factor applied to the source image before setting the
     *                       wallpaper to the device.
     * @param cropRect Desired crop area of the wallpaper in post-scale units. If null, then the
     *                 wallpaper image will be set without any scaling or cropping.
     * @param callback optional callback to be notified when the wallpaper is set.
     */
    public void setCurrentWallpaper(Activity containerActivity, WallpaperInfo wallpaper,
            Asset wallpaperAsset, @Destination final int destination, float wallpaperScale,
            @Nullable Rect cropRect, @Nullable SetWallpaperCallback callback) {
        if (wallpaper instanceof LiveWallpaperInfo) {
            setCurrentLiveWallpaper(containerActivity, (LiveWallpaperInfo) wallpaper, destination,
                    callback);
            return;
        }
        mPreferences.setPendingWallpaperSetStatus(
                WallpaperPreferences.WALLPAPER_SET_PENDING);

        // Save current screen rotation so we can temporarily disable rotation while setting the
        // wallpaper and restore after setting the wallpaper finishes.
        saveAndLockScreenOrientation(containerActivity);

        // Clear MosaicView tiles and Glide's cache and pools to reclaim memory for final cropped
        // bitmap.
        Glide.get(containerActivity).clearMemory();

        // ProgressDialog endlessly updates the UI thread, keeping it from going idle which therefore
        // causes Espresso to hang once the dialog is shown.
        if (!mTestingModeEnabled && !containerActivity.isFinishing()) {
            int themeResId = (VERSION.SDK_INT < VERSION_CODES.LOLLIPOP)
                    ? R.style.ProgressDialogThemePreL : R.style.LightDialogTheme;
            mProgressDialog = new ProgressDialog(containerActivity, themeResId);

            mProgressDialog.setTitle(PROGRESS_DIALOG_NO_TITLE);
            mProgressDialog.setMessage(containerActivity.getString(
                            R.string.set_wallpaper_progress_message));
            mProgressDialog.setIndeterminate(PROGRESS_DIALOG_INDETERMINATE);
            mProgressDialog.show();
        }
        
        Log.i("a", "WallpaperSetter destination "+destination);

        mWallpaperPersister.setIndividualWallpaper(
                wallpaper, wallpaperAsset, cropRect,
                wallpaperScale, destination, new SetWallpaperCallback() {
                    @Override
                    public void onSuccess() {
                        onWallpaperApplied(wallpaper, containerActivity);
                        if (callback != null) {
                            callback.onSuccess();
                        }
                    }

                    @Override
                    public void onError(Throwable throwable) {
                        onWallpaperApplyError(throwable, containerActivity);
                        if (callback != null) {
                            callback.onError(throwable);
                        }
                    }
                });
    }

调用WallpaperPersister接口的setIndividualWallpaper方法去设置壁纸,实现的地方在。

/packages/apps/WallpaperPicker2/src/com/android/module/DefaultWallpaperPersister.java


    @Override
    public void setIndividualWallpaper(final WallpaperInfo wallpaper, Asset asset,
                                       @Nullable Rect cropRect, float scale, @Destination final int destination,
                                       final SetWallpaperCallback callback) {
        // Set wallpaper without downscaling directly from an input stream if there's no crop rect
        // specified by the caller and the asset is streamable.
        if (cropRect == null && asset instanceof StreamableAsset) {
            ((StreamableAsset) asset).fetchInputStream(new StreamReceiver() {
                @Override
                public void onInputStreamOpened(@Nullable InputStream inputStream) {
                    if (inputStream == null) {
                        callback.onError(null /* throwable */);
                        return;
                    }
                    setIndividualWallpaper(wallpaper, inputStream, destination, callback);
                }
            });
            return;
        }

        // If no crop rect is specified but the wallpaper asset is not streamable, then fall back to
        // using the device's display size.
        if (cropRect == null) {
            Display display = ((WindowManager) mAppContext.getSystemService(Context.WINDOW_SERVICE))
                    .getDefaultDisplay();
            Point screenSize = ScreenSizeCalculator.getInstance().getScreenSize(display);
            asset.decodeBitmap(screenSize.x, screenSize.y, new BitmapReceiver() {
                @Override
                public void onBitmapDecoded(@Nullable Bitmap bitmap) {
                    if (bitmap == null) {
                        callback.onError(null /* throwable */);
                        return;
                    }
                    setIndividualWallpaper(wallpaper, bitmap, destination, callback);
                }
            });
            return;
        }

        BitmapCropper bitmapCropper = InjectorProvider.getInjector().getBitmapCropper();
        bitmapCropper.cropAndScaleBitmap(asset, scale, cropRect, new Callback() {
            @Override
            public void onBitmapCropped(Bitmap croppedBitmap) {
                setIndividualWallpaper(wallpaper, croppedBitmap, destination, callback);
            }

            @Override
            public void onError(@Nullable Throwable e) {
                callback.onError(e);
            }
        });
    }

然后调用setIndividualWallpaper

    /**
     * Sets a static individual wallpaper stream to the system via the WallpaperManager.
     *
     * @param wallpaper   Wallpaper model object.
     * @param inputStream JPEG or PNG stream of wallpaper image's bytes.
     * @param destination The destination - where to set the wallpaper to.
     * @param callback    Called once the wallpaper was set or if an error occurred.
     */
    private void setIndividualWallpaper(WallpaperInfo wallpaper, InputStream inputStream,
                                        @Destination int destination, SetWallpaperCallback callback) {
        SetWallpaperTask setWallpaperTask =
                new SetWallpaperTask(wallpaper, inputStream, destination, callback);
        setWallpaperTask.execute();
    }

这里会开个线程去设置壁纸,然后追踪这个SetWallpaperTask


        @Override
        protected Boolean doInBackground(Void... unused) {
            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;
            }
            
            Log.i("a", "DefaultWallpaperPersister mDestination whichWallpaper "+mDestination+" "+whichWallpaper);

            // NOTE: The rotating wallpaper component must be determined here, _before_ actually setting
            // the bitmap/stream on WallpaperManagerCompat, to ensure that the
            // RotatingWallpaperComponentChecker is doing its check while rotation is still enabled.
            // E.g., if "live wallpaper" is the component, then it needs to check while live wallpaper is
            // still set as the active wallpaper on the device. Otherwise, the checker would see a static
            // wallpaper is currently set and it would return the wrong value.
            @RotatingWallpaperComponent int currentRotatingWallpaperComponent =
                    mRotatingWallpaperComponentChecker.getCurrentRotatingWallpaperComponent(mAppContext);

            boolean wasLockWallpaperSet = LockWallpaperStatusChecker.isLockWallpaperSet(mAppContext);

            boolean allowBackup = mWallpaper.getBackupPermission() == WallpaperInfo.BACKUP_ALLOWED;
            final int wallpaperId;
            if (mBitmap != null) {
                // Apply fill or stretch transformations on mBitmap if necessary.
                if (mFillSize != null) {
                    mBitmap = BitmapTransformer.applyFillTransformation(mBitmap, mFillSize);
                }
                if (mStretchSize != null) {
                    mBitmap = Bitmap.createScaledBitmap(mBitmap, mStretchSize.x, mStretchSize.y, true);
                }

                wallpaperId = setBitmapToWallpaperManagerCompat(mBitmap, allowBackup, whichWallpaper);
            } else if (mInputStream != null) {
                wallpaperId = setStreamToWallpaperManagerCompat(mInputStream, allowBackup, whichWallpaper);
            } else {
                Log.e(TAG, "Both the wallpaper bitmap and input stream are null so we're unable to set any "
                        + "kind of wallpaper here.");
                wallpaperId = 0;
            }

            if (wallpaperId > 0) {
                if (mDestination == DEST_HOME_SCREEN
                        && mWallpaperPreferences.getWallpaperPresentationMode()
                        == WallpaperPreferences.PRESENTATION_MODE_ROTATING
                        && !wasLockWallpaperSet
                        && BuildCompat.isAtLeastN()) {
                    Log.i("a", "DefaultWallpaperPersister 883");
                    copyRotatingWallpaperToLock(currentRotatingWallpaperComponent);
                }
                setImageWallpaperMetadata(mDestination, wallpaperId);
                Log.i("a", "DefaultWallpaperPersister 887 wallpaperId "+wallpaperId);
                return true;
            } else {
                return false;
            }
        }

然后会走setBitmapToWallpaperManagerCompat,我这边设置的是图片,所以根据log,代码走这了。

    /**
     * Sets a wallpaper bitmap to the {@link WallpaperManagerCompat}.
     *
     * @return an integer wallpaper ID. This is an actual wallpaper ID on N and later versions of
     * Android, otherwise on pre-N versions of Android will return a positive integer when the
     * operation was successful and zero if the operation encountered an error.
     */
    private int setBitmapToWallpaperManagerCompat(Bitmap wallpaperBitmap, boolean allowBackup,
                                                  int whichWallpaper) {
        ByteArrayOutputStream tmpOut = new ByteArrayOutputStream();
        if (wallpaperBitmap.compress(CompressFormat.JPEG, DEFAULT_COMPRESS_QUALITY, tmpOut)) {
            try {
                Log.i("a", "DefaultWallpaperPersister setBitmapToWallpaperManagerCompat 645 whichWallpaper "+whichWallpaper);
                byte[] outByteArray = tmpOut.toByteArray();
                return mWallpaperManagerCompat.setStream(
                        new ByteArrayInputStream(outByteArray),
                        null /* visibleCropHint */,
                        allowBackup,
                        whichWallpaper);
            } catch (IOException e) {
                Log.e(TAG, "unable to write stream to wallpaper manager");
                return 0;
            }
        } else {
            Log.e(TAG, "unable to compress wallpaper");
            Log.i("a", "DefaultWallpaperPersister setBitmapToWallpaperManagerCompat 658");
            try {
                return mWallpaperManagerCompat.setBitmap(
                        wallpaperBitmap,
                        null /* visibleCropHint */,
                        allowBackup,
                        whichWallpaper);
            } catch (IOException e) {
                Log.e(TAG, "unable to set wallpaper");
                return 0;
            }
        }
    }

然后会走接口WallpaperManagerCompat的setStream 或者setBitmap。

这里接口实现的地方我不确定在哪,

/packages/apps/WallpaperPicker2/src/com/android/compat/WallpaperManagerCompatV16.java

/packages/apps/WallpaperPicker2/src/com/android/compat/WallpaperManagerCompatVN.java

但是他们最后都会调用WallpaperManager的setStream和setBitmap。我们追踪WallpaperManager就行了。我追的是setBitmap。

 

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


    /**
     * Like {@link #setBitmap(Bitmap, Rect, boolean, int)}, but allows to pass in an explicit user
     * id. If the user id doesn't match the user id the process is running under, calling this
     * requires permission {@link android.Manifest.permission#INTERACT_ACROSS_USERS_FULL}.
     * @hide
     */
    @UnsupportedAppUsage(maxTargetSdk = Build.VERSION_CODES.P, trackingBug = 115609023)
    public int setBitmap(Bitmap fullImage, Rect visibleCropHint,
            boolean allowBackup, @SetWallpaperFlags int which, int userId)
            throws IOException {
        validateRect(visibleCropHint);
        if (sGlobals.mService == null) {
            Log.w(TAG, "WallpaperService not running");
            throw new RuntimeException(new DeadSystemException());
        }
        final Bundle result = new Bundle();
        final WallpaperSetCompletion completion = new WallpaperSetCompletion();
        try {
            ParcelFileDescriptor fd = sGlobals.mService.setWallpaper(null,
                    mContext.getOpPackageName(), visibleCropHint, allowBackup,
                    result, which, completion, userId);
            if (fd != null) {
                FileOutputStream fos = null;
                try {
                    fos = new ParcelFileDescriptor.AutoCloseOutputStream(fd);
                    fullImage.compress(Bitmap.CompressFormat.PNG, 90, 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去设置壁纸。这个mService就是IWallpaperManager,这边是用的AIDL的方法。所以我们去找WallpaperManagerService就能找到实现的地方,

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


    @Override
    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);

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

        if (!isWallpaperSupported(callingPackage) || !isSetWallpaperAllowed(callingPackage)) {
            return null;
        }

        // "null" means the no-op crop, preserving the full input image
        if (cropHint == null) {
            cropHint = new Rect(0, 0, 0, 0);
        } else {
            if (cropHint.isEmpty()
                    || cropHint.left < 0
                    || cropHint.top < 0) {
                throw new IllegalArgumentException("Invalid crop rect supplied: " + cropHint);
            }
        }

        synchronized (mLock) {
            Slog.v("a", "setWallpaper which=0x" + Integer.toHexString(which));
            WallpaperData wallpaper;

            /* If we're setting system but not lock, and lock is currently sharing the system
             * wallpaper, we need to migrate that image over to being lock-only before
             * the caller here writes new bitmap data.
             */
            if (which == FLAG_SYSTEM && mLockWallpaperMap.get(userId) == null) {
                
                    Slog.i("a", "Migrating system->lock to preserve");
                
                migrateSystemToLockWallpaperLocked(userId);
            }

            wallpaper = getWallpaperSafeLocked(userId, which);
            Slog.v("a", "getWallpaperSafeLocked wallpaper.whichPending " + wallpaper.whichPending);
            final long ident = Binder.clearCallingIdentity();
            try {
                ParcelFileDescriptor pfd = updateWallpaperBitmapLocked(name, wallpaper, extras);
                if (pfd != null) {
                    Slog.v("a", "updateWallpaperBitmapLocked wallpaper.whichPending " + wallpaper.whichPending);
                    wallpaper.imageWallpaperPending = true;
                    wallpaper.whichPending = which;
                    wallpaper.setComplete = completion;
                    wallpaper.cropHint.set(cropHint);
                    wallpaper.allowBackup = allowBackup;
                }
                return pfd;
            } finally {
                Binder.restoreCallingIdentity(ident);
            }
        }
    }

 

注意一下migrateSystemToLockWallpaperLocked,如果你没设置过lock,并且你现在只去设置home,那么它会先把原先的home壁纸拷贝到lock壁纸,然后再去设置home。(如果这个migrateSystemToLockWallpaperLocked执行没成功的话,那么你会发现你设置了home壁纸,但是lock壁纸也变了。只有第一次有这种情况,之后设置不会,我遇到的。出现Can't migrate system wallpaper:异常了。)

 

下面没有继续追了。

;