/* * Copyright (C) 2008 The Android Open Source Project * * Licensed under the Apache License, Version 2.0 (the "License"); * you may not use this file except in compliance with the License. * You may obtain a copy of the License at * * http://www.apache.org/licenses/LICENSE-2.0 * * Unless required by applicable law or agreed to in writing, software * distributed under the License is distributed on an "AS IS" BASIS, * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. * See the License for the specific language governing permissions and * limitations under the License. */ package com.android.server.pm; import android.content.pm.PackageStats; import android.net.LocalSocket; import android.net.LocalSocketAddress; import android.util.Slog; import java.io.IOException; import java.io.InputStream; import java.io.OutputStream; public final class Installer { private static final String TAG = "Installer"; private static final boolean LOCAL_DEBUG = false; InputStream mIn; OutputStream mOut; LocalSocket mSocket; byte buf[] = new byte[1024]; int buflen = 0; private boolean connect() { if (mSocket != null) { return true; } Slog.i(TAG, "connecting..."); try { mSocket = new LocalSocket(); LocalSocketAddress address = new LocalSocketAddress("installd", LocalSocketAddress.Namespace.RESERVED); mSocket.connect(address); mIn = mSocket.getInputStream(); mOut = mSocket.getOutputStream(); } catch (IOException ex) { disconnect(); return false; } return true; } private void disconnect() { Slog.i(TAG, "disconnecting..."); try { if (mSocket != null) mSocket.close(); } catch (IOException ex) { } try { if (mIn != null) mIn.close(); } catch (IOException ex) { } try { if (mOut != null) mOut.close(); } catch (IOException ex) { } mSocket = null; mIn = null; mOut = null; } private boolean readBytes(byte buffer[], int len) { int off = 0, count; if (len < 0) return false; while (off != len) { try { count = mIn.read(buffer, off, len - off); if (count <= 0) { Slog.e(TAG, "read error " + count); break; } off += count; } catch (IOException ex) { Slog.e(TAG, "read exception"); break; } } if (LOCAL_DEBUG) { Slog.i(TAG, "read " + len + " bytes"); } if (off == len) return true; disconnect(); return false; } private boolean readReply() { int len; buflen = 0; if (!readBytes(buf, 2)) return false; len = (((int) buf[0]) & 0xff) | ((((int) buf[1]) & 0xff) << 8); if ((len < 1) || (len > 1024)) { Slog.e(TAG, "invalid reply length (" + len + ")"); disconnect(); return false; } if (!readBytes(buf, len)) return false; buflen = len; return true; } private boolean writeCommand(String _cmd) { byte[] cmd = _cmd.getBytes(); int len = cmd.length; if ((len < 1) || (len > 1024)) return false; buf[0] = (byte) (len & 0xff); buf[1] = (byte) ((len >> 8) & 0xff); try { mOut.write(buf, 0, 2); mOut.write(cmd, 0, len); } catch (IOException ex) { Slog.e(TAG, "write error"); disconnect(); return false; } return true; } private synchronized String transaction(String cmd) { if (!connect()) { Slog.e(TAG, "connection failed"); return "-1"; } if (!writeCommand(cmd)) { /* * If installd died and restarted in the background (unlikely but * possible) we'll fail on the next write (this one). Try to * reconnect and write the command one more time before giving up. */ Slog.e(TAG, "write command failed? reconnect!"); if (!connect() || !writeCommand(cmd)) { return "-1"; } } if (LOCAL_DEBUG) { Slog.i(TAG, "send: '" + cmd + "'"); } if (readReply()) { String s = new String(buf, 0, buflen); if (LOCAL_DEBUG) { Slog.i(TAG, "recv: '" + s + "'"); } return s; } else { if (LOCAL_DEBUG) { Slog.i(TAG, "fail"); } return "-1"; } } private int execute(String cmd) { String res = transaction(cmd); try { return Integer.parseInt(res); } catch (NumberFormatException ex) { return -1; } } public int install(String name, int uid, int gid) { StringBuilder builder = new StringBuilder("install"); builder.append(' '); builder.append(name); builder.append(' '); builder.append(uid); builder.append(' '); builder.append(gid); return execute(builder.toString()); } public int dexopt(String apkPath, int uid, boolean isPublic) { StringBuilder builder = new StringBuilder("dexopt"); builder.append(' '); builder.append(apkPath); builder.append(' '); builder.append(uid); builder.append(isPublic ? " 1" : " 0"); return execute(builder.toString()); } public int movedex(String srcPath, String dstPath) { StringBuilder builder = new StringBuilder("movedex"); builder.append(' '); builder.append(srcPath); builder.append(' '); builder.append(dstPath); return execute(builder.toString()); } public int rmdex(String codePath) { StringBuilder builder = new StringBuilder("rmdex"); builder.append(' '); builder.append(codePath); return execute(builder.toString()); } public int remove(String name, int userId) { StringBuilder builder = new StringBuilder("remove"); builder.append(' '); builder.append(name); builder.append(' '); builder.append(userId); return execute(builder.toString()); } public int rename(String oldname, String newname) { StringBuilder builder = new StringBuilder("rename"); builder.append(' '); builder.append(oldname); builder.append(' '); builder.append(newname); return execute(builder.toString()); } public int fixUid(String name, int uid, int gid) { StringBuilder builder = new StringBuilder("fixuid"); builder.append(' '); builder.append(name); builder.append(' '); builder.append(uid); builder.append(' '); builder.append(gid); return execute(builder.toString()); } public int deleteCacheFiles(String name, int userId) { StringBuilder builder = new StringBuilder("rmcache"); builder.append(' '); builder.append(name); builder.append(' '); builder.append(userId); return execute(builder.toString()); } public int createUserData(String name, int uid, int userId) { StringBuilder builder = new StringBuilder("mkuserdata"); builder.append(' '); builder.append(name); builder.append(' '); builder.append(uid); builder.append(' '); builder.append(userId); return execute(builder.toString()); } public int removeUserDataDirs(int userId) { StringBuilder builder = new StringBuilder("rmuser"); builder.append(' '); builder.append(userId); return execute(builder.toString()); } public int clearUserData(String name, int userId) { StringBuilder builder = new StringBuilder("rmuserdata"); builder.append(' '); builder.append(name); builder.append(' '); builder.append(userId); return execute(builder.toString()); } /** * Clone all the package data directories from srcUserId to targetUserId. If copyData is true, * some of the data is also copied, otherwise just empty directories are created with the * correct access rights. * @param srcUserId user to copy the data directories from * @param targetUserId user to copy the data directories to * @param copyData whether the data itself is to be copied. If false, empty directories are * created. * @return success/error code */ public int cloneUserData(int srcUserId, int targetUserId, boolean copyData) { StringBuilder builder = new StringBuilder("cloneuserdata"); builder.append(' '); builder.append(srcUserId); builder.append(' '); builder.append(targetUserId); builder.append(' '); builder.append(copyData ? '1' : '0'); return execute(builder.toString()); } public boolean ping() { if (execute("ping") < 0) { return false; } else { return true; } } public int freeCache(long freeStorageSize) { StringBuilder builder = new StringBuilder("freecache"); builder.append(' '); builder.append(String.valueOf(freeStorageSize)); return execute(builder.toString()); } public int getSizeInfo(String pkgName, int persona, String apkPath, String fwdLockApkPath, String asecPath, PackageStats pStats) { StringBuilder builder = new StringBuilder("getsize"); builder.append(' '); builder.append(pkgName); builder.append(' '); builder.append(persona); builder.append(' '); builder.append(apkPath); builder.append(' '); builder.append(fwdLockApkPath != null ? fwdLockApkPath : "!"); builder.append(' '); builder.append(asecPath != null ? asecPath : "!"); String s = transaction(builder.toString()); String res[] = s.split(" "); if ((res == null) || (res.length != 5)) { return -1; } try { pStats.codeSize = Long.parseLong(res[1]); pStats.dataSize = Long.parseLong(res[2]); pStats.cacheSize = Long.parseLong(res[3]); pStats.externalCodeSize = Long.parseLong(res[4]); return Integer.parseInt(res[0]); } catch (NumberFormatException e) { return -1; } } public int moveFiles() { return execute("movefiles"); } /** * Links the native library directory in an application's directory to its * real location. * * @param dataPath data directory where the application is * @param nativeLibPath target native library path * @return -1 on error */ public int linkNativeLibraryDirectory(String dataPath, String nativeLibPath, int userId) { if (dataPath == null) { Slog.e(TAG, "linkNativeLibraryDirectory dataPath is null"); return -1; } else if (nativeLibPath == null) { Slog.e(TAG, "linkNativeLibraryDirectory nativeLibPath is null"); return -1; } StringBuilder builder = new StringBuilder("linklib "); builder.append(dataPath); builder.append(' '); builder.append(nativeLibPath); builder.append(' '); builder.append(userId); return execute(builder.toString()); } }