Installer.java revision 6c4b9de8f1fd594038793c3924b52a44138c319e
1/*
2 * Copyright (C) 2008 The Android Open Source Project
3 *
4 * Licensed under the Apache License, Version 2.0 (the "License");
5 * you may not use this file except in compliance with the License.
6 * You may obtain a copy of the License at
7 *
8 *      http://www.apache.org/licenses/LICENSE-2.0
9 *
10 * Unless required by applicable law or agreed to in writing, software
11 * distributed under the License is distributed on an "AS IS" BASIS,
12 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
13 * See the License for the specific language governing permissions and
14 * limitations under the License.
15 */
16
17package com.android.server.pm;
18
19import android.os.Build;
20import com.android.server.SystemService;
21
22import android.content.Context;
23import android.content.pm.PackageStats;
24import android.net.LocalSocket;
25import android.net.LocalSocketAddress;
26import android.util.Slog;
27import dalvik.system.VMRuntime;
28
29import java.io.IOException;
30import java.io.InputStream;
31import java.io.OutputStream;
32import java.util.List;
33
34public final class Installer extends SystemService {
35    private static final String TAG = "Installer";
36
37    private static final boolean LOCAL_DEBUG = false;
38
39    InputStream mIn;
40    OutputStream mOut;
41    LocalSocket mSocket;
42
43    byte buf[] = new byte[1024];
44    int buflen = 0;
45
46    public Installer(Context context) {
47        super(context);
48    }
49
50    @Override
51    public void onStart() {
52        Slog.i(TAG, "Waiting for installd to be ready.");
53        ping();
54    }
55
56    private boolean connect() {
57        if (mSocket != null) {
58            return true;
59        }
60        Slog.i(TAG, "connecting...");
61        try {
62            mSocket = new LocalSocket();
63
64            LocalSocketAddress address = new LocalSocketAddress("installd",
65                    LocalSocketAddress.Namespace.RESERVED);
66
67            mSocket.connect(address);
68
69            mIn = mSocket.getInputStream();
70            mOut = mSocket.getOutputStream();
71        } catch (IOException ex) {
72            disconnect();
73            return false;
74        }
75        return true;
76    }
77
78    private void disconnect() {
79        Slog.i(TAG, "disconnecting...");
80        try {
81            if (mSocket != null)
82                mSocket.close();
83        } catch (IOException ex) {
84        }
85        try {
86            if (mIn != null)
87                mIn.close();
88        } catch (IOException ex) {
89        }
90        try {
91            if (mOut != null)
92                mOut.close();
93        } catch (IOException ex) {
94        }
95        mSocket = null;
96        mIn = null;
97        mOut = null;
98    }
99
100    private boolean readBytes(byte buffer[], int len) {
101        int off = 0, count;
102        if (len < 0)
103            return false;
104        while (off != len) {
105            try {
106                count = mIn.read(buffer, off, len - off);
107                if (count <= 0) {
108                    Slog.e(TAG, "read error " + count);
109                    break;
110                }
111                off += count;
112            } catch (IOException ex) {
113                Slog.e(TAG, "read exception");
114                break;
115            }
116        }
117        if (LOCAL_DEBUG) {
118            Slog.i(TAG, "read " + len + " bytes");
119        }
120        if (off == len)
121            return true;
122        disconnect();
123        return false;
124    }
125
126    private boolean readReply() {
127        int len;
128        buflen = 0;
129        if (!readBytes(buf, 2))
130            return false;
131        len = (((int) buf[0]) & 0xff) | ((((int) buf[1]) & 0xff) << 8);
132        if ((len < 1) || (len > 1024)) {
133            Slog.e(TAG, "invalid reply length (" + len + ")");
134            disconnect();
135            return false;
136        }
137        if (!readBytes(buf, len))
138            return false;
139        buflen = len;
140        return true;
141    }
142
143    private boolean writeCommand(String _cmd) {
144        byte[] cmd = _cmd.getBytes();
145        int len = cmd.length;
146        if ((len < 1) || (len > 1024))
147            return false;
148        buf[0] = (byte) (len & 0xff);
149        buf[1] = (byte) ((len >> 8) & 0xff);
150        try {
151            mOut.write(buf, 0, 2);
152            mOut.write(cmd, 0, len);
153        } catch (IOException ex) {
154            Slog.e(TAG, "write error");
155            disconnect();
156            return false;
157        }
158        return true;
159    }
160
161    private synchronized String transaction(String cmd) {
162        if (!connect()) {
163            Slog.e(TAG, "connection failed");
164            return "-1";
165        }
166
167        if (!writeCommand(cmd)) {
168            /*
169             * If installd died and restarted in the background (unlikely but
170             * possible) we'll fail on the next write (this one). Try to
171             * reconnect and write the command one more time before giving up.
172             */
173            Slog.e(TAG, "write command failed? reconnect!");
174            if (!connect() || !writeCommand(cmd)) {
175                return "-1";
176            }
177        }
178        if (LOCAL_DEBUG) {
179            Slog.i(TAG, "send: '" + cmd + "'");
180        }
181        if (readReply()) {
182            String s = new String(buf, 0, buflen);
183            if (LOCAL_DEBUG) {
184                Slog.i(TAG, "recv: '" + s + "'");
185            }
186            return s;
187        } else {
188            if (LOCAL_DEBUG) {
189                Slog.i(TAG, "fail");
190            }
191            return "-1";
192        }
193    }
194
195    private int execute(String cmd) {
196        String res = transaction(cmd);
197        try {
198            return Integer.parseInt(res);
199        } catch (NumberFormatException ex) {
200            return -1;
201        }
202    }
203
204    public int install(String name, int uid, int gid, String seinfo) {
205        StringBuilder builder = new StringBuilder("install");
206        builder.append(' ');
207        builder.append(name);
208        builder.append(' ');
209        builder.append(uid);
210        builder.append(' ');
211        builder.append(gid);
212        builder.append(' ');
213        builder.append(seinfo != null ? seinfo : "!");
214        return execute(builder.toString());
215    }
216
217    public int patchoat(String apkPath, int uid, boolean isPublic, String pkgName,
218            String instructionSet) {
219        if (!isValidInstructionSet(instructionSet)) {
220            Slog.e(TAG, "Invalid instruction set: " + instructionSet);
221            return -1;
222        }
223
224        StringBuilder builder = new StringBuilder("patchoat");
225        builder.append(' ');
226        builder.append(apkPath);
227        builder.append(' ');
228        builder.append(uid);
229        builder.append(isPublic ? " 1" : " 0");
230        builder.append(' ');
231        builder.append(pkgName);
232        builder.append(' ');
233        builder.append(instructionSet);
234        return execute(builder.toString());
235    }
236
237    public int patchoat(String apkPath, int uid, boolean isPublic, String instructionSet) {
238        if (!isValidInstructionSet(instructionSet)) {
239            Slog.e(TAG, "Invalid instruction set: " + instructionSet);
240            return -1;
241        }
242
243        StringBuilder builder = new StringBuilder("patchoat");
244        builder.append(' ');
245        builder.append(apkPath);
246        builder.append(' ');
247        builder.append(uid);
248        builder.append(isPublic ? " 1" : " 0");
249        builder.append(" *");         // No pkgName arg present
250        builder.append(' ');
251        builder.append(instructionSet);
252        return execute(builder.toString());
253    }
254
255    public int dexopt(String apkPath, int uid, boolean isPublic, String instructionSet) {
256        if (!isValidInstructionSet(instructionSet)) {
257            Slog.e(TAG, "Invalid instruction set: " + instructionSet);
258            return -1;
259        }
260
261        StringBuilder builder = new StringBuilder("dexopt");
262        builder.append(' ');
263        builder.append(apkPath);
264        builder.append(' ');
265        builder.append(uid);
266        builder.append(isPublic ? " 1" : " 0");
267        builder.append(" *");         // No pkgName arg present
268        builder.append(' ');
269        builder.append(instructionSet);
270        return execute(builder.toString());
271    }
272
273    public int dexopt(String apkPath, int uid, boolean isPublic, String pkgName,
274            String instructionSet) {
275        if (!isValidInstructionSet(instructionSet)) {
276            Slog.e(TAG, "Invalid instruction set: " + instructionSet);
277            return -1;
278        }
279
280        StringBuilder builder = new StringBuilder("dexopt");
281        builder.append(' ');
282        builder.append(apkPath);
283        builder.append(' ');
284        builder.append(uid);
285        builder.append(isPublic ? " 1" : " 0");
286        builder.append(' ');
287        builder.append(pkgName);
288        builder.append(' ');
289        builder.append(instructionSet);
290        return execute(builder.toString());
291    }
292
293    public int idmap(String targetApkPath, String overlayApkPath, int uid) {
294        StringBuilder builder = new StringBuilder("idmap");
295        builder.append(' ');
296        builder.append(targetApkPath);
297        builder.append(' ');
298        builder.append(overlayApkPath);
299        builder.append(' ');
300        builder.append(uid);
301        return execute(builder.toString());
302    }
303
304    public int movedex(String srcPath, String dstPath, String instructionSet) {
305        if (!isValidInstructionSet(instructionSet)) {
306            Slog.e(TAG, "Invalid instruction set: " + instructionSet);
307            return -1;
308        }
309
310        StringBuilder builder = new StringBuilder("movedex");
311        builder.append(' ');
312        builder.append(srcPath);
313        builder.append(' ');
314        builder.append(dstPath);
315        builder.append(' ');
316        builder.append(instructionSet);
317        return execute(builder.toString());
318    }
319
320    public int rmdex(String codePath, String instructionSet) {
321        if (!isValidInstructionSet(instructionSet)) {
322            Slog.e(TAG, "Invalid instruction set: " + instructionSet);
323            return -1;
324        }
325
326        StringBuilder builder = new StringBuilder("rmdex");
327        builder.append(' ');
328        builder.append(codePath);
329        builder.append(' ');
330        builder.append(instructionSet);
331        return execute(builder.toString());
332    }
333
334    public int remove(String name, int userId) {
335        StringBuilder builder = new StringBuilder("remove");
336        builder.append(' ');
337        builder.append(name);
338        builder.append(' ');
339        builder.append(userId);
340        return execute(builder.toString());
341    }
342
343    public int rename(String oldname, String newname) {
344        StringBuilder builder = new StringBuilder("rename");
345        builder.append(' ');
346        builder.append(oldname);
347        builder.append(' ');
348        builder.append(newname);
349        return execute(builder.toString());
350    }
351
352    public int fixUid(String name, int uid, int gid) {
353        StringBuilder builder = new StringBuilder("fixuid");
354        builder.append(' ');
355        builder.append(name);
356        builder.append(' ');
357        builder.append(uid);
358        builder.append(' ');
359        builder.append(gid);
360        return execute(builder.toString());
361    }
362
363    public int deleteCacheFiles(String name, int userId) {
364        StringBuilder builder = new StringBuilder("rmcache");
365        builder.append(' ');
366        builder.append(name);
367        builder.append(' ');
368        builder.append(userId);
369        return execute(builder.toString());
370    }
371
372    public int deleteCodeCacheFiles(String name, int userId) {
373        StringBuilder builder = new StringBuilder("rmcodecache");
374        builder.append(' ');
375        builder.append(name);
376        builder.append(' ');
377        builder.append(userId);
378        return execute(builder.toString());
379    }
380
381    public int createUserData(String name, int uid, int userId, String seinfo) {
382        StringBuilder builder = new StringBuilder("mkuserdata");
383        builder.append(' ');
384        builder.append(name);
385        builder.append(' ');
386        builder.append(uid);
387        builder.append(' ');
388        builder.append(userId);
389        builder.append(' ');
390        builder.append(seinfo != null ? seinfo : "!");
391        return execute(builder.toString());
392    }
393
394    public int createUserConfig(int userId) {
395        StringBuilder builder = new StringBuilder("mkuserconfig");
396        builder.append(' ');
397        builder.append(userId);
398        return execute(builder.toString());
399    }
400
401    public int removeUserDataDirs(int userId) {
402        StringBuilder builder = new StringBuilder("rmuser");
403        builder.append(' ');
404        builder.append(userId);
405        return execute(builder.toString());
406    }
407
408    public int clearUserData(String name, int userId) {
409        StringBuilder builder = new StringBuilder("rmuserdata");
410        builder.append(' ');
411        builder.append(name);
412        builder.append(' ');
413        builder.append(userId);
414        return execute(builder.toString());
415    }
416
417    public boolean ping() {
418        if (execute("ping") < 0) {
419            return false;
420        } else {
421            return true;
422        }
423    }
424
425    public int pruneDexCache(String cacheSubDir) {
426        return execute("prunedexcache " + cacheSubDir);
427    }
428
429    public int freeCache(long freeStorageSize) {
430        StringBuilder builder = new StringBuilder("freecache");
431        builder.append(' ');
432        builder.append(String.valueOf(freeStorageSize));
433        return execute(builder.toString());
434    }
435
436    public int getSizeInfo(String pkgName, int persona, String apkPath, String libDirPath,
437            String fwdLockApkPath, String asecPath, String[] instructionSets, PackageStats pStats) {
438        for (String instructionSet : instructionSets) {
439            if (!isValidInstructionSet(instructionSet)) {
440                Slog.e(TAG, "Invalid instruction set: " + instructionSet);
441                return -1;
442            }
443        }
444
445        StringBuilder builder = new StringBuilder("getsize");
446        builder.append(' ');
447        builder.append(pkgName);
448        builder.append(' ');
449        builder.append(persona);
450        builder.append(' ');
451        builder.append(apkPath);
452        builder.append(' ');
453        // TODO: Extend getSizeInfo to look at the full subdirectory tree,
454        // not just the first level.
455        builder.append(libDirPath != null ? libDirPath : "!");
456        builder.append(' ');
457        builder.append(fwdLockApkPath != null ? fwdLockApkPath : "!");
458        builder.append(' ');
459        builder.append(asecPath != null ? asecPath : "!");
460        builder.append(' ');
461        // TODO: Extend getSizeInfo to look at *all* instrution sets, not
462        // just the primary.
463        builder.append(instructionSets[0]);
464
465        String s = transaction(builder.toString());
466        String res[] = s.split(" ");
467
468        if ((res == null) || (res.length != 5)) {
469            return -1;
470        }
471        try {
472            pStats.codeSize = Long.parseLong(res[1]);
473            pStats.dataSize = Long.parseLong(res[2]);
474            pStats.cacheSize = Long.parseLong(res[3]);
475            pStats.externalCodeSize = Long.parseLong(res[4]);
476            return Integer.parseInt(res[0]);
477        } catch (NumberFormatException e) {
478            return -1;
479        }
480    }
481
482    public int moveFiles() {
483        return execute("movefiles");
484    }
485
486    /**
487     * Links the 32 bit native library directory in an application's data directory to the
488     * real location for backward compatibility. Note that no such symlink is created for
489     * 64 bit shared libraries.
490     *
491     * @return -1 on error
492     */
493    public int linkNativeLibraryDirectory(String dataPath, String nativeLibPath32, int userId) {
494        if (dataPath == null) {
495            Slog.e(TAG, "linkNativeLibraryDirectory dataPath is null");
496            return -1;
497        } else if (nativeLibPath32 == null) {
498            Slog.e(TAG, "linkNativeLibraryDirectory nativeLibPath is null");
499            return -1;
500        }
501
502        StringBuilder builder = new StringBuilder("linklib ");
503        builder.append(dataPath);
504        builder.append(' ');
505        builder.append(nativeLibPath32);
506        builder.append(' ');
507        builder.append(userId);
508
509        return execute(builder.toString());
510    }
511
512    public boolean restoreconData(String pkgName, String seinfo, int uid) {
513        StringBuilder builder = new StringBuilder("restorecondata");
514        builder.append(' ');
515        builder.append(pkgName);
516        builder.append(' ');
517        builder.append(seinfo != null ? seinfo : "!");
518        builder.append(' ');
519        builder.append(uid);
520        return (execute(builder.toString()) == 0);
521    }
522
523    /**
524     * Returns true iff. {@code instructionSet} is a valid instruction set.
525     */
526    private static boolean isValidInstructionSet(String instructionSet) {
527        if (instructionSet == null) {
528            return false;
529        }
530
531        for (String abi : Build.SUPPORTED_ABIS) {
532            if (instructionSet.equals(VMRuntime.getInstructionSet(abi))) {
533                return true;
534            }
535        }
536
537        return false;
538    }
539}
540