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