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