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