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.Context;
24 import android.content.Intent;
25 import android.media.projection.IMediaProjection;
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> 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         i.setClassName("com.android.systemui",
75                 "com.android.systemui.media.MediaProjectionPermissionActivity");
76         return i;
77     }
78 
79     /**
80      * Retrieve the MediaProjection obtained from a succesful screen
81      * capture request. Will be null if the result from the
82      * startActivityForResult() is anything other than RESULT_OK.
83      *
84      * @param resultCode The result code from {@link android.app.Activity#onActivityResult(int,
85      * int, android.content.Intent)}
86      * @param resultData The resulting data from {@link android.app.Activity#onActivityResult(int,
87      * int, android.content.Intent)}
88      */
getMediaProjection(int resultCode, @NonNull Intent resultData)89     public MediaProjection getMediaProjection(int resultCode, @NonNull Intent resultData) {
90         if (resultCode != Activity.RESULT_OK || resultData == null) {
91             return null;
92         }
93         IBinder projection = resultData.getIBinderExtra(EXTRA_MEDIA_PROJECTION);
94         if (projection == null) {
95             return null;
96         }
97         return new MediaProjection(mContext, IMediaProjection.Stub.asInterface(projection));
98     }
99 
100     /**
101      * Get the {@link MediaProjectionInfo} for the active {@link MediaProjection}.
102      * @hide
103      */
getActiveProjectionInfo()104     public MediaProjectionInfo getActiveProjectionInfo() {
105         try {
106             return mService.getActiveProjectionInfo();
107         } catch (RemoteException e) {
108             Log.e(TAG, "Unable to get the active projection info", e);
109         }
110         return null;
111     }
112 
113     /**
114      * Stop the current projection if there is one.
115      * @hide
116      */
stopActiveProjection()117     public void stopActiveProjection() {
118         try {
119             mService.stopActiveProjection();
120         } catch (RemoteException e) {
121             Log.e(TAG, "Unable to stop the currently active media projection", e);
122         }
123     }
124 
125     /**
126      * Add a callback to monitor all of the {@link MediaProjection}s activity.
127      * Not for use by regular applications, must have the MANAGE_MEDIA_PROJECTION permission.
128      * @hide
129      */
addCallback(@onNull Callback callback, @Nullable Handler handler)130     public void addCallback(@NonNull Callback callback, @Nullable Handler handler) {
131         if (callback == null) {
132             throw new IllegalArgumentException("callback must not be null");
133         }
134         CallbackDelegate delegate = new CallbackDelegate(callback, handler);
135         mCallbacks.put(callback, delegate);
136         try {
137             mService.addCallback(delegate);
138         } catch (RemoteException e) {
139             Log.e(TAG, "Unable to add callbacks to MediaProjection service", e);
140         }
141     }
142 
143     /**
144      * Remove a MediaProjection monitoring callback.
145      * @hide
146      */
removeCallback(@onNull Callback callback)147     public void removeCallback(@NonNull Callback callback) {
148         if (callback == null) {
149             throw new IllegalArgumentException("callback must not be null");
150         }
151         CallbackDelegate delegate = mCallbacks.remove(callback);
152         try {
153             if (delegate != null) {
154                 mService.removeCallback(delegate);
155             }
156         } catch (RemoteException e) {
157             Log.e(TAG, "Unable to add callbacks to MediaProjection service", e);
158         }
159     }
160 
161     /** @hide */
162     public static abstract class Callback {
onStart(MediaProjectionInfo info)163         public abstract void onStart(MediaProjectionInfo info);
onStop(MediaProjectionInfo info)164         public abstract void onStop(MediaProjectionInfo info);
165     }
166 
167     /** @hide */
168     private final static class CallbackDelegate extends IMediaProjectionWatcherCallback.Stub {
169         private Callback mCallback;
170         private Handler mHandler;
171 
CallbackDelegate(Callback callback, Handler handler)172         public CallbackDelegate(Callback callback, Handler handler) {
173             mCallback = callback;
174             if (handler == null) {
175                 handler = new Handler();
176             }
177             mHandler = handler;
178         }
179 
180         @Override
onStart(final MediaProjectionInfo info)181         public void onStart(final MediaProjectionInfo info) {
182             mHandler.post(new Runnable() {
183                 @Override
184                 public void run() {
185                     mCallback.onStart(info);
186                 }
187             });
188         }
189 
190         @Override
onStop(final MediaProjectionInfo info)191         public void onStop(final MediaProjectionInfo info) {
192             mHandler.post(new Runnable() {
193                 @Override
194                 public void run() {
195                     mCallback.onStop(info);
196                 }
197             });
198         }
199     }
200 }
201