1/* 2 * Copyright (C) 2009 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 17package android.security; 18 19import android.net.LocalSocketAddress; 20import android.net.LocalSocket; 21import android.util.Config; 22import android.util.Log; 23 24import java.io.IOException; 25import java.io.InputStream; 26import java.io.OutputStream; 27import java.net.Socket; 28 29/* 30 * ServiceCommand is used to connect to a service throught the local socket, 31 * and send out the command, return the result to the caller. 32 * {@hide} 33 */ 34public class ServiceCommand { 35 public static final String SUCCESS = "0"; 36 public static final String FAILED = "-1"; 37 38 // Opcodes for keystore commands. 39 public static final int LOCK = 0; 40 public static final int UNLOCK = 1; 41 public static final int PASSWD = 2; 42 public static final int GET_STATE = 3; 43 public static final int LIST_KEYS = 4; 44 public static final int GET_KEY = 5; 45 public static final int PUT_KEY = 6; 46 public static final int REMOVE_KEY = 7; 47 public static final int RESET = 8; 48 public static final int MAX_CMD_INDEX = 9; 49 50 public static final int BUFFER_LENGTH = 4096; 51 52 private static final boolean DBG = true; 53 54 private String mServiceName; 55 private String mTag; 56 private InputStream mIn; 57 private OutputStream mOut; 58 private LocalSocket mSocket; 59 60 private boolean connect() { 61 if (mSocket != null) { 62 return true; 63 } 64 if (DBG) Log.d(mTag, "connecting..."); 65 try { 66 mSocket = new LocalSocket(); 67 68 LocalSocketAddress address = new LocalSocketAddress( 69 mServiceName, LocalSocketAddress.Namespace.RESERVED); 70 71 mSocket.connect(address); 72 73 mIn = mSocket.getInputStream(); 74 mOut = mSocket.getOutputStream(); 75 } catch (IOException ex) { 76 disconnect(); 77 return false; 78 } 79 return true; 80 } 81 82 private void disconnect() { 83 if (DBG) Log.d(mTag,"disconnecting..."); 84 try { 85 if (mSocket != null) mSocket.close(); 86 } catch (IOException ex) { } 87 try { 88 if (mIn != null) mIn.close(); 89 } catch (IOException ex) { } 90 try { 91 if (mOut != null) mOut.close(); 92 } catch (IOException ex) { } 93 mSocket = null; 94 mIn = null; 95 mOut = null; 96 } 97 98 private boolean readBytes(byte buffer[], int len) { 99 int off = 0, count; 100 if (len < 0) return false; 101 while (off != len) { 102 try { 103 count = mIn.read(buffer, off, len - off); 104 if (count <= 0) { 105 Log.e(mTag, "read error " + count); 106 break; 107 } 108 off += count; 109 } catch (IOException ex) { 110 Log.e(mTag,"read exception", ex); 111 break; 112 } 113 } 114 if (off == len) return true; 115 disconnect(); 116 return false; 117 } 118 119 private Reply readReply() { 120 byte buf[] = new byte[4]; 121 Reply reply = new Reply(); 122 123 if (!readBytes(buf, 4)) return null; 124 reply.len = (((int) buf[0]) & 0xff) | ((((int) buf[1]) & 0xff) << 8) | 125 ((((int) buf[2]) & 0xff) << 16) | 126 ((((int) buf[3]) & 0xff) << 24); 127 128 if (!readBytes(buf, 4)) return null; 129 reply.returnCode = (((int) buf[0]) & 0xff) | 130 ((((int) buf[1]) & 0xff) << 8) | 131 ((((int) buf[2]) & 0xff) << 16) | 132 ((((int) buf[3]) & 0xff) << 24); 133 134 if (reply.len > BUFFER_LENGTH) { 135 Log.e(mTag,"invalid reply length (" + reply.len + ")"); 136 disconnect(); 137 return null; 138 } 139 if (!readBytes(reply.data, reply.len)) return null; 140 return reply; 141 } 142 143 private boolean writeCommand(int cmd, String _data) { 144 byte buf[] = new byte[8]; 145 byte[] data = (_data == null) ? new byte[0] : _data.getBytes(); 146 int len = data.length; 147 // the length of data 148 buf[0] = (byte) (len & 0xff); 149 buf[1] = (byte) ((len >> 8) & 0xff); 150 buf[2] = (byte) ((len >> 16) & 0xff); 151 buf[3] = (byte) ((len >> 24) & 0xff); 152 // the opcode of the command 153 buf[4] = (byte) (cmd & 0xff); 154 buf[5] = (byte) ((cmd >> 8) & 0xff); 155 buf[6] = (byte) ((cmd >> 16) & 0xff); 156 buf[7] = (byte) ((cmd >> 24) & 0xff); 157 try { 158 mOut.write(buf, 0, 8); 159 mOut.write(data, 0, len); 160 } catch (IOException ex) { 161 Log.e(mTag,"write error", ex); 162 disconnect(); 163 return false; 164 } 165 return true; 166 } 167 168 private Reply executeCommand(int cmd, String data) { 169 if (!writeCommand(cmd, data)) { 170 /* If service died and restarted in the background 171 * (unlikely but possible) we'll fail on the next 172 * write (this one). Try to reconnect and write 173 * the command one more time before giving up. 174 */ 175 Log.e(mTag, "write command failed? reconnect!"); 176 if (!connect() || !writeCommand(cmd, data)) { 177 return null; 178 } 179 } 180 return readReply(); 181 } 182 183 public synchronized Reply execute(int cmd, String data) { 184 Reply result; 185 if (!connect()) { 186 Log.e(mTag, "connection failed"); 187 return null; 188 } 189 result = executeCommand(cmd, data); 190 disconnect(); 191 return result; 192 } 193 194 public ServiceCommand(String service) { 195 mServiceName = service; 196 mTag = service; 197 } 198} 199