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.content.Context;
20import android.content.pm.PackageStats;
21import android.os.Build;
22import android.util.Slog;
23import dalvik.system.VMRuntime;
24
25import com.android.internal.os.InstallerConnection;
26import com.android.server.SystemService;
27
28public final class Installer extends SystemService {
29    private static final String TAG = "Installer";
30
31    private final InstallerConnection mInstaller;
32
33    public Installer(Context context) {
34        super(context);
35        mInstaller = new InstallerConnection();
36    }
37
38    @Override
39    public void onStart() {
40        Slog.i(TAG, "Waiting for installd to be ready.");
41        ping();
42    }
43
44    public int install(String name, int uid, int gid, String seinfo) {
45        StringBuilder builder = new StringBuilder("install");
46        builder.append(' ');
47        builder.append(name);
48        builder.append(' ');
49        builder.append(uid);
50        builder.append(' ');
51        builder.append(gid);
52        builder.append(' ');
53        builder.append(seinfo != null ? seinfo : "!");
54        return mInstaller.execute(builder.toString());
55    }
56
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    }
75
76    public int dexopt(String apkPath, int uid, boolean isPublic, String instructionSet) {
77        if (!isValidInstructionSet(instructionSet)) {
78            Slog.e(TAG, "Invalid instruction set: " + instructionSet);
79            return -1;
80        }
81
82        return mInstaller.dexopt(apkPath, uid, isPublic, instructionSet);
83    }
84
85    public int dexopt(String apkPath, int uid, boolean isPublic, String pkgName,
86            String instructionSet, boolean vmSafeMode) {
87        if (!isValidInstructionSet(instructionSet)) {
88            Slog.e(TAG, "Invalid instruction set: " + instructionSet);
89            return -1;
90        }
91
92        return mInstaller.dexopt(apkPath, uid, isPublic, pkgName, instructionSet, vmSafeMode);
93    }
94
95    public int idmap(String targetApkPath, String overlayApkPath, int uid) {
96        StringBuilder builder = new StringBuilder("idmap");
97        builder.append(' ');
98        builder.append(targetApkPath);
99        builder.append(' ');
100        builder.append(overlayApkPath);
101        builder.append(' ');
102        builder.append(uid);
103        return mInstaller.execute(builder.toString());
104    }
105
106    public int movedex(String srcPath, String dstPath, String instructionSet) {
107        if (!isValidInstructionSet(instructionSet)) {
108            Slog.e(TAG, "Invalid instruction set: " + instructionSet);
109            return -1;
110        }
111
112        StringBuilder builder = new StringBuilder("movedex");
113        builder.append(' ');
114        builder.append(srcPath);
115        builder.append(' ');
116        builder.append(dstPath);
117        builder.append(' ');
118        builder.append(instructionSet);
119        return mInstaller.execute(builder.toString());
120    }
121
122    public int rmdex(String codePath, String instructionSet) {
123        if (!isValidInstructionSet(instructionSet)) {
124            Slog.e(TAG, "Invalid instruction set: " + instructionSet);
125            return -1;
126        }
127
128        StringBuilder builder = new StringBuilder("rmdex");
129        builder.append(' ');
130        builder.append(codePath);
131        builder.append(' ');
132        builder.append(instructionSet);
133        return mInstaller.execute(builder.toString());
134    }
135
136    public int remove(String name, int userId) {
137        StringBuilder builder = new StringBuilder("remove");
138        builder.append(' ');
139        builder.append(name);
140        builder.append(' ');
141        builder.append(userId);
142        return mInstaller.execute(builder.toString());
143    }
144
145    public int rename(String oldname, String newname) {
146        StringBuilder builder = new StringBuilder("rename");
147        builder.append(' ');
148        builder.append(oldname);
149        builder.append(' ');
150        builder.append(newname);
151        return mInstaller.execute(builder.toString());
152    }
153
154    public int fixUid(String name, int uid, int gid) {
155        StringBuilder builder = new StringBuilder("fixuid");
156        builder.append(' ');
157        builder.append(name);
158        builder.append(' ');
159        builder.append(uid);
160        builder.append(' ');
161        builder.append(gid);
162        return mInstaller.execute(builder.toString());
163    }
164
165    public int deleteCacheFiles(String name, int userId) {
166        StringBuilder builder = new StringBuilder("rmcache");
167        builder.append(' ');
168        builder.append(name);
169        builder.append(' ');
170        builder.append(userId);
171        return mInstaller.execute(builder.toString());
172    }
173
174    public int deleteCodeCacheFiles(String name, int userId) {
175        StringBuilder builder = new StringBuilder("rmcodecache");
176        builder.append(' ');
177        builder.append(name);
178        builder.append(' ');
179        builder.append(userId);
180        return mInstaller.execute(builder.toString());
181    }
182
183    public int createUserData(String name, int uid, int userId, String seinfo) {
184        StringBuilder builder = new StringBuilder("mkuserdata");
185        builder.append(' ');
186        builder.append(name);
187        builder.append(' ');
188        builder.append(uid);
189        builder.append(' ');
190        builder.append(userId);
191        builder.append(' ');
192        builder.append(seinfo != null ? seinfo : "!");
193        return mInstaller.execute(builder.toString());
194    }
195
196    public int createUserConfig(int userId) {
197        StringBuilder builder = new StringBuilder("mkuserconfig");
198        builder.append(' ');
199        builder.append(userId);
200        return mInstaller.execute(builder.toString());
201    }
202
203    public int removeUserDataDirs(int userId) {
204        StringBuilder builder = new StringBuilder("rmuser");
205        builder.append(' ');
206        builder.append(userId);
207        return mInstaller.execute(builder.toString());
208    }
209
210    public int clearUserData(String name, int userId) {
211        StringBuilder builder = new StringBuilder("rmuserdata");
212        builder.append(' ');
213        builder.append(name);
214        builder.append(' ');
215        builder.append(userId);
216        return mInstaller.execute(builder.toString());
217    }
218
219    public boolean ping() {
220        if (mInstaller.execute("ping") < 0) {
221            return false;
222        } else {
223            return true;
224        }
225    }
226
227    public int freeCache(long freeStorageSize) {
228        StringBuilder builder = new StringBuilder("freecache");
229        builder.append(' ');
230        builder.append(String.valueOf(freeStorageSize));
231        return mInstaller.execute(builder.toString());
232    }
233
234    public int getSizeInfo(String pkgName, int persona, String apkPath, String libDirPath,
235            String fwdLockApkPath, String asecPath, String[] instructionSets, PackageStats pStats) {
236        for (String instructionSet : instructionSets) {
237            if (!isValidInstructionSet(instructionSet)) {
238                Slog.e(TAG, "Invalid instruction set: " + instructionSet);
239                return -1;
240            }
241        }
242
243        StringBuilder builder = new StringBuilder("getsize");
244        builder.append(' ');
245        builder.append(pkgName);
246        builder.append(' ');
247        builder.append(persona);
248        builder.append(' ');
249        builder.append(apkPath);
250        builder.append(' ');
251        // TODO: Extend getSizeInfo to look at the full subdirectory tree,
252        // not just the first level.
253        builder.append(libDirPath != null ? libDirPath : "!");
254        builder.append(' ');
255        builder.append(fwdLockApkPath != null ? fwdLockApkPath : "!");
256        builder.append(' ');
257        builder.append(asecPath != null ? asecPath : "!");
258        builder.append(' ');
259        // TODO: Extend getSizeInfo to look at *all* instrution sets, not
260        // just the primary.
261        builder.append(instructionSets[0]);
262
263        String s = mInstaller.transact(builder.toString());
264        String res[] = s.split(" ");
265
266        if ((res == null) || (res.length != 5)) {
267            return -1;
268        }
269        try {
270            pStats.codeSize = Long.parseLong(res[1]);
271            pStats.dataSize = Long.parseLong(res[2]);
272            pStats.cacheSize = Long.parseLong(res[3]);
273            pStats.externalCodeSize = Long.parseLong(res[4]);
274            return Integer.parseInt(res[0]);
275        } catch (NumberFormatException e) {
276            return -1;
277        }
278    }
279
280    public int moveFiles() {
281        return mInstaller.execute("movefiles");
282    }
283
284    /**
285     * Links the 32 bit native library directory in an application's data directory to the
286     * real location for backward compatibility. Note that no such symlink is created for
287     * 64 bit shared libraries.
288     *
289     * @return -1 on error
290     */
291    public int linkNativeLibraryDirectory(String dataPath, String nativeLibPath32, int userId) {
292        if (dataPath == null) {
293            Slog.e(TAG, "linkNativeLibraryDirectory dataPath is null");
294            return -1;
295        } else if (nativeLibPath32 == null) {
296            Slog.e(TAG, "linkNativeLibraryDirectory nativeLibPath is null");
297            return -1;
298        }
299
300        StringBuilder builder = new StringBuilder("linklib ");
301        builder.append(dataPath);
302        builder.append(' ');
303        builder.append(nativeLibPath32);
304        builder.append(' ');
305        builder.append(userId);
306
307        return mInstaller.execute(builder.toString());
308    }
309
310    public boolean restoreconData(String pkgName, String seinfo, int uid) {
311        StringBuilder builder = new StringBuilder("restorecondata");
312        builder.append(' ');
313        builder.append(pkgName);
314        builder.append(' ');
315        builder.append(seinfo != null ? seinfo : "!");
316        builder.append(' ');
317        builder.append(uid);
318        return (mInstaller.execute(builder.toString()) == 0);
319    }
320
321    /**
322     * Returns true iff. {@code instructionSet} is a valid instruction set.
323     */
324    private static boolean isValidInstructionSet(String instructionSet) {
325        if (instructionSet == null) {
326            return false;
327        }
328
329        for (String abi : Build.SUPPORTED_ABIS) {
330            if (instructionSet.equals(VMRuntime.getInstructionSet(abi))) {
331                return true;
332            }
333        }
334
335        return false;
336    }
337}
338