Skip to content

Commit

Permalink
Merge pull request #286 from jiongxuan/dev
Browse files Browse the repository at this point in the history
Resolved the issue of "Overwriting the plug-in, it may not be notified to other processes"
  • Loading branch information
cundong authored Aug 9, 2017
2 parents 708b652 + 91e358b commit db2f981
Show file tree
Hide file tree
Showing 5 changed files with 73 additions and 61 deletions.
Original file line number Diff line number Diff line change
Expand Up @@ -32,7 +32,6 @@
import com.qihoo360.i.Factory;
import com.qihoo360.i.IModule;
import com.qihoo360.i.IPluginManager;
import com.qihoo360.replugin.utils.ReflectUtils;
import com.qihoo360.mobilesafe.api.Tasks;
import com.qihoo360.replugin.IHostBinderFetcher;
import com.qihoo360.replugin.RePlugin;
Expand All @@ -49,6 +48,7 @@
import com.qihoo360.replugin.helper.LogRelease;
import com.qihoo360.replugin.model.PluginInfo;
import com.qihoo360.replugin.packages.PluginManagerProxy;
import com.qihoo360.replugin.utils.ReflectUtils;

import java.io.FileDescriptor;
import java.io.PrintWriter;
Expand Down Expand Up @@ -997,20 +997,6 @@ final Plugin getPlugin(String plugin) {
return mPlugins.get(plugin);
}

// 是否为新的,或已经更新过的插件
// 注意:若“插件正在运行”时触发更新,则这里应返回false,这样外界将不会发送“新插件”广播
final boolean isNewOrUpdatedPlugin(PluginInfo info) {
Plugin p = mPlugins.get(info.getName());

// 满足三个条件的其一就表示为"新插件",可以做下一步处理
// 1. p为空,表示为全新插件
// 2. 要安装的版本比当前的要高,且未运行
// 3. "待定更新"插件(插件未运行)
return p == null ||
(p.mInfo.getVersionValue() < info.getVersionValue() && !RePlugin.isPluginRunning(info.getName())) ||
(info.getPendingUpdate() != null && !RePlugin.isPluginRunning(info.getName()));
}

final Plugin loadPackageInfoPlugin(String plugin, PluginCommImpl pm) {
Plugin p = Plugin.cloneAndReattach(mContext, mPlugins.get(plugin), mClassLoader, pm);
return loadPlugin(p, Plugin.LOAD_INFO, true);
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -328,26 +328,43 @@ public PluginInfo pluginDownloaded(String path) throws RemoteException {
}

if (pi != null) {
// 插件是否需要更新
if (!mPluginMgr.isNewOrUpdatedPlugin(pi)) {
return pi;
}

// 常驻进程上下文
mPluginMgr.newPluginFound(pi, false);
// 通常到这里,表示“安装已成功”,这时不管处于什么状态,都应该通知外界更新插件内存表
syncPluginInfo2All(pi);
}

// 通知其它进程
Intent intent = new Intent(PmBase.ACTION_NEW_PLUGIN);
intent.putExtra(RePluginConstants.KEY_PERSIST_NEED_RESTART, mNeedRestart);
intent.putExtra("obj", pi);
IPC.sendLocalBroadcast2AllSync(mContext, intent);
return pi;
}

if (LOG) {
LogDebug.d(PLUGIN_TAG, "pluginDownloaded complete, info=" + pi);
}
private void syncPluginInfo2All(PluginInfo pi) {
// PS:若更新了“正在运行”的插件(属于“下次重启进程后更新”),则由于install返回的是“新的PluginInfo”,为防止出现“错误更新”,需要使用原来的
//
// 举例,有一个正在运行的插件A(其Info为PluginInfoOld)升级到新版(其Info为PluginInfoNew),则:
// 1. mManager.getService().install(path) 的返回值为:PluginInfoNew
// 2. PluginInfoOld在常驻进程中的内容修改为:PluginInfoOld.mPendingUpdate = PendingInfoNew
// 3. 同步到各进程,这里存在两种可能:
// a) (有问题)同步的是PluginInfoNew,则所有进程的内存表都强制更新到新的Info上,因此【正在运行的】插件信息将丢失,会出现严重问题
// b) (没问题)同步的是PluginInfoOld,只不过这个Old里面有个mPendingUpdate指向PendingInfoNew,则不会有问题,旧的仍被使用,符合预期
// 4. 最终install方法的返回值是PluginInfoNew,这样最外面拿到的就是安装成功的新插件信息,符合开发者的预期
PluginInfo needToSyncPi;
PluginInfo parent = pi.getParentInfo();
if (parent != null) {
needToSyncPi = parent;
} else {
needToSyncPi = pi;
}

return pi;
// 在常驻进程内更新插件内存表
mPluginMgr.newPluginFound(needToSyncPi, false);

// 通知其它进程去更新
Intent intent = new Intent(PmBase.ACTION_NEW_PLUGIN);
intent.putExtra(RePluginConstants.KEY_PERSIST_NEED_RESTART, mNeedRestart);
intent.putExtra("obj", needToSyncPi);
IPC.sendLocalBroadcast2AllSync(mContext, intent);

if (LOG) {
LogDebug.d(TAG, "syncPluginInfo2All: Sync complete! syncPi=" + needToSyncPi);
}
}

private PluginInfo pluginDownloadedForPn(String path) {
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -34,8 +34,8 @@
import com.qihoo360.replugin.RePluginInternal;
import com.qihoo360.replugin.helper.JSONHelper;
import com.qihoo360.replugin.helper.LogDebug;

import com.qihoo360.replugin.utils.FileUtils;

import org.json.JSONException;
import org.json.JSONObject;

Expand Down Expand Up @@ -101,14 +101,18 @@ public class PluginInfo implements Parcelable, Cloneable {

// 若插件需要更新,则会有此值
private PluginInfo mPendingUpdate;
private boolean mIsThisPendingUpdateInfo;

// 若插件需要卸载,则会有此值
private PluginInfo mPendingDelete;

// 若插件需要同版本覆盖安装更新,则会有此值
private PluginInfo mPendingCover;
private boolean mIsPendingCover;
private boolean mIsPendingCover; // 若当前为“新的PluginInfo”且为“同版本覆盖”,则为了能区分路径,则需要将此字段同步到Json文件中

// 若当前为“新的PluginInfo”,则其“父Info”是什么?
// 通常当前这个Info会包裹在“mPendingUpdate/mPendingDelete/mPendingCover”内
// 此信息【不会】做持久化工作。下次重启进程后会消失
private PluginInfo mParentInfo;

private PluginInfo(JSONObject jo) {
initPluginInfo(jo);
Expand Down Expand Up @@ -150,7 +154,6 @@ private PluginInfo(String pkgName, String alias, int low, int high, int version,
*/
public PluginInfo(PluginInfo pi) {
this.mJson = JSONHelper.cloneNoThrows(pi.mJson);
this.mIsThisPendingUpdateInfo = pi.mIsThisPendingUpdateInfo;
if (pi.mPendingUpdate != null) {
this.mPendingUpdate = new PluginInfo(pi.mPendingUpdate);
}
Expand All @@ -161,6 +164,9 @@ public PluginInfo(PluginInfo pi) {
if (pi.mPendingCover != null) {
this.mPendingCover = new PluginInfo(pi.mPendingCover);
}
if (pi.mParentInfo != null) {
this.mParentInfo = new PluginInfo(pi.mParentInfo);
}
}

private void initPluginInfo(JSONObject jo) {
Expand Down Expand Up @@ -206,11 +212,6 @@ private String makeName(String pkgName, String alias) {
*/
public static PluginInfo parseFromPackageInfo(PackageInfo pi, String path) {
ApplicationInfo ai = pi.applicationInfo;
if (ai == null) {
// 几乎不可能,但为保险起见,返回Null
return null;
}

String pn = pi.packageName;
String alias = null;
int low = 0;
Expand Down Expand Up @@ -322,9 +323,9 @@ public boolean isUsed() {
if (isPnPlugin()) {
// 为兼容以前逻辑,p-n仍是判断dex是否存在
return isDexExtracted();
} else if (isThisPendingUpdateInfo()) {
} else if (getParentInfo() != null) {
// 若PluginInfo是其它PluginInfo中的PendingUpdate,则返回那个PluginInfo的Used即可
return RePlugin.isPluginUsed(getName());
return getParentInfo().isUsed();
} else {
// 若是纯APK,且不是PendingUpdate,则直接从Json中获取
return mJson.optBoolean("used");
Expand Down Expand Up @@ -625,9 +626,7 @@ public void setFrameworkVersionByMeta(Bundle meta) {
setFrameworkVersion(frameVer);
}

/**
* 获取JSON对象。仅内部使用
*/
// @hide
public JSONObject getJSON() {
return mJson;
}
Expand Down Expand Up @@ -669,21 +668,17 @@ public void update(PluginInfo info) {
}

/**
* 此PluginInfo是否是一个位于其它PluginInfo中的PendingUpdate?只在调用RePlugin.install方法才能看到 <p>
* 注意:仅框架内部使用
* 若此Info为“新PluginInfo”,则这里返回的是“其父Info”的内容。通常和PendingUpdate有关
*
* @return 是否是PendingUpdate的PluginInfo
* @return 父PluginInfo
*/
public boolean isThisPendingUpdateInfo() {
return mIsThisPendingUpdateInfo;
public PluginInfo getParentInfo() {
return mParentInfo;
}

/**
* 此PluginInfo是否是一个位于其它PluginInfo中的PendingUpdate? <p>
* 注意:仅框架内部使用
*/
public void setIsThisPendingUpdateInfo(boolean updateInfo) {
mIsThisPendingUpdateInfo = updateInfo;
// @hide
public void setParentInfo(PluginInfo parent) {
mParentInfo = parent;
}

static PluginInfo createByJO(JSONObject jo) {
Expand Down Expand Up @@ -769,8 +764,8 @@ private void toContentString(StringBuilder b) {
}

// 当前是否为PendingUpdate的信息
if (mIsThisPendingUpdateInfo) {
b.append("[isTPUI] ");
if (mParentInfo != null) {
b.append("[HAS_PARENT] ");
}

// 插件类型
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -28,16 +28,16 @@
import com.qihoo360.loader2.MP;
import com.qihoo360.loader2.PluginNativeLibsHelper;
import com.qihoo360.mobilesafe.api.Tasks;
import com.qihoo360.replugin.utils.pkg.PackageFilesUtil;
import com.qihoo360.replugin.RePlugin;
import com.qihoo360.replugin.RePluginEventCallbacks;
import com.qihoo360.replugin.RePluginInternal;
import com.qihoo360.replugin.base.IPC;
import com.qihoo360.replugin.utils.FileUtils;
import com.qihoo360.replugin.helper.LogDebug;
import com.qihoo360.replugin.helper.LogRelease;
import com.qihoo360.replugin.model.PluginInfo;
import com.qihoo360.replugin.model.PluginInfoList;
import com.qihoo360.replugin.utils.FileUtils;
import com.qihoo360.replugin.utils.pkg.PackageFilesUtil;

import java.io.File;
import java.io.IOException;
Expand Down Expand Up @@ -297,7 +297,6 @@ private void updateOrLater(PluginInfo curPli, PluginInfo instPli) {
}
if (instPli.getVersion() > curPli.getVersion()) {
// 高版本升级
instPli.setIsThisPendingUpdateInfo(true);
curPli.setPendingUpdate(instPli);
curPli.setPendingDelete(null);
curPli.setPendingCover(null);
Expand All @@ -313,6 +312,9 @@ private void updateOrLater(PluginInfo curPli, PluginInfo instPli) {
LogDebug.w(TAG, "updateOrLater: Plugin need update same version. clear PendingDelete.");
}
}

// 设置其Parent为curPli,在PmBase.newPluginFound时会用到
instPli.setParentInfo(curPli);
} else {
if (LogDebug.LOG) {
LogDebug.i(TAG, "updateOrLater: Not running. Update now! pn=" + curPli.getName());
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -98,6 +98,18 @@ public void run() {
}, 1000);
}
});

// 刻意使用Thread的ClassLoader来测试效果
testThreadClassLoader();
}

private void testThreadClassLoader() {
// 在2.1.7及以前版本,如果直接调用此方法,则拿到的ClassLoader可能是PathClassLoader或者为空。有极个别Java库会用到此方法
// 这里务必确保:cl == getClassLoader(),才符合预期
ClassLoader cl = Thread.currentThread().getContextClassLoader();
if (cl != getClassLoader()) {
throw new RuntimeException("Thread.current.classLoader != getClassLoader(). cl=" + cl + "; getC=" + getClassLoader());
}
}

private static final int REQUEST_CODE_DEMO1 = 0x011;
Expand Down

0 comments on commit db2f981

Please sign in to comment.