1 /*
2  * Copyright (C) 2014 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 
17 package android.media.projection;
18 
19 import android.annotation.NonNull;
20 import android.annotation.Nullable;
21 import android.content.Context;
22 import android.hardware.display.DisplayManager;
23 import android.hardware.display.VirtualDisplay;
24 import android.media.AudioRecord;
25 import android.media.projection.IMediaProjection;
26 import android.media.projection.IMediaProjectionCallback;
27 import android.os.Handler;
28 import android.os.RemoteException;
29 import android.util.ArrayMap;
30 import android.util.Log;
31 import android.view.Surface;
32 
33 import java.util.Map;
34 
35 /**
36  * A token granting applications the ability to capture screen contents and/or
37  * record system audio. The exact capabilities granted depend on the type of
38  * MediaProjection.
39  *
40  * <p>
41  * A screen capture session can be started through {@link
42  * MediaProjectionManager#createScreenCaptureIntent}. This grants the ability to
43  * capture screen contents, but not system audio.
44  * </p>
45  */
46 public final class MediaProjection {
47     private static final String TAG = "MediaProjection";
48 
49     private final IMediaProjection mImpl;
50     private final Context mContext;
51     private final Map<Callback, CallbackRecord> mCallbacks;
52 
53     /** @hide */
MediaProjection(Context context, IMediaProjection impl)54     public MediaProjection(Context context, IMediaProjection impl) {
55         mCallbacks = new ArrayMap<Callback, CallbackRecord>();
56         mContext = context;
57         mImpl = impl;
58         try {
59             mImpl.start(new MediaProjectionCallback());
60         } catch (RemoteException e) {
61             throw new RuntimeException("Failed to start media projection", e);
62         }
63     }
64 
65     /** Register a listener to receive notifications about when the {@link
66      * MediaProjection} changes state.
67      *
68      * @param callback The callback to call.
69      * @param handler The handler on which the callback should be invoked, or
70      * null if the callback should be invoked on the calling thread's looper.
71      *
72      * @see #unregisterCallback
73      */
registerCallback(Callback callback, Handler handler)74     public void registerCallback(Callback callback, Handler handler) {
75         if (callback == null) {
76             throw new IllegalArgumentException("callback should not be null");
77         }
78         if (handler == null) {
79             handler = new Handler();
80         }
81         mCallbacks.put(callback, new CallbackRecord(callback, handler));
82     }
83 
84     /** Unregister a MediaProjection listener.
85      *
86      * @param callback The callback to unregister.
87      *
88      * @see #registerCallback
89      */
unregisterCallback(Callback callback)90     public void unregisterCallback(Callback callback) {
91         if (callback == null) {
92             throw new IllegalArgumentException("callback should not be null");
93         }
94         mCallbacks.remove(callback);
95     }
96 
97     /**
98      * @hide
99      */
createVirtualDisplay(@onNull String name, int width, int height, int dpi, boolean isSecure, @Nullable Surface surface, @Nullable VirtualDisplay.Callback callback, @Nullable Handler handler)100     public VirtualDisplay createVirtualDisplay(@NonNull String name,
101             int width, int height, int dpi, boolean isSecure, @Nullable Surface surface,
102             @Nullable VirtualDisplay.Callback callback, @Nullable Handler handler) {
103         DisplayManager dm = (DisplayManager) mContext.getSystemService(Context.DISPLAY_SERVICE);
104         int flags = isSecure ? DisplayManager.VIRTUAL_DISPLAY_FLAG_SECURE : 0;
105         return dm.createVirtualDisplay(this, name, width, height, dpi, surface,
106                     flags | DisplayManager.VIRTUAL_DISPLAY_FLAG_AUTO_MIRROR |
107                     DisplayManager.VIRTUAL_DISPLAY_FLAG_PRESENTATION, callback, handler);
108     }
109 
110     /**
111      * Creates a {@link android.hardware.display.VirtualDisplay} to capture the
112      * contents of the screen.
113      *
114      * @param name The name of the virtual display, must be non-empty.
115      * @param width The width of the virtual display in pixels. Must be
116      * greater than 0.
117      * @param height The height of the virtual display in pixels. Must be
118      * greater than 0.
119      * @param dpi The density of the virtual display in dpi. Must be greater
120      * than 0.
121      * @param surface The surface to which the content of the virtual display
122      * should be rendered, or null if there is none initially.
123      * @param flags A combination of virtual display flags. See {@link DisplayManager} for the full
124      * list of flags.
125      * @param callback Callback to call when the virtual display's state
126      * changes, or null if none.
127      * @param handler The {@link android.os.Handler} on which the callback should be
128      * invoked, or null if the callback should be invoked on the calling
129      * thread's main {@link android.os.Looper}.
130      *
131      * @see android.hardware.display.VirtualDisplay
132      */
createVirtualDisplay(@onNull String name, int width, int height, int dpi, int flags, @Nullable Surface surface, @Nullable VirtualDisplay.Callback callback, @Nullable Handler handler)133     public VirtualDisplay createVirtualDisplay(@NonNull String name,
134             int width, int height, int dpi, int flags, @Nullable Surface surface,
135             @Nullable VirtualDisplay.Callback callback, @Nullable Handler handler) {
136         DisplayManager dm = (DisplayManager) mContext.getSystemService(Context.DISPLAY_SERVICE);
137         return dm.createVirtualDisplay(
138                     this, name, width, height, dpi, surface, flags, callback, handler);
139     }
140 
141     /**
142      * Creates an AudioRecord to capture audio played back by the system.
143      * @hide
144      */
createAudioRecord( int sampleRateInHz, int channelConfig, int audioFormat, int bufferSizeInBytes)145     public AudioRecord createAudioRecord(
146             int sampleRateInHz, int channelConfig,
147             int audioFormat, int bufferSizeInBytes) {
148         return null;
149     }
150 
151     /**
152      * Stops projection.
153      */
stop()154     public void stop() {
155         try {
156             mImpl.stop();
157         } catch (RemoteException e) {
158             Log.e(TAG, "Unable to stop projection", e);
159         }
160     }
161 
162     /**
163      * Get the underlying IMediaProjection.
164      * @hide
165      */
getProjection()166     public IMediaProjection getProjection() {
167         return mImpl;
168     }
169 
170     /**
171      * Callbacks for the projection session.
172      */
173     public static abstract class Callback {
174         /**
175          * Called when the MediaProjection session is no longer valid.
176          * <p>
177          * Once a MediaProjection has been stopped, it's up to the application to release any
178          * resources it may be holding (e.g. {@link android.hardware.display.VirtualDisplay}s).
179          * </p>
180          */
onStop()181         public void onStop() { }
182     }
183 
184     private final class MediaProjectionCallback extends IMediaProjectionCallback.Stub {
185         @Override
onStop()186         public void onStop() {
187             for (CallbackRecord cbr : mCallbacks.values()) {
188                 cbr.onStop();
189             }
190         }
191     }
192 
193     private final static class CallbackRecord {
194         private final Callback mCallback;
195         private final Handler mHandler;
196 
CallbackRecord(Callback callback, Handler handler)197         public CallbackRecord(Callback callback, Handler handler) {
198             mCallback = callback;
199             mHandler = handler;
200         }
201 
onStop()202         public void onStop() {
203             mHandler.post(new Runnable() {
204                 @Override
205                 public void run() {
206                     mCallback.onStop();
207                 }
208             });
209         }
210     }
211 }
212