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.app.Activity;
22 import android.content.Context;
23 import android.content.Intent;
24 import android.media.projection.IMediaProjection;
25 import android.os.Handler;
26 import android.os.IBinder;
27 import android.os.RemoteException;
28 import android.os.ServiceManager;
29 import android.util.ArrayMap;
30 import android.util.Log;
31 
32 import java.util.Map;
33 
34 /**
35  * Manages the retrieval of certain types of {@link MediaProjection} tokens.
36  *
37  * <p>
38  * Get an instance of this class by calling {@link
39  * android.content.Context#getSystemService(java.lang.String)
40  * Context.getSystemService()} with the argument {@link
41  * android.content.Context#MEDIA_PROJECTION_SERVICE}.
42  * </p>
43  */
44 public final class MediaProjectionManager {
45     private static final String TAG = "MediaProjectionManager";
46     /** @hide */
47     public static final String EXTRA_APP_TOKEN = "android.media.projection.extra.EXTRA_APP_TOKEN";
48     /** @hide */
49     public static final String EXTRA_MEDIA_PROJECTION =
50             "android.media.projection.extra.EXTRA_MEDIA_PROJECTION";
51 
52     /** @hide */
53     public static final int TYPE_SCREEN_CAPTURE = 0;
54     /** @hide */
55     public static final int TYPE_MIRRORING = 1;
56     /** @hide */
57     public static final int TYPE_PRESENTATION = 2;
58 
59     private Context mContext;
60     private Map<Callback, CallbackDelegate> mCallbacks;
61     private IMediaProjectionManager mService;
62 
63     /** @hide */
MediaProjectionManager(Context context)64     public MediaProjectionManager(Context context) {
65         mContext = context;
66         IBinder b = ServiceManager.getService(Context.MEDIA_PROJECTION_SERVICE);
67         mService = IMediaProjectionManager.Stub.asInterface(b);
68         mCallbacks = new ArrayMap<>();
69     }
70 
71     /**
72      * Returns an Intent that <b>must</b> passed to startActivityForResult()
73      * in order to start screen capture. The activity will prompt
74      * the user whether to allow screen capture.  The result of this
75      * activity should be passed to getMediaProjection.
76      */
createScreenCaptureIntent()77     public Intent createScreenCaptureIntent() {
78         Intent i = new Intent();
79         i.setClassName("com.android.systemui",
80                 "com.android.systemui.media.MediaProjectionPermissionActivity");
81         return i;
82     }
83 
84     /**
85      * Retrieve the MediaProjection obtained from a succesful screen
86      * capture request. Will be null if the result from the
87      * startActivityForResult() is anything other than RESULT_OK.
88      *
89      * @param resultCode The result code from {@link android.app.Activity#onActivityResult(int,
90      * int, android.content.Intent)}
91      * @param resultData The resulting data from {@link android.app.Activity#onActivityResult(int,
92      * int, android.content.Intent)}
93      */
getMediaProjection(int resultCode, @NonNull Intent resultData)94     public MediaProjection getMediaProjection(int resultCode, @NonNull Intent resultData) {
95         if (resultCode != Activity.RESULT_OK || resultData == null) {
96             return null;
97         }
98         IBinder projection = resultData.getIBinderExtra(EXTRA_MEDIA_PROJECTION);
99         if (projection == null) {
100             return null;
101         }
102         return new MediaProjection(mContext, IMediaProjection.Stub.asInterface(projection));
103     }
104 
105     /**
106      * Get the {@link MediaProjectionInfo} for the active {@link MediaProjection}.
107      * @hide
108      */
getActiveProjectionInfo()109     public MediaProjectionInfo getActiveProjectionInfo() {
110         try {
111             return mService.getActiveProjectionInfo();
112         } catch (RemoteException e) {
113             Log.e(TAG, "Unable to get the active projection info", e);
114         }
115         return null;
116     }
117 
118     /**
119      * Stop the current projection if there is one.
120      * @hide
121      */
stopActiveProjection()122     public void stopActiveProjection() {
123         try {
124             mService.stopActiveProjection();
125         } catch (RemoteException e) {
126             Log.e(TAG, "Unable to stop the currently active media projection", e);
127         }
128     }
129 
130     /**
131      * Add a callback to monitor all of the {@link MediaProjection}s activity.
132      * Not for use by regular applications, must have the MANAGE_MEDIA_PROJECTION permission.
133      * @hide
134      */
addCallback(@onNull Callback callback, @Nullable Handler handler)135     public void addCallback(@NonNull Callback callback, @Nullable Handler handler) {
136         if (callback == null) {
137             throw new IllegalArgumentException("callback must not be null");
138         }
139         CallbackDelegate delegate = new CallbackDelegate(callback, handler);
140         mCallbacks.put(callback, delegate);
141         try {
142             mService.addCallback(delegate);
143         } catch (RemoteException e) {
144             Log.e(TAG, "Unable to add callbacks to MediaProjection service", e);
145         }
146     }
147 
148     /**
149      * Remove a MediaProjection monitoring callback.
150      * @hide
151      */
removeCallback(@onNull Callback callback)152     public void removeCallback(@NonNull Callback callback) {
153         if (callback == null) {
154             throw new IllegalArgumentException("callback must not be null");
155         }
156         CallbackDelegate delegate = mCallbacks.remove(callback);
157         try {
158             if (delegate != null) {
159                 mService.removeCallback(delegate);
160             }
161         } catch (RemoteException e) {
162             Log.e(TAG, "Unable to add callbacks to MediaProjection service", e);
163         }
164     }
165 
166     /** @hide */
167     public static abstract class Callback {
onStart(MediaProjectionInfo info)168         public abstract void onStart(MediaProjectionInfo info);
onStop(MediaProjectionInfo info)169         public abstract void onStop(MediaProjectionInfo info);
170     }
171 
172     /** @hide */
173     private final static class CallbackDelegate extends IMediaProjectionWatcherCallback.Stub {
174         private Callback mCallback;
175         private Handler mHandler;
176 
CallbackDelegate(Callback callback, Handler handler)177         public CallbackDelegate(Callback callback, Handler handler) {
178             mCallback = callback;
179             if (handler == null) {
180                 handler = new Handler();
181             }
182             mHandler = handler;
183         }
184 
185         @Override
onStart(final MediaProjectionInfo info)186         public void onStart(final MediaProjectionInfo info) {
187             mHandler.post(new Runnable() {
188                 @Override
189                 public void run() {
190                     mCallback.onStart(info);
191                 }
192             });
193         }
194 
195         @Override
onStop(final MediaProjectionInfo info)196         public void onStop(final MediaProjectionInfo info) {
197             mHandler.post(new Runnable() {
198                 @Override
199                 public void run() {
200                     mCallback.onStop(info);
201                 }
202             });
203         }
204     }
205 }
206