1package com.android.internal.util; 2 3import android.annotation.NonNull; 4import android.content.ComponentName; 5import android.content.Context; 6import android.content.Intent; 7import android.content.ServiceConnection; 8import android.os.Handler; 9import android.os.IBinder; 10import android.os.Message; 11import android.os.Messenger; 12import android.os.RemoteException; 13import android.os.UserHandle; 14import android.util.Log; 15 16public class ScreenshotHelper { 17 private static final String TAG = "ScreenshotHelper"; 18 19 private static final String SYSUI_PACKAGE = "com.android.systemui"; 20 private static final String SYSUI_SCREENSHOT_SERVICE = 21 "com.android.systemui.screenshot.TakeScreenshotService"; 22 private static final String SYSUI_SCREENSHOT_ERROR_RECEIVER = 23 "com.android.systemui.screenshot.ScreenshotServiceErrorReceiver"; 24 25 // Time until we give up on the screenshot & show an error instead. 26 private final int SCREENSHOT_TIMEOUT_MS = 10000; 27 28 private final Object mScreenshotLock = new Object(); 29 private ServiceConnection mScreenshotConnection = null; 30 private final Context mContext; 31 32 public ScreenshotHelper(Context context) { 33 mContext = context; 34 } 35 36 /** 37 * Request a screenshot be taken. 38 * 39 * @param screenshotType The type of screenshot, for example either 40 * {@link android.view.WindowManager.TAKE_SCREENSHOT_FULLSCREEN} 41 * or {@link android.view.WindowManager.TAKE_SCREENSHOT_SELECTED_REGION} 42 * @param hasStatus {@code true} if the status bar is currently showing. {@code false} if not. 43 * @param hasNav {@code true} if the navigation bar is currently showing. {@code false} if not. 44 * @param handler A handler used in case the screenshot times out 45 */ 46 public void takeScreenshot(final int screenshotType, final boolean hasStatus, 47 final boolean hasNav, @NonNull Handler handler) { 48 synchronized (mScreenshotLock) { 49 if (mScreenshotConnection != null) { 50 return; 51 } 52 final ComponentName serviceComponent = new ComponentName(SYSUI_PACKAGE, 53 SYSUI_SCREENSHOT_SERVICE); 54 final Intent serviceIntent = new Intent(); 55 56 final Runnable mScreenshotTimeout = new Runnable() { 57 @Override public void run() { 58 synchronized (mScreenshotLock) { 59 if (mScreenshotConnection != null) { 60 mContext.unbindService(mScreenshotConnection); 61 mScreenshotConnection = null; 62 notifyScreenshotError(); 63 } 64 } 65 } 66 }; 67 68 serviceIntent.setComponent(serviceComponent); 69 ServiceConnection conn = new ServiceConnection() { 70 @Override 71 public void onServiceConnected(ComponentName name, IBinder service) { 72 synchronized (mScreenshotLock) { 73 if (mScreenshotConnection != this) { 74 return; 75 } 76 Messenger messenger = new Messenger(service); 77 Message msg = Message.obtain(null, screenshotType); 78 final ServiceConnection myConn = this; 79 Handler h = new Handler(handler.getLooper()) { 80 @Override 81 public void handleMessage(Message msg) { 82 synchronized (mScreenshotLock) { 83 if (mScreenshotConnection == myConn) { 84 mContext.unbindService(mScreenshotConnection); 85 mScreenshotConnection = null; 86 handler.removeCallbacks(mScreenshotTimeout); 87 } 88 } 89 } 90 }; 91 msg.replyTo = new Messenger(h); 92 msg.arg1 = hasStatus ? 1: 0; 93 msg.arg2 = hasNav ? 1: 0; 94 try { 95 messenger.send(msg); 96 } catch (RemoteException e) { 97 Log.e(TAG, "Couldn't take screenshot: " + e); 98 } 99 } 100 } 101 102 @Override 103 public void onServiceDisconnected(ComponentName name) { 104 synchronized (mScreenshotLock) { 105 if (mScreenshotConnection != null) { 106 mContext.unbindService(mScreenshotConnection); 107 mScreenshotConnection = null; 108 handler.removeCallbacks(mScreenshotTimeout); 109 notifyScreenshotError(); 110 } 111 } 112 } 113 }; 114 if (mContext.bindServiceAsUser(serviceIntent, conn, 115 Context.BIND_AUTO_CREATE | Context.BIND_FOREGROUND_SERVICE_WHILE_AWAKE, 116 UserHandle.CURRENT)) { 117 mScreenshotConnection = conn; 118 handler.postDelayed(mScreenshotTimeout, SCREENSHOT_TIMEOUT_MS); 119 } 120 } 121 } 122 123 /** 124 * Notifies the screenshot service to show an error. 125 */ 126 private void notifyScreenshotError() { 127 // If the service process is killed, then ask it to clean up after itself 128 final ComponentName errorComponent = new ComponentName(SYSUI_PACKAGE, 129 SYSUI_SCREENSHOT_ERROR_RECEIVER); 130 // Broadcast needs to have a valid action. We'll just pick 131 // a generic one, since the receiver here doesn't care. 132 Intent errorIntent = new Intent(Intent.ACTION_USER_PRESENT); 133 errorIntent.setComponent(errorComponent); 134 errorIntent.addFlags(Intent.FLAG_RECEIVER_REGISTERED_ONLY_BEFORE_BOOT | 135 Intent.FLAG_RECEIVER_FOREGROUND); 136 mContext.sendBroadcastAsUser(errorIntent, UserHandle.CURRENT); 137 } 138 139} 140