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 static com.android.systemui.screenshot.LogConfig.DEBUG_ACTIONS;
22 import static com.android.systemui.screenshot.LogConfig.logTag;
23 import static com.android.systemui.screenshot.ScreenshotNotificationSmartActionsProvider.ScreenshotSmartActionType;
24 
25 import android.app.ActivityManager;
26 import android.app.Notification;
27 import android.content.ComponentName;
28 import android.content.Context;
29 import android.content.Intent;
30 import android.graphics.Bitmap;
31 import android.net.Uri;
32 import android.os.Handler;
33 import android.os.SystemClock;
34 import android.os.UserHandle;
35 import android.util.Log;
36 
37 import com.android.internal.annotations.VisibleForTesting;
38 import com.android.systemui.SystemUIFactory;
39 import com.android.systemui.dagger.SysUISingleton;
40 import com.android.systemui.shared.system.ActivityManagerWrapper;
41 
42 import java.util.Collections;
43 import java.util.List;
44 import java.util.concurrent.CompletableFuture;
45 import java.util.concurrent.TimeUnit;
46 import java.util.concurrent.TimeoutException;
47 
48 import javax.inject.Inject;
49 
50 /**
51  * Collects the static functions for retrieving and acting on smart actions.
52  */
53 @SysUISingleton
54 public class ScreenshotSmartActions {
55     private static final String TAG = logTag(ScreenshotSmartActions.class);
56 
57     @Inject
ScreenshotSmartActions()58     public ScreenshotSmartActions() {}
59 
60     @VisibleForTesting
getSmartActionsFuture( String screenshotId, Uri screenshotUri, Bitmap image, ScreenshotNotificationSmartActionsProvider smartActionsProvider, ScreenshotSmartActionType actionType, boolean smartActionsEnabled, UserHandle userHandle)61     CompletableFuture<List<Notification.Action>> getSmartActionsFuture(
62             String screenshotId, Uri screenshotUri, Bitmap image,
63             ScreenshotNotificationSmartActionsProvider smartActionsProvider,
64             ScreenshotSmartActionType actionType,
65             boolean smartActionsEnabled, UserHandle userHandle) {
66         if (DEBUG_ACTIONS) {
67             Log.d(TAG, String.format(
68                     "getSmartActionsFuture id=%s, uri=%s, provider=%s, actionType=%s, "
69                             + "smartActionsEnabled=%b, userHandle=%s",
70                     screenshotId, screenshotUri, smartActionsProvider.getClass(), actionType,
71                     smartActionsEnabled, userHandle));
72         }
73         if (!smartActionsEnabled) {
74             if (DEBUG_ACTIONS) {
75                 Log.d(TAG, "Screenshot Intelligence not enabled, returning empty list.");
76             }
77             return CompletableFuture.completedFuture(Collections.emptyList());
78         }
79         if (image.getConfig() != Bitmap.Config.HARDWARE) {
80             if (DEBUG_ACTIONS) {
81                 Log.d(TAG, String.format("Bitmap expected: Hardware, Bitmap found: %s. "
82                                 + "Returning empty list.", image.getConfig()));
83             }
84             return CompletableFuture.completedFuture(Collections.emptyList());
85         }
86         CompletableFuture<List<Notification.Action>> smartActionsFuture;
87         long startTimeMs = SystemClock.uptimeMillis();
88         try {
89             ActivityManager.RunningTaskInfo runningTask =
90                     ActivityManagerWrapper.getInstance().getRunningTask();
91             ComponentName componentName =
92                     (runningTask != null && runningTask.topActivity != null)
93                             ? runningTask.topActivity
94                             : new ComponentName("", "");
95             smartActionsFuture = smartActionsProvider.getActions(screenshotId, screenshotUri, image,
96                     componentName, actionType, userHandle);
97         } catch (Throwable e) {
98             long waitTimeMs = SystemClock.uptimeMillis() - startTimeMs;
99             smartActionsFuture = CompletableFuture.completedFuture(Collections.emptyList());
100             if (DEBUG_ACTIONS) {
101                 Log.e(TAG, "Failed to get future for screenshot notification smart actions.", e);
102             }
103             notifyScreenshotOp(screenshotId, smartActionsProvider,
104                     ScreenshotNotificationSmartActionsProvider.ScreenshotOp.REQUEST_SMART_ACTIONS,
105                     ScreenshotNotificationSmartActionsProvider.ScreenshotOpStatus.ERROR,
106                     waitTimeMs);
107         }
108         return smartActionsFuture;
109     }
110 
111     @VisibleForTesting
getSmartActions(String screenshotId, CompletableFuture<List<Notification.Action>> smartActionsFuture, int timeoutMs, ScreenshotNotificationSmartActionsProvider smartActionsProvider, ScreenshotSmartActionType actionType)112     List<Notification.Action> getSmartActions(String screenshotId,
113             CompletableFuture<List<Notification.Action>> smartActionsFuture, int timeoutMs,
114             ScreenshotNotificationSmartActionsProvider smartActionsProvider,
115             ScreenshotSmartActionType actionType) {
116         long startTimeMs = SystemClock.uptimeMillis();
117         if (DEBUG_ACTIONS) {
118             Log.d(TAG,
119                     String.format("getSmartActions id=%s, timeoutMs=%d, actionType=%s, provider=%s",
120                             screenshotId, timeoutMs, actionType, smartActionsProvider.getClass()));
121         }
122         try {
123             List<Notification.Action> actions = smartActionsFuture.get(timeoutMs,
124                     TimeUnit.MILLISECONDS);
125             long waitTimeMs = SystemClock.uptimeMillis() - startTimeMs;
126             if (DEBUG_ACTIONS) {
127                 Log.d(TAG, String.format("Got %d smart actions. Wait time: %d ms, actionType=%s",
128                         actions.size(), waitTimeMs, actionType));
129             }
130             notifyScreenshotOp(screenshotId, smartActionsProvider,
131                     ScreenshotNotificationSmartActionsProvider.ScreenshotOp.WAIT_FOR_SMART_ACTIONS,
132                     ScreenshotNotificationSmartActionsProvider.ScreenshotOpStatus.SUCCESS,
133                     waitTimeMs);
134             return actions;
135         } catch (Throwable e) {
136             long waitTimeMs = SystemClock.uptimeMillis() - startTimeMs;
137             if (DEBUG_ACTIONS) {
138                 Log.e(TAG, String.format(
139                         "Error getting smart actions. Wait time: %d ms, actionType=%s",
140                         waitTimeMs, actionType), e);
141             }
142             ScreenshotNotificationSmartActionsProvider.ScreenshotOpStatus status =
143                     (e instanceof TimeoutException)
144                             ? ScreenshotNotificationSmartActionsProvider.ScreenshotOpStatus.TIMEOUT
145                             : ScreenshotNotificationSmartActionsProvider.ScreenshotOpStatus.ERROR;
146             notifyScreenshotOp(screenshotId, smartActionsProvider,
147                     ScreenshotNotificationSmartActionsProvider.ScreenshotOp.WAIT_FOR_SMART_ACTIONS,
148                     status, waitTimeMs);
149             return Collections.emptyList();
150         }
151     }
152 
notifyScreenshotOp(String screenshotId, ScreenshotNotificationSmartActionsProvider smartActionsProvider, ScreenshotNotificationSmartActionsProvider.ScreenshotOp op, ScreenshotNotificationSmartActionsProvider.ScreenshotOpStatus status, long durationMs)153     void notifyScreenshotOp(String screenshotId,
154             ScreenshotNotificationSmartActionsProvider smartActionsProvider,
155             ScreenshotNotificationSmartActionsProvider.ScreenshotOp op,
156             ScreenshotNotificationSmartActionsProvider.ScreenshotOpStatus status, long durationMs) {
157         if (DEBUG_ACTIONS) {
158             Log.d(TAG, String.format("%s notifyOp: %s id=%s, status=%s, durationMs=%d",
159                     smartActionsProvider.getClass(), op, screenshotId, status, durationMs));
160         }
161         try {
162             smartActionsProvider.notifyOp(screenshotId, op, status, durationMs);
163         } catch (Throwable e) {
164             Log.e(TAG, "Error in notifyScreenshotOp: ", e);
165         }
166     }
167 
notifyScreenshotAction(Context context, String screenshotId, String action, boolean isSmartAction, Intent intent)168     void notifyScreenshotAction(Context context, String screenshotId, String action,
169             boolean isSmartAction, Intent intent) {
170         try {
171             ScreenshotNotificationSmartActionsProvider provider =
172                     SystemUIFactory.getInstance().createScreenshotNotificationSmartActionsProvider(
173                             context, THREAD_POOL_EXECUTOR, new Handler());
174             if (DEBUG_ACTIONS) {
175                 Log.d(TAG, String.format("%s notifyAction: %s id=%s, isSmartAction=%b",
176                         provider.getClass(), action, screenshotId, isSmartAction));
177             }
178             provider.notifyAction(screenshotId, action, isSmartAction, intent);
179         } catch (Throwable e) {
180             Log.e(TAG, "Error in notifyScreenshotAction: ", e);
181         }
182     }
183 }
184