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><service></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