blob: 43d938cea3d8816007963238ef3052c5be20e073 [file] [log] [blame]
San Mehat67bd2cd2010-01-12 12:18:49 -08001/*
2 * Copyright (C) 2007 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 com.android.server;
18
San Mehat67bd2cd2010-01-12 12:18:49 -080019import android.net.LocalSocket;
Jeff Sharkeyfa23c5a2011-08-09 21:44:24 -070020import android.net.LocalSocketAddress;
Chia-chi Yehe5750a32011-08-03 14:42:11 -070021import android.os.Handler;
22import android.os.HandlerThread;
23import android.os.Message;
San Mehat67bd2cd2010-01-12 12:18:49 -080024import android.os.SystemClock;
Joe Onorato8a9b2202010-02-26 18:56:32 -080025import android.util.Slog;
San Mehat67bd2cd2010-01-12 12:18:49 -080026
27import java.io.IOException;
28import java.io.InputStream;
29import java.io.OutputStream;
San Mehat67bd2cd2010-01-12 12:18:49 -080030import java.util.ArrayList;
San Mehat67bd2cd2010-01-12 12:18:49 -080031import java.util.concurrent.BlockingQueue;
32import java.util.concurrent.LinkedBlockingQueue;
33
34/**
35 * Generic connector class for interfacing with a native
36 * daemon which uses the libsysutils FrameworkListener
37 * protocol.
38 */
Jeff Sharkeyfa23c5a2011-08-09 21:44:24 -070039final class NativeDaemonConnector implements Runnable, Handler.Callback, Watchdog.Monitor {
San Mehat1ff43712010-02-04 15:09:02 -080040 private static final boolean LOCAL_LOGD = false;
San Mehat67bd2cd2010-01-12 12:18:49 -080041
42 private BlockingQueue<String> mResponseQueue;
43 private OutputStream mOutputStream;
44 private String TAG = "NativeDaemonConnector";
45 private String mSocket;
46 private INativeDaemonConnectorCallbacks mCallbacks;
Chia-chi Yehe5750a32011-08-03 14:42:11 -070047 private Handler mCallbackHandler;
San Mehat67bd2cd2010-01-12 12:18:49 -080048
Jeff Sharkeyfa23c5a2011-08-09 21:44:24 -070049 /** Lock held whenever communicating with native daemon. */
50 private Object mDaemonLock = new Object();
51
Kenny Root961aa8c2010-03-22 18:02:45 -070052 private final int BUFFER_SIZE = 4096;
53
San Mehat67bd2cd2010-01-12 12:18:49 -080054 class ResponseCode {
55 public static final int ActionInitiated = 100;
56
57 public static final int CommandOkay = 200;
58
59 // The range of 400 -> 599 is reserved for cmd failures
60 public static final int OperationFailed = 400;
61 public static final int CommandSyntaxError = 500;
62 public static final int CommandParameterError = 501;
63
64 public static final int UnsolicitedInformational = 600;
65
66 //
67 public static final int FailedRangeStart = 400;
68 public static final int FailedRangeEnd = 599;
69 }
70
71 NativeDaemonConnector(INativeDaemonConnectorCallbacks callbacks,
72 String socket, int responseQueueSize, String logTag) {
73 mCallbacks = callbacks;
74 if (logTag != null)
75 TAG = logTag;
76 mSocket = socket;
77 mResponseQueue = new LinkedBlockingQueue<String>(responseQueueSize);
78 }
79
Chia-chi Yehe5750a32011-08-03 14:42:11 -070080 @Override
San Mehat67bd2cd2010-01-12 12:18:49 -080081 public void run() {
Chia-chi Yehe5750a32011-08-03 14:42:11 -070082 HandlerThread thread = new HandlerThread(TAG + ".CallbackHandler");
83 thread.start();
84 mCallbackHandler = new Handler(thread.getLooper(), this);
San Mehat67bd2cd2010-01-12 12:18:49 -080085
86 while (true) {
87 try {
88 listenToSocket();
89 } catch (Exception e) {
Joe Onorato8a9b2202010-02-26 18:56:32 -080090 Slog.e(TAG, "Error in NativeDaemonConnector", e);
San Mehat4c27e0e2010-01-29 05:22:17 -080091 SystemClock.sleep(5000);
San Mehat67bd2cd2010-01-12 12:18:49 -080092 }
93 }
94 }
95
Chia-chi Yehe5750a32011-08-03 14:42:11 -070096 @Override
97 public boolean handleMessage(Message msg) {
98 String event = (String) msg.obj;
99 try {
100 if (!mCallbacks.onEvent(msg.what, event, event.split(" "))) {
101 Slog.w(TAG, String.format(
102 "Unhandled event '%s'", event));
103 }
104 } catch (Exception e) {
105 Slog.e(TAG, String.format(
106 "Error handling '%s'", event), e);
107 }
108 return true;
109 }
110
San Mehat4c27e0e2010-01-29 05:22:17 -0800111 private void listenToSocket() throws IOException {
Kenny Root961aa8c2010-03-22 18:02:45 -0700112 LocalSocket socket = null;
San Mehat67bd2cd2010-01-12 12:18:49 -0800113
114 try {
115 socket = new LocalSocket();
116 LocalSocketAddress address = new LocalSocketAddress(mSocket,
117 LocalSocketAddress.Namespace.RESERVED);
118
119 socket.connect(address);
San Mehat67bd2cd2010-01-12 12:18:49 -0800120
121 InputStream inputStream = socket.getInputStream();
122 mOutputStream = socket.getOutputStream();
123
anga030bc882011-02-01 14:10:25 +0100124 mCallbacks.onDaemonConnected();
125
Kenny Root961aa8c2010-03-22 18:02:45 -0700126 byte[] buffer = new byte[BUFFER_SIZE];
127 int start = 0;
San Mehat67bd2cd2010-01-12 12:18:49 -0800128
129 while (true) {
Kenny Root961aa8c2010-03-22 18:02:45 -0700130 int count = inputStream.read(buffer, start, BUFFER_SIZE - start);
San Mehat67bd2cd2010-01-12 12:18:49 -0800131 if (count < 0) break;
132
Kenny Root12da9d72010-09-02 22:18:14 -0700133 // Add our starting point to the count and reset the start.
134 count += start;
135 start = 0;
136
San Mehat67bd2cd2010-01-12 12:18:49 -0800137 for (int i = 0; i < count; i++) {
138 if (buffer[i] == 0) {
139 String event = new String(buffer, start, i - start);
Joe Onorato8a9b2202010-02-26 18:56:32 -0800140 if (LOCAL_LOGD) Slog.d(TAG, String.format("RCV <- {%s}", event));
San Mehat67bd2cd2010-01-12 12:18:49 -0800141
Chia-chi Yehe5750a32011-08-03 14:42:11 -0700142 String[] tokens = event.split(" ", 2);
San Mehat67bd2cd2010-01-12 12:18:49 -0800143 try {
144 int code = Integer.parseInt(tokens[0]);
145
146 if (code >= ResponseCode.UnsolicitedInformational) {
Chia-chi Yehe5750a32011-08-03 14:42:11 -0700147 mCallbackHandler.sendMessage(
148 mCallbackHandler.obtainMessage(code, event));
Irfan Sheriff1cd94ef2011-01-16 14:31:55 -0800149 } else {
150 try {
151 mResponseQueue.put(event);
152 } catch (InterruptedException ex) {
153 Slog.e(TAG, "Failed to put response onto queue", ex);
154 }
San Mehat67bd2cd2010-01-12 12:18:49 -0800155 }
156 } catch (NumberFormatException nfe) {
Joe Onorato8a9b2202010-02-26 18:56:32 -0800157 Slog.w(TAG, String.format("Bad msg (%s)", event));
San Mehat67bd2cd2010-01-12 12:18:49 -0800158 }
159 start = i + 1;
160 }
161 }
Kenny Root12da9d72010-09-02 22:18:14 -0700162
163 // We should end at the amount we read. If not, compact then
164 // buffer and read again.
Kenny Root961aa8c2010-03-22 18:02:45 -0700165 if (start != count) {
166 final int remaining = BUFFER_SIZE - start;
167 System.arraycopy(buffer, start, buffer, 0, remaining);
168 start = remaining;
169 } else {
170 start = 0;
171 }
San Mehat67bd2cd2010-01-12 12:18:49 -0800172 }
173 } catch (IOException ex) {
Joe Onorato8a9b2202010-02-26 18:56:32 -0800174 Slog.e(TAG, "Communications error", ex);
San Mehat4c27e0e2010-01-29 05:22:17 -0800175 throw ex;
176 } finally {
Jeff Sharkeyfa23c5a2011-08-09 21:44:24 -0700177 synchronized (mDaemonLock) {
San Mehat4c27e0e2010-01-29 05:22:17 -0800178 if (mOutputStream != null) {
179 try {
180 mOutputStream.close();
181 } catch (IOException e) {
Joe Onorato8a9b2202010-02-26 18:56:32 -0800182 Slog.w(TAG, "Failed closing output stream", e);
San Mehat4c27e0e2010-01-29 05:22:17 -0800183 }
184 mOutputStream = null;
San Mehat67bd2cd2010-01-12 12:18:49 -0800185 }
San Mehat4c27e0e2010-01-29 05:22:17 -0800186 }
San Mehat67bd2cd2010-01-12 12:18:49 -0800187
San Mehat4c27e0e2010-01-29 05:22:17 -0800188 try {
189 if (socket != null) {
190 socket.close();
191 }
192 } catch (IOException ex) {
Joe Onorato8a9b2202010-02-26 18:56:32 -0800193 Slog.w(TAG, "Failed closing socket", ex);
San Mehat67bd2cd2010-01-12 12:18:49 -0800194 }
195 }
San Mehat67bd2cd2010-01-12 12:18:49 -0800196 }
197
Jeff Sharkeyfa23c5a2011-08-09 21:44:24 -0700198 private void sendCommandLocked(String command) throws NativeDaemonConnectorException {
199 sendCommandLocked(command, null);
San Mehat67bd2cd2010-01-12 12:18:49 -0800200 }
201
202 /**
203 * Sends a command to the daemon with a single argument
204 *
205 * @param command The command to send to the daemon
206 * @param argument The argument to send with the command (or null)
207 */
Jeff Sharkeyfa23c5a2011-08-09 21:44:24 -0700208 private void sendCommandLocked(String command, String argument)
209 throws NativeDaemonConnectorException {
210 if (LOCAL_LOGD) Slog.d(TAG, String.format("SND -> {%s} {%s}", command, argument));
211 if (mOutputStream == null) {
212 Slog.e(TAG, "No connection to daemon", new IllegalStateException());
213 throw new NativeDaemonConnectorException("No output stream!");
214 } else {
215 StringBuilder builder = new StringBuilder(command);
216 if (argument != null) {
217 builder.append(argument);
218 }
219 builder.append('\0');
San Mehat67bd2cd2010-01-12 12:18:49 -0800220
Jeff Sharkeyfa23c5a2011-08-09 21:44:24 -0700221 try {
222 mOutputStream.write(builder.toString().getBytes());
223 } catch (IOException ex) {
224 Slog.e(TAG, "IOException in sendCommand", ex);
San Mehat67bd2cd2010-01-12 12:18:49 -0800225 }
226 }
227 }
228
San Mehatdeba6932010-01-20 15:14:31 -0800229 /**
230 * Issue a command to the native daemon and return the responses
231 */
Jeff Sharkeyfa23c5a2011-08-09 21:44:24 -0700232 public ArrayList<String> doCommand(String cmd) throws NativeDaemonConnectorException {
233 synchronized (mDaemonLock) {
234 return doCommandLocked(cmd);
235 }
236 }
237
238 private ArrayList<String> doCommandLocked(String cmd) throws NativeDaemonConnectorException {
Irfan Sheriff1cd94ef2011-01-16 14:31:55 -0800239 mResponseQueue.clear();
Jeff Sharkeyfa23c5a2011-08-09 21:44:24 -0700240 sendCommandLocked(cmd);
San Mehat67bd2cd2010-01-12 12:18:49 -0800241
242 ArrayList<String> response = new ArrayList<String>();
243 boolean complete = false;
244 int code = -1;
245
246 while (!complete) {
247 try {
Robert Greenwalte5c3afb2010-09-22 14:32:35 -0700248 // TODO - this should not block forever
San Mehat67bd2cd2010-01-12 12:18:49 -0800249 String line = mResponseQueue.take();
Joe Onorato8a9b2202010-02-26 18:56:32 -0800250 if (LOCAL_LOGD) Slog.d(TAG, String.format("RSP <- {%s}", line));
San Mehat67bd2cd2010-01-12 12:18:49 -0800251 String[] tokens = line.split(" ");
252 try {
253 code = Integer.parseInt(tokens[0]);
254 } catch (NumberFormatException nfe) {
San Mehat4c27e0e2010-01-29 05:22:17 -0800255 throw new NativeDaemonConnectorException(
San Mehat67bd2cd2010-01-12 12:18:49 -0800256 String.format("Invalid response from daemon (%s)", line));
257 }
258
San Mehatec4caa02010-02-03 10:48:21 -0800259 if ((code >= 200) && (code < 600)) {
San Mehat67bd2cd2010-01-12 12:18:49 -0800260 complete = true;
San Mehatec4caa02010-02-03 10:48:21 -0800261 }
San Mehat67bd2cd2010-01-12 12:18:49 -0800262 response.add(line);
263 } catch (InterruptedException ex) {
Joe Onorato8a9b2202010-02-26 18:56:32 -0800264 Slog.e(TAG, "Failed to process response", ex);
San Mehat67bd2cd2010-01-12 12:18:49 -0800265 }
266 }
267
268 if (code >= ResponseCode.FailedRangeStart &&
269 code <= ResponseCode.FailedRangeEnd) {
San Mehatec4caa02010-02-03 10:48:21 -0800270 /*
271 * Note: The format of the last response in this case is
272 * "NNN <errmsg>"
273 */
274 throw new NativeDaemonConnectorException(
275 code, cmd, response.get(response.size()-1).substring(4));
San Mehat67bd2cd2010-01-12 12:18:49 -0800276 }
277 return response;
278 }
San Mehatdeba6932010-01-20 15:14:31 -0800279
Jeff Sharkeyfa23c5a2011-08-09 21:44:24 -0700280 /**
San Mehatdeba6932010-01-20 15:14:31 -0800281 * Issues a list command and returns the cooked list
282 */
283 public String[] doListCommand(String cmd, int expectedResponseCode)
San Mehat4c27e0e2010-01-29 05:22:17 -0800284 throws NativeDaemonConnectorException {
San Mehatdeba6932010-01-20 15:14:31 -0800285
286 ArrayList<String> rsp = doCommand(cmd);
287 String[] rdata = new String[rsp.size()-1];
288 int idx = 0;
289
San Mehat1ff43712010-02-04 15:09:02 -0800290 for (int i = 0; i < rsp.size(); i++) {
291 String line = rsp.get(i);
San Mehatdeba6932010-01-20 15:14:31 -0800292 try {
293 String[] tok = line.split(" ");
294 int code = Integer.parseInt(tok[0]);
295 if (code == expectedResponseCode) {
San Mehat80120b42010-01-26 12:48:39 -0800296 rdata[idx++] = line.substring(tok[0].length() + 1);
San Mehatdeba6932010-01-20 15:14:31 -0800297 } else if (code == NativeDaemonConnector.ResponseCode.CommandOkay) {
Joe Onorato8a9b2202010-02-26 18:56:32 -0800298 if (LOCAL_LOGD) Slog.d(TAG, String.format("List terminated with {%s}", line));
San Mehat4086f752010-02-17 09:03:29 -0800299 int last = rsp.size() -1;
300 if (i != last) {
Joe Onorato8a9b2202010-02-26 18:56:32 -0800301 Slog.w(TAG, String.format("Recv'd %d lines after end of list {%s}", (last-i), cmd));
San Mehat4086f752010-02-17 09:03:29 -0800302 for (int j = i; j <= last ; j++) {
Joe Onorato8a9b2202010-02-26 18:56:32 -0800303 Slog.w(TAG, String.format("ExtraData <%s>", rsp.get(i)));
San Mehat4086f752010-02-17 09:03:29 -0800304 }
San Mehat1ff43712010-02-04 15:09:02 -0800305 }
San Mehatdeba6932010-01-20 15:14:31 -0800306 return rdata;
307 } else {
San Mehat4c27e0e2010-01-29 05:22:17 -0800308 throw new NativeDaemonConnectorException(
San Mehatdeba6932010-01-20 15:14:31 -0800309 String.format("Expected list response %d, but got %d",
310 expectedResponseCode, code));
311 }
312 } catch (NumberFormatException nfe) {
San Mehat4c27e0e2010-01-29 05:22:17 -0800313 throw new NativeDaemonConnectorException(
314 String.format("Error reading code '%s'", line));
San Mehatdeba6932010-01-20 15:14:31 -0800315 }
316 }
San Mehat4c27e0e2010-01-29 05:22:17 -0800317 throw new NativeDaemonConnectorException("Got an empty response");
San Mehatdeba6932010-01-20 15:14:31 -0800318 }
Jeff Sharkeyfa23c5a2011-08-09 21:44:24 -0700319
320 /** {@inheritDoc} */
321 public void monitor() {
322 synchronized (mDaemonLock) { }
323 }
San Mehat67bd2cd2010-01-12 12:18:49 -0800324}