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 ComponentName topComponent = screenshotRequest.getTopComponent(); 190 mUiEventLogger.log(ScreenshotEvent.getScreenshotSource(screenshotRequest.getSource()), 0, 191 topComponent == null ? "" : topComponent.getPackageName()); 192 193 switch (msg.what) { 194 case WindowManager.TAKE_SCREENSHOT_FULLSCREEN: 195 if (DEBUG_SERVICE) { 196 Log.d(TAG, "handleMessage: TAKE_SCREENSHOT_FULLSCREEN"); 197 } 198 mScreenshot.takeScreenshotFullscreen(topComponent, uriConsumer, requestCallback); 199 break; 200 case WindowManager.TAKE_SCREENSHOT_SELECTED_REGION: 201 if (DEBUG_SERVICE) { 202 Log.d(TAG, "handleMessage: TAKE_SCREENSHOT_SELECTED_REGION"); 203 } 204 mScreenshot.takeScreenshotPartial(topComponent, uriConsumer, requestCallback); 205 break; 206 case WindowManager.TAKE_SCREENSHOT_PROVIDED_IMAGE: 207 if (DEBUG_SERVICE) { 208 Log.d(TAG, "handleMessage: TAKE_SCREENSHOT_PROVIDED_IMAGE"); 209 } 210 Bitmap screenshot = BitmapUtil.bundleToHardwareBitmap( 211 screenshotRequest.getBitmapBundle()); 212 Rect screenBounds = screenshotRequest.getBoundsInScreen(); 213 Insets insets = screenshotRequest.getInsets(); 214 int taskId = screenshotRequest.getTaskId(); 215 int userId = screenshotRequest.getUserId(); 216 217 if (screenshot == null) { 218 Log.e(TAG, "Got null bitmap from screenshot message"); 219 mNotificationsController.notifyScreenshotError( 220 R.string.screenshot_failed_to_capture_text); 221 requestCallback.reportError(); 222 } else { 223 mScreenshot.handleImageAsScreenshot(screenshot, screenBounds, insets, 224 taskId, userId, topComponent, uriConsumer, requestCallback); 225 } 226 break; 227 default: 228 Log.w(TAG, "Invalid screenshot option: " + msg.what); 229 return false; 230 } 231 return true; 232 }; 233 sendComplete(Messenger target)234 private static void sendComplete(Messenger target) { 235 try { 236 if (DEBUG_CALLBACK) { 237 Log.d(TAG, "sendComplete: " + target); 238 } 239 target.send(Message.obtain(null, SCREENSHOT_MSG_PROCESS_COMPLETE)); 240 } catch (RemoteException e) { 241 Log.d(TAG, "ignored remote exception", e); 242 } 243 } 244 reportUri(Messenger target, Uri uri)245 private static void reportUri(Messenger target, Uri uri) { 246 try { 247 if (DEBUG_CALLBACK) { 248 Log.d(TAG, "reportUri: " + target + " -> " + uri); 249 } 250 target.send(Message.obtain(null, SCREENSHOT_MSG_URI, uri)); 251 } catch (RemoteException e) { 252 Log.d(TAG, "ignored remote exception", e); 253 } 254 } 255 } 256