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