1/*
2 * Copyright (C) 2012 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.media;
18
19import dalvik.system.CloseGuard;
20
21import android.os.Handler;
22import android.view.Surface;
23
24/**
25 * Listens for Wifi remote display connections managed by the media server.
26 *
27 * @hide
28 */
29public final class RemoteDisplay {
30    /* these constants must be kept in sync with IRemoteDisplayClient.h */
31
32    public static final int DISPLAY_FLAG_SECURE = 1 << 0;
33
34    public static final int DISPLAY_ERROR_UNKOWN = 1;
35    public static final int DISPLAY_ERROR_CONNECTION_DROPPED = 2;
36
37    private final CloseGuard mGuard = CloseGuard.get();
38    private final Listener mListener;
39    private final Handler mHandler;
40
41    private int mPtr;
42
43    private native int nativeListen(String iface);
44    private native void nativeDispose(int ptr);
45
46    private RemoteDisplay(Listener listener, Handler handler) {
47        mListener = listener;
48        mHandler = handler;
49    }
50
51    @Override
52    protected void finalize() throws Throwable {
53        try {
54            dispose(true);
55        } finally {
56            super.finalize();
57        }
58    }
59
60    /**
61     * Starts listening for displays to be connected on the specified interface.
62     *
63     * @param iface The interface address and port in the form "x.x.x.x:y".
64     * @param listener The listener to invoke when displays are connected or disconnected.
65     * @param handler The handler on which to invoke the listener.
66     */
67    public static RemoteDisplay listen(String iface, Listener listener, Handler handler) {
68        if (iface == null) {
69            throw new IllegalArgumentException("iface must not be null");
70        }
71        if (listener == null) {
72            throw new IllegalArgumentException("listener must not be null");
73        }
74        if (handler == null) {
75            throw new IllegalArgumentException("handler must not be null");
76        }
77
78        RemoteDisplay display = new RemoteDisplay(listener, handler);
79        display.startListening(iface);
80        return display;
81    }
82
83    /**
84     * Disconnects the remote display and stops listening for new connections.
85     */
86    public void dispose() {
87        dispose(false);
88    }
89
90    private void dispose(boolean finalized) {
91        if (mPtr != 0) {
92            if (mGuard != null) {
93                if (finalized) {
94                    mGuard.warnIfOpen();
95                } else {
96                    mGuard.close();
97                }
98            }
99
100            nativeDispose(mPtr);
101            mPtr = 0;
102        }
103    }
104
105    private void startListening(String iface) {
106        mPtr = nativeListen(iface);
107        if (mPtr == 0) {
108            throw new IllegalStateException("Could not start listening for "
109                    + "remote display connection on \"" + iface + "\"");
110        }
111        mGuard.open("dispose");
112    }
113
114    // Called from native.
115    private void notifyDisplayConnected(final Surface surface,
116            final int width, final int height, final int flags) {
117        mHandler.post(new Runnable() {
118            @Override
119            public void run() {
120                mListener.onDisplayConnected(surface, width, height, flags);
121            }
122        });
123    }
124
125    // Called from native.
126    private void notifyDisplayDisconnected() {
127        mHandler.post(new Runnable() {
128            @Override
129            public void run() {
130                mListener.onDisplayDisconnected();
131            }
132        });
133    }
134
135    // Called from native.
136    private void notifyDisplayError(final int error) {
137        mHandler.post(new Runnable() {
138            @Override
139            public void run() {
140                mListener.onDisplayError(error);
141            }
142        });
143    }
144
145    /**
146     * Listener invoked when the remote display connection changes state.
147     */
148    public interface Listener {
149        void onDisplayConnected(Surface surface, int width, int height, int flags);
150        void onDisplayDisconnected();
151        void onDisplayError(int error);
152    }
153}
154