Android壁纸服务WallpaperManagerService

  Android壁纸服务WallpaperManagerService启动在SystemServer中。当NonCoreServices没有被禁止且config_enableWallpaperService的config值为true时,启动WallpaperManagerService。

/frameworks/base/services/java/com/android/server/SystemServer.java

            ...
            if (!disableNonCoreServices && context.getResources().getBoolean(
                        R.bool.config_enableWallpaperService)) {
                try {
                    Slog.i(TAG, "Wallpaper Service");
                    wallpaper = new WallpaperManagerService(context);
                    ServiceManager.addService(Context.WALLPAPER_SERVICE, wallpaper);
                } catch (Throwable e) {
                    reportWtf("starting Wallpaper Service", e);
                }
            }
            ...

  mImageWallpaper的值是一个ComponentName,其值是”com.android.systemui/com.android.systemui.ImageWallpaper”。getWallpaperDir则在设备上创建/data/system/users/0的目录。loadSettingsLocked用来读取保存在xml文件里面的壁纸信息。

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

    public WallpaperManagerService(Context context) {
        if (DEBUG) Slog.v(TAG, "WallpaperService startup");
        mContext = context;
        mImageWallpaper = ComponentName.unflattenFromString(
                context.getResources().getString(R.string.image_wallpaper_component));
        mIWindowManager = IWindowManager.Stub.asInterface(
                ServiceManager.getService(Context.WINDOW_SERVICE));
        mIPackageManager = AppGlobals.getPackageManager();
        mMonitor = new MyPackageMonitor();
        mMonitor.register(context, null, UserHandle.ALL, true);
        getWallpaperDir(UserHandle.USER_OWNER).mkdirs();
        loadSettingsLocked(UserHandle.USER_OWNER);
    }

  loadSettingsLocked中,先是读取/data/system/users/0/wallpaper_info.xml的内容,将一些信息例如壁纸的宽度(width),壁纸的高度(height),名字(name),下一个壁纸组件(component)等信息记录在WallpaperData中,这个WallpaperData和userid组成一个键值对保存在mWallpaperMap中。这样,不同的userid就可以拥有不同的壁纸配置信息。最后,getMaximumSizeDimension返回的是当前屏幕逻辑高度和逻辑宽度的最大值,若壁纸信息记录的宽度或高度小于这个值,则把壁纸的宽度或高度设置成getMaximumSizeDimension的返回值。屏幕逻辑高度和逻辑宽度根据当前的分辨率而定,可能1920x1080的屏幕物理分辨率是1920x1080,但是修改分辨率后,例如1280x720,那么逻辑宽度和逻辑高度就分别为1280和720。

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

    private void loadSettingsLocked(int userId) {
        if (DEBUG) Slog.v(TAG, "loadSettingsLocked");

        JournaledFile journal = makeJournaledFile(userId);
        FileInputStream stream = null;
        File file = journal.chooseForRead();
        if (!file.exists()) {
            // This should only happen one time, when upgrading from a legacy system
            migrateFromOld();
        }
        WallpaperData wallpaper = mWallpaperMap.get(userId);
        if (wallpaper == null) {
            wallpaper = new WallpaperData(userId);
            mWallpaperMap.put(userId, wallpaper);
        }
        boolean success = false;
        try {
            stream = new FileInputStream(file);
            XmlPullParser parser = Xml.newPullParser();
            parser.setInput(stream, null);

            int type;
            do {
                type = parser.next();
                if (type == XmlPullParser.START_TAG) {
                    String tag = parser.getName();
                    if ("wp".equals(tag)) {
                        wallpaper.width = Integer.parseInt(parser.getAttributeValue(null, "width"));
                        wallpaper.height = Integer.parseInt(parser
                                .getAttributeValue(null, "height"));
                        wallpaper.padding.left = getAttributeInt(parser, "paddingLeft", 0);
                        wallpaper.padding.top = getAttributeInt(parser, "paddingTop", 0);
                        wallpaper.padding.right = getAttributeInt(parser, "paddingRight", 0);
                        wallpaper.padding.bottom = getAttributeInt(parser, "paddingBottom", 0);
                        wallpaper.name = parser.getAttributeValue(null, "name");
                        String comp = parser.getAttributeValue(null, "component");
                        wallpaper.nextWallpaperComponent = comp != null
                                ? ComponentName.unflattenFromString(comp)
                                : null;
                        if (wallpaper.nextWallpaperComponent == null
                                || "android".equals(wallpaper.nextWallpaperComponent
                                        .getPackageName())) {
                            wallpaper.nextWallpaperComponent = mImageWallpaper;
                        }

                        if (DEBUG) {
                            Slog.v(TAG, "mWidth:" + wallpaper.width);
                            Slog.v(TAG, "mHeight:" + wallpaper.height);
                            Slog.v(TAG, "mName:" + wallpaper.name);
                            Slog.v(TAG, "mNextWallpaperComponent:"
                                    + wallpaper.nextWallpaperComponent);
                        }
                    }
                }
            } while (type != XmlPullParser.END_DOCUMENT);
            success = true;
        } catch (FileNotFoundException e) {
            Slog.w(TAG, "no current wallpaper -- first boot?");
        } catch (NullPointerException e) {
            Slog.w(TAG, "failed parsing " + file + " " + e);
        } catch (NumberFormatException e) {
            Slog.w(TAG, "failed parsing " + file + " " + e);
        } catch (XmlPullParserException e) {
            Slog.w(TAG, "failed parsing " + file + " " + e);
        } catch (IOException e) {
            Slog.w(TAG, "failed parsing " + file + " " + e);
        } catch (IndexOutOfBoundsException e) {
            Slog.w(TAG, "failed parsing " + file + " " + e);
        }
        try {
            if (stream != null) {
                stream.close();
            }
        } catch (IOException e) {
            // Ignore
        }

        if (!success) {
            wallpaper.width = -1;
            wallpaper.height = -1;
            wallpaper.padding.set(0, 0, 0, 0);
            wallpaper.name = "";
        }

        // We always want to have some reasonable width hint.
        int baseSize = getMaximumSizeDimension();
        if (wallpaper.width < baseSize) {
            wallpaper.width = baseSize;
        }
        if (wallpaper.height < baseSize) {
            wallpaper.height = baseSize;
        }
    }

  在SystemServer的另一处,调用了WallpaperManagerService的systemRunning函数来真正运行壁纸服务并加载壁纸。其中,switchWallpaper用来启动真正的壁纸组件,WallpaperObserver使用inotify来监视/data/system/users/0/下面文件的变化,若发生变化的是”wallpaper”或者”wallpaper_info.xml”文件,则回调onEvent函数作相应的处理。下面来看看switchWallpaper
的实现。

/frameworks/base/services/java/com/android/server/SystemServer.java

                ...
                try {
                    if (wallpaperF != null) wallpaperF.systemRunning();
                } catch (Throwable e) {
                    reportWtf("Notifying WallpaperService running", e);
                }
                ...

  switchWallpaper主要是调用bindWallpaperComponentLocked来启动真正的壁纸组件”com.android.systemui/com.android.systemui.ImageWallpaper”。

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

    void switchWallpaper(WallpaperData wallpaper, IRemoteCallback reply) {
        synchronized (mLock) {
            RuntimeException e = null;
            try {
                ComponentName cname = wallpaper.wallpaperComponent != null ?
                        wallpaper.wallpaperComponent : wallpaper.nextWallpaperComponent;
                if (bindWallpaperComponentLocked(cname, true, false, wallpaper, reply)) {
                    return;
                }
            } catch (RuntimeException e1) {
                e = e1;
            }
            Slog.w(TAG, "Failure starting previous wallpaper", e);
            clearWallpaperLocked(false, wallpaper.userId, reply);
        }
    }

  只看bindWallpaperComponentLocked的关键部分,简而言之就是用bindServiceAsUser来启动一个WallpaperService,而SystemUI中的ImageWallpaper继承自WallpaperService,可以借此启动。若bindWallpaperComponentLocked没有出错返回,则更新WallpaperData的wallpaperComponent和connection为启动的壁纸组件的包名和ServiceConnection。最后,往WindowManagerService添加一个类型为TYPE_WALLPAPER的WindowToken,将mLastWallpaper设为当前的WallpaperData。

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

    boolean bindWallpaperComponentLocked(ComponentName componentName, boolean force,
            boolean fromUser, WallpaperData wallpaper, IRemoteCallback reply) {
            ...
            // Bind the service!
            if (DEBUG) Slog.v(TAG, "Binding to:" + componentName);
            WallpaperConnection newConn = new WallpaperConnection(wi, wallpaper);
            intent.setComponent(componentName);
            intent.putExtra(Intent.EXTRA_CLIENT_LABEL,
                    com.android.internal.R.string.wallpaper_binding_label);
            intent.putExtra(Intent.EXTRA_CLIENT_INTENT, PendingIntent.getActivityAsUser(
                    mContext, 0,
                    Intent.createChooser(new Intent(Intent.ACTION_SET_WALLPAPER),
                            mContext.getText(com.android.internal.R.string.chooser_wallpaper)),
                    0, null, new UserHandle(serviceUserId)));
            if (!mContext.bindServiceAsUser(intent, newConn,
                    Context.BIND_AUTO_CREATE | Context.BIND_SHOWING_UI,
                    new UserHandle(serviceUserId))) {
                String msg = "Unable to bind service: "
                        + componentName;
                if (fromUser) {
                    throw new IllegalArgumentException(msg);
                }
                Slog.w(TAG, msg);
                return false;
            }
            if (wallpaper.userId == mCurrentUserId && mLastWallpaper != null) {
                detachWallpaperLocked(mLastWallpaper);
            }
            wallpaper.wallpaperComponent = componentName;
            wallpaper.connection = newConn;
            newConn.mReply = reply;
            try {
                if (wallpaper.userId == mCurrentUserId) {
                    if (DEBUG)
                        Slog.v(TAG, "Adding window token: " + newConn.mToken);
                    mIWindowManager.addWindowToken(newConn.mToken,
                            WindowManager.LayoutParams.TYPE_WALLPAPER);
                    mLastWallpaper = wallpaper;
                }
            } catch (RemoteException e) {
            }
        } catch (RemoteException e) {
            String msg = "Remote exception for " + componentName + "\n" + e;
            if (fromUser) {
                throw new IllegalArgumentException(msg);
            }
            Slog.w(TAG, msg);
            return false;
        }
        return true;
    }

  我们知道,当通过bindService启动一个Service时,就会回调ServiceConnection的onServiceConnected函数。看看实现了ServiceConnection接口的WallpaperConnection对onServiceConnected的实现。
  服务端的WallpaperService通过onBind返回一个IBinder对象,作为onServiceConnected的第二个参数传入,这个IBinder对象是一个IWallpaperServiceWrapper对象。asInterface调用返回一个IWallpaperService.Stub的代理对象并保存在mService中。attachServiceLocked
用来启动壁纸的Engine。Engine类提供了许多壁纸呈现的流程的回调接口及属性获取接口,并通过updateSurface来实现添加壁纸窗口和申请Surface的操作,当壁纸的属性发生变化时,我们可以在对应的流程回调接口加入例如drawFrame(画图渲染)等的操作。saveSettingsLocked用来将Engine启动过程中对WallpaperData的修改写回到配置文件wallpaper_info.xml中。

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

        @Override
        public void onServiceConnected(ComponentName name, IBinder service) {
            synchronized (mLock) {
                if (mWallpaper.connection == this) {
                    mService = IWallpaperService.Stub.asInterface(service);
                    attachServiceLocked(this, mWallpaper);
                    // XXX should probably do saveSettingsLocked() later
                    // when we have an engine, but I'm not sure about
                    // locking there and anyway we always need to be able to
                    // recover if there is something wrong.
                    saveSettingsLocked(mWallpaper);
                }
            }
        }

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

    @Override
    public final IBinder onBind(Intent intent) {
        return new IWallpaperServiceWrapper(this);
    }

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

    void attachServiceLocked(WallpaperConnection conn, WallpaperData wallpaper) {
        try {
            conn.mService.attach(conn, conn.mToken,
                    WindowManager.LayoutParams.TYPE_WALLPAPER, false,
                    wallpaper.width, wallpaper.height, wallpaper.padding);
        } catch (RemoteException e) {
            Slog.w(TAG, "Failed attaching wallpaper; clearing", e);
            if (!wallpaper.wallpaperUpdating) {
                bindWallpaperComponentLocked(null, false, false, wallpaper, null);
            }
        }
    }

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

        @Override
        public void attach(IWallpaperConnection conn, IBinder windowToken,
                int windowType, boolean isPreview, int reqWidth, int reqHeight, Rect padding) {
            new IWallpaperEngineWrapper(mTarget, conn, windowToken,
                    windowType, isPreview, reqWidth, reqHeight, padding);
        }

  IWallpaperEngineWrapper的构造函数中,发送了一个DO_ATTACH的消息到WallpaperService的Looper的MessageQueue中。

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

        IWallpaperEngineWrapper(WallpaperService context,
                IWallpaperConnection conn, IBinder windowToken,
                int windowType, boolean isPreview, int reqWidth, int reqHeight, Rect padding) {
            mCaller = new HandlerCaller(context, context.getMainLooper(), this, true);
            mConnection = conn;
            mWindowToken = windowToken;
            mWindowType = windowType;
            mIsPreview = isPreview;
            mReqWidth = reqWidth;
            mReqHeight = reqHeight;
            mDisplayPadding.set(padding);

            Message msg = mCaller.obtainMessage(DO_ATTACH);
            mCaller.sendMessage(msg);
        }

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

        public void executeMessage(Message message) {
            switch (message.what) {
                case DO_ATTACH: {
                    try {
                        mConnection.attachEngine(this);
                    } catch (RemoteException e) {
                        Log.w(TAG, "Wallpaper host disappeared", e);
                        return;
                    }
                    Engine engine = onCreateEngine();
                    mEngine = engine;
                    mActiveEngines.add(engine);
                    engine.attach(this);
                    return;
                }

  WallpaperConnection将mEngine成员设为传过来的IWallpaperEngineWrapper对象。当我们还没调用attachEngine来attach一个Engine到WallpaperConnection时,而我们希望修改attach到Engine后改变Engine的宽度和高度(mDimensionsChanged),或边距(mPaddingChanged),则可以通过WallpaperManager#suggestDesiredDimensions(改变宽度和高度)和WallpaperManager#setDisplayPadding(改变边距)来实现,这样,这些改变属性的操作将会延迟到attach之后才会执行。WallpaperManager对象可以通过ContextImpl#getSystemService(Context.WINDOW_SERVICE)获得.。

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

        @Override
        public void attachEngine(IWallpaperEngine engine) {
            synchronized (mLock) {
                mEngine = engine;
                if (mDimensionsChanged) {
                    try {
                        mEngine.setDesiredSize(mWallpaper.width, mWallpaper.height);
                    } catch (RemoteException e) {
                        Slog.w(TAG, "Failed to set wallpaper dimensions", e);
                    }
                    mDimensionsChanged = false;
                }
                if (mPaddingChanged) {
                    try {
                        mEngine.setDisplayPadding(mWallpaper.padding);
                    } catch (RemoteException e) {
                        Slog.w(TAG, "Failed to set wallpaper padding", e);
                    }
                    mPaddingChanged = false;
                }
            }
        }

  ImageWallpaper类复写了onCreateEngine方法,创建了一个继承自Engine类的DrawableEngine。这个DrawableEngine将被保存在IWallpaperEngineWrapper的mEngine成员中。

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

    @Override
    public Engine onCreateEngine() {
        mEngine = new DrawableEngine();
        return mEngine;
    }

  mSurfaceHolder是Engine的一个内部类BaseSurfaceHolder对象,其继承了BaseSurfaceHolder类,提供了管理Surface对象和保存Surface生命周期回调接口的能力。onCreate函数在子类DrawableEngine有实现,
用来初始化壁纸使用的Bitmap和使用的Bitmap及Surface的大小,见于updateSurfaceSize函数。最后调用updateSurface进行首次的添加Window,获取Surface和渲染的操作。

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

        void attach(IWallpaperEngineWrapper wrapper) {
            if (DEBUG) Log.v(TAG, "attach: " + this + " wrapper=" + wrapper);
            if (mDestroyed) {
                return;
            }

            mIWallpaperEngine = wrapper;
            mCaller = wrapper.mCaller;
            mConnection = wrapper.mConnection;
            mWindowToken = wrapper.mWindowToken;
            mSurfaceHolder.setSizeFromLayout();
            mInitializing = true;
            mSession = WindowManagerGlobal.getWindowSession();

            mWindow.setSession(mSession);

            mDisplayManager = (DisplayManager)getSystemService(Context.DISPLAY_SERVICE);
            mDisplayManager.registerDisplayListener(mDisplayListener, mCaller.getHandler());
            mDisplay = mDisplayManager.getDisplay(Display.DEFAULT_DISPLAY);

            if (DEBUG) Log.v(TAG, "onCreate(): " + this);
            onCreate(mSurfaceHolder);

            mInitializing = false;
            mReportedVisible = false;
            updateSurface(false, false, false);
        }

  getDefaultDisplaySize获取了屏幕的逻辑宽高。forgetLoadedWallpaper表示忘记原来的壁纸Bitmap,即取消一些变量对Bitmap的引用,防止Bitmap无法被回收造成内存泄露。updateWallpaperLocked用来获得壁纸对应的Bitmap及其宽高数据。我们在壁纸宽度和屏幕逻辑宽度,壁纸高度和屏幕逻辑高度中均选出一个较大值,作为Surface的宽高。
  壁纸的选取地方有(以下按查找顺序排列):1./data/system/users/{userid}/wallpaper;2.ro.config.wallpaper属性配置的壁纸路径;3.默认壁纸default_wallpaper.jpg,存在于/frameworks/base/core/res/res/drawable-xxx/default_wallpaper.jpg。

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

        void updateSurfaceSize(SurfaceHolder surfaceHolder) {
            Point p = getDefaultDisplaySize();

            // Load background image dimensions, if we haven't saved them yet
            if (mBackgroundWidth <= 0 || mBackgroundHeight <= 0) {
                // Need to load the image to get dimensions
                mWallpaperManager.forgetLoadedWallpaper();
                updateWallpaperLocked();
                if (mBackgroundWidth <= 0 || mBackgroundHeight <= 0) {
                    // Default to the display size if we can't find the dimensions
                    mBackgroundWidth = p.x;
                    mBackgroundHeight = p.y;
                }
            }

            // Force the wallpaper to cover the screen in both dimensions
            int surfaceWidth = Math.max(p.x, mBackgroundWidth);
            int surfaceHeight = Math.max(p.y, mBackgroundHeight);

            if (FIXED_SIZED_SURFACE) {
                // Used a fixed size surface, because we are special.  We can do
                // this because we know the current design of window animations doesn't
                // cause this to break.
                surfaceHolder.setFixedSize(surfaceWidth, surfaceHeight);
            } else {
                surfaceHolder.setSizeFromLayout();
            }
        }

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

        private void updateWallpaperLocked() {
            Throwable exception = null;
            try {
                Log.d(TAG,"choose a bitmap for wallpaper.");
                mBackground = null;
                mBackgroundWidth = -1;
                mBackgroundHeight = -1;
                mBackground = mWallpaperManager.getBitmap();
                mBackgroundWidth = mBackground.getWidth();
                mBackgroundHeight = mBackground.getHeight();
            } catch (RuntimeException e) {
                exception = e;
            } catch (OutOfMemoryError e) {
                exception = e;
            }
            Log.v(TAG,"mBackgroundWidth is "+mBackgroundWidth+",mBackgroundHeight is "+mBackgroundHeight);

            if (exception != null) {
                mBackground = null;
                mBackgroundWidth = -1;
                mBackgroundHeight = -1;
                // Note that if we do fail at this, and the default wallpaper can't
                // be loaded, we will go into a cycle.  Don't do a build where the
                // default wallpaper can't be loaded.
                Log.w(TAG, "Unable to load wallpaper!", exception);
                try {
                    mWallpaperManager.clear();
                } catch (IOException ex) {
                    // now we're really screwed.
                    Log.w(TAG, "Unable reset to default wallpaper!", ex);
                }
            }
        }

  当强制layout或Window未创建或Surface未创建或属性改变或Window属性改变或需要重绘时,updateWallpaperLocked分为以下几步:1.更新Engine的对应属性为Surface的属性,更新Window属性;2.当Window尚未添加到WMS时(mCreated为false),使用addToDisplay添加窗口;3.relayout,申请Surface保存在mSurfaceHolder.mSurface中;4.根据具体的属性变化进行BaseSurfaceHolder中CallBack的回调和DrawableEngine的回调。在ImageWallpaper的实现中,会在onVisibilityChanged(可视性改变),onOffsetsChanged(壁纸窗口发生偏移),onSurfaceChanged(Surface属性改变),onSurfaceRedrawNeeded(壁纸窗口需要重绘)中调用drawFrame函数进行绘图渲染。

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

        void updateSurface(boolean forceRelayout, boolean forceReport, boolean redrawNeeded) {
            if (mDestroyed) {
                Log.w(TAG, "Ignoring updateSurface: destroyed");
            }

            boolean fixedSize = false;
            int myWidth = mSurfaceHolder.getRequestedWidth();
            Log.v(TAG,"mRequestedWidth is "+myWidth);
            if (myWidth <= 0) myWidth = ViewGroup.LayoutParams.MATCH_PARENT;
            else fixedSize = true;
            int myHeight = mSurfaceHolder.getRequestedHeight();
            Log.v(TAG,"mRequestedHeight is "+myHeight);
            if (myHeight <= 0) myHeight = ViewGroup.LayoutParams.MATCH_PARENT;
            else fixedSize = true;

            final boolean creating = !mCreated;//Window是否需要添加
            final boolean surfaceCreating = !mSurfaceCreated;//Surface是否已得到
            final boolean formatChanged = mFormat != mSurfaceHolder.getRequestedFormat();//像素格式是否改变
            boolean sizeChanged = mWidth != myWidth || mHeight != myHeight;//Surafce大小是否改变
            boolean insetsChanged = !mCreated;//边衬区域是否改变
            final boolean typeChanged = mType != mSurfaceHolder.getRequestedType();//Surface buffer类型改变
            final boolean flagsChanged = mCurWindowFlags != mWindowFlags ||
                    mCurWindowPrivateFlags != mWindowPrivateFlags;//Window的Flags或PrivateFlags是否改变
            if (forceRelayout || creating || surfaceCreating || formatChanged || sizeChanged
                    || typeChanged || flagsChanged || redrawNeeded
                    || !mIWallpaperEngine.mShownReported) {

                if (DEBUG) Log.v(TAG, "Changes: creating=" + creating
                        + " format=" + formatChanged + " size=" + sizeChanged);

                try {
                    mWidth = myWidth;
                    mHeight = myHeight;
                    mFormat = mSurfaceHolder.getRequestedFormat();
                    mType = mSurfaceHolder.getRequestedType();
                    //初始化传给WindowManagerService的WindowManager.LayoutParams参数
                    mLayout.x = 0;
                    mLayout.y = 0;
                    mLayout.width = myWidth;
                    mLayout.height = myHeight;

                    mLayout.format = mFormat;

                    mCurWindowFlags = mWindowFlags;
                    mLayout.flags = mWindowFlags
                            | WindowManager.LayoutParams.FLAG_LAYOUT_NO_LIMITS
                            | WindowManager.LayoutParams.FLAG_LAYOUT_IN_SCREEN
                            | WindowManager.LayoutParams.FLAG_NOT_FOCUSABLE
                            ;
                    mCurWindowPrivateFlags = mWindowPrivateFlags;
                    mLayout.privateFlags = mWindowPrivateFlags;

                    mLayout.memoryType = mType;
                    mLayout.token = mWindowToken;

                    if (!mCreated) {
                        // Retrieve watch round and outset info
                        final WindowManager windowService = (WindowManager)getSystemService(
                                Context.WINDOW_SERVICE);
                        TypedArray windowStyle = obtainStyledAttributes(
                                com.android.internal.R.styleable.Window);
                        final Display display = windowService.getDefaultDisplay();
                        final boolean shouldUseBottomOutset =
                                display.getDisplayId() == Display.DEFAULT_DISPLAY;
                        if (shouldUseBottomOutset && windowStyle.hasValue(
                                R.styleable.Window_windowOutsetBottom)) {
                            if (mOutsetBottom == null) mOutsetBottom = new TypedValue();
                            windowStyle.getValue(R.styleable.Window_windowOutsetBottom,
                                    mOutsetBottom);
                        } else {
                            mOutsetBottom = null;
                        }
                        mWindowIsRound = getResources().getBoolean(
                                com.android.internal.R.bool.config_windowIsRound);
                        windowStyle.recycle();

                        // detect emulator
                        mIsEmulator = Build.HARDWARE.contains("goldfish");
                        mIsCircularEmulator = SystemProperties.getBoolean(
                                ViewRootImpl.PROPERTY_EMULATOR_CIRCULAR, false);

                        // Add window
                        mLayout.type = mIWallpaperEngine.mWindowType;
                        mLayout.gravity = Gravity.START|Gravity.TOP;
                        Log.v(TAG,"title is "+WallpaperService.this.getClass().getName());
                        mLayout.setTitle(WallpaperService.this.getClass().getName());
                        mLayout.windowAnimations =
                                com.android.internal.R.style.Animation_Wallpaper;
                        mInputChannel = new InputChannel();
                        if (mSession.addToDisplay(mWindow, mWindow.mSeq, mLayout, View.VISIBLE,
                            Display.DEFAULT_DISPLAY, mContentInsets, mStableInsets,
                                mInputChannel) < 0) {
                            Log.w(TAG, "Failed to add window while updating wallpaper surface.");
                            return;
                        }
                        mCreated = true;

                        mInputEventReceiver = new WallpaperInputEventReceiver(
                                mInputChannel, Looper.myLooper());
                    }
                    //上锁
                    mSurfaceHolder.mSurfaceLock.lock();
                    mDrawingAllowed = true;

                    if (!fixedSize) {
                    //宽高为非固定值的情况下,更新Surface边距(Surface与Window的边距)
                        mLayout.surfaceInsets.set(mIWallpaperEngine.mDisplayPadding);
                    } else {
                        //宽高为固定值的情况下,不设边距
                        mLayout.surfaceInsets.set(0, 0, 0, 0);
                    }
                    //relayout
                    final int relayoutResult = mSession.relayout(
                        mWindow, mWindow.mSeq, mLayout, mWidth, mHeight,
                            View.VISIBLE, 0, mWinFrame, mOverscanInsets, mContentInsets,
                            mVisibleInsets, mStableInsets, mConfiguration, mSurfaceHolder.mSurface);

                    if (DEBUG) Log.v(TAG, "New surface: " + mSurfaceHolder.mSurface
                            + ", frame=" + mWinFrame);

                    int w = mWinFrame.width();
                    int h = mWinFrame.height();

                    if (!fixedSize) {
                    //宽高非固定值的情况下,更新各种边距值
                        final Rect padding = mIWallpaperEngine.mDisplayPadding;
                        w += padding.left + padding.right;
                        h += padding.top + padding.bottom;
                        mOverscanInsets.left += padding.left;
                        mOverscanInsets.top += padding.top;
                        mOverscanInsets.right += padding.right;
                        mOverscanInsets.bottom += padding.bottom;
                        mContentInsets.left += padding.left;
                        mContentInsets.top += padding.top;
                        mContentInsets.right += padding.right;
                        mContentInsets.bottom += padding.bottom;
                        mStableInsets.left += padding.left;
                        mStableInsets.top += padding.top;
                        mStableInsets.right += padding.right;
                        mStableInsets.bottom += padding.bottom;
                    }

                    if (mCurWidth != w) {
                        sizeChanged = true;
                        mCurWidth = w;
                    }
                    if (mCurHeight != h) {
                        sizeChanged = true;
                        mCurHeight = h;
                    }

                    insetsChanged |= !mDispatchedOverscanInsets.equals(mOverscanInsets);
                    insetsChanged |= !mDispatchedContentInsets.equals(mContentInsets);
                    insetsChanged |= !mDispatchedStableInsets.equals(mStableInsets);

                    mSurfaceHolder.setSurfaceFrameSize(w, h);
                    mSurfaceHolder.mSurfaceLock.unlock();

                    if (!mSurfaceHolder.mSurface.isValid()) {
                        reportSurfaceDestroyed();
                        if (DEBUG) Log.v(TAG, "Layout: Surface destroyed");
                        return;
                    }

                    boolean didSurface = false;

                    try {
                        mSurfaceHolder.ungetCallbacks();

                        if (surfaceCreating) {
                            //Surface创建成功后的操作
                            mIsCreating = true;
                            didSurface = true;
                            if (DEBUG) Log.v(TAG, "onSurfaceCreated("
                                    + mSurfaceHolder + "): " + this);
                            onSurfaceCreated(mSurfaceHolder);
                            SurfaceHolder.Callback callbacks[] = mSurfaceHolder.getCallbacks();
                            if (callbacks != null) {
                                for (SurfaceHolder.Callback c : callbacks) {
                                    c.surfaceCreated(mSurfaceHolder);
                                }
                            }
                        }

                        redrawNeeded |= creating || (relayoutResult
                                & WindowManagerGlobal.RELAYOUT_RES_FIRST_TIME) != 0;

                        if (forceReport || creating || surfaceCreating
                                || formatChanged || sizeChanged) {
                            //Surface发生改变的操作
                            if (DEBUG) {
                                RuntimeException e = new RuntimeException();
                                e.fillInStackTrace();
                                Log.w(TAG, "forceReport=" + forceReport + " creating=" + creating
                                        + " formatChanged=" + formatChanged
                                        + " sizeChanged=" + sizeChanged, e);
                            }
                            if (DEBUG) Log.v(TAG, "onSurfaceChanged("
                                    + mSurfaceHolder + ", " + mFormat
                                    + ", " + mCurWidth + ", " + mCurHeight
                                    + "): " + this);
                            didSurface = true;
                            onSurfaceChanged(mSurfaceHolder, mFormat,
                                    mCurWidth, mCurHeight);
                            SurfaceHolder.Callback callbacks[] = mSurfaceHolder.getCallbacks();
                            if (callbacks != null) {
                                for (SurfaceHolder.Callback c : callbacks) {
                                    c.surfaceChanged(mSurfaceHolder, mFormat,
                                            mCurWidth, mCurHeight);
                                }
                            }
                        }

                        if (insetsChanged) {
                        //边距发生改变的操作
                            mDispatchedOverscanInsets.set(mOverscanInsets);
                            mDispatchedContentInsets.set(mContentInsets);
                            mDispatchedStableInsets.set(mStableInsets);
                            final boolean isRound = (mIsEmulator && mIsCircularEmulator)
                                    || mWindowIsRound;
                            mFinalSystemInsets.set(mDispatchedOverscanInsets);
                            mFinalStableInsets.set(mDispatchedStableInsets);
                            if (mOutsetBottom != null) {
                                final DisplayMetrics metrics = getResources().getDisplayMetrics();
                                mFinalSystemInsets.bottom =
                                        ( (int) mOutsetBottom.getDimension(metrics) )
                                        + mIWallpaperEngine.mDisplayPadding.bottom;
                            }
                            WindowInsets insets = new WindowInsets(mFinalSystemInsets,
                                    null, mFinalStableInsets, isRound);
                            onApplyWindowInsets(insets);
                        }

                        if (redrawNeeded) {
                            //需要重绘的操作
                            onSurfaceRedrawNeeded(mSurfaceHolder);
                            SurfaceHolder.Callback callbacks[] = mSurfaceHolder.getCallbacks();
                            if (callbacks != null) {
                                for (SurfaceHolder.Callback c : callbacks) {
                                    if (c instanceof SurfaceHolder.Callback2) {
                                        ((SurfaceHolder.Callback2)c).surfaceRedrawNeeded(
                                                mSurfaceHolder);
                                    }
                                }
                            }
                        }

                        if (didSurface && !mReportedVisible) {
                        //Surface发生了改变且壁纸尚不可见
                            // This wallpaper is currently invisible, but its
                            // surface has changed.  At this point let's tell it
                            // again that it is invisible in case the report about
                            // the surface caused it to start running.  We really
                            // don't want wallpapers running when not visible.
                            if (mIsCreating) {
                            //属于创建Surface的情况
                                // Some wallpapers will ignore this call if they
                                // had previously been told they were invisble,
                                // so if we are creating a new surface then toggle
                                // the state to get them to notice.
                                if (DEBUG) Log.v(TAG, "onVisibilityChanged(true) at surface: "
                                        + this);
                                onVisibilityChanged(true);
                            }
                            if (DEBUG) Log.v(TAG, "onVisibilityChanged(false) at surface: "
                                        + this);
                            onVisibilityChanged(false);
                        }

                    } finally {
                        mIsCreating = false;
                        mSurfaceCreated = true;
                        if (redrawNeeded) {
                            //需要重绘的情况,一般发生在首次relayout或者首次获得Surface,需要显示一个新的窗口
                            mSession.finishDrawing(mWindow);
                        }
                        mIWallpaperEngine.reportShown();
                    }
                } catch (RemoteException ex) {
                }
                if (DEBUG) Log.v(
                    TAG, "Layout: x=" + mLayout.x + " y=" + mLayout.y +
                    " w=" + mLayout.width + " h=" + mLayout.height);
            }
        }

 &esmp;drawFrame作为壁纸服务的绘图渲染函数。

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

        void drawFrame() {
            try {
                int newRotation = ((WindowManager) getSystemService(WINDOW_SERVICE)).
                        getDefaultDisplay().getRotation();

                // Sometimes a wallpaper is not large enough to cover the screen in one dimension.
                // Call updateSurfaceSize -- it will only actually do the update if the dimensions
                // should change
                if (newRotation != mLastRotation) {
                    // Update surface size (if necessary)
                    //如果屏幕的旋转发生了变化,更新SUrface的大小
                    updateSurfaceSize(getSurfaceHolder());
                }
                SurfaceHolder sh = getSurfaceHolder();
                final Rect frame = sh.getSurfaceFrame();
                final int dw = frame.width();
                final int dh = frame.height();
                //Surface中的Window的frame的宽和高对比上次保存的此值是否有变化
                boolean surfaceDimensionsChanged = dw != mLastSurfaceWidth
                        || dh != mLastSurfaceHeight;

                boolean redrawNeeded = surfaceDimensionsChanged || newRotation != mLastRotation;
                if (!redrawNeeded && !mOffsetsChanged) {
                    //当Surface中的Window的frame的宽和高没有变化&旋转状态没有变化&壁纸偏移没有变化,直接返回
                    if (DEBUG) {
                        Log.d(TAG, "Suppressed drawFrame since redraw is not needed "
                                + "and offsets have not changed.");
                    }
                    return;
                }
                mLastRotation = newRotation;

                // Load bitmap if it is not yet loaded or if it was loaded at a different size
                if (mBackground == null || surfaceDimensionsChanged) {
                    if (DEBUG) {
                        Log.d(TAG, "Reloading bitmap: mBackground, bgw, bgh, dw, dh = " +
                                mBackground + ", " +
                                ((mBackground == null) ? 0 : mBackground.getWidth()) + ", " +
                                ((mBackground == null) ? 0 : mBackground.getHeight()) + ", " +
                                dw + ", " + dh);
                    }
                    //没有壁纸图片或者当Surface中的Window的frame的宽和高有变化
                    mWallpaperManager.forgetLoadedWallpaper();
                    updateWallpaperLocked();
                    if (mBackground == null) {
                        if (DEBUG) {
                            Log.d(TAG, "Unable to load bitmap");
                        }
                        return;
                    }
                    if (DEBUG) {
                        if (dw != mBackground.getWidth() || dh != mBackground.getHeight()) {
                            Log.d(TAG, "Surface != bitmap dimensions: surface w/h, bitmap w/h: " +
                                    dw + ", " + dh + ", " + mBackground.getWidth() + ", " +
                                    mBackground.getHeight());
                        }
                    }
                }

                // Center the scaled image
                //mScale表示壁纸图片的拉伸比例
                mScale = Math.max(1f, Math.max(dw / (float) mBackground.getWidth(),
                        dh / (float) mBackground.getHeight()));
                final int availw = dw - (int) (mBackground.getWidth() * mScale);
                final int availh = dh - (int) (mBackground.getHeight() * mScale);
                int xPixels = availw / 2;
                int yPixels = availh / 2;

                // Adjust the image for xOffset/yOffset values. If window manager is handling offsets,
                // mXOffset and mYOffset are set to 0.5f by default and therefore xPixels and yPixels
                // will remain unchanged
                final int availwUnscaled = dw - mBackground.getWidth();
                final int availhUnscaled = dh - mBackground.getHeight();
                if (availwUnscaled < 0)
                    xPixels += (int) (availwUnscaled * (mXOffset - .5f) + .5f);
                if (availhUnscaled < 0)
                    yPixels += (int) (availhUnscaled * (mYOffset - .5f) + .5f);

                mOffsetsChanged = false;
                mRedrawNeeded = false;
                if (surfaceDimensionsChanged) {
                    mLastSurfaceWidth = dw;
                    mLastSurfaceHeight = dh;
                }
                if (!redrawNeeded && xPixels == mLastXTranslation && yPixels == mLastYTranslation) {
                    if (DEBUG) {
                        Log.d(TAG, "Suppressed drawFrame since the image has not "
                                + "actually moved an integral number of pixels.");
                    }
                    return;
                }
                mLastXTranslation = xPixels;
                mLastYTranslation = yPixels;

                if (DEBUG) {
                    Log.d(TAG, "Redrawing wallpaper");
                }

                if (mIsHwAccelerated) {
                    //一般情况下都启动硬件加速
                    if (!drawWallpaperWithOpenGL(sh, availw, availh, xPixels, yPixels)) {
                        //OpenGL渲染失败则使用canvas绘制
                        drawWallpaperWithCanvas(sh, availw, availh, xPixels, yPixels);
                    }
                } else {
                    drawWallpaperWithCanvas(sh, availw, availh, xPixels, yPixels);
                }
            } finally {
                if (FIXED_SIZED_SURFACE && !mIsHwAccelerated) {
                    // If the surface is fixed-size, we should only need to
                    // draw it once and then we'll let the window manager
                    // position it appropriately.  As such, we no longer needed
                    // the loaded bitmap.  Yay!
                    // hw-accelerated renderer retains bitmap for faster rotation
                    mBackground = null;
                    mWallpaperManager.forgetLoadedWallpaper();
                }
            }
        }

  关于壁纸偏移。首先关注的是mOffsetsChanged变量。在对壁纸的窗口进行relayout时,传入了一个继承自IWindow.Stub的BaseIWindow对象,里面实现了WMS对壁纸窗口进行调整后的回调。其中有resized(ContentInsets,VisibleInsets,Surface的大小改变或者Configuration改变时的回调),moved(窗口坐标改变的回调),dispatchAppVisibility(窗口可见性改变的回调),dispatchWallpaperOffsets(窗口偏移发生改变的回调)等。那mOffsetsChanged什么时候为true表示壁纸窗口偏移发生了改变呢?1.调用setDimensionHints来设置Engine的期望壁纸宽高时(没有设置则为传过来的WallpaperData记录的壁纸宽高值),因为壁纸窗口偏移的值需要根据Engine的期望壁纸宽高来决定,但是在SystemUI的ImageWallpaper的相应实现里没有利用到Engine的期望壁纸宽高。2.dispatchWallpaperOffsets回调来更新偏移值时。在Surface已创建的情况下,若壁纸窗口处于可见状态,onOffsetsChanged(ImageWallpaper的实现)会检查传入的xOffset和yOffset是否与mXOffset和mYOffset保存的值相同,若有一个不同,则认为是壁纸窗口偏移发生了变化,设置mOffsetsChanged为true。若壁纸窗口不处于可见状态,则直接设置mOffsetsChanged为true,而不必要更新mXOffset,mYOffset,更不需要调用drawFrame重画一帧,这些等到壁纸壁纸窗口可见以后再更新就可以了。3.dispatchAppVisibility回调更新壁纸窗口可见性时,如果在壁纸窗口不可见时有人申请了更新窗口偏移值(当时没更新mXOffset,mYOffset,只是设置mOffsetsChanged为true),则再壁纸窗口变得可见后再重新处理这些请求,见onOffsetsChanged的实现。

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

        void doOffsetsChanged(boolean always) {
            if (mDestroyed) {
                return;
            }

            if (!always && !mOffsetsChanged) {
                return;
            }

            float xOffset;
            float yOffset;
            float xOffsetStep;
            float yOffsetStep;
            boolean sync;
            synchronized (mLock) {
                xOffset = mPendingXOffset;
                yOffset = mPendingYOffset;
                xOffsetStep = mPendingXOffsetStep;
                yOffsetStep = mPendingYOffsetStep;
                sync = mPendingSync;
                mPendingSync = false;
                mOffsetMessageEnqueued = false;
            }

            if (mSurfaceCreated) {
                if (mReportedVisible) {
                    if (DEBUG) Log.v(TAG, "Offsets change in " + this
                            + ": " + xOffset + "," + yOffset);
                    final int availw = mIWallpaperEngine.mReqWidth-mCurWidth;
                    final int xPixels = availw > 0 ? -(int)(availw*xOffset+.5f) : 0;
                    final int availh = mIWallpaperEngine.mReqHeight-mCurHeight;
                    final int yPixels = availh > 0 ? -(int)(availh*yOffset+.5f) : 0;
                    onOffsetsChanged(xOffset, yOffset, xOffsetStep, yOffsetStep, xPixels, yPixels);
                } else {
                    mOffsetsChanged = true;
                }
            }

            if (sync) {
                try {
                    if (DEBUG) Log.v(TAG, "Reporting offsets change complete");
                    mSession.wallpaperOffsetsComplete(mWindow.asBinder());
                } catch (RemoteException e) {
                }
            }
        }

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

        @Override
        public void onOffsetsChanged(float xOffset, float yOffset,
                float xOffsetStep, float yOffsetStep,
                int xPixels, int yPixels) {
            if (DEBUG) {
                Log.d(TAG, "onOffsetsChanged: xOffset=" + xOffset + ", yOffset=" + yOffset
                        + ", xOffsetStep=" + xOffsetStep + ", yOffsetStep=" + yOffsetStep
                        + ", xPixels=" + xPixels + ", yPixels=" + yPixels);
            }

            if (mXOffset != xOffset || mYOffset != yOffset) {
                if (DEBUG) {
                    Log.d(TAG, "Offsets changed to (" + xOffset + "," + yOffset + ").");
                }
                mXOffset = xOffset;
                mYOffset = yOffset;
                mOffsetsChanged = true;
            }
            drawFrame();
        }

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

        void reportVisibility() {
            if (!mDestroyed) {
                boolean visible = mVisible
                        & mDisplay != null && mDisplay.getState() != Display.STATE_OFF;
                if (mReportedVisible != visible) {
                    mReportedVisible = visible;
                    if (DEBUG) Log.v(TAG, "onVisibilityChanged(" + visible
                            + "): " + this);
                    if (visible) {
                        // If becoming visible, in preview mode the surface
                        // may have been destroyed so now we need to make
                        // sure it is re-created.
                        doOffsetsChanged(false);
                        updateSurface(false, false, false);
                    }
                    onVisibilityChanged(visible);
                }
            }
        }

  但实际上壁纸的分辨率和屏幕的分辨率不相同,我们应该将壁纸画在哪个位置也是一个问题。有3种情形:1.壁纸可以完全包含屏幕;2.屏幕可以完全包含壁纸;3.壁纸和屏幕相交但不互相完全包含。
  对于壁纸可以完全包含屏幕的情况,就是根据mXOffset和mYOffset的值把屏幕的位置放在壁纸的适当位置,当mXOffset和mYOffset的值均为0.5时,屏幕放在壁纸的中央位置,壁纸坐标(left,top,right,bottom)为(-(屏幕宽-壁纸宽)/2,-(屏幕高-壁纸高)/2,(屏幕宽-壁纸宽)/2+壁纸宽,(屏幕高-壁纸高)/2+壁纸高),mXOffset的值从0到1增长时,会将壁纸往左边移动(假设屏幕位置不动),mYOffset的值从0到1增长时,会将壁纸往上边移动,也就是说,mXOffset和mYOffset的值越大,屏幕占用的区域就越接近壁纸的右下方;对于屏幕可以完全包含壁纸的情况,需要计算壁纸的拉伸值mScale(屏幕宽/壁纸宽,屏幕高/壁纸高中的最大值),屏幕宽高值分别减去壁纸的宽高乘以拉伸值得到的值得到availw和availh,这个时候availw和availh必然有一个为0,有一个为负数,则把壁纸放在负数对应的那条边的中间位置以屏幕边的中点位置进行以mScale值为比例的拉伸。对于壁纸和屏幕相交但不互相完全包含的情况,方法同屏幕可以完全包含壁纸的情况基本一致,都是先拉伸壁纸到恰好可以可以完全包含屏幕后,将壁纸贴合沿着拉伸后屏幕中长度还未和壁纸一致的边进行移动,直到屏幕恰好在在拉伸后壁纸中央。

你可能感兴趣的:(Framework)