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 deleteCacheFiles(String name) {
    247         StringBuilder builder = new StringBuilder("rmcache");
    248         builder.append(' ');
    249         builder.append(name);
    250         return execute(builder.toString());
    251     }
    252 
    253     public int createUserData(String name, int uid, int userId) {
    254         StringBuilder builder = new StringBuilder("mkuserdata");
    255         builder.append(' ');
    256         builder.append(name);
    257         builder.append(' ');
    258         builder.append(uid);
    259         builder.append(' ');
    260         builder.append(userId);
    261         return execute(builder.toString());
    262     }
    263 
    264     public int removeUserDataDirs(int userId) {
    265         StringBuilder builder = new StringBuilder("rmuser");
    266         builder.append(' ');
    267         builder.append(userId);
    268         return execute(builder.toString());
    269     }
    270 
    271     public int clearUserData(String name, int userId) {
    272         StringBuilder builder = new StringBuilder("rmuserdata");
    273         builder.append(' ');
    274         builder.append(name);
    275         builder.append(' ');
    276         builder.append(userId);
    277         return execute(builder.toString());
    278     }
    279 
    280     public boolean ping() {
    281         if (execute("ping") < 0) {
    282             return false;
    283         } else {
    284             return true;
    285         }
    286     }
    287 
    288     public int freeCache(long freeStorageSize) {
    289         StringBuilder builder = new StringBuilder("freecache");
    290         builder.append(' ');
    291         builder.append(String.valueOf(freeStorageSize));
    292         return execute(builder.toString());
    293     }
    294 
    295     /*
    296      * @param packagePathSuffix The name of the path relative to install
    297      * directory. Say if the path name is /data/app/com.test-1.apk, the package
    298      * suffix path will be com.test-1
    299      */
    300     public int setForwardLockPerm(String packagePathSuffix, int gid) {
    301         StringBuilder builder = new StringBuilder("protect");
    302         builder.append(' ');
    303         builder.append(packagePathSuffix);
    304         builder.append(' ');
    305         builder.append(gid);
    306         return execute(builder.toString());
    307     }
    308 
    309     public int getSizeInfo(String pkgName, String apkPath, String fwdLockApkPath,
    310             String asecPath, PackageStats pStats) {
    311         StringBuilder builder = new StringBuilder("getsize");
    312         builder.append(' ');
    313         builder.append(pkgName);
    314         builder.append(' ');
    315         builder.append(apkPath);
    316         builder.append(' ');
    317         builder.append(fwdLockApkPath != null ? fwdLockApkPath : "!");
    318         builder.append(' ');
    319         builder.append(asecPath != null ? asecPath : "!");
    320 
    321         String s = transaction(builder.toString());
    322         String res[] = s.split(" ");
    323 
    324         if ((res == null) || (res.length != 5)) {
    325             return -1;
    326         }
    327         try {
    328             pStats.codeSize = Long.parseLong(res[1]);
    329             pStats.dataSize = Long.parseLong(res[2]);
    330             pStats.cacheSize = Long.parseLong(res[3]);
    331             pStats.externalCodeSize = Long.parseLong(res[4]);
    332             return Integer.parseInt(res[0]);
    333         } catch (NumberFormatException e) {
    334             return -1;
    335         }
    336     }
    337 
    338     public int moveFiles() {
    339         return execute("movefiles");
    340     }
    341 
    342     public int linkNativeLibraryDirectory(String dataPath, String nativeLibPath) {
    343         if (dataPath == null) {
    344             Slog.e(TAG, "unlinkNativeLibraryDirectory dataPath is null");
    345             return -1;
    346         } else if (nativeLibPath == null) {
    347             Slog.e(TAG, "unlinkNativeLibraryDirectory nativeLibPath is null");
    348             return -1;
    349         }
    350 
    351         StringBuilder builder = new StringBuilder("linklib ");
    352         builder.append(dataPath);
    353         builder.append(' ');
    354         builder.append(nativeLibPath);
    355 
    356         return execute(builder.toString());
    357     }
    358 
    359     public int unlinkNativeLibraryDirectory(String dataPath) {
    360         if (dataPath == null) {
    361             Slog.e(TAG, "unlinkNativeLibraryDirectory dataPath is null");
    362             return -1;
    363         }
    364 
    365         StringBuilder builder = new StringBuilder("unlinklib ");
    366         builder.append(dataPath);
    367 
    368         return execute(builder.toString());
    369     }
    370 }
    371