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