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 long mPtr;
42
43    private native long nativeListen(String iface);
44    private native void nativeDispose(long ptr);
45    private native void nativePause(long ptr);
46    private native void nativeResume(long ptr);
47
48    private RemoteDisplay(Listener listener, Handler handler) {
49        mListener = listener;
50        mHandler = handler;
51    }
52
53    @Override
54    protected void finalize() throws Throwable {
55        try {
56            dispose(true);
57        } finally {
58            super.finalize();
59        }
60    }
61
62    /**
63     * Starts listening for displays to be connected on the specified interface.
64     *
65     * @param iface The interface address and port in the form "x.x.x.x:y".
66     * @param listener The listener to invoke when displays are connected or disconnected.
67     * @param handler The handler on which to invoke the listener.
68     */
69    public static RemoteDisplay listen(String iface, Listener listener, Handler handler) {
70        if (iface == null) {
71            throw new IllegalArgumentException("iface must not be null");
72        }
73        if (listener == null) {
74            throw new IllegalArgumentException("listener must not be null");
75        }
76        if (handler == null) {
77            throw new IllegalArgumentException("handler must not be null");
78        }
79
80        RemoteDisplay display = new RemoteDisplay(listener, handler);
81        display.startListening(iface);
82        return display;
83    }
84
85    /**
86     * Disconnects the remote display and stops listening for new connections.
87     */
88    public void dispose() {
89        dispose(false);
90    }
91
92    public void pause() {
93        nativePause(mPtr);
94    }
95
96    public void resume() {
97        nativeResume(mPtr);
98    }
99
100    private void dispose(boolean finalized) {
101        if (mPtr != 0) {
102            if (mGuard != null) {
103                if (finalized) {
104                    mGuard.warnIfOpen();
105                } else {
106                    mGuard.close();
107                }
108            }
109
110            nativeDispose(mPtr);
111            mPtr = 0;
112        }
113    }
114
115    private void startListening(String iface) {
116        mPtr = nativeListen(iface);
117        if (mPtr == 0) {
118            throw new IllegalStateException("Could not start listening for "
119                    + "remote display connection on \"" + iface + "\"");
120        }
121        mGuard.open("dispose");
122    }
123
124    // Called from native.
125    private void notifyDisplayConnected(final Surface surface,
126            final int width, final int height, final int flags, final int session) {
127        mHandler.post(new Runnable() {
128            @Override
129            public void run() {
130                mListener.onDisplayConnected(surface, width, height, flags, session);
131            }
132        });
133    }
134
135    // Called from native.
136    private void notifyDisplayDisconnected() {
137        mHandler.post(new Runnable() {
138            @Override
139            public void run() {
140                mListener.onDisplayDisconnected();
141            }
142        });
143    }
144
145    // Called from native.
146    private void notifyDisplayError(final int error) {
147        mHandler.post(new Runnable() {
148            @Override
149            public void run() {
150                mListener.onDisplayError(error);
151            }
152        });
153    }
154
155    /**
156     * Listener invoked when the remote display connection changes state.
157     */
158    public interface Listener {
159        void onDisplayConnected(Surface surface,
160                int width, int height, int flags, int session);
161        void onDisplayDisconnected();
162        void onDisplayError(int error);
163    }
164}
165