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