/*
 * Copyright (C) 2014 The Android Open Source Project
 *
 * Licensed under the Apache License, Version 2.0 (the "License");
 * you may not use this file except in compliance with the License.
 * You may obtain a copy of the License at
 *
 *      http://www.apache.org/licenses/LICENSE-2.0
 *
 * Unless required by applicable law or agreed to in writing, software
 * distributed under the License is distributed on an "AS IS" BASIS,
 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
 * See the License for the specific language governing permissions and
 * limitations under the License.
 */

package android.media.projection;

import android.annotation.NonNull;
import android.annotation.Nullable;
import android.content.Context;
import android.hardware.display.DisplayManager;
import android.hardware.display.VirtualDisplay;
import android.media.AudioRecord;
import android.media.projection.IMediaProjection;
import android.media.projection.IMediaProjectionCallback;
import android.os.Handler;
import android.os.RemoteException;
import android.util.ArrayMap;
import android.util.Log;
import android.view.Surface;

import java.util.Map;

A token granting applications the ability to capture screen contents and/or record system audio. The exact capabilities granted depend on the type of MediaProjection.

A screen capture session can be started through MediaProjectionManager.createScreenCaptureIntent. This grants the ability to capture screen contents, but not system audio.

/** * A token granting applications the ability to capture screen contents and/or * record system audio. The exact capabilities granted depend on the type of * MediaProjection. * * <p> * A screen capture session can be started through {@link * MediaProjectionManager#createScreenCaptureIntent}. This grants the ability to * capture screen contents, but not system audio. * </p> */
public final class MediaProjection { private static final String TAG = "MediaProjection"; private final IMediaProjection mImpl; private final Context mContext; private final Map<Callback, CallbackRecord> mCallbacks;
@hide
/** @hide */
public MediaProjection(Context context, IMediaProjection impl) { mCallbacks = new ArrayMap<Callback, CallbackRecord>(); mContext = context; mImpl = impl; try { mImpl.start(new MediaProjectionCallback()); } catch (RemoteException e) { throw new RuntimeException("Failed to start media projection", e); } }
Register a listener to receive notifications about when the MediaProjection changes state.
Params:
  • callback – The callback to call.
  • handler – The handler on which the callback should be invoked, or null if the callback should be invoked on the calling thread's looper.
See Also:
/** Register a listener to receive notifications about when the {@link * MediaProjection} changes state. * * @param callback The callback to call. * @param handler The handler on which the callback should be invoked, or * null if the callback should be invoked on the calling thread's looper. * * @see #unregisterCallback */
public void registerCallback(Callback callback, Handler handler) { if (callback == null) { throw new IllegalArgumentException("callback should not be null"); } if (handler == null) { handler = new Handler(); } mCallbacks.put(callback, new CallbackRecord(callback, handler)); }
Unregister a MediaProjection listener.
Params:
  • callback – The callback to unregister.
See Also:
/** Unregister a MediaProjection listener. * * @param callback The callback to unregister. * * @see #registerCallback */
public void unregisterCallback(Callback callback) { if (callback == null) { throw new IllegalArgumentException("callback should not be null"); } mCallbacks.remove(callback); }
@hide
/** * @hide */
public VirtualDisplay createVirtualDisplay(@NonNull String name, int width, int height, int dpi, boolean isSecure, @Nullable Surface surface, @Nullable VirtualDisplay.Callback callback, @Nullable Handler handler) { DisplayManager dm = (DisplayManager) mContext.getSystemService(Context.DISPLAY_SERVICE); int flags = isSecure ? DisplayManager.VIRTUAL_DISPLAY_FLAG_SECURE : 0; return dm.createVirtualDisplay(this, name, width, height, dpi, surface, flags | DisplayManager.VIRTUAL_DISPLAY_FLAG_AUTO_MIRROR | DisplayManager.VIRTUAL_DISPLAY_FLAG_PRESENTATION, callback, handler, null /* uniqueId */); }
Creates a VirtualDisplay to capture the contents of the screen.
Params:
  • name – The name of the virtual display, must be non-empty.
  • width – The width of the virtual display in pixels. Must be greater than 0.
  • height – The height of the virtual display in pixels. Must be greater than 0.
  • dpi – The density of the virtual display in dpi. Must be greater than 0.
  • surface – The surface to which the content of the virtual display should be rendered, or null if there is none initially.
  • flags – A combination of virtual display flags. See DisplayManager for the full list of flags.
  • callback – Callback to call when the virtual display's state changes, or null if none.
  • handler – The Handler on which the callback should be invoked, or null if the callback should be invoked on the calling thread's main Looper.
See Also:
/** * Creates a {@link android.hardware.display.VirtualDisplay} to capture the * contents of the screen. * * @param name The name of the virtual display, must be non-empty. * @param width The width of the virtual display in pixels. Must be * greater than 0. * @param height The height of the virtual display in pixels. Must be * greater than 0. * @param dpi The density of the virtual display in dpi. Must be greater * than 0. * @param surface The surface to which the content of the virtual display * should be rendered, or null if there is none initially. * @param flags A combination of virtual display flags. See {@link DisplayManager} for the full * list of flags. * @param callback Callback to call when the virtual display's state * changes, or null if none. * @param handler The {@link android.os.Handler} on which the callback should be * invoked, or null if the callback should be invoked on the calling * thread's main {@link android.os.Looper}. * * @see android.hardware.display.VirtualDisplay */
public VirtualDisplay createVirtualDisplay(@NonNull String name, int width, int height, int dpi, int flags, @Nullable Surface surface, @Nullable VirtualDisplay.Callback callback, @Nullable Handler handler) { DisplayManager dm = (DisplayManager) mContext.getSystemService(Context.DISPLAY_SERVICE); return dm.createVirtualDisplay(this, name, width, height, dpi, surface, flags, callback, handler, null /* uniqueId */); }
Creates an AudioRecord to capture audio played back by the system.
@hide
/** * Creates an AudioRecord to capture audio played back by the system. * @hide */
public AudioRecord createAudioRecord( int sampleRateInHz, int channelConfig, int audioFormat, int bufferSizeInBytes) { return null; }
Stops projection.
/** * Stops projection. */
public void stop() { try { mImpl.stop(); } catch (RemoteException e) { Log.e(TAG, "Unable to stop projection", e); } }
Get the underlying IMediaProjection.
@hide
/** * Get the underlying IMediaProjection. * @hide */
public IMediaProjection getProjection() { return mImpl; }
Callbacks for the projection session.
/** * Callbacks for the projection session. */
public static abstract class Callback {
Called when the MediaProjection session is no longer valid.

Once a MediaProjection has been stopped, it's up to the application to release any resources it may be holding (e.g. VirtualDisplays).

/** * Called when the MediaProjection session is no longer valid. * <p> * Once a MediaProjection has been stopped, it's up to the application to release any * resources it may be holding (e.g. {@link android.hardware.display.VirtualDisplay}s). * </p> */
public void onStop() { } } private final class MediaProjectionCallback extends IMediaProjectionCallback.Stub { @Override public void onStop() { for (CallbackRecord cbr : mCallbacks.values()) { cbr.onStop(); } } } private final static class CallbackRecord { private final Callback mCallback; private final Handler mHandler; public CallbackRecord(Callback callback, Handler handler) { mCallback = callback; mHandler = handler; } public void onStop() { mHandler.post(new Runnable() { @Override public void run() { mCallback.onStop(); } }); } } }