SystemUI插件化机制Plugin
1.SystemUI的启动
在SystemServer中startOtherServices中调用startSystemUi方法启动SystemUIService。
SystemUIService在onCreate中
((SystemUIApplication) getApplication()).startServicesIfNeeded();
进一步调用
startServicesIfNeeded(SERVICES);
其中SERVICES是一个服务列表,会依次调用
mServices[i].start();
来启动SystemUI的各种服务,也就是各种SystemUI的子类。
2.加载插件模块
Dependency.get(PluginManager.class).addPluginListener(
new PluginListener<OverlayPlugin>() {
private ArraySet<OverlayPlugin> mOverlays;
@Override
public void onPluginConnected(OverlayPlugin plugin, Context pluginContext) {
StatusBar statusBar = getComponent(StatusBar.class);
if (statusBar != null) {
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;
Dependency.get方法:
public static <T> T get(Class<T> cls) {
return sDependency.getDependency(cls);
}
protected final <T> T getDependency(Class<T> cls) {
return getDependencyInner(cls);
}
private synchronized <T> T getDependencyInner(Object key) {
@SuppressWarnings("unchecked")
T obj = (T) mDependencies.get(key);
if (obj == null) {
obj = createDependency(key);
mDependencies.put(key, obj);
}
return obj;
}
先从mDependencies里去查找,找不到再创建一个Dependency出来,看一下createDependency
protected <T> T createDependency(Object cls) {
Preconditions.checkArgument(cls instanceof DependencyKey<?> || cls instanceof Class<?>);
@SuppressWarnings("unchecked")
DependencyProvider<T> provider = mProviders.get(cls);
if (provider == null) {
throw new IllegalArgumentException("Unsupported dependency " + cls);
}
return provider.createDependency();
}
这里从mProviders里面去找,存在就创建,不存在就抛出异常。这个mProviders是在什么地方初始化的呢?前面讲到mServices[i].start();其中Dependency.class也是在这个mServices中,SystemUI启动的时候会调用其start()@Dependency.java方法
@Override
public void start() {
mProviders.put(TIME_TICK_HANDLER, () -> {
HandlerThread thread = new HandlerThread("TimeTick");
thread.start();
return new Handler(thread.getLooper());
});
mProviders.put(BG_LOOPER, () -> {
HandlerThread thread = new HandlerThread("SysUiBg",
Process.THREAD_PRIORITY_BACKGROUND);
thread.start();
return thread.getLooper();
});
mProviders.put(MAIN_HANDLER, () -> new Handler(Looper.getMainLooper()));
mProviders.put(ActivityStarter.class, () -> new ActivityStarterDelegate());
......
// 这里添加了很多class到mProviders中
......
// Put all dependencies above here so the factory can override them if it wants.
SystemUIFactory.getInstance().injectDependencies(mProviders, mContext);
mProviders 是一个ArrayMap<Object, DependencyProvider>
private final ArrayMap<Object, DependencyProvider> mProviders = new ArrayMap<>();
所以上面的startServicesIfNeeded方法中Dependency.get(PluginManager.class)得到的是PluginManagerImpl对象,调用了void addPluginListener(PluginListener<T> listener, Class<?> cls,
boolean allowMultiple)方法,实际调用到addPluginListener(String action, PluginListener<T> listener,
Class cls, boolean 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();
}
这个action是调用PluginManager.getAction(cls)得到的
getAction@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();
}
实际上是获取类的注解
PluginManagerImpl将listener,PluginInstanceManager 以键值对保存起来,存到mPluginMap中。
看一下startListening()@PluginManagerImpl
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);
}
注册了广播监听器,PluginManagerImpl本身就是一个BroadcastReceiver,看看其onReceive方法
@Override
public void onReceive(Context context, Intent intent) {
if (Intent.ACTION_USER_UNLOCKED.equals(intent.getAction())) {
for (PluginInstanceManager manager : mPluginMap.values()) {
// mPluginMap保存的是listener,PluginInstanceManage键值对
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()) {
// plugin发生变化
manager.onPackageChange(pkg);
}
} else {
for (PluginInstanceManager manager : mPluginMap.values()) {
// plugin被移除
manager.onPackageRemoved(pkg);
}
}
}
}
看一下manager.loadAll()
public void loadAll() {
if (DEBUG) Log.d(TAG, "startListening");
mPluginHandler.sendEmptyMessage(PluginHandler.QUERY_ALL);
}
public void handleMessage(Message msg) {
switch (msg.what) {
case QUERY_ALL:
if (DEBUG) Log.d(TAG, "queryAll " + mAction);
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) {
// This isn't actually a service and shouldn't ever be started, but is
// a convenient PM based way to manage our plugins.
Intent intent = new Intent(mAction);
if (pkgName != null) {
intent.setPackage(pkgName);
}
// plugin在AndroidManifest中必须声明为<service>,packageManager读取到所有的service信息
List<ResolveInfo> result =
mPm.queryIntentServices(intent, 0);
if (DEBUG) Log.d(TAG, "Found " + result.size() + " plugins");
if (result.size() > 1 && !mAllowMultiple) {
// TODO: Show warning.
Log.w(TAG, "Multiple plugins found for " + mAction);
return;
}
for (ResolveInfo info : result) {
ComponentName name = new ComponentName(info.serviceInfo.packageName,
info.serviceInfo.name);
// 通过ComponentName创建出插件
PluginInfo<T> t = handleLoadPlugin(name);
if (t == null) continue;
mMainHandler.obtainMessage(mMainHandler.PLUGIN_CONNECTED, t).sendToTarget();
mPlugins.add(t);
}
}
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);
}
mListener.onPluginConnected(info.mPlugin, info.mPluginContext);
break;
终于看到了onPluginConnected的回调。
这里有个关键的方法handleLoadPlugin,这个方法创建出插件并且返回PluginInfo
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;
}
// Create our own ClassLoader so we can use our own code as the parent.
// 构造类加载器和Context,类加载器加载插件的代码,Context加载插件的资源。
ClassLoader classLoader = mManager.getClassLoader(info.sourceDir, info.packageName);
Context pluginContext = new PluginContextWrapper(
mContext.createApplicationContext(info, 0), classLoader);
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");
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;
}
}
比较重要的两行
// 构造类加载器和Context,类加载器加载插件的代码,Context加载插件的资源。
ClassLoader classLoader = mManager.getClassLoader(info.sourceDir, info.packageName);
Context pluginContext = new PluginContextWrapper(
mContext.createApplicationContext(info, 0), classLoader);
这里的mManager就是PluginManagerImpl,看看它的getClassLoader
public ClassLoader getClassLoader(String sourceDir, String pkg) {
if (mClassLoaders.containsKey(pkg)) {
return mClassLoaders.get(pkg);
}
ClassLoader classLoader = new PathClassLoader(sourceDir, getParentClassLoader());
mClassLoaders.put(pkg, classLoader);
return classLoader;
}
默认采用的是PathClassLoader来加载插件的代码
@Override
public Context createApplicationContext(ApplicationInfo application, int flags)
throws NameNotFoundException {
LoadedApk pi = mMainThread.getPackageInfo(application, mResources.getCompatibilityInfo(),
flags | CONTEXT_REGISTER_PACKAGE);
if (pi != null) {
ContextImpl c = new ContextImpl(this, mMainThread, pi, null, mActivityToken,
new UserHandle(UserHandle.getUserId(application.uid)), flags, null);
final int displayId = mDisplay != null
? mDisplay.getDisplayId() : Display.DEFAULT_DISPLAY;
c.setResources(createResources(mActivityToken, pi, null, displayId, null,
getDisplayAdjustments(displayId).getCompatibilityInfo()));
if (c.mResources != null) {
return c;
}
}
throw new PackageManager.NameNotFoundException(
"Application package " + application.packageName + " not found");
}
c.setResources,可以看到Context中包含了插件的资源信息。最后封装成一个PluginContextWrapper,构造出pluginContext,然后再和plugin一起封装成PluginInfo返回。