patchoat相关代码在Android M版中的变化(1)

patchoat相关代码在Android M版中的变化(1)

在Android L中,patchoat被framework直接调用的情况还是不少的,但是在Android M中,这个命令被取消了。所以虽然功能上没有什么新意,逻辑上还是有点小变化的。下面我们分析一下。

Android L上SystemServer的patchoat流程

我们先看看SystemServer的patchoat的过程吧。

ZygoteInit.performSystemServerDexOpt

先说SystemServer,启动SystemServer时,如果systemServerClasspath不为空,则调用performSystemServerDexOpt.

495    /**
496     * Finish remaining work for the newly forked system server process.
497     */
498    private static void handleSystemServerProcess(
499            ZygoteConnection.Arguments parsedArgs)
500            throws ZygoteInit.MethodAndArgsCaller {
501
502        closeServerSocket();
503
504        // set umask to 0077 so new files and directories will default to owner-only permissions.
505        Os.umask(S_IRWXG | S_IRWXO);
506
507        if (parsedArgs.niceName != null) {
508            Process.setArgV0(parsedArgs.niceName);
509        }
510
511        final String systemServerClasspath = Os.getenv("SYSTEMSERVERCLASSPATH");
512        if (systemServerClasspath != null) {
513            performSystemServerDexOpt(systemServerClasspath);
514        }
...

我们再看performSystemServerDexOpt。

547    /**
548     * Performs dex-opt on the elements of {@code classPath}, if needed. We
549     * choose the instruction set of the current runtime.
550     */
551    private static void performSystemServerDexOpt(String classPath) {
552        final String[] classPathElements = classPath.split(":");
553        final InstallerConnection installer = new InstallerConnection();
554        final String instructionSet = VMRuntime.getRuntime().vmInstructionSet();
555
556        try {
557            for (String classPathElement : classPathElements) {
558                final byte dexopt = DexFile.isDexOptNeededInternal(classPathElement, "*", instructionSet,
559                        false /* defer */);
560                if (dexopt == DexFile.DEXOPT_NEEDED) {
561                    installer.dexopt(classPathElement, Process.SYSTEM_UID, false, instructionSet);
562                } else if (dexopt == DexFile.PATCHOAT_NEEDED) {
563                    installer.patchoat(classPathElement, Process.SYSTEM_UID, false, instructionSet);
564                }
565            }
566        } catch (IOException ioe) {
567            throw new RuntimeException("Error starting system_server", ioe);
568        } finally {
569            installer.disconnect();
570        }
571    }

最终会调用到PM中的Installer的patchoat命令。

Installer.patchoat

57    public int patchoat(String apkPath, int uid, boolean isPublic, String pkgName,
58            String instructionSet) {
59        if (!isValidInstructionSet(instructionSet)) {
60            Slog.e(TAG, "Invalid instruction set: " + instructionSet);
61            return -1;
62        }
63
64        return mInstaller.patchoat(apkPath, uid, isPublic, pkgName, instructionSet);
65    }
66
67    public int patchoat(String apkPath, int uid, boolean isPublic, String instructionSet) {
68        if (!isValidInstructionSet(instructionSet)) {
69            Slog.e(TAG, "Invalid instruction set: " + instructionSet);
70            return -1;
71        }
72
73        return mInstaller.patchoat(apkPath, uid, isPublic, instructionSet);
74    }

这个mInstaller是个什么东东呢?

36    private final InstallerConnection mInstaller;
37
38    public Installer(Context context) {
39        super(context);
40        mInstaller = new InstallerConnection();
41    }

InstallerConnection.patchoat

114    public int patchoat(String apkPath, int uid, boolean isPublic, String instructionSet) {
115        return patchoat(apkPath, uid, isPublic, "*", instructionSet);
116    }
117
118    public int patchoat(String apkPath, int uid, boolean isPublic, String pkgName,
119            String instructionSet) {
120        StringBuilder builder = new StringBuilder("patchoat");
121        builder.append(' ');
122        builder.append(apkPath);
123        builder.append(' ');
124        builder.append(uid);
125        builder.append(isPublic ? " 1" : " 0");
126        builder.append(' ');
127        builder.append(pkgName);
128        builder.append(' ');
129        builder.append(instructionSet);
130        return execute(builder.toString());
131    }

拼装了一个命令,然后调用execute方法将其执行。

84    public int execute(String cmd) {
85        String res = transact(cmd);
86        try {
87            return Integer.parseInt(res);
88        } catch (NumberFormatException ex) {
89            return -1;
90        }
91    }

先说说InstallerConnection的原理,它是通过socket与installd通信的一个代理,下面是官方注释:

29/**
30 * Represents a connection to {@code installd}. Allows multiple connect and
31 * disconnect cycles.
32 *
33 * @hide for internal use only
34 */

有了这个背景,我们再看transact调用的通信功能就比较清晰了:

48    public synchronized String transact(String cmd) {
49        if (!connect()) {
50            Slog.e(TAG, "connection failed");
51            return "-1";
52        }
53
54        if (!writeCommand(cmd)) {
55            /*
56             * If installd died and restarted in the background (unlikely but
57             * possible) we'll fail on the next write (this one). Try to
58             * reconnect and write the command one more time before giving up.
59             */
60            Slog.e(TAG, "write command failed? reconnect!");
61            if (!connect() || !writeCommand(cmd)) {
62                return "-1";
63            }
64        }
65        if (LOCAL_DEBUG) {
66            Slog.i(TAG, "send: '" + cmd + "'");
67        }
68
69        final int replyLength = readReply();
70        if (replyLength > 0) {
71            String s = new String(buf, 0, replyLength);
72            if (LOCAL_DEBUG) {
73                Slog.i(TAG, "recv: '" + s + "'");
74            }
75            return s;
76        } else {
77            if (LOCAL_DEBUG) {
78                Slog.i(TAG, "fail");
79            }
80            return "-1";
81        }
82    }

writeCommand是向输出中写数据:

202    private boolean writeCommand(String cmdString) {
203        final byte[] cmd = cmdString.getBytes();
204        final int len = cmd.length;
205        if ((len < 1) || (len > buf.length)) {
206            return false;
207        }
208
209        buf[0] = (byte) (len & 0xff);
210        buf[1] = (byte) ((len >> 8) & 0xff);
211        try {
212            mOut.write(buf, 0, 2);
213            mOut.write(cmd, 0, len);
214        } catch (IOException ex) {
215            Slog.e(TAG, "write error");
216            disconnect();
217            return false;
218        }
219        return true;
220    }

我们继续追,看这个mOut是如何来的:

133    private boolean connect() {
134        if (mSocket != null) {
135            return true;
136        }
137        Slog.i(TAG, "connecting...");
138        try {
139            mSocket = new LocalSocket();
140
141            LocalSocketAddress address = new LocalSocketAddress("installd",
142                    LocalSocketAddress.Namespace.RESERVED);
143
144            mSocket.connect(address);
145
146            mIn = mSocket.getInputStream();
147            mOut = mSocket.getOutputStream();
148        } catch (IOException ex) {
149            disconnect();
150            return false;
151        }
152        return true;
153    }

于是我们跨越java,继续往installd中追。

installd中的命令入口

164struct cmdinfo cmds[] = {
165    { "ping",                 0, do_ping },
166    { "install",              4, do_install },
167    { "dexopt",               6, do_dexopt },
168    { "markbootcomplete",     1, do_mark_boot_complete },
169    { "movedex",              3, do_move_dex },
170    { "rmdex",                2, do_rm_dex },
171    { "remove",               2, do_remove },
172    { "rename",               2, do_rename },
173    { "fixuid",               3, do_fixuid },
174    { "freecache",            1, do_free_cache },
175    { "rmcache",              2, do_rm_cache },
176    { "rmcodecache",          2, do_rm_code_cache },
177    { "getsize",              7, do_get_size },
178    { "rmuserdata",           2, do_rm_user_data },
179    { "movefiles",            0, do_movefiles },
180    { "linklib",              3, do_linklib },
181    { "mkuserdata",           4, do_mk_user_data },
182    { "mkuserconfig",         1, do_mk_user_config },
183    { "rmuser",               1, do_rm_user },
184    { "idmap",                3, do_idmap },
185    { "restorecondata",       3, do_restorecon_data },
186    { "patchoat",             5, do_patchoat },
187};

这个命令对应的是do_patchoat函数

153static int do_patchoat(char **arg, char reply[REPLY_MAX]) {
154    /* apk_path, uid, is_public, pkgname, instruction_set, vm_safe_mode, should_relocate */
155    return dexopt(arg[0], atoi(arg[1]), atoi(arg[2]), arg[3], arg[4], 0, 1);
156}

最终执行到的是dexopt函数。

Android M中的变化

patchoat这一分支从cmds中已经取消了,所有的过程统一交给installer.dexopt中处理。

464    /**
465     * Performs dex-opt on the elements of {@code classPath}, if needed. We
466     * choose the instruction set of the current runtime.
467     */
468    private static void performSystemServerDexOpt(String classPath) {
469        final String[] classPathElements = classPath.split(":");
470        final InstallerConnection installer = new InstallerConnection();
471        installer.waitForConnection();
472        final String instructionSet = VMRuntime.getRuntime().vmInstructionSet();
473
474        try {
475            for (String classPathElement : classPathElements) {
476                final int dexoptNeeded = DexFile.getDexOptNeeded(
477                        classPathElement, "*", instructionSet, false /* defer */);
478                if (dexoptNeeded != DexFile.NO_DEXOPT_NEEDED) {
479                    installer.dexopt(classPathElement, Process.SYSTEM_UID, false,
480                            instructionSet, dexoptNeeded, false /* boot complete */);
481                }
482            }
483        } catch (IOException ioe) {
484            throw new RuntimeException("Error starting system_server", ioe);
485        } finally {
486            installer.disconnect();
487        }
488    }

命令合成了一个之后,java层就不得不多加一些状态的判断逻辑。有一些在L版上在PackageManagerService中的逻辑被独立成一个PackageDexOptimizer类中。比如performDexOptLI方法,不但移到了PackageDexOptimizer类中,而且还增加了对DexFile.PATCHOAT_NEEDED等状态的判断。

路径位于:frameworks/base/services/core/java/com/android/server/pm/PackageDexOptimizer.java中。

167                    } else if (dexoptNeeded == DexFile.PATCHOAT_NEEDED) {
168                        dexoptType = "patchoat";
169                    } else if (dexoptNeeded == DexFile.SELF_PATCHOAT_NEEDED) {
170                        dexoptType = "self patchoat";
171                    } else {
172                        throw new IllegalStateException("Invalid dexopt needed: " + dexoptNeeded);
173                    }
174
175                    Log.i(TAG, "Running dexopt (" + dexoptType + ") on: " + path + " pkg="
176                            + pkg.applicationInfo.packageName + " isa=" + dexCodeInstructionSet
177                            + " vmSafeMode=" + vmSafeMode + " debuggable=" + debuggable
178                            + " oatDir = " + oatDir + " bootComplete=" + bootComplete);
179                    final int sharedGid = UserHandle.getSharedAppGid(pkg.applicationInfo.uid);
180                    final int ret = mPackageManagerService.mInstaller.dexopt(path, sharedGid,
181                            !pkg.isForwardLocked(), pkg.packageName, dexCodeInstructionSet,
182                            dexoptNeeded, vmSafeMode, debuggable, oatDir, bootComplete);

我们对比一下L版本上还在PMS类中的比如performDexOptLI方法,可是不管这些事情的。

4881                    final byte isDexOptNeeded = DexFile.isDexOptNeededInternal(path,
4882                            pkg.packageName, dexCodeInstructionSet, defer);
4883                    if (forceDex || (!defer && isDexOptNeeded == DexFile.DEXOPT_NEEDED)) {
4884                        Log.i(TAG, "Running dexopt on: " + path + " pkg="
4885                                + pkg.applicationInfo.packageName + " isa=" + dexCodeInstructionSet
4886                                + " vmSafeMode=" + vmSafeMode);
4887                        final int sharedGid = UserHandle.getSharedAppGid(pkg.applicationInfo.uid);
4888                        final int ret = mInstaller.dexopt(path, sharedGid, !isForwardLocked(pkg),
4889                                pkg.packageName, dexCodeInstructionSet, vmSafeMode);
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值