SystemUI-Plugin源码分析

0.前情提要

SystemUIApplication.java

1.SystemUIApplication的startServicesIfNeeded函数

public void startServicesIfNeeded() {
    String[] names =getResources().getStringArray(R.array.config_systemUIServiceComponents);
    startServicesIfNeeded(names);
}

2.在startServicesIfNeeded中先是启动了一堆组件,然后添加了Plugin的监听

private void startServicesIfNeeded(String[] services) {
     //启动了一堆组件
     
     //获取PluginManagerImpl对象,并添加插件监听
     Dependency.get(PluginManager.class).addPluginListener(
             new PluginListener<OverlayPlugin>() {
                 private ArraySet<OverlayPlugin> mOverlays;

                 @Override
                 public void onPluginConnected(OverlayPlugin plugin, Context pluginContext) {
                     //onPluginConnected连接成功以后,拿到对象
                     StatusBar statusBar = getComponent(StatusBar.class);
                     if (statusBar != null) {
                         //调用plugin的setup事件
                         plugin.setup(statusBar.getStatusBarWindow(),
                                 statusBar.getNavigationBarView());
                     }
                     // Lazy init.
                     if (mOverlays == null) mOverlays = new ArraySet<>();
                     if (plugin.holdStatusBarOpen()) {
                         mOverlays.add(plugin);
                         Dependency.get(StatusBarWindowManager.class).setStateListener(b ->
                                 mOverlays.forEach(o -> o.setCollapseDesired(b)));
                         Dependency.get(StatusBarWindowManager.class).setForcePluginOpen(
                                 mOverlays.size() != 0);

                     }
                 }

                 @Override
                 public void onPluginDisconnected(OverlayPlugin plugin) {
                     mOverlays.remove(plugin);
                     Dependency.get(StatusBarWindowManager.class).setForcePluginOpen(
                             mOverlays.size() != 0);
                 }
             }, OverlayPlugin.class, true /* Allow multiple plugins */);

     mServicesStarted = true;
 }

PluginManagerIml.java

3.这里通过Dependency拿PluginManager.class对应的类就是PluginManagerIml.java,可以看到它调用的是重载方法2,最终会调用的是重载方法4,其实对应的action=PluginManager.getAction(PluginManager.class),

public class PluginManagerImpl extends BroadcastReceiver implements PluginManager {

   public <T extends Plugin> void addPluginListener(PluginListener<T> listener, Class<?> cls,
           boolean allowMultiple) {
       addPluginListener(PluginManager.getAction(cls), listener, cls, allowMultiple);
   }

   public <T extends Plugin> void addPluginListener(String action, PluginListener<T> listener,
           Class cls, boolean allowMultiple) {
       if (!isDebuggable) {
           // Never ever ever allow these on production builds, they are only for prototyping.
           return;
       }
       mPluginPrefs.addAction(action);
       PluginInstanceManager p = mFactory.createPluginInstanceManager(mContext, action, listener,
               allowMultiple, mLooper, cls, this);
       p.loadAll();
       mPluginMap.put(listener, p);
       startListening();
   }
}

核心流程

简化一下也就是相当于执行以下这段程序,关键的步骤也就是以下的流程

    public <T extends Plugin> void addPluginListener(PluginListener<T> listener) {
        if (!isDebuggable) {
            // Never ever ever allow these on production builds, they are only for prototyping.
            return;
        }
        //getAction,拿到的OverlayPlugin的关于ProvidesInterface注解里的action值
        //action值是 OverlayPlugin.ACTION="com.android.systemui.action.PLUGIN_OVERLAY"
        String action=PluginManager.getAction(OverlayPlugin.class);
       
        //将拿到的action值添加到SharedPreferences中
        mPluginPrefs.addAction(action);
        
        //第一步,通过PluginInstanceManagerFactory工厂创建一个PluginInstanceManager的实例
        PluginInstanceManager p = mFactory.createPluginInstanceManager(mContext, action, 
     listener,true, mLooper, OverlayPlugin.class, this);

        //第二步,加载Plugin实例
        p.loadAll();
        
        //把Plugin实例添加到map映射中
        mPluginMap.put(listener, p);
        
        //第三步,监听Plugin实例
        startListening();
    }

PluginManager.java

public interface PluginManager {
    static <P> String getAction(Class<P> cls) {
        ProvidesInterface info = cls.getDeclaredAnnotation(ProvidesInterface.class);
        if (info == null) {
            throw new RuntimeException(cls + " doesn't provide an interface");
        }
        if (TextUtils.isEmpty(info.action())) {
            throw new RuntimeException(cls + " doesn't provide an action");
        }
        return info.action();
    }
}

OverlayPlugin.java

@ProvidesInterface(action = OverlayPlugin.ACTION, version = OverlayPlugin.VERSION)
public interface OverlayPlugin extends Plugin {

    String ACTION = "com.android.systemui.action.PLUGIN_OVERLAY";
    int VERSION = 2;

    void setup(View statusBar, View navBar);

    default boolean holdStatusBarOpen() {
        return false;
    }

    /**
     * Only called if the plugin has returned true to holdStatusBarOpen().
     */
    default void setCollapseDesired(boolean collapseDesired) {
    }
}

注解 ProvidesInterface.java

@Retention(RetentionPolicy.RUNTIME)
public @interface ProvidesInterface {
    int version();

    String action() default "";

}

1.第一步,创建PluginInstanceManager 对象

PluginInstanceManagerFactory.java

接下来我们要看下PluginInstanceManagerFactory里面是创建的PluginInstanceManager对象的方式也比较简单,直接就是new一个对象,其实的参数值我们列一下
context:Application对象(来自于SystemUIApplication)
action:"com.android.systemui.action.PLUGIN_OVERLAY"
listener:PluginListener对象(SystemUIApplication中的new PluginListener对象)
allowMultiple:true
looper:new HandlerThread("SysUiBg",Process.THREAD_PRIORITY_BACKGROUND).getLooper();
(来自于SystemUIApplication)
versionInfo:OverlayPlugin.cls,用于保存OverlayPlugin的版本信息
manager:PluginManagerImpl .this

public static class PluginInstanceManagerFactory {
 public <T extends Plugin> PluginInstanceManager createPluginInstanceManager(Context context,
       String action, PluginListener<T> listener, boolean allowMultiple, Looper looper,
              Class<?> cls, PluginManagerImpl manager) {
            return new PluginInstanceManager(context, action, listener, allowMultiple, looper,
                 new VersionInfo().addClass(cls), manager);
        }
    }

PluginInstanceManager.java

public class PluginInstanceManager<T extends Plugin> {
    PluginInstanceManager(Context context, String action, PluginListener<T> listener,
            boolean allowMultiple, Looper looper, VersionInfo version, PluginManagerImpl manager) {
        this(context, context.getPackageManager(), action, listener, allowMultiple, looper,
         version,manager, Build.IS_DEBUGGABLE);
    }

    @VisibleForTesting
    PluginInstanceManager(Context context, PackageManager pm, String action,
            PluginListener<T> listener, boolean allowMultiple, Looper looper, 
            VersionInfo version,PluginManagerImpl manager, boolean debuggable) {
        mMainHandler = new MainHandler(Looper.getMainLooper());
        mPluginHandler = new PluginHandler(looper);
        mManager = manager;
        mContext = context;
        mPm = pm;
        mAction = action;
        mListener = listener;//listener在此就已经注册进来了
        mAllowMultiple = allowMultiple;
        mVersion = version;
        isDebuggable = debuggable;
    }
}

综上所述,SystemUI一起来的时候,就通过PluginManagerIml将其listener注册到了PluginInstanceManager中,

2.第二步,通过loadAll生成PluginInfo对象

  private class PluginHandler extends Handler {
        private final ArrayList<PluginInfo<T>> mPlugins = new ArrayList<>();
        @Override
        public void handleMessage(Message msg) {
            switch (msg.what) {
                case QUERY_ALL:
                    //先将plugins中所有Pluginc对象onDestroy,并clear掉plugins
                    for (int i = mPlugins.size() - 1; i >= 0; i--) {
                        PluginInfo<T> plugin = mPlugins.get(i);
                        mListener.onPluginDisconnected(plugin.mPlugin);
                        if (!(plugin.mPlugin instanceof PluginFragment)) {
                            // Only call onDestroy for plugins that aren't fragments, as fragments
                            // will get the onDestroy as part of the fragment lifecycle.
                            plugin.mPlugin.onDestroy();
                        }
                    }
                    mPlugins.clear();
                    handleQueryPlugins(null);
                    break;
            }
        }
        
         private void handleQueryPlugins(String pkgName) {
            //这实际上不是一项服务,也不应该启动,但它是一种方便的基于PM的插件管理方式。
            Intent intent = new Intent(mAction);
            if (pkgName != null) {
                intent.setPackage(pkgName);
            }
            //通过"com.android.systemui.action.PLUGIN_OVERLAY"查询到对应的ResolveInfo列表
            List<ResolveInfo> result =
                    mPm.queryIntentServices(intent, 0);
            if (DEBUG) Log.d(TAG, "Found " + result.size() + " plugins");
            if (result.size() > 1 && !mAllowMultiple) {
                //如果列表大于1的话,不处理
                Log.w(TAG, "Multiple plugins found for " + mAction);
                return;
            }
            for (ResolveInfo info : result) {
                //拿到PLUGIN_OVERLAY对应的ComponentName 
                ComponentName name = new ComponentName(info.serviceInfo.packageName,
                        info.serviceInfo.name);
                //生成PluginInfo对象
                PluginInfo<T> t = handleLoadPlugin(name);
                if (t == null) continue;
                //通知listener,pluginInfo对象已经成功连接了
                mMainHandler.obtainMessage(mMainHandler.PLUGIN_CONNECTED, t).sendToTarget();
                mPlugins.add(t);
            }
        }
        
         protected PluginInfo<T> handleLoadPlugin(ComponentName component) {
            // This was already checked, but do it again here to make extra extra sure, we don't
            // use these on production builds.
            if (!isDebuggable) {
                // Never ever ever allow these on production builds, they are only for prototyping.
                Log.d(TAG, "Somehow hit second debuggable check");
                return null;
            }
            //获取包名和类名
            String pkg = component.getPackageName();
            String cls = component.getClassName();
            try {
                ApplicationInfo info = mPm.getApplicationInfo(pkg, 0);
                // TODO: This probably isn't needed given that we don't have IGNORE_SECURITY on
                if (mPm.checkPermission(PLUGIN_PERMISSION, pkg)
                        != PackageManager.PERMISSION_GRANTED) {
                    Log.d(TAG, "Plugin doesn't have permission: " + pkg);
                    return null;
                }
                //创建PluginManagerImpl的ClassLoader,这样我们就可以使用我们自己的代码作为父代码。
                ClassLoader classLoader = mManager.getClassLoader(info.sourceDir, info.packageName);
                //构建PluginContextWrapper
                Context pluginContext = new PluginContextWrapper(
                        mContext.createApplicationContext(info, 0), classLoader);
                //通过反射生成对应的plugin对象
                Class<?> pluginClass = Class.forName(cls, true, classLoader);
                // TODO: Only create the plugin before version check if we need it for
                // legacy version check.
                T plugin = (T) pluginClass.newInstance();
                try {
                    VersionInfo version = checkVersion(pluginClass, plugin, mVersion);
                    if (DEBUG) Log.d(TAG, "createPlugin");
                    //最终创建PluginInfo对象
                    return new PluginInfo(pkg, cls, plugin, pluginContext, version);
                } catch (InvalidVersionException e) {
                    final int icon = mContext.getResources().getIdentifier("tuner", "drawable",
                            mContext.getPackageName());
                    final int color = Resources.getSystem().getIdentifier(
                            "system_notification_accent_color", "color", "android");
                    final Notification.Builder nb = new Notification.Builder(mContext,
                            PluginManager.NOTIFICATION_CHANNEL_ID)
                                    .setStyle(new Notification.BigTextStyle())
                                    .setSmallIcon(icon)
                                    .setWhen(0)
                                    .setShowWhen(false)
                                    .setVisibility(Notification.VISIBILITY_PUBLIC)
                                    .setColor(mContext.getColor(color));
                    String label = cls;
                    try {
                        label = mPm.getServiceInfo(component, 0).loadLabel(mPm).toString();
                    } catch (NameNotFoundException e2) {
                    }
                    if (!e.isTooNew()) {
                        // Localization not required as this will never ever appear in a user build.
                        nb.setContentTitle("Plugin \"" + label + "\" is too old")
                                .setContentText("Contact plugin developer to get an updated"
                                        + " version.\n" + e.getMessage());
                    } else {
                        // Localization not required as this will never ever appear in a user build.
                        nb.setContentTitle("Plugin \"" + label + "\" is too new")
                                .setContentText("Check to see if an OTA is available.\n"
                                        + e.getMessage());
                    }
                    Intent i = new Intent(PluginManagerImpl.DISABLE_PLUGIN).setData(
                            Uri.parse("package://" + component.flattenToString()));
                    PendingIntent pi = PendingIntent.getBroadcast(mContext, 0, i, 0);
                    nb.addAction(new Action.Builder(null, "Disable plugin", pi).build());
                    mContext.getSystemService(NotificationManager.class)
                            .notifyAsUser(cls, SystemMessage.NOTE_PLUGIN, nb.build(),
                                    UserHandle.ALL);
                    // TODO: Warn user.
                    Log.w(TAG, "Plugin has invalid interface version " + plugin.getVersion()
                            + ", expected " + mVersion);
                    return null;
                }
            } catch (Throwable e) {
                Log.w(TAG, "Couldn't load plugin: " + pkg, e);
                return null;
            }
        }
    }

3.第三步,回调onPluginConnected

    private class MainHandler extends Handler {
        private static final int PLUGIN_CONNECTED = 1;
        private static final int PLUGIN_DISCONNECTED = 2;

        public MainHandler(Looper looper) {
            super(looper);
        }

        @Override
        public void handleMessage(Message msg) {
            switch (msg.what) {
                case PLUGIN_CONNECTED:
                    if (DEBUG) Log.d(TAG, "onPluginConnected");
                    PluginPrefs.setHasPlugins(mContext);
                    PluginInfo<T> info = (PluginInfo<T>) msg.obj;
                    mManager.handleWtfs();
                    if (!(msg.obj instanceof PluginFragment)) {
                        // Only call onDestroy for plugins that aren't fragments, as fragments
                        // will get the onCreate as part of the fragment lifecycle.
                        info.mPlugin.onCreate(mContext, info.mPluginContext);
                    }
                    //回调onPluginConnected结果
                    mListener.onPluginConnected(info.mPlugin, info.mPluginContext);
                    break;
                case PLUGIN_DISCONNECTED:
                    if (DEBUG) Log.d(TAG, "onPluginDisconnected");
                    mListener.onPluginDisconnected((T) msg.obj);
                    if (!(msg.obj instanceof PluginFragment)) {
                        // Only call onDestroy for plugins that aren't fragments, as fragments
                        // will get the onDestroy as part of the fragment lifecycle.
                        ((T) msg.obj).onDestroy();
                    }
                    break;
                default:
                    super.handleMessage(msg);
                    break;
            }
        }
    }

通过SystemUIApplication.java里面的函数,我们知道onPluginConnected成功以后,会先拿到StatusBar对象,并通过plugin的setup函数将StatusBar对象传递出去。

4.第四步,startListener(PluginManagerImpl.java)

    private void startListening() {
        if (mListening) return;
        mListening = true;
        IntentFilter filter = new IntentFilter(Intent.ACTION_PACKAGE_ADDED);
        filter.addAction(Intent.ACTION_PACKAGE_CHANGED);
        filter.addAction(Intent.ACTION_PACKAGE_REMOVED);
        filter.addAction(PLUGIN_CHANGED);
        filter.addAction(DISABLE_PLUGIN);
        filter.addDataScheme("package");
        mContext.registerReceiver(this, filter);
        filter = new IntentFilter(Intent.ACTION_USER_UNLOCKED);
        mContext.registerReceiver(this, filter);
    }
    
    
    @Override
    public void onReceive(Context context, Intent intent) {
        if (Intent.ACTION_USER_UNLOCKED.equals(intent.getAction())) {
            for (PluginInstanceManager manager : mPluginMap.values()) {
                manager.loadAll();
            }
        } else if (DISABLE_PLUGIN.equals(intent.getAction())) {
            //组件不可用
            Uri uri = intent.getData();
            ComponentName component = ComponentName.unflattenFromString(
                    uri.toString().substring(10));
            mContext.getPackageManager().setComponentEnabledSetting(component,
                    PackageManager.COMPONENT_ENABLED_STATE_DISABLED,
                    PackageManager.DONT_KILL_APP);
            mContext.getSystemService(NotificationManager.class).cancel(component.getClassName(),
                    SystemMessage.NOTE_PLUGIN);
        } else {
            Uri data = intent.getData();
            String pkg = data.getEncodedSchemeSpecificPart();
            if (mOneShotPackages.contains(pkg)) {
                int icon = mContext.getResources().getIdentifier("tuner", "drawable",
                        mContext.getPackageName());
                int color = Resources.getSystem().getIdentifier(
                        "system_notification_accent_color", "color", "android");
                String label = pkg;
                try {
                    PackageManager pm = mContext.getPackageManager();
                    label = pm.getApplicationInfo(pkg, 0).loadLabel(pm).toString();
                } catch (NameNotFoundException e) {
                }
                // Localization not required as this will never ever appear in a user build.
                final Notification.Builder nb =
                        new Notification.Builder(mContext, NOTIFICATION_CHANNEL_ID)
                                .setSmallIcon(icon)
                                .setWhen(0)
                                .setShowWhen(false)
                                .setPriority(Notification.PRIORITY_MAX)
                                .setVisibility(Notification.VISIBILITY_PUBLIC)
                                .setColor(mContext.getColor(color))
                                .setContentTitle("Plugin \"" + label + "\" has updated")
                                .setContentText("Restart SysUI for changes to take effect.");
                Intent i = new Intent("com.android.systemui.action.RESTART").setData(
                            Uri.parse("package://" + pkg));
                PendingIntent pi = PendingIntent.getBroadcast(mContext, 0, i, 0);
                nb.addAction(new Action.Builder(null, "Restart SysUI", pi).build());
                mContext.getSystemService(NotificationManager.class).notifyAsUser(pkg,
                        SystemMessage.NOTE_PLUGIN, nb.build(), UserHandle.ALL);
            }
            if (clearClassLoader(pkg)) {
                Toast.makeText(mContext, "Reloading " + pkg, Toast.LENGTH_LONG).show();
            }
            if (!Intent.ACTION_PACKAGE_REMOVED.equals(intent.getAction())) {
                for (PluginInstanceManager manager : mPluginMap.values()) {
                    manager.onPackageChange(pkg);
                }
            } else {
                for (PluginInstanceManager manager : mPluginMap.values()) {
                    manager.onPackageRemoved(pkg);
                }
            }
        }
    }
最后编辑于
©著作权归作者所有,转载或内容合作请联系作者
【社区内容提示】社区部分内容疑似由AI辅助生成,浏览时请结合常识与多方信息审慎甄别。
平台声明:文章内容(如有图片或视频亦包括在内)由作者上传并发布,文章内容仅代表作者本人观点,简书系信息发布平台,仅提供信息存储服务。

相关阅读更多精彩内容

友情链接更多精彩内容