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