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