Loading core/java/com/android/internal/util/ScreenshotRequest.java +3 −0 Original line number Diff line number Diff line Loading @@ -173,6 +173,9 @@ public class ScreenshotRequest implements Parcelable { public Builder( @WindowManager.ScreenshotType int type, @WindowManager.ScreenshotSource int source) { if (type != TAKE_SCREENSHOT_FULLSCREEN && type != TAKE_SCREENSHOT_PROVIDED_IMAGE) { throw new IllegalArgumentException("Invalid screenshot type requested!"); } mType = type; mSource = source; } Loading core/tests/screenshothelpertests/src/com/android/internal/util/ScreenshotRequestTest.java +6 −0 Original line number Diff line number Diff line Loading @@ -131,6 +131,12 @@ public final class ScreenshotRequestTest { assertEquals(Insets.NONE, out.getInsets()); } @Test public void testInvalidType() { assertThrows(IllegalArgumentException.class, () -> new ScreenshotRequest.Builder(5, 2).build()); } private Bitmap makeHardwareBitmap(int width, int height) { HardwareBuffer buffer = HardwareBuffer.create( width, height, HardwareBuffer.RGBA_8888, 1, HardwareBuffer.USAGE_GPU_SAMPLED_IMAGE); Loading packages/SystemUI/src/com/android/systemui/screenshot/ScreenshotEvent.java +2 −0 Original line number Diff line number Diff line Loading @@ -46,6 +46,8 @@ public enum ScreenshotEvent implements UiEventLogger.UiEventEnum { SCREENSHOT_SAVED(306), @UiEvent(doc = "screenshot failed to save") SCREENSHOT_NOT_SAVED(336), @UiEvent(doc = "failed to capture screenshot") SCREENSHOT_CAPTURE_FAILED(1281), @UiEvent(doc = "screenshot preview tapped") SCREENSHOT_PREVIEW_TAPPED(307), @UiEvent(doc = "screenshot edit button tapped") Loading packages/SystemUI/src/com/android/systemui/screenshot/TakeScreenshotService.java +37 −28 Original line number Diff line number Diff line Loading @@ -26,6 +26,7 @@ import static com.android.systemui.screenshot.LogConfig.DEBUG_CALLBACK; import static com.android.systemui.screenshot.LogConfig.DEBUG_DISMISS; import static com.android.systemui.screenshot.LogConfig.DEBUG_SERVICE; import static com.android.systemui.screenshot.LogConfig.logTag; import static com.android.systemui.screenshot.ScreenshotEvent.SCREENSHOT_CAPTURE_FAILED; import static com.android.systemui.screenshot.ScreenshotEvent.SCREENSHOT_DISMISSED_OTHER; import android.annotation.MainThread; Loading Loading @@ -202,6 +203,7 @@ public class TakeScreenshotService extends Service { // animation and error notification. if (!mUserManager.isUserUnlocked()) { Log.w(TAG, "Skipping screenshot because storage is locked!"); logFailedRequest(request); mNotificationsController.notifyScreenshotError( R.string.screenshot_failed_to_save_user_locked_text); callback.reportError(); Loading @@ -212,6 +214,7 @@ public class TakeScreenshotService extends Service { mBgExecutor.execute(() -> { Log.w(TAG, "Skipping screenshot because an IT admin has disabled " + "screenshots on the device"); logFailedRequest(request); String blockedByAdminText = mDevicePolicyManager.getResources().getString( SCREENSHOT_BLOCKED_BY_ADMIN, () -> mContext.getString(R.string.screenshot_blocked_by_admin)); Loading @@ -225,38 +228,43 @@ public class TakeScreenshotService extends Service { if (mFeatureFlags.isEnabled(Flags.SCREENSHOT_METADATA)) { Log.d(TAG, "Processing screenshot data"); ScreenshotData screenshotData = ScreenshotData.fromRequest(request); try { mProcessor.processAsync(screenshotData, (data) -> dispatchToController(data, onSaved, callback)); } catch (IllegalStateException e) { Log.e(TAG, "Failed to process screenshot request!", e); logFailedRequest(request); mNotificationsController.notifyScreenshotError( R.string.screenshot_failed_to_capture_text); callback.reportError(); } } else { try { mProcessor.processAsync(request, (r) -> dispatchToController(r, onSaved, callback)); } catch (IllegalStateException e) { Log.e(TAG, "Failed to process screenshot request!", e); logFailedRequest(request); mNotificationsController.notifyScreenshotError( R.string.screenshot_failed_to_capture_text); callback.reportError(); } } } private void dispatchToController(ScreenshotData screenshot, Consumer<Uri> uriConsumer, RequestCallback callback) { mUiEventLogger.log(ScreenshotEvent.getScreenshotSource(screenshot.getSource()), 0, screenshot.getPackageNameString()); if (screenshot.getType() == WindowManager.TAKE_SCREENSHOT_PROVIDED_IMAGE && screenshot.getBitmap() == null) { Log.e(TAG, "Got null bitmap from screenshot message"); mNotificationsController.notifyScreenshotError( R.string.screenshot_failed_to_capture_text); callback.reportError(); return; } mScreenshot.handleScreenshot(screenshot, uriConsumer, callback); } private void dispatchToController(ScreenshotRequest request, Consumer<Uri> uriConsumer, RequestCallback callback) { ComponentName topComponent = request.getTopComponent(); mUiEventLogger.log(ScreenshotEvent.getScreenshotSource(request.getSource()), 0, topComponent == null ? "" : topComponent.getPackageName()); String packageName = topComponent == null ? "" : topComponent.getPackageName(); mUiEventLogger.log( ScreenshotEvent.getScreenshotSource(request.getSource()), 0, packageName); switch (request.getType()) { case WindowManager.TAKE_SCREENSHOT_FULLSCREEN: Loading @@ -275,21 +283,22 @@ public class TakeScreenshotService extends Service { int taskId = request.getTaskId(); int userId = request.getUserId(); if (screenshot == null) { Log.e(TAG, "Got null bitmap from screenshot message"); mNotificationsController.notifyScreenshotError( R.string.screenshot_failed_to_capture_text); callback.reportError(); } else { mScreenshot.handleImageAsScreenshot(screenshot, screenBounds, insets, taskId, userId, topComponent, uriConsumer, callback); } break; default: Log.w(TAG, "Invalid screenshot option: " + request.getType()); Log.wtf(TAG, "Invalid screenshot option: " + request.getType()); } } private void logFailedRequest(ScreenshotRequest request) { ComponentName topComponent = request.getTopComponent(); String packageName = topComponent == null ? "" : topComponent.getPackageName(); mUiEventLogger.log( ScreenshotEvent.getScreenshotSource(request.getSource()), 0, packageName); mUiEventLogger.log(SCREENSHOT_CAPTURE_FAILED, 0, packageName); } private static void sendComplete(Messenger target) { try { if (DEBUG_CALLBACK) { Loading packages/SystemUI/tests/src/com/android/systemui/screenshot/RequestProcessorTest.kt +35 −6 Original line number Diff line number Diff line Loading @@ -23,7 +23,7 @@ import android.graphics.Insets import android.graphics.Rect import android.hardware.HardwareBuffer import android.os.UserHandle import android.view.WindowManager.ScreenshotSource.SCREENSHOT_KEY_CHORD import android.view.WindowManager.ScreenshotSource.SCREENSHOT_KEY_OTHER import android.view.WindowManager.ScreenshotSource.SCREENSHOT_OTHER import android.view.WindowManager.TAKE_SCREENSHOT_FULLSCREEN import android.view.WindowManager.TAKE_SCREENSHOT_PROVIDED_IMAGE Loading @@ -35,6 +35,7 @@ import com.google.common.truth.Truth.assertThat import kotlinx.coroutines.CoroutineScope import kotlinx.coroutines.Dispatchers import kotlinx.coroutines.runBlocking import org.junit.Assert import org.junit.Test private const val USER_ID = 1 Loading @@ -55,7 +56,7 @@ class RequestProcessorTest { flags.set(Flags.SCREENSHOT_WORK_PROFILE_POLICY, false) val request = ScreenshotRequest.Builder(TAKE_SCREENSHOT_FULLSCREEN, SCREENSHOT_KEY_CHORD).build() ScreenshotRequest.Builder(TAKE_SCREENSHOT_FULLSCREEN, SCREENSHOT_KEY_OTHER).build() val processor = RequestProcessor(imageCapture, policy, flags, scope) var result: ScreenshotRequest? = null Loading @@ -78,8 +79,10 @@ class RequestProcessorTest { fun testProcessAsync_ScreenshotData() { flags.set(Flags.SCREENSHOT_WORK_PROFILE_POLICY, false) val request = ScreenshotData.fromRequest( ScreenshotRequest.Builder(TAKE_SCREENSHOT_FULLSCREEN, SCREENSHOT_KEY_CHORD).build()) val request = ScreenshotData.fromRequest( ScreenshotRequest.Builder(TAKE_SCREENSHOT_FULLSCREEN, SCREENSHOT_KEY_OTHER).build() ) val processor = RequestProcessor(imageCapture, policy, flags, scope) var result: ScreenshotData? = null Loading @@ -102,7 +105,7 @@ class RequestProcessorTest { flags.set(Flags.SCREENSHOT_WORK_PROFILE_POLICY, false) val request = ScreenshotRequest.Builder(TAKE_SCREENSHOT_FULLSCREEN, SCREENSHOT_KEY_CHORD).build() ScreenshotRequest.Builder(TAKE_SCREENSHOT_FULLSCREEN, SCREENSHOT_KEY_OTHER).build() val processor = RequestProcessor(imageCapture, policy, flags, scope) val processedRequest = processor.process(request) Loading Loading @@ -162,7 +165,7 @@ class RequestProcessorTest { ) val request = ScreenshotRequest.Builder(TAKE_SCREENSHOT_FULLSCREEN, SCREENSHOT_KEY_CHORD).build() ScreenshotRequest.Builder(TAKE_SCREENSHOT_FULLSCREEN, SCREENSHOT_KEY_OTHER).build() val processor = RequestProcessor(imageCapture, policy, flags, scope) val processedRequest = processor.process(request) Loading Loading @@ -190,6 +193,32 @@ class RequestProcessorTest { assertThat(processedRequest.topComponent).isEqualTo(component) } @Test fun testFullScreenshot_managedProfile_nullBitmap() { flags.set(Flags.SCREENSHOT_WORK_PROFILE_POLICY, true) // Provide a null task bitmap when asked imageCapture.image = null // Indicate that the primary content belongs to a manged profile policy.setManagedProfile(USER_ID, true) policy.setDisplayContentInfo( policy.getDefaultDisplayId(), DisplayContentInfo(component, bounds, UserHandle.of(USER_ID), TASK_ID) ) val request = ScreenshotRequest.Builder(TAKE_SCREENSHOT_FULLSCREEN, SCREENSHOT_KEY_OTHER).build() val processor = RequestProcessor(imageCapture, policy, flags, scope) Assert.assertThrows(IllegalStateException::class.java) { runBlocking { processor.process(request) } } Assert.assertThrows(IllegalStateException::class.java) { runBlocking { processor.process(ScreenshotData.fromRequest(request)) } } } @Test fun testProvidedImageScreenshot_workProfilePolicyDisabled() = runBlocking { flags.set(Flags.SCREENSHOT_WORK_PROFILE_POLICY, false) Loading Loading
core/java/com/android/internal/util/ScreenshotRequest.java +3 −0 Original line number Diff line number Diff line Loading @@ -173,6 +173,9 @@ public class ScreenshotRequest implements Parcelable { public Builder( @WindowManager.ScreenshotType int type, @WindowManager.ScreenshotSource int source) { if (type != TAKE_SCREENSHOT_FULLSCREEN && type != TAKE_SCREENSHOT_PROVIDED_IMAGE) { throw new IllegalArgumentException("Invalid screenshot type requested!"); } mType = type; mSource = source; } Loading
core/tests/screenshothelpertests/src/com/android/internal/util/ScreenshotRequestTest.java +6 −0 Original line number Diff line number Diff line Loading @@ -131,6 +131,12 @@ public final class ScreenshotRequestTest { assertEquals(Insets.NONE, out.getInsets()); } @Test public void testInvalidType() { assertThrows(IllegalArgumentException.class, () -> new ScreenshotRequest.Builder(5, 2).build()); } private Bitmap makeHardwareBitmap(int width, int height) { HardwareBuffer buffer = HardwareBuffer.create( width, height, HardwareBuffer.RGBA_8888, 1, HardwareBuffer.USAGE_GPU_SAMPLED_IMAGE); Loading
packages/SystemUI/src/com/android/systemui/screenshot/ScreenshotEvent.java +2 −0 Original line number Diff line number Diff line Loading @@ -46,6 +46,8 @@ public enum ScreenshotEvent implements UiEventLogger.UiEventEnum { SCREENSHOT_SAVED(306), @UiEvent(doc = "screenshot failed to save") SCREENSHOT_NOT_SAVED(336), @UiEvent(doc = "failed to capture screenshot") SCREENSHOT_CAPTURE_FAILED(1281), @UiEvent(doc = "screenshot preview tapped") SCREENSHOT_PREVIEW_TAPPED(307), @UiEvent(doc = "screenshot edit button tapped") Loading
packages/SystemUI/src/com/android/systemui/screenshot/TakeScreenshotService.java +37 −28 Original line number Diff line number Diff line Loading @@ -26,6 +26,7 @@ import static com.android.systemui.screenshot.LogConfig.DEBUG_CALLBACK; import static com.android.systemui.screenshot.LogConfig.DEBUG_DISMISS; import static com.android.systemui.screenshot.LogConfig.DEBUG_SERVICE; import static com.android.systemui.screenshot.LogConfig.logTag; import static com.android.systemui.screenshot.ScreenshotEvent.SCREENSHOT_CAPTURE_FAILED; import static com.android.systemui.screenshot.ScreenshotEvent.SCREENSHOT_DISMISSED_OTHER; import android.annotation.MainThread; Loading Loading @@ -202,6 +203,7 @@ public class TakeScreenshotService extends Service { // animation and error notification. if (!mUserManager.isUserUnlocked()) { Log.w(TAG, "Skipping screenshot because storage is locked!"); logFailedRequest(request); mNotificationsController.notifyScreenshotError( R.string.screenshot_failed_to_save_user_locked_text); callback.reportError(); Loading @@ -212,6 +214,7 @@ public class TakeScreenshotService extends Service { mBgExecutor.execute(() -> { Log.w(TAG, "Skipping screenshot because an IT admin has disabled " + "screenshots on the device"); logFailedRequest(request); String blockedByAdminText = mDevicePolicyManager.getResources().getString( SCREENSHOT_BLOCKED_BY_ADMIN, () -> mContext.getString(R.string.screenshot_blocked_by_admin)); Loading @@ -225,38 +228,43 @@ public class TakeScreenshotService extends Service { if (mFeatureFlags.isEnabled(Flags.SCREENSHOT_METADATA)) { Log.d(TAG, "Processing screenshot data"); ScreenshotData screenshotData = ScreenshotData.fromRequest(request); try { mProcessor.processAsync(screenshotData, (data) -> dispatchToController(data, onSaved, callback)); } catch (IllegalStateException e) { Log.e(TAG, "Failed to process screenshot request!", e); logFailedRequest(request); mNotificationsController.notifyScreenshotError( R.string.screenshot_failed_to_capture_text); callback.reportError(); } } else { try { mProcessor.processAsync(request, (r) -> dispatchToController(r, onSaved, callback)); } catch (IllegalStateException e) { Log.e(TAG, "Failed to process screenshot request!", e); logFailedRequest(request); mNotificationsController.notifyScreenshotError( R.string.screenshot_failed_to_capture_text); callback.reportError(); } } } private void dispatchToController(ScreenshotData screenshot, Consumer<Uri> uriConsumer, RequestCallback callback) { mUiEventLogger.log(ScreenshotEvent.getScreenshotSource(screenshot.getSource()), 0, screenshot.getPackageNameString()); if (screenshot.getType() == WindowManager.TAKE_SCREENSHOT_PROVIDED_IMAGE && screenshot.getBitmap() == null) { Log.e(TAG, "Got null bitmap from screenshot message"); mNotificationsController.notifyScreenshotError( R.string.screenshot_failed_to_capture_text); callback.reportError(); return; } mScreenshot.handleScreenshot(screenshot, uriConsumer, callback); } private void dispatchToController(ScreenshotRequest request, Consumer<Uri> uriConsumer, RequestCallback callback) { ComponentName topComponent = request.getTopComponent(); mUiEventLogger.log(ScreenshotEvent.getScreenshotSource(request.getSource()), 0, topComponent == null ? "" : topComponent.getPackageName()); String packageName = topComponent == null ? "" : topComponent.getPackageName(); mUiEventLogger.log( ScreenshotEvent.getScreenshotSource(request.getSource()), 0, packageName); switch (request.getType()) { case WindowManager.TAKE_SCREENSHOT_FULLSCREEN: Loading @@ -275,21 +283,22 @@ public class TakeScreenshotService extends Service { int taskId = request.getTaskId(); int userId = request.getUserId(); if (screenshot == null) { Log.e(TAG, "Got null bitmap from screenshot message"); mNotificationsController.notifyScreenshotError( R.string.screenshot_failed_to_capture_text); callback.reportError(); } else { mScreenshot.handleImageAsScreenshot(screenshot, screenBounds, insets, taskId, userId, topComponent, uriConsumer, callback); } break; default: Log.w(TAG, "Invalid screenshot option: " + request.getType()); Log.wtf(TAG, "Invalid screenshot option: " + request.getType()); } } private void logFailedRequest(ScreenshotRequest request) { ComponentName topComponent = request.getTopComponent(); String packageName = topComponent == null ? "" : topComponent.getPackageName(); mUiEventLogger.log( ScreenshotEvent.getScreenshotSource(request.getSource()), 0, packageName); mUiEventLogger.log(SCREENSHOT_CAPTURE_FAILED, 0, packageName); } private static void sendComplete(Messenger target) { try { if (DEBUG_CALLBACK) { Loading
packages/SystemUI/tests/src/com/android/systemui/screenshot/RequestProcessorTest.kt +35 −6 Original line number Diff line number Diff line Loading @@ -23,7 +23,7 @@ import android.graphics.Insets import android.graphics.Rect import android.hardware.HardwareBuffer import android.os.UserHandle import android.view.WindowManager.ScreenshotSource.SCREENSHOT_KEY_CHORD import android.view.WindowManager.ScreenshotSource.SCREENSHOT_KEY_OTHER import android.view.WindowManager.ScreenshotSource.SCREENSHOT_OTHER import android.view.WindowManager.TAKE_SCREENSHOT_FULLSCREEN import android.view.WindowManager.TAKE_SCREENSHOT_PROVIDED_IMAGE Loading @@ -35,6 +35,7 @@ import com.google.common.truth.Truth.assertThat import kotlinx.coroutines.CoroutineScope import kotlinx.coroutines.Dispatchers import kotlinx.coroutines.runBlocking import org.junit.Assert import org.junit.Test private const val USER_ID = 1 Loading @@ -55,7 +56,7 @@ class RequestProcessorTest { flags.set(Flags.SCREENSHOT_WORK_PROFILE_POLICY, false) val request = ScreenshotRequest.Builder(TAKE_SCREENSHOT_FULLSCREEN, SCREENSHOT_KEY_CHORD).build() ScreenshotRequest.Builder(TAKE_SCREENSHOT_FULLSCREEN, SCREENSHOT_KEY_OTHER).build() val processor = RequestProcessor(imageCapture, policy, flags, scope) var result: ScreenshotRequest? = null Loading @@ -78,8 +79,10 @@ class RequestProcessorTest { fun testProcessAsync_ScreenshotData() { flags.set(Flags.SCREENSHOT_WORK_PROFILE_POLICY, false) val request = ScreenshotData.fromRequest( ScreenshotRequest.Builder(TAKE_SCREENSHOT_FULLSCREEN, SCREENSHOT_KEY_CHORD).build()) val request = ScreenshotData.fromRequest( ScreenshotRequest.Builder(TAKE_SCREENSHOT_FULLSCREEN, SCREENSHOT_KEY_OTHER).build() ) val processor = RequestProcessor(imageCapture, policy, flags, scope) var result: ScreenshotData? = null Loading @@ -102,7 +105,7 @@ class RequestProcessorTest { flags.set(Flags.SCREENSHOT_WORK_PROFILE_POLICY, false) val request = ScreenshotRequest.Builder(TAKE_SCREENSHOT_FULLSCREEN, SCREENSHOT_KEY_CHORD).build() ScreenshotRequest.Builder(TAKE_SCREENSHOT_FULLSCREEN, SCREENSHOT_KEY_OTHER).build() val processor = RequestProcessor(imageCapture, policy, flags, scope) val processedRequest = processor.process(request) Loading Loading @@ -162,7 +165,7 @@ class RequestProcessorTest { ) val request = ScreenshotRequest.Builder(TAKE_SCREENSHOT_FULLSCREEN, SCREENSHOT_KEY_CHORD).build() ScreenshotRequest.Builder(TAKE_SCREENSHOT_FULLSCREEN, SCREENSHOT_KEY_OTHER).build() val processor = RequestProcessor(imageCapture, policy, flags, scope) val processedRequest = processor.process(request) Loading Loading @@ -190,6 +193,32 @@ class RequestProcessorTest { assertThat(processedRequest.topComponent).isEqualTo(component) } @Test fun testFullScreenshot_managedProfile_nullBitmap() { flags.set(Flags.SCREENSHOT_WORK_PROFILE_POLICY, true) // Provide a null task bitmap when asked imageCapture.image = null // Indicate that the primary content belongs to a manged profile policy.setManagedProfile(USER_ID, true) policy.setDisplayContentInfo( policy.getDefaultDisplayId(), DisplayContentInfo(component, bounds, UserHandle.of(USER_ID), TASK_ID) ) val request = ScreenshotRequest.Builder(TAKE_SCREENSHOT_FULLSCREEN, SCREENSHOT_KEY_OTHER).build() val processor = RequestProcessor(imageCapture, policy, flags, scope) Assert.assertThrows(IllegalStateException::class.java) { runBlocking { processor.process(request) } } Assert.assertThrows(IllegalStateException::class.java) { runBlocking { processor.process(ScreenshotData.fromRequest(request)) } } } @Test fun testProvidedImageScreenshot_workProfilePolicyDisabled() = runBlocking { flags.set(Flags.SCREENSHOT_WORK_PROFILE_POLICY, false) Loading