1 /*
2  * Copyright (C) 2019 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.os.AsyncTask.THREAD_POOL_EXECUTOR;
20 
21 import android.app.ActivityManager;
22 import android.app.Notification;
23 import android.content.ComponentName;
24 import android.content.Context;
25 import android.graphics.Bitmap;
26 import android.net.Uri;
27 import android.os.Handler;
28 import android.os.SystemClock;
29 import android.os.UserHandle;
30 import android.util.Slog;
31 
32 import com.android.internal.annotations.VisibleForTesting;
33 import com.android.systemui.SystemUIFactory;
34 import com.android.systemui.shared.system.ActivityManagerWrapper;
35 
36 import java.util.Collections;
37 import java.util.List;
38 import java.util.concurrent.CompletableFuture;
39 import java.util.concurrent.TimeUnit;
40 import java.util.concurrent.TimeoutException;
41 
42 /**
43  * Collects the static functions for retrieving and acting on smart actions.
44  */
45 public class ScreenshotSmartActions {
46     private static final String TAG = "ScreenshotSmartActions";
47 
48     @VisibleForTesting
getSmartActionsFuture( String screenshotId, Uri screenshotUri, Bitmap image, ScreenshotNotificationSmartActionsProvider smartActionsProvider, boolean smartActionsEnabled, UserHandle userHandle)49     static CompletableFuture<List<Notification.Action>> getSmartActionsFuture(
50             String screenshotId, Uri screenshotUri, Bitmap image,
51             ScreenshotNotificationSmartActionsProvider smartActionsProvider,
52             boolean smartActionsEnabled, UserHandle userHandle) {
53         if (!smartActionsEnabled) {
54             Slog.i(TAG, "Screenshot Intelligence not enabled, returning empty list.");
55             return CompletableFuture.completedFuture(Collections.emptyList());
56         }
57         if (image.getConfig() != Bitmap.Config.HARDWARE) {
58             Slog.w(TAG, String.format(
59                     "Bitmap expected: Hardware, Bitmap found: %s. Returning empty list.",
60                     image.getConfig()));
61             return CompletableFuture.completedFuture(Collections.emptyList());
62         }
63 
64         Slog.d(TAG, "Screenshot from user profile: " + userHandle.getIdentifier());
65         CompletableFuture<List<Notification.Action>> smartActionsFuture;
66         long startTimeMs = SystemClock.uptimeMillis();
67         try {
68             ActivityManager.RunningTaskInfo runningTask =
69                     ActivityManagerWrapper.getInstance().getRunningTask();
70             ComponentName componentName =
71                     (runningTask != null && runningTask.topActivity != null)
72                             ? runningTask.topActivity
73                             : new ComponentName("", "");
74             smartActionsFuture = smartActionsProvider.getActions(
75                     screenshotId, screenshotUri, image, componentName, userHandle);
76         } catch (Throwable e) {
77             long waitTimeMs = SystemClock.uptimeMillis() - startTimeMs;
78             smartActionsFuture = CompletableFuture.completedFuture(Collections.emptyList());
79             Slog.e(TAG, "Failed to get future for screenshot notification smart actions.", e);
80             notifyScreenshotOp(screenshotId, smartActionsProvider,
81                     ScreenshotNotificationSmartActionsProvider.ScreenshotOp.REQUEST_SMART_ACTIONS,
82                     ScreenshotNotificationSmartActionsProvider.ScreenshotOpStatus.ERROR,
83                     waitTimeMs);
84         }
85         return smartActionsFuture;
86     }
87 
88     @VisibleForTesting
getSmartActions(String screenshotId, CompletableFuture<List<Notification.Action>> smartActionsFuture, int timeoutMs, ScreenshotNotificationSmartActionsProvider smartActionsProvider)89     static List<Notification.Action> getSmartActions(String screenshotId,
90             CompletableFuture<List<Notification.Action>> smartActionsFuture, int timeoutMs,
91             ScreenshotNotificationSmartActionsProvider smartActionsProvider) {
92         long startTimeMs = SystemClock.uptimeMillis();
93         try {
94             List<Notification.Action> actions = smartActionsFuture.get(timeoutMs,
95                     TimeUnit.MILLISECONDS);
96             long waitTimeMs = SystemClock.uptimeMillis() - startTimeMs;
97             Slog.d(TAG, String.format("Got %d smart actions. Wait time: %d ms",
98                     actions.size(), waitTimeMs));
99             notifyScreenshotOp(screenshotId, smartActionsProvider,
100                     ScreenshotNotificationSmartActionsProvider.ScreenshotOp.WAIT_FOR_SMART_ACTIONS,
101                     ScreenshotNotificationSmartActionsProvider.ScreenshotOpStatus.SUCCESS,
102                     waitTimeMs);
103             return actions;
104         } catch (Throwable e) {
105             long waitTimeMs = SystemClock.uptimeMillis() - startTimeMs;
106             Slog.e(TAG, String.format("Error getting smart actions. Wait time: %d ms", waitTimeMs),
107                     e);
108             ScreenshotNotificationSmartActionsProvider.ScreenshotOpStatus status =
109                     (e instanceof TimeoutException)
110                             ? ScreenshotNotificationSmartActionsProvider.ScreenshotOpStatus.TIMEOUT
111                             : ScreenshotNotificationSmartActionsProvider.ScreenshotOpStatus.ERROR;
112             notifyScreenshotOp(screenshotId, smartActionsProvider,
113                     ScreenshotNotificationSmartActionsProvider.ScreenshotOp.WAIT_FOR_SMART_ACTIONS,
114                     status, waitTimeMs);
115             return Collections.emptyList();
116         }
117     }
118 
notifyScreenshotOp(String screenshotId, ScreenshotNotificationSmartActionsProvider smartActionsProvider, ScreenshotNotificationSmartActionsProvider.ScreenshotOp op, ScreenshotNotificationSmartActionsProvider.ScreenshotOpStatus status, long durationMs)119     static void notifyScreenshotOp(String screenshotId,
120             ScreenshotNotificationSmartActionsProvider smartActionsProvider,
121             ScreenshotNotificationSmartActionsProvider.ScreenshotOp op,
122             ScreenshotNotificationSmartActionsProvider.ScreenshotOpStatus status, long durationMs) {
123         try {
124             smartActionsProvider.notifyOp(screenshotId, op, status, durationMs);
125         } catch (Throwable e) {
126             Slog.e(TAG, "Error in notifyScreenshotOp: ", e);
127         }
128     }
129 
notifyScreenshotAction(Context context, String screenshotId, String action, boolean isSmartAction)130     static void notifyScreenshotAction(Context context, String screenshotId, String action,
131             boolean isSmartAction) {
132         try {
133             ScreenshotNotificationSmartActionsProvider provider =
134                     SystemUIFactory.getInstance().createScreenshotNotificationSmartActionsProvider(
135                             context, THREAD_POOL_EXECUTOR, new Handler());
136             provider.notifyAction(screenshotId, action, isSmartAction);
137         } catch (Throwable e) {
138             Slog.e(TAG, "Error in notifyScreenshotAction: ", e);
139         }
140     }
141 }
142