• Home
  • Line#
  • Scopes#
  • Navigate#
  • Raw
  • Download
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.annotation.SystemService;
22 import android.app.Activity;
23 import android.content.ComponentName;
24 import android.content.Context;
25 import android.content.Intent;
26 import android.os.Handler;
27 import android.os.IBinder;
28 import android.os.RemoteException;
29 import android.os.ServiceManager;
30 import android.util.ArrayMap;
31 import android.util.Log;
32 
33 import java.util.Map;
34 
35 /**
36  * Manages the retrieval of certain types of {@link MediaProjection} tokens.
37  */
38 @SystemService(Context.MEDIA_PROJECTION_SERVICE)
39 public final class MediaProjectionManager {
40     private static final String TAG = "MediaProjectionManager";
41     /** @hide */
42     public static final String EXTRA_APP_TOKEN = "android.media.projection.extra.EXTRA_APP_TOKEN";
43     /** @hide */
44     public static final String EXTRA_MEDIA_PROJECTION =
45             "android.media.projection.extra.EXTRA_MEDIA_PROJECTION";
46 
47     /** @hide */
48     public static final int TYPE_SCREEN_CAPTURE = 0;
49     /** @hide */
50     public static final int TYPE_MIRRORING = 1;
51     /** @hide */
52     public static final int TYPE_PRESENTATION = 2;
53 
54     private Context mContext;
55     private Map<Callback, CallbackDelegate> mCallbacks;
56     private IMediaProjectionManager mService;
57 
58     /** @hide */
MediaProjectionManager(Context context)59     public MediaProjectionManager(Context context) {
60         mContext = context;
61         IBinder b = ServiceManager.getService(Context.MEDIA_PROJECTION_SERVICE);
62         mService = IMediaProjectionManager.Stub.asInterface(b);
63         mCallbacks = new ArrayMap<>();
64     }
65 
66     /**
67      * Returns an Intent that <b>must</b> be passed to startActivityForResult()
68      * in order to start screen capture. The activity will prompt
69      * the user whether to allow screen capture.  The result of this
70      * activity should be passed to getMediaProjection.
71      */
createScreenCaptureIntent()72     public Intent createScreenCaptureIntent() {
73         Intent i = new Intent();
74         final ComponentName mediaProjectionPermissionDialogComponent =
75                 ComponentName.unflattenFromString(mContext.getResources().getString(
76                         com.android.internal.R.string
77                         .config_mediaProjectionPermissionDialogComponent));
78         i.setComponent(mediaProjectionPermissionDialogComponent);
79         return i;
80     }
81 
82     /**
83      * Retrieves the {@link MediaProjection} obtained from a successful screen
84      * capture request. The result code and data from the request are provided
85      * by overriding {@link Activity#onActivityResult(int, int, Intent)
86      * onActivityResult(int, int, Intent)}, which is called after starting an
87      * activity using {@link #createScreenCaptureIntent()}.
88      *
89      * <p>Starting from Android {@link android.os.Build.VERSION_CODES#R}, if
90      * your application requests the
91      * {@link android.Manifest.permission#SYSTEM_ALERT_WINDOW
92      * SYSTEM_ALERT_WINDOW} permission, and the user has not explicitly denied
93      * it, the permission will be automatically granted until the projection is
94      * stopped. The permission allows your app to display user controls on top
95      * of the screen being captured.
96      *
97      * <p>Apps targeting SDK version {@link android.os.Build.VERSION_CODES#Q} or
98      * later must set the
99      * {@link android.R.attr#foregroundServiceType foregroundServiceType}
100      * attribute to {@code mediaProjection} in the
101      * <a href="/guide/topics/manifest/service-element">
102      * <code>&lt;service&gt;</code></a> element of the app's manifest file;
103      * {@code mediaProjection} is equivalent to
104      * {@link android.content.pm.ServiceInfo#FOREGROUND_SERVICE_TYPE_MEDIA_PROJECTION
105      * FOREGROUND_SERVICE_TYPE_MEDIA_PROJECTION}.
106      *
107      * @see <a href="/guide/components/foreground-services">
108      *      Foreground services developer guide</a>
109      * @see <a href="/guide/topics/large-screens/media-projection">
110      *      Media projection developer guide</a>
111      *
112      * @param resultCode The result code from
113      *      {@link android.app.Activity#onActivityResult(int, int, android.content.Intent)
114      *      onActivityResult(int, int, Intent)}.
115      * @param resultData The result data from
116      *      {@link android.app.Activity#onActivityResult(int, int, android.content.Intent)
117      *      onActivityResult(int, int, Intent)}.
118      * @return The media projection obtained from a successful screen capture
119      *      request, or null if the result of the screen capture request is not
120      *      {@link Activity#RESULT_OK RESULT_OK}.
121      * @throws IllegalStateException On
122      *      pre-{@link android.os.Build.VERSION_CODES#Q Q} devices if a
123      *      previously obtained {@code MediaProjection} from the same
124      *      {@code resultData} has not yet been stopped.
125      */
getMediaProjection(int resultCode, @NonNull Intent resultData)126     public MediaProjection getMediaProjection(int resultCode, @NonNull Intent resultData) {
127         if (resultCode != Activity.RESULT_OK || resultData == null) {
128             return null;
129         }
130         IBinder projection = resultData.getIBinderExtra(EXTRA_MEDIA_PROJECTION);
131         if (projection == null) {
132             return null;
133         }
134         return new MediaProjection(mContext, IMediaProjection.Stub.asInterface(projection));
135     }
136 
137     /**
138      * Get the {@link MediaProjectionInfo} for the active {@link MediaProjection}.
139      * @hide
140      */
getActiveProjectionInfo()141     public MediaProjectionInfo getActiveProjectionInfo() {
142         try {
143             return mService.getActiveProjectionInfo();
144         } catch (RemoteException e) {
145             Log.e(TAG, "Unable to get the active projection info", e);
146         }
147         return null;
148     }
149 
150     /**
151      * Stop the current projection if there is one.
152      * @hide
153      */
stopActiveProjection()154     public void stopActiveProjection() {
155         try {
156             mService.stopActiveProjection();
157         } catch (RemoteException e) {
158             Log.e(TAG, "Unable to stop the currently active media projection", e);
159         }
160     }
161 
162     /**
163      * Add a callback to monitor all of the {@link MediaProjection}s activity.
164      * Not for use by regular applications, must have the MANAGE_MEDIA_PROJECTION permission.
165      * @hide
166      */
addCallback(@onNull Callback callback, @Nullable Handler handler)167     public void addCallback(@NonNull Callback callback, @Nullable Handler handler) {
168         if (callback == null) {
169             throw new IllegalArgumentException("callback must not be null");
170         }
171         CallbackDelegate delegate = new CallbackDelegate(callback, handler);
172         mCallbacks.put(callback, delegate);
173         try {
174             mService.addCallback(delegate);
175         } catch (RemoteException e) {
176             Log.e(TAG, "Unable to add callbacks to MediaProjection service", e);
177         }
178     }
179 
180     /**
181      * Remove a MediaProjection monitoring callback.
182      * @hide
183      */
removeCallback(@onNull Callback callback)184     public void removeCallback(@NonNull Callback callback) {
185         if (callback == null) {
186             throw new IllegalArgumentException("callback must not be null");
187         }
188         CallbackDelegate delegate = mCallbacks.remove(callback);
189         try {
190             if (delegate != null) {
191                 mService.removeCallback(delegate);
192             }
193         } catch (RemoteException e) {
194             Log.e(TAG, "Unable to add callbacks to MediaProjection service", e);
195         }
196     }
197 
198     /** @hide */
199     public static abstract class Callback {
onStart(MediaProjectionInfo info)200         public abstract void onStart(MediaProjectionInfo info);
onStop(MediaProjectionInfo info)201         public abstract void onStop(MediaProjectionInfo info);
202     }
203 
204     /** @hide */
205     private final static class CallbackDelegate extends IMediaProjectionWatcherCallback.Stub {
206         private Callback mCallback;
207         private Handler mHandler;
208 
CallbackDelegate(Callback callback, Handler handler)209         public CallbackDelegate(Callback callback, Handler handler) {
210             mCallback = callback;
211             if (handler == null) {
212                 handler = new Handler();
213             }
214             mHandler = handler;
215         }
216 
217         @Override
onStart(final MediaProjectionInfo info)218         public void onStart(final MediaProjectionInfo info) {
219             mHandler.post(new Runnable() {
220                 @Override
221                 public void run() {
222                     mCallback.onStart(info);
223                 }
224             });
225         }
226 
227         @Override
onStop(final MediaProjectionInfo info)228         public void onStop(final MediaProjectionInfo info) {
229             mHandler.post(new Runnable() {
230                 @Override
231                 public void run() {
232                     mCallback.onStop(info);
233                 }
234             });
235         }
236     }
237 }
238