1 /* 2 * Copyright (C) 2011 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 com.android.systemui.screenshot; 18 19 import static android.content.Intent.ACTION_CLOSE_SYSTEM_DIALOGS; 20 21 import static com.android.internal.util.ScreenshotHelper.SCREENSHOT_MSG_PROCESS_COMPLETE; 22 import static com.android.internal.util.ScreenshotHelper.SCREENSHOT_MSG_URI; 23 import static com.android.systemui.screenshot.LogConfig.DEBUG_CALLBACK; 24 import static com.android.systemui.screenshot.LogConfig.DEBUG_DISMISS; 25 import static com.android.systemui.screenshot.LogConfig.DEBUG_SERVICE; 26 import static com.android.systemui.screenshot.LogConfig.logTag; 27 28 import android.annotation.MainThread; 29 import android.app.Service; 30 import android.content.BroadcastReceiver; 31 import android.content.ComponentName; 32 import android.content.Context; 33 import android.content.Intent; 34 import android.content.IntentFilter; 35 import android.graphics.Bitmap; 36 import android.graphics.Insets; 37 import android.graphics.Rect; 38 import android.net.Uri; 39 import android.os.Handler; 40 import android.os.IBinder; 41 import android.os.Looper; 42 import android.os.Message; 43 import android.os.Messenger; 44 import android.os.RemoteException; 45 import android.os.UserManager; 46 import android.util.Log; 47 import android.view.WindowManager; 48 49 import androidx.annotation.NonNull; 50 51 import com.android.internal.logging.UiEventLogger; 52 import com.android.internal.util.ScreenshotHelper; 53 import com.android.systemui.R; 54 import com.android.systemui.shared.recents.utilities.BitmapUtil; 55 56 import java.util.function.Consumer; 57 58 import javax.inject.Inject; 59 60 public class TakeScreenshotService extends Service { 61 private static final String TAG = logTag(TakeScreenshotService.class); 62 63 private ScreenshotController mScreenshot; 64 65 private final UserManager mUserManager; 66 private final UiEventLogger mUiEventLogger; 67 private final ScreenshotNotificationsController mNotificationsController; 68 private final Handler mHandler; 69 70 private final BroadcastReceiver mCloseSystemDialogs = new BroadcastReceiver() { 71 @Override 72 public void onReceive(Context context, Intent intent) { 73 if (ACTION_CLOSE_SYSTEM_DIALOGS.equals(intent.getAction()) && mScreenshot != null) { 74 if (DEBUG_DISMISS) { 75 Log.d(TAG, "Received ACTION_CLOSE_SYSTEM_DIALOGS"); 76 } 77 if (!mScreenshot.isPendingSharedTransition()) { 78 mScreenshot.dismissScreenshot(false); 79 } 80 } 81 } 82 }; 83 84 /** Informs about coarse grained state of the Controller. */ 85 interface RequestCallback { 86 /** Respond to the current request indicating the screenshot request failed.*/ reportError()87 void reportError(); 88 89 /** The controller has completed handling this request UI has been removed */ onFinish()90 void onFinish(); 91 } 92 93 @Inject TakeScreenshotService(ScreenshotController screenshotController, UserManager userManager, UiEventLogger uiEventLogger, ScreenshotNotificationsController notificationsController)94 public TakeScreenshotService(ScreenshotController screenshotController, UserManager userManager, 95 UiEventLogger uiEventLogger, 96 ScreenshotNotificationsController notificationsController) { 97 if (DEBUG_SERVICE) { 98 Log.d(TAG, "new " + this); 99 } 100 mHandler = new Handler(Looper.getMainLooper(), this::handleMessage); 101 mScreenshot = screenshotController; 102 mUserManager = userManager; 103 mUiEventLogger = uiEventLogger; 104 mNotificationsController = notificationsController; 105 } 106 107 @Override onCreate()108 public void onCreate() { 109 if (DEBUG_SERVICE) { 110 Log.d(TAG, "onCreate()"); 111 } 112 } 113 114 @Override onBind(@onNull Intent intent)115 public IBinder onBind(@NonNull Intent intent) { 116 registerReceiver(mCloseSystemDialogs, new IntentFilter(ACTION_CLOSE_SYSTEM_DIALOGS)); 117 final Messenger m = new Messenger(mHandler); 118 if (DEBUG_SERVICE) { 119 Log.d(TAG, "onBind: returning connection: " + m); 120 } 121 return m.getBinder(); 122 } 123 124 @Override onUnbind(Intent intent)125 public boolean onUnbind(Intent intent) { 126 if (DEBUG_SERVICE) { 127 Log.d(TAG, "onUnbind"); 128 } 129 if (mScreenshot != null) { 130 mScreenshot.removeWindow(); 131 mScreenshot = null; 132 } 133 unregisterReceiver(mCloseSystemDialogs); 134 return false; 135 } 136 137 @Override onDestroy()138 public void onDestroy() { 139 super.onDestroy(); 140 if (mScreenshot != null) { 141 mScreenshot.removeWindow(); 142 mScreenshot.releaseContext(); 143 mScreenshot = null; 144 } 145 if (DEBUG_SERVICE) { 146 Log.d(TAG, "onDestroy"); 147 } 148 } 149 150 static class RequestCallbackImpl implements RequestCallback { 151 private final Messenger mReplyTo; 152 RequestCallbackImpl(Messenger replyTo)153 RequestCallbackImpl(Messenger replyTo) { 154 mReplyTo = replyTo; 155 } 156 reportError()157 public void reportError() { 158 reportUri(mReplyTo, null); 159 sendComplete(mReplyTo); 160 } 161 162 @Override onFinish()163 public void onFinish() { 164 sendComplete(mReplyTo); 165 } 166 } 167 168 /** Respond to incoming Message via Binder (Messenger) */ 169 @MainThread handleMessage(Message msg)170 private boolean handleMessage(Message msg) { 171 final Messenger replyTo = msg.replyTo; 172 final Consumer<Uri> uriConsumer = (uri) -> reportUri(replyTo, uri); 173 RequestCallback requestCallback = new RequestCallbackImpl(replyTo); 174 175 // If the storage for this user is locked, we have no place to store 176 // the screenshot, so skip taking it instead of showing a misleading 177 // animation and error notification. 178 if (!mUserManager.isUserUnlocked()) { 179 Log.w(TAG, "Skipping screenshot because storage is locked!"); 180 mNotificationsController.notifyScreenshotError( 181 R.string.screenshot_failed_to_save_user_locked_text); 182 requestCallback.reportError(); 183 return true; 184 } 185 186 ScreenshotHelper.ScreenshotRequest screenshotRequest = 187 (ScreenshotHelper.ScreenshotRequest) msg.obj; 188 189 mUiEventLogger.log(ScreenshotEvent.getScreenshotSource(screenshotRequest.getSource())); 190 191 switch (msg.what) { 192 case WindowManager.TAKE_SCREENSHOT_FULLSCREEN: 193 if (DEBUG_SERVICE) { 194 Log.d(TAG, "handleMessage: TAKE_SCREENSHOT_FULLSCREEN"); 195 } 196 mScreenshot.takeScreenshotFullscreen(uriConsumer, requestCallback); 197 break; 198 case WindowManager.TAKE_SCREENSHOT_SELECTED_REGION: 199 if (DEBUG_SERVICE) { 200 Log.d(TAG, "handleMessage: TAKE_SCREENSHOT_SELECTED_REGION"); 201 } 202 mScreenshot.takeScreenshotPartial(uriConsumer, requestCallback); 203 break; 204 case WindowManager.TAKE_SCREENSHOT_PROVIDED_IMAGE: 205 if (DEBUG_SERVICE) { 206 Log.d(TAG, "handleMessage: TAKE_SCREENSHOT_PROVIDED_IMAGE"); 207 } 208 Bitmap screenshot = BitmapUtil.bundleToHardwareBitmap( 209 screenshotRequest.getBitmapBundle()); 210 Rect screenBounds = screenshotRequest.getBoundsInScreen(); 211 Insets insets = screenshotRequest.getInsets(); 212 int taskId = screenshotRequest.getTaskId(); 213 int userId = screenshotRequest.getUserId(); 214 ComponentName topComponent = screenshotRequest.getTopComponent(); 215 216 if (screenshot == null) { 217 Log.e(TAG, "Got null bitmap from screenshot message"); 218 mNotificationsController.notifyScreenshotError( 219 R.string.screenshot_failed_to_capture_text); 220 requestCallback.reportError(); 221 } else { 222 mScreenshot.handleImageAsScreenshot(screenshot, screenBounds, insets, 223 taskId, userId, topComponent, uriConsumer, requestCallback); 224 } 225 break; 226 default: 227 Log.w(TAG, "Invalid screenshot option: " + msg.what); 228 return false; 229 } 230 return true; 231 }; 232 sendComplete(Messenger target)233 private static void sendComplete(Messenger target) { 234 try { 235 if (DEBUG_CALLBACK) { 236 Log.d(TAG, "sendComplete: " + target); 237 } 238 target.send(Message.obtain(null, SCREENSHOT_MSG_PROCESS_COMPLETE)); 239 } catch (RemoteException e) { 240 Log.d(TAG, "ignored remote exception", e); 241 } 242 } 243 reportUri(Messenger target, Uri uri)244 private static void reportUri(Messenger target, Uri uri) { 245 try { 246 if (DEBUG_CALLBACK) { 247 Log.d(TAG, "reportUri: " + target + " -> " + uri); 248 } 249 target.send(Message.obtain(null, SCREENSHOT_MSG_URI, uri)); 250 } catch (RemoteException e) { 251 Log.d(TAG, "ignored remote exception", e); 252 } 253 } 254 } 255