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 public final 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, int userId) {
    258         StringBuilder builder = new StringBuilder("rmcache");
    259         builder.append(' ');
    260         builder.append(name);
    261         builder.append(' ');
    262         builder.append(userId);
    263         return execute(builder.toString());
    264     }
    265 
    266     public int createUserData(String name, int uid, int userId) {
    267         StringBuilder builder = new StringBuilder("mkuserdata");
    268         builder.append(' ');
    269         builder.append(name);
    270         builder.append(' ');
    271         builder.append(uid);
    272         builder.append(' ');
    273         builder.append(userId);
    274         return execute(builder.toString());
    275     }
    276 
    277     public int removeUserDataDirs(int userId) {
    278         StringBuilder builder = new StringBuilder("rmuser");
    279         builder.append(' ');
    280         builder.append(userId);
    281         return execute(builder.toString());
    282     }
    283 
    284     public int clearUserData(String name, int userId) {
    285         StringBuilder builder = new StringBuilder("rmuserdata");
    286         builder.append(' ');
    287         builder.append(name);
    288         builder.append(' ');
    289         builder.append(userId);
    290         return execute(builder.toString());
    291     }
    292 
    293     /**
    294      * Clone all the package data directories from srcUserId to targetUserId. If copyData is true,
    295      * some of the data is also copied, otherwise just empty directories are created with the
    296      * correct access rights.
    297      * @param srcUserId user to copy the data directories from
    298      * @param targetUserId user to copy the data directories to
    299      * @param copyData whether the data itself is to be copied. If false, empty directories are
    300      * created.
    301      * @return success/error code
    302      */
    303     public int cloneUserData(int srcUserId, int targetUserId, boolean copyData) {
    304         StringBuilder builder = new StringBuilder("cloneuserdata");
    305         builder.append(' ');
    306         builder.append(srcUserId);
    307         builder.append(' ');
    308         builder.append(targetUserId);
    309         builder.append(' ');
    310         builder.append(copyData ? '1' : '0');
    311         return execute(builder.toString());
    312     }
    313 
    314     public boolean ping() {
    315         if (execute("ping") < 0) {
    316             return false;
    317         } else {
    318             return true;
    319         }
    320     }
    321 
    322     public int freeCache(long freeStorageSize) {
    323         StringBuilder builder = new StringBuilder("freecache");
    324         builder.append(' ');
    325         builder.append(String.valueOf(freeStorageSize));
    326         return execute(builder.toString());
    327     }
    328 
    329     public int getSizeInfo(String pkgName, int persona, String apkPath, String fwdLockApkPath,
    330             String asecPath, PackageStats pStats) {
    331         StringBuilder builder = new StringBuilder("getsize");
    332         builder.append(' ');
    333         builder.append(pkgName);
    334         builder.append(' ');
    335         builder.append(persona);
    336         builder.append(' ');
    337         builder.append(apkPath);
    338         builder.append(' ');
    339         builder.append(fwdLockApkPath != null ? fwdLockApkPath : "!");
    340         builder.append(' ');
    341         builder.append(asecPath != null ? asecPath : "!");
    342 
    343         String s = transaction(builder.toString());
    344         String res[] = s.split(" ");
    345 
    346         if ((res == null) || (res.length != 5)) {
    347             return -1;
    348         }
    349         try {
    350             pStats.codeSize = Long.parseLong(res[1]);
    351             pStats.dataSize = Long.parseLong(res[2]);
    352             pStats.cacheSize = Long.parseLong(res[3]);
    353             pStats.externalCodeSize = Long.parseLong(res[4]);
    354             return Integer.parseInt(res[0]);
    355         } catch (NumberFormatException e) {
    356             return -1;
    357         }
    358     }
    359 
    360     public int moveFiles() {
    361         return execute("movefiles");
    362     }
    363 
    364     /**
    365      * Links the native library directory in an application's directory to its
    366      * real location.
    367      *
    368      * @param dataPath data directory where the application is
    369      * @param nativeLibPath target native library path
    370      * @return -1 on error
    371      */
    372     public int linkNativeLibraryDirectory(String dataPath, String nativeLibPath, int userId) {
    373         if (dataPath == null) {
    374             Slog.e(TAG, "linkNativeLibraryDirectory dataPath is null");
    375             return -1;
    376         } else if (nativeLibPath == null) {
    377             Slog.e(TAG, "linkNativeLibraryDirectory nativeLibPath is null");
    378             return -1;
    379         }
    380 
    381         StringBuilder builder = new StringBuilder("linklib ");
    382         builder.append(dataPath);
    383         builder.append(' ');
    384         builder.append(nativeLibPath);
    385         builder.append(' ');
    386         builder.append(userId);
    387 
    388         return execute(builder.toString());
    389     }
    390 }
    391