diff options
| author | Calvin Huang <calhuang@google.com> | 2020-05-20 06:43:36 +0000 |
|---|---|---|
| committer | Automerger Merge Worker <android-build-automerger-merge-worker@system.gserviceaccount.com> | 2020-05-20 06:43:36 +0000 |
| commit | a9994028a88c4b2a693e7de15a15446ffb73f6d1 (patch) | |
| tree | d39c199c499c31dabb9f50de34e5da3c84210673 | |
| parent | 6b3b05ece8dfc786927b30406a1724e04a54895b (diff) | |
| parent | 66e02249efa278b871e5d2870d946c6ae7c25ca9 (diff) | |
| download | platform_packages_apps_Car_Cluster-a9994028a88c4b2a693e7de15a15446ffb73f6d1.tar.gz platform_packages_apps_Car_Cluster-a9994028a88c4b2a693e7de15a15446ffb73f6d1.tar.bz2 platform_packages_apps_Car_Cluster-a9994028a88c4b2a693e7de15a15446ffb73f6d1.zip | |
Remove NetworkedVirtualDisplay am: 66e02249ef
Change-Id: I6e66593b3237702d1558dd5f0ef646ac915d705f
| -rw-r--r-- | src/android/car/cluster/ClusterDisplayProvider.java | 16 | ||||
| -rw-r--r-- | src/android/car/cluster/NetworkedVirtualDisplay.java | 452 | ||||
| -rw-r--r-- | src/android/car/cluster/PipeThread.java | 81 | ||||
| -rw-r--r-- | src/android/car/cluster/SenderThread.java | 47 | ||||
| -rw-r--r-- | src/android/car/cluster/SocketThread.java | 86 |
5 files changed, 2 insertions, 680 deletions
diff --git a/src/android/car/cluster/ClusterDisplayProvider.java b/src/android/car/cluster/ClusterDisplayProvider.java index e1ee495..66329a4 100644 --- a/src/android/car/cluster/ClusterDisplayProvider.java +++ b/src/android/car/cluster/ClusterDisplayProvider.java @@ -36,10 +36,7 @@ import java.util.List; * This class provides a display for instrument cluster renderer. * <p> * By default it will try to provide physical secondary display if it is connected, if secondary - * display is not connected during creation of this class then it will start networked virtual - * display and listens for incoming connections. - * - * @see {@link NetworkedVirtualDisplay} + * display is not connected during creation of this class then it will throw a IllegalStateException */ public class ClusterDisplayProvider { private static final String TAG = "Cluster.DisplayProvider"; @@ -51,7 +48,6 @@ public class ClusterDisplayProvider { private final DisplayListener mListener; private final DisplayManager mDisplayManager; - private NetworkedVirtualDisplay mNetworkedVirtualDisplay; private int mClusterDisplayId = -1; ClusterDisplayProvider(Context context, DisplayListener clusterDisplayListener) { @@ -80,8 +76,7 @@ public class ClusterDisplayProvider { mListener.onDisplayAdded(clusterDisplay.getDisplayId()); trackClusterDisplay(null /* no need to track display by name */); } else { - Log.i(TAG, "No physical cluster display found, starting network display"); - setupNetworkDisplay(context); + throw new IllegalStateException("ClusterDisplay is mandatory"); } } @@ -99,13 +94,6 @@ public class ClusterDisplayProvider { throw new IllegalStateException("Can't find the OccupantZoneInfo for driver"); } - private void setupNetworkDisplay(Context context) { - mNetworkedVirtualDisplay = new NetworkedVirtualDisplay(context, - NETWORKED_DISPLAY_WIDTH, NETWORKED_DISPLAY_HEIGHT, NETWORKED_DISPLAY_DPI); - String displayName = mNetworkedVirtualDisplay.start(); - trackClusterDisplay(displayName); - } - private void trackClusterDisplay(@Nullable String displayName) { mDisplayManager.registerDisplayListener(new DisplayListener() { @Override diff --git a/src/android/car/cluster/NetworkedVirtualDisplay.java b/src/android/car/cluster/NetworkedVirtualDisplay.java deleted file mode 100644 index 831f61e..0000000 --- a/src/android/car/cluster/NetworkedVirtualDisplay.java +++ /dev/null @@ -1,452 +0,0 @@ -/* - * Copyright (C) 2018 The Android Open Source Project - * - * Licensed under the Apache License, Version 2.0 (the "License"); - * you may not use this file except in compliance with the License. - * You may obtain a copy of the License at - * - * http://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, - * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - * See the License for the specific language governing permissions and - * limitations under the License. - */ - -package android.car.cluster; - -import android.annotation.NonNull; -import android.content.Context; -import android.hardware.display.DisplayManager; -import android.hardware.display.DisplayManager.DisplayListener; -import android.hardware.display.VirtualDisplay; -import android.media.MediaCodec; -import android.media.MediaCodec.BufferInfo; -import android.media.MediaCodec.CodecException; -import android.media.MediaCodecInfo; -import android.media.MediaCodecInfo.CodecProfileLevel; -import android.media.MediaFormat; -import android.os.Handler; -import android.os.HandlerThread; -import android.os.Looper; -import android.os.Message; -import android.util.Log; -import android.view.Display; -import android.view.Surface; - -import java.io.IOException; -import java.io.InputStream; -import java.io.OutputStream; -import java.io.RandomAccessFile; -import java.net.ServerSocket; -import java.net.Socket; -import java.nio.ByteBuffer; -import java.util.UUID; - -/** - * This class encapsulates all work related to managing networked virtual display. - * <p> - * It opens a socket and listens on port {@code PORT} for connections, or the emulator pipe. Once - * connection is established it creates virtual display and media encoder and starts streaming video - * to that socket. If the receiving part is disconnected, it will keep port open and virtual - * display won't be destroyed. - */ -public class NetworkedVirtualDisplay { - private static final String TAG = "Cluster." + NetworkedVirtualDisplay.class.getSimpleName(); - - private final String mUniqueId = UUID.randomUUID().toString(); - - private final DisplayManager mDisplayManager; - private final int mWidth; - private final int mHeight; - private final int mDpi; - - private static final int FPS = 25; - private static final int BITRATE = 6144000; - private static final String MEDIA_FORMAT_MIMETYPE = MediaFormat.MIMETYPE_VIDEO_AVC; - - public static final int MSG_START = 0; - public static final int MSG_STOP = 1; - public static final int MSG_SEND_FRAME = 2; - - private static final String PIPE_NAME = "pipe:qemud:carCluster"; - private static final String PIPE_DEVICE = "/dev/qemu_pipe"; - - // Constants shared with emulator in car-cluster-widget.cpp - public static final int PIPE_START = 1; - public static final int PIPE_STOP = 2; - - private static final int PORT = 5151; - - private SenderThread mActiveThread; - private HandlerThread mBroadcastThread = new HandlerThread("BroadcastThread"); - - private VirtualDisplay mVirtualDisplay; - private MediaCodec mVideoEncoder; - private Handler mHandler; - private byte[] mBuffer = null; - private int mLastFrameLength = 0; - - private final DebugCounter mCounter = new DebugCounter(); - - NetworkedVirtualDisplay(Context context, int width, int height, int dpi) { - mDisplayManager = context.getSystemService(DisplayManager.class); - mWidth = width; - mHeight = height; - mDpi = dpi; - - DisplayListener displayListener = new DisplayListener() { - @Override - public void onDisplayAdded(int i) { - final Display display = mDisplayManager.getDisplay(i); - if (display != null && getDisplayName().equals(display.getName())) { - onVirtualDisplayReady(display); - } - } - - @Override - public void onDisplayRemoved(int i) {} - - @Override - public void onDisplayChanged(int i) {} - }; - - mDisplayManager.registerDisplayListener(displayListener, new Handler()); - } - - /** - * Opens socket and creates virtual display asynchronously once connection established. Clients - * of this class may subscribe to - * {@link android.hardware.display.DisplayManager#registerDisplayListener( - * DisplayListener, Handler)} to be notified when virtual display is created. - * Note, that this method should be called only once. - * - * @return Unique display name associated with the instance of this class. - * - * @see {@link Display#getName()} - * - * @throws IllegalStateException thrown if networked display already started - */ - public String start() { - if (mBroadcastThread.isAlive()) { - throw new IllegalStateException("Already started"); - } - - mBroadcastThread.start(); - mHandler = new BroadcastThreadHandler(mBroadcastThread.getLooper()); - mHandler.sendMessage(Message.obtain(mHandler, MSG_START)); - return getDisplayName(); - } - - public void release() { - mHandler.sendMessage(Message.obtain(mHandler, MSG_STOP)); - mBroadcastThread.quitSafely(); - - if (mVirtualDisplay != null) { - mVirtualDisplay.setSurface(null); - mVirtualDisplay.release(); - mVirtualDisplay = null; - } - } - - private String getDisplayName() { - return "Cluster-" + mUniqueId; - } - - private VirtualDisplay createVirtualDisplay() { - Log.i(TAG, "createVirtualDisplay " + mWidth + "x" + mHeight +"@" + mDpi); - return mDisplayManager.createVirtualDisplay(getDisplayName(), mWidth, mHeight, mDpi, - null, 0 /* flags */, null, null ); - } - - private void onVirtualDisplayReady(Display display) { - Log.i(TAG, "onVirtualDisplayReady, display: " + display); - } - - private void startCasting(Handler handler) { - Log.i(TAG, "Start casting..."); - if (mVideoEncoder != null) { - Log.i(TAG, "Already started casting"); - return; - } - mVideoEncoder = createVideoStream(handler); - - if (mVirtualDisplay == null) { - mVirtualDisplay = createVirtualDisplay(); - } - - mVirtualDisplay.setSurface(mVideoEncoder.createInputSurface()); - mVideoEncoder.start(); - Log.i(TAG, "Video encoder started"); - } - - private MediaCodec createVideoStream(Handler handler) { - MediaCodec encoder; - try { - encoder = MediaCodec.createEncoderByType(MEDIA_FORMAT_MIMETYPE); - } catch (IOException e) { - Log.e(TAG, "Failed to create video encoder for " + MEDIA_FORMAT_MIMETYPE, e); - return null; - } - - encoder.setCallback(new MediaCodec.Callback() { - @Override - public void onInputBufferAvailable(@NonNull MediaCodec codec, int index) { - // Nothing to do - } - - @Override - public void onOutputBufferAvailable(@NonNull MediaCodec codec, int index, - @NonNull BufferInfo info) { - mCounter.outputBuffers++; - doOutputBufferAvailable(index, info); - } - - @Override - public void onError(@NonNull MediaCodec codec, @NonNull CodecException e) { - Log.e(TAG, "onError, codec: " + codec, e); - mCounter.bufferErrors++; - stopCasting(); - startCasting(handler); - } - - @Override - public void onOutputFormatChanged(@NonNull MediaCodec codec, - @NonNull MediaFormat format) { - Log.i(TAG, "onOutputFormatChanged, codec: " + codec + ", format: " + format); - - } - }, handler); - - configureVideoEncoder(encoder, mWidth, mHeight); - return encoder; - } - - private void doOutputBufferAvailable(int index, @NonNull BufferInfo info) { - mHandler.removeMessages(MSG_SEND_FRAME); - - ByteBuffer encodedData = mVideoEncoder.getOutputBuffer(index); - if (encodedData == null) { - throw new RuntimeException("couldn't fetch buffer at index " + index); - } - - if (info.size != 0) { - encodedData.position(info.offset); - encodedData.limit(info.offset + info.size); - mLastFrameLength = encodedData.remaining(); - if (mBuffer == null || mBuffer.length < mLastFrameLength) { - Log.i(TAG, "Allocating new buffer: " + mLastFrameLength); - mBuffer = new byte[mLastFrameLength]; - } - encodedData.get(mBuffer, 0, mLastFrameLength); - mVideoEncoder.releaseOutputBuffer(index, false); - - // Send this frame asynchronously (avoiding blocking on the socket). We might miss - // frames if the consumer is not fast enough, but this is acceptable. - sendFrameAsync(0); - } else { - Log.e(TAG, "Skipping empty buffer"); - mVideoEncoder.releaseOutputBuffer(index, false); - } - } - - private void sendFrameAsync(long delayMs) { - Message msg = mHandler.obtainMessage(MSG_SEND_FRAME); - mHandler.sendMessageDelayed(msg, delayMs); - } - - private void sendFrame(byte[] buf, int len) { - if (mActiveThread != null) { - mActiveThread.send(buf, len); - } - } - - private void stopCasting() { - Log.i(TAG, "Stopping casting..."); - - if (mVirtualDisplay != null) { - Surface surface = mVirtualDisplay.getSurface(); - if (surface != null) surface.release(); - } - - if (mVideoEncoder != null) { - // Releasing encoder as stop/start didn't work well (couldn't create or reuse input - // surface). - try { - mVideoEncoder.stop(); - mVideoEncoder.release(); - } catch (IllegalStateException e) { - // do nothing, already released - } - mVideoEncoder = null; - } - Log.i(TAG, "Casting stopped"); - } - - private class BroadcastThreadHandler extends Handler { - private static final int MAX_FAIL_COUNT = 10; - private int mFailConnectCounter; - - BroadcastThreadHandler(Looper looper) { - super(looper); - } - - @Override - public void handleMessage(Message msg) { - switch (msg.what) { - case MSG_START: - Log.i(TAG, "Received start message"); - - // Make sure mActiveThread cannot start multiple times - if (mActiveThread != null) { - Log.w(TAG, "Trying to start a running thread. Race condition may exist"); - break; - } - - // Failure to connect to either pipe or network returns null - if (mActiveThread == null) { - mActiveThread = tryPipeConnect(); - } - if (mActiveThread == null) { - mActiveThread = tryNetworkConnect(); - } - if (mActiveThread == null) { - // When failed attempt limit is reached, clean up and quit this thread. - mFailConnectCounter++; - if (mFailConnectCounter >= MAX_FAIL_COUNT) { - Log.e(TAG, "Too many failed connection attempts; aborting"); - release(); - throw new RuntimeException("Abort after failed connection attempts"); - } - mHandler.sendMessage(Message.obtain(mHandler, MSG_START)); - break; - } - - try { - mFailConnectCounter = 0; - mCounter.clientsConnected++; - mActiveThread.start(); - startCasting(this); - } catch (Exception e) { - Log.e(TAG, "Failed to start thread", e); - Log.e(TAG, "DebugCounter: " + mCounter); - } - break; - - case MSG_STOP: - Log.i(TAG, "Received stop message"); - stopCasting(); - mCounter.clientsDisconnected++; - if (mActiveThread != null) { - mActiveThread.close(); - try { - mActiveThread.join(); - } catch (InterruptedException e) { - Log.e(TAG, "Waiting for active thread to close failed", e); - } - mActiveThread = null; - } - break; - - case MSG_SEND_FRAME: - if (mActiveThread == null) { - // Stop the chaining signal if there's no client to send to - break; - } - sendFrame(mBuffer, mLastFrameLength); - // We will keep sending last frame every second as a heartbeat. - sendFrameAsync(1000L); - break; - } - } - - // Returns null if can't establish pipe connection - // Otherwise returns the corresponding client thread - private PipeThread tryPipeConnect() { - try { - RandomAccessFile pipe = new RandomAccessFile(PIPE_DEVICE, "rw"); - byte[] temp = new byte[PIPE_NAME.length() + 1]; - temp[PIPE_NAME.length()] = 0; - System.arraycopy(PIPE_NAME.getBytes(), 0, temp, 0, PIPE_NAME.length()); - pipe.write(temp); - - // At this point, the pipe exists, so we will just wait for a start signal - // This is in case pipe still sends leftover stops from last instantiation - int signal = pipe.read(); - while (signal != PIPE_START) { - Log.i(TAG, "Received non-start signal: " + signal); - signal = pipe.read(); - } - return new PipeThread(mHandler, pipe); - } catch (IOException e) { - Log.w(TAG, "Failed to establish pipe connection", e); - return null; - } - } - - // Returns null if can't establish network connection - // Otherwise returns the corresponding client thread - private SocketThread tryNetworkConnect() { - try { - ServerSocket serverSocket = new ServerSocket(PORT); - Log.i(TAG, "Server socket opened"); - Socket socket = serverSocket.accept(); - socket.setTcpNoDelay(true); - socket.setKeepAlive(true); - socket.setSoLinger(true, 0); - - InputStream inputStream = socket.getInputStream(); - OutputStream outputStream = socket.getOutputStream(); - - return new SocketThread(mHandler, serverSocket, inputStream, outputStream); - } catch (IOException e) { - Log.w(TAG, "Failed to establish network connection", e); - return null; - } - } - } - - private static void configureVideoEncoder(MediaCodec codec, int width, int height) { - MediaFormat format = MediaFormat.createVideoFormat(MEDIA_FORMAT_MIMETYPE, width, height); - - format.setInteger(MediaFormat.KEY_COLOR_FORMAT, - MediaCodecInfo.CodecCapabilities.COLOR_FormatSurface); - format.setInteger(MediaFormat.KEY_BIT_RATE, BITRATE); - format.setInteger(MediaFormat.KEY_FRAME_RATE, FPS); - format.setInteger(MediaFormat.KEY_CAPTURE_RATE, FPS); - format.setInteger(MediaFormat.KEY_CHANNEL_COUNT, 1); - format.setFloat(MediaFormat.KEY_I_FRAME_INTERVAL, 1); // 1 second between I-frames - format.setInteger(MediaFormat.KEY_LEVEL, CodecProfileLevel.AVCLevel31); - format.setInteger(MediaFormat.KEY_PROFILE, - MediaCodecInfo.CodecProfileLevel.AVCProfileBaseline); - - codec.configure(format, null, null, MediaCodec.CONFIGURE_FLAG_ENCODE); - } - - @Override - public String toString() { - return getClass() + "{" - + ", receiver connected: " + (mActiveThread != null) - + ", encoder: " + mVideoEncoder - + ", virtualDisplay" + mVirtualDisplay - + "}"; - } - - private static class DebugCounter { - long outputBuffers; - long bufferErrors; - long clientsConnected; - long clientsDisconnected; - - @Override - public String toString() { - return getClass().getSimpleName() + "{" - + "outputBuffers=" + outputBuffers - + ", bufferErrors=" + bufferErrors - + ", clientsConnected=" + clientsConnected - + ", clientsDisconnected= " + clientsDisconnected - + "}"; - } - } -} diff --git a/src/android/car/cluster/PipeThread.java b/src/android/car/cluster/PipeThread.java deleted file mode 100644 index a8f6308..0000000 --- a/src/android/car/cluster/PipeThread.java +++ /dev/null @@ -1,81 +0,0 @@ -/* - * Copyright (C) 2018 The Android Open Source Project - * - * Licensed under the Apache License, Version 2.0 (the "License"); - * you may not use this file except in compliance with the License. - * You may obtain a copy of the License at - * - * http://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, - * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - * See the License for the specific language governing permissions and - * limitations under the License. - */ -package android.car.cluster; -import android.os.Handler; -import android.util.Log; - -import java.io.IOException; -import java.io.RandomAccessFile; -import java.nio.ByteBuffer; -import java.nio.ByteOrder; - -/** - * Thread that can send data to the emulator using a qemud service. - */ -public class PipeThread extends SenderThread { - private static final String TAG = "Cluster." + PipeThread.class.getSimpleName(); - - private RandomAccessFile mPipe; - - /** - * Creates instance of pipe thread that can write to given pipe file. - * - * @param handler {@link Handler} used to message broadcaster. - * @param pipe {@link RandomAccessFile} file already connected to pipe. - */ - PipeThread(Handler handler, RandomAccessFile pipe) { - super(handler); - mPipe = pipe; - } - - public void run() { - try { - int signal = mPipe.read(); - while (signal != NetworkedVirtualDisplay.PIPE_STOP) { - Log.i(TAG, "Received non-stop signal: " + signal); - signal = mPipe.read(); - } - restart(); - } catch (IOException e) { - Log.e(TAG, "Failed to read from pipe"); - restart(); - } - } - - @Override - public void send(byte[] buf, int len) { - try { - // First sends the size prior to sending the data, since receiving side only sees - // the size of the buffer, which could be significant larger than the actual data. - mPipe.write(ByteBuffer.allocate(4) - .order(ByteOrder.LITTLE_ENDIAN).putInt(len).array()); - mPipe.write(buf); - } catch (IOException e) { - Log.e(TAG, "Write to pipe failed"); - restart(); - } - } - - @Override - public void close() { - try { - mPipe.close(); - } catch (IOException e) { - Log.e(TAG, "Failed to close pipe", e); - } - } -} - diff --git a/src/android/car/cluster/SenderThread.java b/src/android/car/cluster/SenderThread.java deleted file mode 100644 index de461e2..0000000 --- a/src/android/car/cluster/SenderThread.java +++ /dev/null @@ -1,47 +0,0 @@ -/* - * Copyright (C) 2018 The Android Open Source Project - * - * Licensed under the Apache License, Version 2.0 (the "License"); - * you may not use this file except in compliance with the License. - * You may obtain a copy of the License at - * - * http://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, - * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - * See the License for the specific language governing permissions and - * limitations under the License. - */ -package android.car.cluster; -import android.os.Handler; -import android.os.Message; -import android.util.Log; - -/** - * This class serves as a template for sending to specific clients of the broadcaster. - */ -public abstract class SenderThread extends Thread { - private static final String TAG = "Cluster.SenderThread"; - - private Handler mHandler; - - SenderThread(Handler handler) { - mHandler = handler; - } - - abstract void send(byte[] buf, int len); - abstract void close(); - - /** - * Tells the broadcasting thread to stop and close everything in progress, and start over again. - * It will kill the current instance of this thread, and produce a new one. - */ - synchronized void restart() { - if (mHandler.hasMessages(NetworkedVirtualDisplay.MSG_START)) return; - Log.i(TAG, "Sending STOP and START msgs to NetworkedVirtualDisplay"); - - mHandler.sendMessage(Message.obtain(mHandler, NetworkedVirtualDisplay.MSG_STOP)); - mHandler.sendMessage(Message.obtain(mHandler, NetworkedVirtualDisplay.MSG_START)); - } -} diff --git a/src/android/car/cluster/SocketThread.java b/src/android/car/cluster/SocketThread.java deleted file mode 100644 index 7f1c61a..0000000 --- a/src/android/car/cluster/SocketThread.java +++ /dev/null @@ -1,86 +0,0 @@ -/* - * Copyright (C) 2018 The Android Open Source Project - * - * Licensed under the Apache License, Version 2.0 (the "License"); - * you may not use this file except in compliance with the License. - * You may obtain a copy of the License at - * - * http://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, - * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - * See the License for the specific language governing permissions and - * limitations under the License. - */ -package android.car.cluster; -import android.os.Handler; -import android.util.Log; - -import java.io.IOException; -import java.io.InputStream; -import java.io.OutputStream; -import java.net.ServerSocket; - -/** - * The thread that will send data on an opened socket. - */ -public class SocketThread extends SenderThread { - private static final String TAG = "Cluster." + SocketThread.class.getSimpleName(); - private ServerSocket mServerSocket; - private OutputStream mOutputStream; - private InputStream mInputStream; - - /** - * Create instance of thread that can write to given open socket. - * - * @param handler {@link Handler} used to message the broadcaster. - * @param serverSocket {@link ServerSocket} should be already opened. - * @param inputStream {@link InputStream} corresponding to opened socket. - * @param outputStream {@link OutputStream} corresponding to opened socket. - */ - SocketThread(Handler handler, ServerSocket serverSocket, InputStream inputStream, - OutputStream outputStream) { - super(handler); - mServerSocket = serverSocket; - mInputStream = inputStream; - mOutputStream = outputStream; - } - - public void run() { - try { - // This read should block until something disconnects (or something - // similar) which should cause an exception, in which case we should - // try to setup again and reconnect - mInputStream.read(); - } catch (IOException e) { - Log.e(TAG, "Socket thread disconnected."); - } - restart(); - } - - @Override - public void send(byte[] buf, int len) { - try { - mOutputStream.write(buf, 0, len); - } catch (IOException e) { - Log.e(TAG, "Failed to write data to socket, retrying connection"); - restart(); - } - } - - @Override - public void close() { - if (mServerSocket != null) { - try { - mServerSocket.close(); - } catch (IOException e) { - Log.w(TAG, "Failed to close server socket, ignoring"); - } - mServerSocket = null; - } - mInputStream = null; - mOutputStream = null; - } -} - |
