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