当前位置: 首页 > article >正文

frameworks 之 WallpaperManagerService 壁纸管理

frameworks 之 WallpaperManagerService

  • 1. 启动服务
  • 2. 初始化
    • 2.1 构造方法
    • 2.2 onBootPhase
  • 3. 绑定对应的壁纸服务
  • 3. 1 绑定成功回调
  • 4. 壁纸的添加
  • 5. 壁纸的移除

讲解壁纸启动流程以及添加窗口和移除窗口时机
涉及到的类如下

  • frameworks/base/services/java/com/android/server/SystemServer.java
  • frameworks/base/services/core/java/com/android/server/wallpaper/WallpaperManagerService.java
  • frameworks/base/services/core/java/com/android/server/SystemServiceManager.java
  • frameworks/base/packages/SystemUI/src/com/android/systemui/ImageWallpaper.java
  • frameworks/base/core/java/android/service/wallpaper/WallpaperService.java

1. 启动服务

壁纸管理的启动来自 SystemServer

  1. 调用 startOtherServices 时候方法时候,会从资源中 获取判断 config_enableWallpaperService 的值 是否为true, 为ture才启动。
  2. 调用 SystemServiceManagerstartService 方法,并将要启动的类(WALLPAPER_SERVICE_CLASS)传递进入。
private static final String WALLPAPER_SERVICE_CLASS =
            "com.android.server.wallpaper.WallpaperManagerService$Lifecycle";
private void startOtherServices(@NonNull TimingsTraceAndSlog t) {
	...
			// 启动壁纸管理服务,判断是否要启动,默认为true
            // WALLPAPER_SERVICE_CLASS: com.android.server.wallpaper.WallpaperManagerService$Lifecycle
            if (context.getResources().getBoolean(R.bool.config_enableWallpaperService)) {
                t.traceBegin("StartWallpaperManagerService");
                mSystemServiceManager.startService(WALLPAPER_SERVICE_CLASS);
                t.traceEnd();
            } else {
                Slog.i(TAG, "Wallpaper service disabled by config");
            }
	...
}

startService 方法会基于反射构造对应的类,并调用其构造方法 创建该类。

	public SystemService startService(String className) {
        final Class<SystemService> serviceClass = loadClassFromLoader(className,
                this.getClass().getClassLoader());
        return startService(serviceClass);
    }
	public <T extends SystemService> T startService(Class<T> serviceClass) {
        try {
            final String name = serviceClass.getName();
            Slog.i(TAG, "Starting " + name);
            Trace.traceBegin(Trace.TRACE_TAG_SYSTEM_SERVER, "StartService " + name);

            // Create the service.
            if (!SystemService.class.isAssignableFrom(serviceClass)) {
                throw new RuntimeException("Failed to create " + name
                        + ": service must extend " + SystemService.class.getName());
            }
            final T service;
            try {
                Constructor<T> constructor = serviceClass.getConstructor(Context.class);
                service = constructor.newInstance(mContext);
            } 
            ...
            startService(service);
            return service;
        } finally {
            Trace.traceEnd(Trace.TRACE_TAG_SYSTEM_SERVER);
        }
    }

在将该类放到 mServices 数组中,调用其 onStart 方法。

	public void startService(@NonNull final SystemService service) {
        // Register it.
        mServices.add(service);
        // Start it.
        long time = SystemClock.elapsedRealtime();
        try {
            service.onStart();
        } catch (RuntimeException ex) {
            throw new RuntimeException("Failed to start service " + service.getClass().getName()
                    + ": onStart threw an exception", ex);
        }
        warnIfTooLong(SystemClock.elapsedRealtime() - time, service, "onStart");
    }

查看新构建的类 对应的 onStart 方法。

  1. 从 资源中 读取 config_wallpaperManagerServiceName 的值, 默认值为 com.android.server.wallpaper.WallpaperManagerService
  2. 调用类加载方法创建类 WallpaperManagerService,并调用其构造方法
  3. 通过 publishBinderService 方法,将该服务添加到 ServiceManger 中。

这样壁纸管理服务就启动了。

	public static class Lifecycle extends SystemService {
        private IWallpaperManagerService mService;

        public Lifecycle(Context context) {
            super(context);
        }

        @Override
        public void onStart() {
            try {
                // 从 SystemServiceManager 调用 startService调用
                final Class<? extends IWallpaperManagerService> klass =
                        (Class<? extends IWallpaperManagerService>)Class.forName(
                                getContext().getResources().getString(
                                        R.string.config_wallpaperManagerServiceName));
                mService = klass.getConstructor(Context.class).newInstance(getContext());
                publishBinderService(Context.WALLPAPER_SERVICE, mService);
            } catch (Exception exp) {
                Slog.wtf(TAG, "Failed to instantiate WallpaperManagerService", exp);
            }
        }
	}

2. 初始化

2.1 构造方法

  1. 从资源文件获取 image_wallpaper_component 的值,默认为 com.android.systemui/com.android.systemui.ImageWallpaper。会根据该值去绑定对应的服务。并保存到 mImageWallpaper 属性。
  2. 从 WallpaperManager 的 getDefaultWallpaperComponent 方法获取默认的壁纸值。保存到 mDefaultWallpaperComponent 属性。该方法实际是从系统属性读取ro.config.wallpaper_component 读取
  3. 初始化各种管理服务的类
  4. 创建 LocalService,通过 addService 方法添加到 LocalServices 中。
public WallpaperManagerService(Context context) {
        if (DEBUG) Slog.v(TAG, "WallpaperService startup");
        mContext = context;
        mShuttingDown = false;
        // 从 string 获取对应的壁纸容器服务 默认为com.android.systemui/com.android.systemui.ImageWallpaper
        mImageWallpaper = ComponentName.unflattenFromString(
                context.getResources().getString(R.string.image_wallpaper_component));
        // 从系统属性(ro.config.wallpaper_component)获取默认的 壁纸组件
        mDefaultWallpaperComponent = WallpaperManager.getDefaultWallpaperComponent(context);
        mWindowManagerInternal = LocalServices.getService(WindowManagerInternal.class);
        mIPackageManager = AppGlobals.getPackageManager();
        mAppOpsManager = (AppOpsManager) mContext.getSystemService(Context.APP_OPS_SERVICE);
        mDisplayManager = mContext.getSystemService(DisplayManager.class);
        mDisplayManager.registerDisplayListener(mDisplayListener, null /* handler */);
        mActivityManager = mContext.getSystemService(ActivityManager.class);
        mMonitor = new MyPackageMonitor();
        mColorsChangedListeners = new SparseArray<>();
        // 将对应的WallpaperManagerInternal实现类添加到 LocalServices中
        LocalServices.addService(WallpaperManagerInternal.class, new LocalService());
    }

2.2 onBootPhase

AMS 初始化好会触发 systemReady 方法。

	mActivityManagerService.systemReady(() -> {
            Slog.i(TAG, "Making services ready");
            t.traceBegin("StartActivityManagerReadyPhase");
            mSystemServiceManager.startBootPhase(t, SystemService.PHASE_ACTIVITY_MANAGER_READY);
            t.traceEnd();
	}

mSystemServiceManager 调用 startBootPhase 方法。会遍历对应 mServices 数组,并调用 onBootPhase

public void startBootPhase(@NonNull TimingsTraceAndSlog t, int phase) {
        if (phase <= mCurrentPhase) {
            throw new IllegalArgumentException("Next phase must be larger than previous");
        }
        mCurrentPhase = phase;

        Slog.i(TAG, "Starting phase " + mCurrentPhase);
        try {
            t.traceBegin("OnBootPhase_" + phase);
            // 初始化的服务都会放在 mServices 里,会调用的 onBootPhase
            final int serviceLen = mServices.size();
            for (int i = 0; i < serviceLen; i++) {
                final SystemService service = mServices.get(i);
                long time = SystemClock.elapsedRealtime();
                t.traceBegin("OnBootPhase_" + phase + "_" + service.getClass().getName());
                try {
                    service.onBootPhase(mCurrentPhase);
                } catch (Exception ex) {
                    throw new RuntimeException("Failed to boot service "
                            + service.getClass().getName()
                            + ": onBootPhase threw an exception during phase "
                            + mCurrentPhase, ex);
                }
                warnIfTooLong(SystemClock.elapsedRealtime() - time, service, "onBootPhase");
                t.traceEnd();
            }
        } finally {
            t.traceEnd();
        }

        if (phase == SystemService.PHASE_BOOT_COMPLETED) {
            final long totalBootTime = SystemClock.uptimeMillis() - mRuntimeStartUptime;
            t.logDuration("TotalBootTime", totalBootTime);
            SystemServerInitThreadPool.shutdown();
        }
    }

所以会调用 WallpaperManagerService 的 onBootPhase 方法

public static class Lifecycle extends SystemService {
	...
		@Override
        public void onBootPhase(int phase) {
            if (mService != null) {
                mService.onBootPhase(phase);
            }
        }
	...
}

接下来调用对应 systemReady 方法

  1. 调用 initialize 进行初始化壁纸数据, initialize 方法主要做了,
    1.1 对壁纸目录进行初始化
    1.2 调用 loadSettingsLocked 对数据进行初始化
    1.3 在一次调用 getWallpaperSafeLocked 获取系统当前用户的壁纸数据
void initialize() {
        mMonitor.register(mContext, null, UserHandle.ALL, true);
        //1. 获取系统当前用户的壁纸目录
        getWallpaperDir(UserHandle.USER_SYSTEM).mkdirs();

        // Initialize state from the persistent store, then guarantee that the
        // WallpaperData for the system imagery is instantiated & active, creating
        // it from defaults if necessary.
        // 加载本地数据
        loadSettingsLocked(UserHandle.USER_SYSTEM, false);
        // 在一次调用getWallpaperSafeLocked方法获取系统当前用户的壁纸数据
        getWallpaperSafeLocked(UserHandle.USER_SYSTEM, FLAG_SYSTEM);
    }

loadSettingsLocked 方法
1.1.1 获取需要解析的文件,路径为 data/system/users/0/wallpaper_info.xml,默认为空
1.1.2 调用chooseForRead,规则是 正式文件存在就选出正式文件,并删除临时文件;如果正式文件不存在就将临时文件重名为正式文件
1.1.3 根据用户id 从 mWallpaperMap 获取对应的数据,如果不存在则创建,并调用 initializeFallbackWallpaper 进行绑定(里面会调用 bindWallpaperComponentLocked)。
1.1.4 解析对应的 wallpaper_info.xml 文件。

private void loadSettingsLocked(int userId, boolean keepDimensionHints) {
        // 调用JournaledFile的chooseForRead方法获取接下来需要解析的文件,其实就是data/system/users/0/wallpaper_info.xml文件
        JournaledFile journal = makeJournaledFile(userId);
        FileInputStream stream = null;
        // 作用 正式文件存在就选出正式文件,并删除临时文件;如果正式文件不存在就将临时文件重名为正式文件
        File file = journal.chooseForRead();
        // 获取当前用户的壁纸数据
        WallpaperData wallpaper = mWallpaperMap.get(userId);
        // 如果为空则创建
        if (wallpaper == null) {
            // Do this once per boot
            migrateFromOld();
            // 参数意义 文件路径, 文件名称, 裁减文件
            wallpaper = new WallpaperData(userId, getWallpaperDir(userId),
                    WALLPAPER, WALLPAPER_CROP);
            wallpaper.allowBackup = true;
            mWallpaperMap.put(userId, wallpaper);
            if (!wallpaper.cropExists()) {
                if (wallpaper.sourceExists()) {
                    generateCrop(wallpaper);
                } else {
                    Slog.i(TAG, "No static wallpaper imagery; defaults will be shown");
                }
            }
            // 进行服务绑定
            initializeFallbackWallpaper();
        }
        boolean success = false;
        final DisplayData wpdData = getDisplayDataOrCreate(DEFAULT_DISPLAY);
        try {
            stream = new FileInputStream(file);
            // 开始解析 wallpaper_info.xml
            TypedXmlPullParser parser = Xml.resolvePullParser(stream);
            ...
    }
  1. 注册对应的监听 用户移除广播, 关机广播,注册用户切换
 void systemReady() {
        if (DEBUG) Slog.v(TAG, "systemReady");
        initialize();
        //获取系统当前用户的壁纸数据
        WallpaperData wallpaper = mWallpaperMap.get(UserHandle.USER_SYSTEM);
        // If we think we're going to be using the system image wallpaper imagery, make
        // sure we have something to render
        if (mImageWallpaper.equals(wallpaper.nextWallpaperComponent)) {
            // No crop file? Make sure we've finished the processing sequence if necessary
            if (!wallpaper.cropExists()) {
                if (DEBUG) {
                    Slog.i(TAG, "No crop; regenerating from source");
                }
                generateCrop(wallpaper);
            }
            // Still nothing?  Fall back to default.
            if (!wallpaper.cropExists()) {
                if (DEBUG) {
                    Slog.i(TAG, "Unable to regenerate crop; resetting");
                }
                clearWallpaperLocked(false, FLAG_SYSTEM, UserHandle.USER_SYSTEM, null);
            }
        } else {
            if (DEBUG) {
                Slog.i(TAG, "Nondefault wallpaper component; gracefully ignoring");
            }
        }
        // 注册广播接收者,监听用户移除事件
        IntentFilter userFilter = new IntentFilter();
        userFilter.addAction(Intent.ACTION_USER_REMOVED);
        mContext.registerReceiver(new BroadcastReceiver() {
            @Override
            public void onReceive(Context context, Intent intent) {
                final String action = intent.getAction();
                if (Intent.ACTION_USER_REMOVED.equals(action)) {
                    onRemoveUser(intent.getIntExtra(Intent.EXTRA_USER_HANDLE,
                            UserHandle.USER_NULL));
                }
            }
        }, userFilter);
        //注册广播接收者,监听关机广播
        final IntentFilter shutdownFilter = new IntentFilter(Intent.ACTION_SHUTDOWN);
        mContext.registerReceiver(new BroadcastReceiver() {
            @Override
            public void onReceive(Context context, Intent intent) {
                if (Intent.ACTION_SHUTDOWN.equals(intent.getAction())) {
                    if (DEBUG) {
                        Slog.i(TAG, "Shutting down");
                    }
                    synchronized (mLock) {
                        mShuttingDown = true;
                    }
                }
            }
        }, shutdownFilter);
        //为ActivityManagerService注册回调方法,监听用户切换事件
        try {
            ActivityManager.getService().registerUserSwitchObserver(
                    new UserSwitchObserver() {
                        @Override
                        public void onUserSwitching(int newUserId, IRemoteCallback reply) {
                            errorCheck(newUserId);
                            switchUser(newUserId, reply);
                        }
                    }, TAG);
        } catch (RemoteException e) {
            e.rethrowAsRuntimeException();
        }
    }

3. 绑定对应的壁纸服务

bindWallpaperComponentLocked 方法会绑定对应的壁纸服务

  1. 会优先判断 设置的 mDefaultWallpaperComponent,如果还没就直接获取对应的 mImageWallpaper
  2. 判断是否有 android.permission.BIND_WALLPAPER 权限
  3. 判断是否默认的systemUi壁纸,不是的话需要判断该服务是否壁纸服务,通过遍历所有的壁纸服务,然后判断是否与要用的服务有想等的存在。
  4. 判断是否有 android.Manifest.permission.AMBIENT_WALLPAPER 权限
  5. 创建对应的 WallpaperConnection的绑定监听,对应的也是AIDL IWallpaperConnection 的实现类。
  6. 调用 bindServiceAsUser 进行绑定
  7. 判断是否一致,调用 detachWallpaperLocked 移除上一次的壁纸。
boolean bindWallpaperComponentLocked(ComponentName componentName, boolean force,
            boolean fromUser, WallpaperData wallpaper, IRemoteCallback reply) {
        if (DEBUG_LIVE) {
            Slog.v(TAG, "bindWallpaperComponentLocked: componentName=" + componentName);
        }
        // Has the component changed?
        if (!force && changingToSame(componentName, wallpaper)) {
            return true;
        }

        try {
            // 会优先 设置的 mDefaultWallpaperComponent,如果还没就直接获取对应的 mImageWallpaper
            if (componentName == null) {
                componentName = mDefaultWallpaperComponent;
                if (componentName == null) {
                    // Fall back to static image wallpaper
                    componentName = mImageWallpaper;
                    //clearWallpaperComponentLocked();
                    //return;
                    if (DEBUG_LIVE) Slog.v(TAG, "No default component; using image wallpaper");
                }
            }
            int serviceUserId = wallpaper.userId;
            // 判断该壁纸服务是否存在
            ServiceInfo si = mIPackageManager.getServiceInfo(componentName,
                    PackageManager.GET_META_DATA | PackageManager.GET_PERMISSIONS, serviceUserId);
            if (si == null) {
                // The wallpaper component we're trying to use doesn't exist
                Slog.w(TAG, "Attempted wallpaper " + componentName + " is unavailable");
                return false;
            }
            if (!android.Manifest.permission.BIND_WALLPAPER.equals(si.permission)) {
                String msg = "Selected service does not have "
                        + android.Manifest.permission.BIND_WALLPAPER
                        + ": " + componentName;
                if (fromUser) {
                    throw new SecurityException(msg);
                }
                Slog.w(TAG, msg);
                return false;
            }

            WallpaperInfo wi = null;

            Intent intent = new Intent(WallpaperService.SERVICE_INTERFACE);
            // 判断是否默认的systemUi壁纸,不是的话需要判断该服务是否壁纸服务
            if (componentName != null && !componentName.equals(mImageWallpaper)) {
                // Make sure the selected service is actually a wallpaper service.
                List<ResolveInfo> ris =
                        mIPackageManager.queryIntentServices(intent,
                                intent.resolveTypeIfNeeded(mContext.getContentResolver()),
                                PackageManager.GET_META_DATA, serviceUserId).getList();
                for (int i=0; i<ris.size(); i++) {
                    ServiceInfo rsi = ris.get(i).serviceInfo;
                    if (rsi.name.equals(si.name) &&
                            rsi.packageName.equals(si.packageName)) {
                        try {
                            wi = new WallpaperInfo(mContext, ris.get(i));
                        } catch (XmlPullParserException e) {
                            if (fromUser) {
                                throw new IllegalArgumentException(e);
                            }
                            Slog.w(TAG, e);
                            return false;
                        } catch (IOException e) {
                            if (fromUser) {
                                throw new IllegalArgumentException(e);
                            }
                            Slog.w(TAG, e);
                            return false;
                        }
                        break;
                    }
                }
                if (wi == null) {
                    String msg = "Selected service is not a wallpaper: "
                            + componentName;
                    if (fromUser) {
                        throw new SecurityException(msg);
                    }
                    Slog.w(TAG, msg);
                    return false;
                }
            }
            // 再次判断权限
            if (wi != null && wi.supportsAmbientMode()) {
                final int hasPrivilege = mIPackageManager.checkPermission(
                        android.Manifest.permission.AMBIENT_WALLPAPER, wi.getPackageName(),
                        serviceUserId);
                if (hasPrivilege != PackageManager.PERMISSION_GRANTED) {
                    String msg = "Selected service does not have "
                            + android.Manifest.permission.AMBIENT_WALLPAPER
                            + ": " + componentName;
                    if (fromUser) {
                        throw new SecurityException(msg);
                    }
                    Slog.w(TAG, msg);
                    return false;
                }
            }

            // Bind the service!
            if (DEBUG) Slog.v(TAG, "Binding to:" + componentName);
            final int componentUid = mIPackageManager.getPackageUid(componentName.getPackageName(),
                    MATCH_DIRECT_BOOT_AUTO, wallpaper.userId);
            // 是 IWallpaperConnection.Stub 的实现类 服务端 继承ServiceConnection绑定监听
            WallpaperConnection newConn = new WallpaperConnection(wi, wallpaper, componentUid);
            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)),
                    PendingIntent.FLAG_IMMUTABLE, null, new UserHandle(serviceUserId)));
            if (!mContext.bindServiceAsUser(intent, newConn,
                    Context.BIND_AUTO_CREATE | Context.BIND_SHOWING_UI
                            | Context.BIND_FOREGROUND_SERVICE_WHILE_AWAKE
                            | Context.BIND_INCLUDE_CAPABILITIES,
                    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
                    && !wallpaper.equals(mFallbackWallpaper)) {
                detachWallpaperLocked(mLastWallpaper);
            }
            wallpaper.wallpaperComponent = componentName;
            wallpaper.connection = newConn;
            newConn.mReply = reply;
            if (wallpaper.userId == mCurrentUserId && !wallpaper.equals(mFallbackWallpaper)) {
                mLastWallpaper = wallpaper;
            }
            updateFallbackConnection();
        } 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;
    }

3. 1 绑定成功回调

回调绑定成功后,会调用 onServiceConnected

  1. 将对应的AIDL的 IWallpaperService 保存到 mService 变量。
  2. 调用 attachServiceLocked 对所有屏幕 进行attach操作
  3. 调用 saveSettingsLocked 方法,将对应的保存到 wallpaper_info.xml 文件
public void onServiceConnected(ComponentName name, IBinder service) {
            synchronized (mLock) {
                if (mWallpaper.connection == this) {
                    // 绑定成功,这是对应的 WallpaperService 服务类 AIDL
                    mService = IWallpaperService.Stub.asInterface(service);
                    // 遍历所有屏幕,进行attach操作
                    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.
                    if (!mWallpaper.equals(mFallbackWallpaper)) {
                        saveSettingsLocked(mWallpaper.userId);
                    }
                    FgThread.getHandler().removeCallbacks(mResetRunnable);
                    // 绑定成功移除对应重绑任务
                    mContext.getMainThreadHandler().removeCallbacks(mTryToRebindRunnable);
                }
            }
        }

4. 壁纸的添加

绑定服务成功后,会调用 attachServiceLocked 方法,该方法遍历后又会调用 connectLocked 方法

  1. 直接调用 WMS的 addWindowToken 方法,添加对应的token到层级树中也是在token的构造方法添加
  2. 调用 服务端的 attach 方法。该方法主要将 mToken 和 当前的 connection 也即是 IWallpaperConnection AIDL 实现方法,传递给绑定的壁纸服务,方便调用。
void connectLocked(WallpaperConnection connection, WallpaperData wallpaper) {
                if (connection.mService == null) {
                    Slog.w(TAG, "WallpaperService is not connected yet");
                    return;
                }
                if (DEBUG) Slog.v(TAG, "Adding window token: " + mToken);
                // 直接调用 WMS的 addWindowToken 方法构造新的token添加到层级树
                mWindowManagerInternal.addWindowToken(mToken, TYPE_WALLPAPER, mDisplayId,
                        null /* options */);
                final DisplayData wpdData = getDisplayDataOrCreate(mDisplayId);
                try {
                    connection.mService.attach(connection, mToken, TYPE_WALLPAPER, false,
                            wpdData.mWidth, wpdData.mHeight,
                            wpdData.mPadding, mDisplayId);
                }
            }

查看attach 是实现方法,因为默认绑定的为 ImageWallpaper 服务,该服务继承 WallpaperService。所以 attach 要在 WallpaperService类查看。
IWallpaperServiceWrapper 是对应的 AILD实现。
该方法构造了 IWallpaperEngineWrapper 对象 并接收传过来的对应参数。

		@Override
        public void attach(IWallpaperConnection conn, IBinder windowToken,
                int windowType, boolean isPreview, int reqWidth, int reqHeight, Rect padding,
                int displayId) {
            // 创建了对应的IWallpaperEngineWrapper windowToken是从WallpaperManagerService给到的
            // 到时候会根据ibinder 从WMS的tokenMap拿出 windowToken
            mEngineWrapper = new IWallpaperEngineWrapper(mTarget, conn, windowToken,
                    windowType, isPreview, reqWidth, reqHeight, padding, displayId);
        }

构造方法里

  1. 将传过来的 壁纸管理AIDL对象保存到 mConnection, 将token 保存到 mWindowToken,并将其他参数都保存到成员变量
  2. 发送 DO_ATTACH 消息进行壁纸加载
IWallpaperEngineWrapper(WallpaperService context,
                IWallpaperConnection conn, IBinder windowToken,
                int windowType, boolean isPreview, int reqWidth, int reqHeight, Rect padding,
                int displayId) {
            mWallpaperManager = getSystemService(WallpaperManager.class);
            mCaller = new HandlerCaller(context, context.getMainLooper(), this, true);
            mConnection = conn;
            mWindowToken = windowToken;
            mWindowType = windowType;
            ...
            if (mDisplay == null) {
                // Ignore this engine.
                throw new IllegalArgumentException("Cannot find display with id" + mDisplayId);
            }
            Message msg = mCaller.obtainMessage(DO_ATTACH);
            mCaller.sendMessage(msg);
        }

收到 DO_ATTACH 消息 主要做了

  1. 创建对应子类的 Engine,ImageWallpaper 创建的是 GLEngine
  2. 通过 mConnection 调用 attachEngine,执行 WallpaperManagerService 的方法将对应的渲染引起给对应的屏幕管理。
  3. 调用 attach 方法进行 窗口加载
				case DO_ATTACH: {
                    // 拉起的是imageWallpaper 所以是GlEngine
                    Engine engine = onCreateEngine();
                    mEngine = engine;
                    try {
                        mConnection.attachEngine(this, mDisplayId);
                    } catch (RemoteException e) {
                        engine.detach();
                        Log.w(TAG, "Wallpaper host disappeared", e);
                        return;
                    }
                    mActiveEngines.add(engine);
                    // 执行attach方法进行壁纸展示
                    engine.attach(this);
                    return;
                }

attach 方法

  1. 初始化参数,将传过来对应的参数放到对应的Engine 里
  2. 调用 updateSurface 开始加载窗口
		void attach(IWallpaperEngineWrapper wrapper) {
			...
            mIWallpaperEngine = wrapper;
            mCaller = wrapper.mCaller;
            mConnection = wrapper.mConnection;
            mWindowToken = wrapper.mWindowToken;
            mSurfaceHolder.setSizeFromLayout();
            mInitializing = true;
            mSession = WindowManagerGlobal.getWindowSession();

            mWindow.setSession(mSession);
			...
            updateSurface(false, false, false);
        }

updateSurface 方法

  1. 填充对应 layoutParame 参数,并将token赋值给 token layoutParams的 token属性。
  2. 调用 mSession.addToDisplay,创建对应的windowState和之前文章的WMS一样。
  3. 创建 WallpaperInputEventReceiver 接收输入事件。
  4. 调用 mSession.relayout 创建对应的 surfaceControl 到 mSurfaceControl
  5. 判断 mSurfaceControl 是否可用,可用创建 BbqSurfaceControl 挂载到 mSurfaceControl
  6. 最后调用 mSession.finishDrawing 进行展示
void updateSurface(boolean forceRelayout, boolean forceReport, boolean redrawNeeded) {
            ...
                // 填充对应layoutParame参数
                try {
                    ...
                    mLayout.flags = mWindowFlags
                            | WindowManager.LayoutParams.FLAG_LAYOUT_NO_LIMITS
                            | WindowManager.LayoutParams.FLAG_LAYOUT_INSET_DECOR
                            | WindowManager.LayoutParams.FLAG_LAYOUT_IN_SCREEN
                            | WindowManager.LayoutParams.FLAG_NOT_FOCUSABLE;
                    mCurWindowPrivateFlags = mWindowPrivateFlags;
                    mLayout.privateFlags = mWindowPrivateFlags;

                    mLayout.memoryType = mType;
                    // 设置对应的token
                    mLayout.token = mWindowToken;

                    if (!mCreated) {
                        // Retrieve watch round info
                        TypedArray windowStyle = obtainStyledAttributes(
                                com.android.internal.R.styleable.Window);
                        windowStyle.recycle();

                        // Add window
                        mLayout.type = mIWallpaperEngine.mWindowType;
                        mLayout.gravity = Gravity.START|Gravity.TOP;
                        mLayout.setFitInsetsTypes(0 /* types */);
                        mLayout.setTitle(WallpaperService.this.getClass().getName());
                        mLayout.windowAnimations =
                                com.android.internal.R.style.Animation_Wallpaper;
                        InputChannel inputChannel = new InputChannel();
                        // 添加windowState到对应的界面,通过token查找
                        if (mSession.addToDisplay(mWindow, mLayout, View.VISIBLE,
                                mDisplay.getDisplayId(), mRequestedVisibilities, inputChannel,
                                mInsetsState, mTempControls) < 0) {
                            Log.w(TAG, "Failed to add window while updating wallpaper surface.");
                            return;
                        }
                        mSession.setShouldZoomOutWallpaper(mWindow, shouldZoomOutWallpaper());
                        mCreated = true;
                        // 设置输入
                        mInputEventReceiver = new WallpaperInputEventReceiver(
                                inputChannel, Looper.myLooper());
                    }
                  	...
                    // 调用relayout 创建 surfaceControl 到 mSurfaceControl
                    final int relayoutResult = mSession.relayout(
                            mWindow, mLayout, mWidth, mHeight,
                            View.VISIBLE, 0, -1, mWinFrames, mMergedConfiguration, mSurfaceControl,
                            mInsetsState, mTempControls, mSurfaceSize);
                    if (mSurfaceControl.isValid()) {
                        // 添加 BbqSurfaceControl 到 mSurfaceControl 下
                        if (mBbqSurfaceControl == null) {
                            mBbqSurfaceControl = new SurfaceControl.Builder()
                                    .setName("Wallpaper BBQ wrapper")
                                    .setHidden(false)
                                    // TODO(b/192291754)
                                    .setMetadata(METADATA_WINDOW_TYPE, TYPE_WALLPAPER)
                                    .setBLASTLayer() // 设置不是容器
                                    .setParent(mSurfaceControl)
                                    .setCallsite("Wallpaper#relayout")
                                    .build();
                            updateSurfaceDimming();
                        }
                        ...
                    try {
                        
                    } finally {
                        mIsCreating = false;
                        mSurfaceCreated = true;
                        if (redrawNeeded) {
                            resetWindowPages();
                            // 最后进行展示
                            mSession.finishDrawing(mWindow, null /* postDrawTransaction */);
                            processLocalColors(mPendingXOffset, mPendingXOffsetStep);
                        }
                        reposition();
                        reportEngineShown(shouldWaitForEngineShown());
                    }
                }
            }
        }

5. 壁纸的移除

移除是在WallpaperManagerService 绑定壁纸服务的成功时候,会调用 detachWallpaperLocked 方法

  1. 移除对应 壁纸服务的 detach 方法,该方法会移除对应的windowState 已经释放对应的 SurfaceContacl
  2. 遍历并调用对应的 disconnectLocked 方法,该方法会调用 mWindowManagerInternal。removeWindowToken 移除对应的token,并调用引擎的 destroy 方法。
private void detachWallpaperLocked(WallpaperData wallpaper) {
        if (wallpaper.connection != null) {
        ...
            try {
                // It can be null if user switching happens before service connection.
                // 移除对应的壁纸
                if (wallpaper.connection.mService != null) {
                    wallpaper.connection.mService.detach();
                }
            } catch (RemoteException e) {
                Slog.w(TAG, "Failed detaching wallpaper service ", e);
            }
            mContext.unbindService(wallpaper.connection);
            wallpaper.connection.forEachDisplayConnector(
                    WallpaperConnection.DisplayConnector::disconnectLocked);
            ...
        }
    }


			void disconnectLocked() {
                if (DEBUG) Slog.v(TAG, "Removing window token: " + mToken);
                mWindowManagerInternal.removeWindowToken(mToken, false/* removeWindows */,
                        mDisplayId);
                try {
                    if (mEngine != null) {
                        mEngine.destroy();
                    }
                } catch (RemoteException e) {
                }
                mEngine = null;
            }

http://www.kler.cn/a/468701.html

相关文章:

  • SSL,TLS协议分析
  • HashMap总结使用+原理+面试
  • 25/1/6 算法笔记<强化学习> 初玩V-REP
  • 【视觉SLAM:六、视觉里程计Ⅰ:特征点法】
  • 【数据可视化-11】全国大学数据可视化分析
  • 《探秘计算机视觉与深度学习:开启智能视觉新时代》
  • spring mvc源码学习笔记之三
  • c语言的文件操作与文件缓冲区
  • 抖音生活服务2024年覆盖线下商家门店超610万,交易额同比提升81%
  • LeetCode 0732.我的日程安排表 III:线段树
  • [GCC]代码演示-Wl,-rpath-link链接时库搜索路径
  • 力扣hot100——动态规划 多维动态规划
  • 手动安装 Maven 依赖到本地仓库
  • Nginx:限流限速
  • 美食烹饪互动平台
  • 深入理解静态库与动态库
  • Go语言的 的并发编程(Concurrency)核心知识
  • PTA6-18 数字校验
  • MySQL和Hive中的行转列、列转行
  • Nginx——负载均衡与缓存(四/五)
  • 【开源免费】基于SpringBoot+Vue.JS海滨学院班级回忆录系统(JAVA毕业设计)
  • WIN10系统查看连接的无线网密码
  • 【微信小程序获取用户手机号
  • C++23 格式化输出新特性详解: std::print 和 std::println
  • 小E君自助餐厅流量分析
  • UOS 系统 Qt 版本切换