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