Home | History | Annotate | Download | only in os
      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.internal.os;
     18 
     19 import android.net.LocalSocket;
     20 import android.net.LocalSocketAddress;
     21 import android.os.SystemClock;
     22 import android.util.Slog;
     23 import libcore.io.IoUtils;
     24 import libcore.io.Streams;
     25 
     26 import java.io.IOException;
     27 import java.io.InputStream;
     28 import java.io.OutputStream;
     29 
     30 /**
     31  * Represents a connection to {@code installd}. Allows multiple connect and
     32  * disconnect cycles.
     33  *
     34  * @hide for internal use only
     35  */
     36 public class InstallerConnection {
     37     private static final String TAG = "InstallerConnection";
     38     private static final boolean LOCAL_DEBUG = false;
     39 
     40     private InputStream mIn;
     41     private OutputStream mOut;
     42     private LocalSocket mSocket;
     43 
     44     private final byte buf[] = new byte[1024];
     45 
     46     public InstallerConnection() {
     47     }
     48 
     49     public synchronized String transact(String cmd) {
     50         if (!connect()) {
     51             Slog.e(TAG, "connection failed");
     52             return "-1";
     53         }
     54 
     55         if (!writeCommand(cmd)) {
     56             /*
     57              * If installd died and restarted in the background (unlikely but
     58              * possible) we'll fail on the next write (this one). Try to
     59              * reconnect and write the command one more time before giving up.
     60              */
     61             Slog.e(TAG, "write command failed? reconnect!");
     62             if (!connect() || !writeCommand(cmd)) {
     63                 return "-1";
     64             }
     65         }
     66         if (LOCAL_DEBUG) {
     67             Slog.i(TAG, "send: '" + cmd + "'");
     68         }
     69 
     70         final int replyLength = readReply();
     71         if (replyLength > 0) {
     72             String s = new String(buf, 0, replyLength);
     73             if (LOCAL_DEBUG) {
     74                 Slog.i(TAG, "recv: '" + s + "'");
     75             }
     76             return s;
     77         } else {
     78             if (LOCAL_DEBUG) {
     79                 Slog.i(TAG, "fail");
     80             }
     81             return "-1";
     82         }
     83     }
     84 
     85     public int execute(String cmd) {
     86         String res = transact(cmd);
     87         try {
     88             return Integer.parseInt(res);
     89         } catch (NumberFormatException ex) {
     90             return -1;
     91         }
     92     }
     93 
     94     public int dexopt(String apkPath, int uid, boolean isPublic,
     95             String instructionSet, int dexoptNeeded) {
     96         return dexopt(apkPath, uid, isPublic, "*", instructionSet, dexoptNeeded,
     97                 false, false, null);
     98     }
     99 
    100     public int dexopt(String apkPath, int uid, boolean isPublic, String pkgName,
    101             String instructionSet, int dexoptNeeded, boolean vmSafeMode,
    102             boolean debuggable, String outputPath) {
    103         StringBuilder builder = new StringBuilder("dexopt");
    104         builder.append(' ');
    105         builder.append(apkPath);
    106         builder.append(' ');
    107         builder.append(uid);
    108         builder.append(isPublic ? " 1" : " 0");
    109         builder.append(' ');
    110         builder.append(pkgName);
    111         builder.append(' ');
    112         builder.append(instructionSet);
    113         builder.append(' ');
    114         builder.append(dexoptNeeded);
    115         builder.append(vmSafeMode ? " 1" : " 0");
    116         builder.append(debuggable ? " 1" : " 0");
    117         builder.append(' ');
    118         builder.append(outputPath != null ? outputPath : "!");
    119         return execute(builder.toString());
    120     }
    121 
    122     private boolean connect() {
    123         if (mSocket != null) {
    124             return true;
    125         }
    126         Slog.i(TAG, "connecting...");
    127         try {
    128             mSocket = new LocalSocket();
    129 
    130             LocalSocketAddress address = new LocalSocketAddress("installd",
    131                     LocalSocketAddress.Namespace.RESERVED);
    132 
    133             mSocket.connect(address);
    134 
    135             mIn = mSocket.getInputStream();
    136             mOut = mSocket.getOutputStream();
    137         } catch (IOException ex) {
    138             disconnect();
    139             return false;
    140         }
    141         return true;
    142     }
    143 
    144     public void disconnect() {
    145         Slog.i(TAG, "disconnecting...");
    146         IoUtils.closeQuietly(mSocket);
    147         IoUtils.closeQuietly(mIn);
    148         IoUtils.closeQuietly(mOut);
    149 
    150         mSocket = null;
    151         mIn = null;
    152         mOut = null;
    153     }
    154 
    155 
    156     private boolean readFully(byte[] buffer, int len) {
    157         try {
    158             Streams.readFully(mIn, buffer, 0, len);
    159         } catch (IOException ioe) {
    160             Slog.e(TAG, "read exception");
    161             disconnect();
    162             return false;
    163         }
    164 
    165         if (LOCAL_DEBUG) {
    166             Slog.i(TAG, "read " + len + " bytes");
    167         }
    168 
    169         return true;
    170     }
    171 
    172     private int readReply() {
    173         if (!readFully(buf, 2)) {
    174             return -1;
    175         }
    176 
    177         final int len = (((int) buf[0]) & 0xff) | ((((int) buf[1]) & 0xff) << 8);
    178         if ((len < 1) || (len > buf.length)) {
    179             Slog.e(TAG, "invalid reply length (" + len + ")");
    180             disconnect();
    181             return -1;
    182         }
    183 
    184         if (!readFully(buf, len)) {
    185             return -1;
    186         }
    187 
    188         return len;
    189     }
    190 
    191     private boolean writeCommand(String cmdString) {
    192         final byte[] cmd = cmdString.getBytes();
    193         final int len = cmd.length;
    194         if ((len < 1) || (len > buf.length)) {
    195             return false;
    196         }
    197 
    198         buf[0] = (byte) (len & 0xff);
    199         buf[1] = (byte) ((len >> 8) & 0xff);
    200         try {
    201             mOut.write(buf, 0, 2);
    202             mOut.write(cmd, 0, len);
    203         } catch (IOException ex) {
    204             Slog.e(TAG, "write error");
    205             disconnect();
    206             return false;
    207         }
    208         return true;
    209     }
    210 
    211     public void waitForConnection() {
    212         for (;;) {
    213             if (execute("ping") >= 0) {
    214                 return;
    215             }
    216             Slog.w(TAG, "installd not ready");
    217             SystemClock.sleep(1000);
    218         }
    219     }
    220 }
    221