|
文章记录自己的学习过程,供日后参考。 第一部分:通信通道的建立
首先要知道壁纸设置的大体流程:上层应用调用wallpaperManager.setStream()或其他接口进行设置壁纸,WallpaperManagerService首先将壁纸copy到/data/system/user/0/目录下,WallpaperManagerService对该目录注册了一个WallpaperObserver,拷贝壁纸成功后会触发调用WallpaperObserver.onEvent()函数,onEvent()函数首先调用notifyCallbacksLocked()触发回调,然后再调用bindWallpaperComponentLocked()做进行进一步的壁纸更换。虽然整个壁纸机制有WallpaperManager、WallpaperService、ImageWallpaper、WallpaperManagerService、Globals、DrawableEngine、WallpaperData、WallpaperConnection等数据结构,感觉挺复杂的,我们应该剥离他的外表,看清他的本质。我们只要搞清了他们之间怎么交互的?交流些什么东西?就搞清楚了整个壁纸机制,说白了只要搞清楚IWallpaperService.aidl、IWallpaperEngine.aidl、IWallpaperConnection.aidl这三个文件就OK了,因为这三个文件是通信的关键所在。下面这张图代表了WallpaperService与WallpaperManagerService的交互关系。 1.IWallpaperService.aidl
[html] view plain copy
 
- oneway interface IWallpaperService {
- void attach(IWallpaperConnection connection,
- IBinder windowToken, int windowType, boolean isPreview,
- int reqWidth, int reqHeight);
- }
aidl语言专为Binder设计,因此里面的函数可看做是通信接口。
(1).WallpaperService.IWallpaperServiceWrapper extends IWallpaperService.Stub; (2).WallpaperService.onBind()
[html] view plain copy
 
- /**
- * Implement to return the implementation of the internal accessibility
- * service interface. Subclasses should not override.
- */
- @Override
- public final IBinder onBind(Intent intent) {
- return new IWallpaperServiceWrapper(this);
- }
从上面两点可以知道:IWallpaperService的Binder本地端在WallpaperService中;WallpaperService服务将是调用mContext.bindService()启动的;IWallpaperService的代理对象将在ServiceConnection.onServiceConnected()中获取。很容易验证WallPaperManagerService中确实是这么干的。
WallPaperManagerService.bindWallpaperComponentLocked():
[html] view plain copy
 
- boolean bindWallpaperComponentLocked(ComponentName componentName, boolean force,
- boolean fromUser, WallpaperData wallpaper, IRemoteCallback reply) {
- ........
- Intent intent = new Intent(WallpaperService.SERVICE_INTERFACE);
- ........
- 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))) {
- ..........
- }
- }
文章最开头就说了,每次设置壁纸都会调用bindWallpaperComponentLocked()函数,也就是说每次设置壁纸都会重新new WallpaperConnection,重新调用mContext.bindServiceAsUser()绑定启动WallpaperService,为啥要每次重新bind一次呢?防止systemUI挂掉,无法设置壁纸?不管了,反正记住每次设置壁纸时都会bind一次WallpaperService,并且调用WallpaperConnection.onServiceConnected()函数。onServiceConnected()函数中真的获取了IWallpaperService的Binder代理对象。
[html] view plain copy
 
- public void onServiceConnected(ComponentName name, IBinder service) {
- synchronized (mLock) {
- if (mWallpaper.connection == this) {
- mWallpaper.lastDiedTime = SystemClock.uptimeMillis();
- 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);
- }
- }
- }
mService = IWallpaperService.Stub.asInterface(service);语句获取了IWallpaperService的Binder代理对象。再看看WallpaperConnection.onServiceConnected()函数中调用WallPaperManagerService.attachServiceLocked()干什么?
[html] view plain copy
 
- void attachServiceLocked(WallpaperConnection conn, WallpaperData wallpaper) {
- try {
- conn.mService.attach(conn, conn.mToken,
- WindowManager.LayoutParams.TYPE_WALLPAPER, false,
- wallpaper.width, wallpaper.height);
- } catch (RemoteException e) {
- Slog.w(TAG, "Failed attaching wallpaper; clearing", e);
- if (!wallpaper.wallpaperUpdating) {
- bindWallpaperComponentLocked(null, false, false, wallpaper, null);
- }
- }
- }
嗯,跨Binder调用IWallpaperService本地端的attach()。看看attach()做了什么?
[html] view plain copy
 
- public void attach(IWallpaperConnection conn, IBinder windowToken,
- int windowType, boolean isPreview, int reqWidth, int reqHeight) {
- new IWallpaperEngineWrapper(mTarget, conn, windowToken,
- windowType, isPreview, reqWidth, reqHeight);
- }
直接new IWallpaperEngineWrapper对象,注意传进来的参数,包含壁纸显示参数。IWallpaperEngineWrapper extends IWallpaperEngine.Stub implements HandlerCaller.Callback,所以又知道了IWallpaperEngine的Binder本地端保存在WallpaperService中,代理端返回给WallPaperManagerService.WallpaperConnection.mEngine变量的呢?这个研究到后边应该自然就知道了。IWallpaperEngineWrapper的构造函数将被调用,在构造函数中会post一个handle消息DO_ATTACH,然后消息在executeMessage()中执行: [html] view plain copy
 
- 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;
- }
感觉牵涉的越来越多了,没关系,一个个来分析。mConnection的值从attach()函数的参数获取,很容易知道就是IWallpaperConnection的Binder代理对象,本地对象就是WallpaperConnection,在WallpaperManagerService中。上面代码调用mConnection.attachEngine(this), this指向IWallpaperEngineWrapper,说白了就是把IWallpaperEngine的Binder代理对象传给WallpaperManagerService,至此,WallpaperManagerService与WallpaperService的三条Binder通信通道全部建立起来了。 总结:设置壁纸时,触发调用WallpaperManagerService.bindWallpaperComponentLocked(),函数会bindService(WallpaperService),在bind过程中WallpaperService返回一个IWallpaperService的代理对象给WallpaperManagerService,WallpaperManagerService侧接着调用IWallpaperService.attach()函数将WallpaperManagerService侧的IWallpaperConnection代理对象传给WallpaperService,WallpaperService侧同时也会调用刚传过来的IWallpaperConnection.attachEngine()将IWallpaperEngine的代理对象传过去。嗯,有点乱。 从上面的图中,看出现在attach()、attachEngine()两个函数光荣地完成了使命。下面来分析engineShown()、setDesiredSize()、setVisibility()、dispatchPointer()、dispatchWallpaperCommand()五个函数。 1.engineShown() 先看下这个函数的实现: [html] view plain copy
 
- public void engineShown(IWallpaperEngine engine) {
- synchronized (mLock) {
- if (mReply != null) {
- long ident = Binder.clearCallingIdentity();
- try {
- mReply.sendResult(null);
- } catch (RemoteException e) {
- Binder.restoreCallingIdentity(ident);
- }
- mReply = null;
- }
- }
- }
mReply是从bindWallpaperComponentLocked()函数的参数赋值的,是个null。所以这个函数基本没干什么,但是可以通过名字知道,WallpaperService这边将壁纸显示成功后会调用这个函数。那就研究下在哪里调用engineShown(),这样可以反推显示等逻辑所在的地方。嗯,engineShown()在IWallpaperEngineWrapper.reportShown()中调用。
[html] view plain copy
 
- public void reportShown() {
- if (!mShownReported) {
- mShownReported = true;
- try {
- mConnection.engineShown(this);
- } catch (RemoteException e) {
- Log.w(TAG, "Wallpaper host disappeared", e);
- return;
- }
- }
- }
再看看reportShown()在哪调用,发现在updateSurface()中调用,浏览下updateSurface()函数,可以肯定壁纸刷新就在updateSurface()中完成。哈哈,下面来研究这个updateSurface()函数。
这个函数在很多地方调用,可以看做是壁纸刷新函数。 [html] view plain copy
 
- void updateSurface(boolean forceRelayout, boolean forceReport, boolean redrawNeeded) {
- if (mDestroyed) {
- Log.w(TAG, "Ignoring updateSurface: destroyed");
- }
-
- int myWidth = mSurfaceHolder.getRequestedWidth();
- if (myWidth <= 0) myWidth = ViewGroup.LayoutParams.MATCH_PARENT;
- int myHeight = mSurfaceHolder.getRequestedHeight();
- if (myHeight <= 0) myHeight = ViewGroup.LayoutParams.MATCH_PARENT;
-
- final boolean creating = !mCreated;
- final boolean surfaceCreating = !mSurfaceCreated;
- final boolean formatChanged = mFormat != mSurfaceHolder.getRequestedFormat();
- boolean sizeChanged = mWidth != myWidth || mHeight != myHeight;
- final boolean typeChanged = mType != mSurfaceHolder.getRequestedType();
- final boolean flagsChanged = mCurWindowFlags != mWindowFlags ||
- mCurWindowPrivateFlags != mWindowPrivateFlags;
- 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();
-
- 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) { //创建输入通道、addToDisplay()、创建一个触摸事件接受者;
- mLayout.type = mIWallpaperEngine.mWindowType;
- mLayout.gravity = Gravity.START|Gravity.TOP;
- 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, 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;
-
- final int relayoutResult = mSession.relayout( //relayout();
- mWindow, mWindow.mSeq, mLayout, mWidth, mHeight,
- View.VISIBLE, 0, mWinFrame, mOverscanInsets, mContentInsets,
- mVisibleInsets, mConfiguration, mSurfaceHolder.mSurface);
-
- if (DEBUG) Log.v(TAG, "New surface: " + mSurfaceHolder.mSurface
- + ", frame=" + mWinFrame);
-
- int w = mWinFrame.width();
- if (mCurWidth != w) {
- sizeChanged = true;
- mCurWidth = w;
- }
- int h = mWinFrame.height();
- if (mCurHeight != h) {
- sizeChanged = true;
- mCurHeight = h;
- }
-
- mSurfaceHolder.setSurfaceFrameSize(w, h); //设置surface Frame Size;
- 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) {
- mIsCreating = true;
- didSurface = true;
- if (DEBUG) Log.v(TAG, "onSurfaceCreated("
- + mSurfaceHolder + "): " + this);
- onSurfaceCreated(mSurfaceHolder); //创建surface;
- 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) {
- 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 (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) {
- // 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) {
- // 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 surface
- // 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) {
- 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);
- }
- }
2.setDesiredSize() 没啥用的一个函数。 3.setVisibility() 没看到有调用。 4.dispatchPointer() 接受触摸事件。动态壁纸需要接受触摸事件。 第二部分:设置一张壁纸WallpaperService中的调用逻辑 1.DO_DETACH 从WallpaperManagerService.detachWallpaperLocked()-->IWallpaperEngineWrapper.destroy()-->Engine.detach() a.mSession.remove(mWindow);---------------------移除壁纸窗口; b.mSurfaceHolder.mSurface.release();------------释放surface; c.mInputChannel.dispose();--------------------------关闭输入通道;
主要做了三件事:remove窗口;释放surface;关闭输入通道; 2.DO_ATTACH 从WallpaperManagerService.attachServiceLocked()-->IWallpaperService.attach()
a.mSurfaceHolder.setSizeFromLayout();--------------------------------重置surface的尺寸为(-1,-1); b.mSession = WindowManagerGlobal.getWindowSession();-------获取WindowSession;
c.registerReceiver(mReceiver, filter);--------------------------------------注册ACTION_SCREEN_ON和ACTION_SCREEN_OFF广播接收器; d.Engine.onCreate(mSurfaceHolder);-->updateSurfaceSize(): 1).mWallpaperManager.forgetLoadedWallpaper();------------------清除mWallpaper和mDefaultWallpaper值; 2).updateWallpaperLocked();-->mBackground = mWallpaperManager.getBitmap();-----------获取壁纸赋给mBackground; 3).surfaceHolder.setFixedSize(surfaceWidth, surfaceHeight);----从mBackground中取值来修正surface的大小;
e.updateSurface(false, false, false);----------------------------------------因为前面清除了surface,所以这个函数肯定会重新new Surface;
1). mInputChannel = new InputChannel(); 2).mSession.addToDisplay(mWindow, mWindow.mSeq, mLayout, View.VISIBLE,Display.DEFAULT_DISPLAY, mContentInsets, mInputChannel); 3).mInputEventReceiver = new WallpaperInputEventReceiver(mInputChannel, Looper.myLooper()); 4).mSession.relayout(mWindow, mWindow.mSeq, mLayout, mWidth, mHeight,View.VISIBLE, 0, mWinFrame, mOverscanInsets, mContentInsets, mVisibleInsets, mConfiguration, mSurfaceHolder.mSurface); 5).mSurfaceHolder.setSurfaceFrameSize(w, h); 6).onSurfaceCreated(mSurfaceHolder); 7).onSurfaceChanged(mSurfaceHolder, mFormat, mCurWidth, mCurHeight); drawFrame(); 8).onSurfaceRedrawNeeded(mSurfaceHolder); drawFrame(); 9)onVisibilityChanged(true); drawFrame(); 3.MSG_UPDATE_SURFACE a.mEngine.updateSurface(true, false, false) 1).mSession.relayout(mWindow, mWindow.mSeq, mLayout, mWidth, mHeight,View.VISIBLE, 0, mWinFrame, mOverscanInsets, mContentInsets, mVisibleInsets, mConfiguration, mSurfaceHolder.mSurface); 2).mSurfaceHolder.setSurfaceFrameSize(w, h); 4.MSG_WINDOW_RESIZED a.mEngine.updateSurface(true, false, reportDraw); 1).mSession.relayout(mWindow, mWindow.mSeq, mLayout, mWidth, mHeight,View.VISIBLE, 0, mWinFrame, mOverscanInsets, mContentInsets, mVisibleInsets, mConfiguration, mSurfaceHolder.mSurface); 2).mSurfaceHolder.setSurfaceFrameSize(w, h); 3).onSurfaceRedrawNeeded(mSurfaceHolder); 4)mSession.finishDrawing(mWindow);
b.mEngine.doOffsetsChanged(true);----------没做什么;
总结:从上面的逻辑看,壁纸这块逻辑代码写的比较乱。
|