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 int markBootComplete(String instructionSet) {
220        if (!isValidInstructionSet(instructionSet)) {
221            Slog.e(TAG, "Invalid instruction set: " + instructionSet);
222            return -1;
223        }
224
225        StringBuilder builder = new StringBuilder("markbootcomplete");
226        builder.append(' ');
227        builder.append(instructionSet);
228        return mInstaller.execute(builder.toString());
229    }
230
231    public boolean ping() {
232        if (mInstaller.execute("ping") < 0) {
233            return false;
234        } else {
235            return true;
236        }
237    }
238
239    public int freeCache(long freeStorageSize) {
240        StringBuilder builder = new StringBuilder("freecache");
241        builder.append(' ');
242        builder.append(String.valueOf(freeStorageSize));
243        return mInstaller.execute(builder.toString());
244    }
245
246    public int getSizeInfo(String pkgName, int persona, String apkPath, String libDirPath,
247            String fwdLockApkPath, String asecPath, String[] instructionSets, PackageStats pStats) {
248        for (String instructionSet : instructionSets) {
249            if (!isValidInstructionSet(instructionSet)) {
250                Slog.e(TAG, "Invalid instruction set: " + instructionSet);
251                return -1;
252            }
253        }
254
255        StringBuilder builder = new StringBuilder("getsize");
256        builder.append(' ');
257        builder.append(pkgName);
258        builder.append(' ');
259        builder.append(persona);
260        builder.append(' ');
261        builder.append(apkPath);
262        builder.append(' ');
263        // TODO: Extend getSizeInfo to look at the full subdirectory tree,
264        // not just the first level.
265        builder.append(libDirPath != null ? libDirPath : "!");
266        builder.append(' ');
267        builder.append(fwdLockApkPath != null ? fwdLockApkPath : "!");
268        builder.append(' ');
269        builder.append(asecPath != null ? asecPath : "!");
270        builder.append(' ');
271        // TODO: Extend getSizeInfo to look at *all* instrution sets, not
272        // just the primary.
273        builder.append(instructionSets[0]);
274
275        String s = mInstaller.transact(builder.toString());
276        String res[] = s.split(" ");
277
278        if ((res == null) || (res.length != 5)) {
279            return -1;
280        }
281        try {
282            pStats.codeSize = Long.parseLong(res[1]);
283            pStats.dataSize = Long.parseLong(res[2]);
284            pStats.cacheSize = Long.parseLong(res[3]);
285            pStats.externalCodeSize = Long.parseLong(res[4]);
286            return Integer.parseInt(res[0]);
287        } catch (NumberFormatException e) {
288            return -1;
289        }
290    }
291
292    public int moveFiles() {
293        return mInstaller.execute("movefiles");
294    }
295
296    /**
297     * Links the 32 bit native library directory in an application's data directory to the
298     * real location for backward compatibility. Note that no such symlink is created for
299     * 64 bit shared libraries.
300     *
301     * @return -1 on error
302     */
303    public int linkNativeLibraryDirectory(String dataPath, String nativeLibPath32, int userId) {
304        if (dataPath == null) {
305            Slog.e(TAG, "linkNativeLibraryDirectory dataPath is null");
306            return -1;
307        } else if (nativeLibPath32 == null) {
308            Slog.e(TAG, "linkNativeLibraryDirectory nativeLibPath is null");
309            return -1;
310        }
311
312        StringBuilder builder = new StringBuilder("linklib ");
313        builder.append(dataPath);
314        builder.append(' ');
315        builder.append(nativeLibPath32);
316        builder.append(' ');
317        builder.append(userId);
318
319        return mInstaller.execute(builder.toString());
320    }
321
322    public boolean restoreconData(String pkgName, String seinfo, int uid) {
323        StringBuilder builder = new StringBuilder("restorecondata");
324        builder.append(' ');
325        builder.append(pkgName);
326        builder.append(' ');
327        builder.append(seinfo != null ? seinfo : "!");
328        builder.append(' ');
329        builder.append(uid);
330        return (mInstaller.execute(builder.toString()) == 0);
331    }
332
333    /**
334     * Returns true iff. {@code instructionSet} is a valid instruction set.
335     */
336    private static boolean isValidInstructionSet(String instructionSet) {
337        if (instructionSet == null) {
338            return false;
339        }
340
341        for (String abi : Build.SUPPORTED_ABIS) {
342            if (instructionSet.equals(VMRuntime.getInstructionSet(abi))) {
343                return true;
344            }
345        }
346
347        return false;
348    }
349}
350