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