• Home
  • Line#
  • Scopes#
  • Navigate#
  • Raw
  • Download
1 /*
2  * Copyright (C) 2018 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 android.service.contentsuggestions;
18 
19 import static com.android.internal.util.function.pooled.PooledLambda.obtainMessage;
20 
21 import android.annotation.CallSuper;
22 import android.annotation.NonNull;
23 import android.annotation.Nullable;
24 import android.annotation.SystemApi;
25 import android.app.Service;
26 import android.app.contentsuggestions.ClassificationsRequest;
27 import android.app.contentsuggestions.ContentSuggestionsManager;
28 import android.app.contentsuggestions.IClassificationsCallback;
29 import android.app.contentsuggestions.ISelectionsCallback;
30 import android.app.contentsuggestions.SelectionsRequest;
31 import android.content.Intent;
32 import android.graphics.Bitmap;
33 import android.graphics.ColorSpace;
34 import android.hardware.HardwareBuffer;
35 import android.os.Bundle;
36 import android.os.Handler;
37 import android.os.IBinder;
38 import android.os.Looper;
39 import android.os.RemoteException;
40 import android.util.Log;
41 import android.util.Slog;
42 import android.window.TaskSnapshot;
43 
44 /**
45  * @hide
46  */
47 @SystemApi
48 public abstract class ContentSuggestionsService extends Service {
49 
50     private static final String TAG = ContentSuggestionsService.class.getSimpleName();
51 
52     private Handler mHandler;
53 
54     /**
55      * The action for the intent used to define the content suggestions service.
56      *
57      * <p>To be supported, the service must also require the
58      * * {@link android.Manifest.permission#BIND_CONTENT_SUGGESTIONS_SERVICE} permission so
59      * * that other applications can not abuse it.
60      */
61     public static final String SERVICE_INTERFACE =
62             "android.service.contentsuggestions.ContentSuggestionsService";
63 
64     private final IContentSuggestionsService mInterface = new IContentSuggestionsService.Stub() {
65         @Override
66         public void provideContextImage(int taskId, TaskSnapshot snapshot,
67                 Bundle imageContextRequestExtras) {
68             if (imageContextRequestExtras.containsKey(ContentSuggestionsManager.EXTRA_BITMAP)
69                     && snapshot != null) {
70                 throw new IllegalArgumentException("Two bitmaps provided; expected one.");
71             }
72 
73             Bitmap wrappedBuffer = null;
74             if (imageContextRequestExtras.containsKey(ContentSuggestionsManager.EXTRA_BITMAP)) {
75                 wrappedBuffer = imageContextRequestExtras.getParcelable(
76                         ContentSuggestionsManager.EXTRA_BITMAP, android.graphics.Bitmap.class);
77             } else {
78                 if (snapshot != null) {
79                     final HardwareBuffer snapshotBuffer = snapshot.getHardwareBuffer();
80                     ColorSpace colorSpace = snapshot.getColorSpace();
81                     int colorSpaceId = 0;
82                     if (colorSpace != null) {
83                         colorSpaceId = colorSpace.getId();
84                     }
85                     if (colorSpaceId >= 0 && colorSpaceId < ColorSpace.Named.values().length) {
86                         colorSpace = ColorSpace.get(ColorSpace.Named.values()[colorSpaceId]);
87                     }
88                     wrappedBuffer = Bitmap.wrapHardwareBuffer(snapshotBuffer, colorSpace);
89                     snapshotBuffer.close();
90                 }
91             }
92 
93             mHandler.sendMessage(
94                     obtainMessage(ContentSuggestionsService::onProcessContextImage,
95                             ContentSuggestionsService.this, taskId,
96                             wrappedBuffer,
97                             imageContextRequestExtras));
98         }
99 
100         @Override
101         public void suggestContentSelections(SelectionsRequest request,
102                 ISelectionsCallback callback) {
103             mHandler.sendMessage(obtainMessage(
104                     ContentSuggestionsService::onSuggestContentSelections,
105                     ContentSuggestionsService.this, request, wrapSelectionsCallback(callback)));
106 
107         }
108 
109         @Override
110         public void classifyContentSelections(ClassificationsRequest request,
111                 IClassificationsCallback callback) {
112             mHandler.sendMessage(obtainMessage(
113                     ContentSuggestionsService::onClassifyContentSelections,
114                     ContentSuggestionsService.this, request, wrapClassificationCallback(callback)));
115         }
116 
117         @Override
118         public void notifyInteraction(String requestId, Bundle interaction) {
119             mHandler.sendMessage(
120                     obtainMessage(ContentSuggestionsService::onNotifyInteraction,
121                             ContentSuggestionsService.this, requestId, interaction));
122         }
123     };
124 
125     @CallSuper
126     @Override
onCreate()127     public void onCreate() {
128         super.onCreate();
129         mHandler = new Handler(Looper.getMainLooper(), null, true);
130     }
131 
132     /** @hide */
133     @Override
onBind(Intent intent)134     public final IBinder onBind(Intent intent) {
135         if (SERVICE_INTERFACE.equals(intent.getAction())) {
136             return mInterface.asBinder();
137         }
138         Log.w(TAG, "Tried to bind to wrong intent (should be " + SERVICE_INTERFACE + ": " + intent);
139         return null;
140     }
141 
142     /**
143      * Called by the system to provide the snapshot for the task associated with the given
144      * {@param taskId}.
145      */
onProcessContextImage( int taskId, @Nullable Bitmap contextImage, @NonNull Bundle extras)146     public abstract void onProcessContextImage(
147             int taskId, @Nullable Bitmap contextImage, @NonNull Bundle extras);
148 
149     /**
150      * Content selections have been request through {@link ContentSuggestionsManager}, implementer
151      * should reply on the callback with selections.
152      */
onSuggestContentSelections(@onNull SelectionsRequest request, @NonNull ContentSuggestionsManager.SelectionsCallback callback)153     public abstract void onSuggestContentSelections(@NonNull SelectionsRequest request,
154             @NonNull ContentSuggestionsManager.SelectionsCallback callback);
155 
156     /**
157      * Content classifications have been request through {@link ContentSuggestionsManager},
158      * implementer should reply on the callback with classifications.
159      */
onClassifyContentSelections(@onNull ClassificationsRequest request, @NonNull ContentSuggestionsManager.ClassificationsCallback callback)160     public abstract void onClassifyContentSelections(@NonNull ClassificationsRequest request,
161             @NonNull ContentSuggestionsManager.ClassificationsCallback callback);
162 
163     /**
164      * User interactions have been reported through {@link ContentSuggestionsManager}, implementer
165      * should handle those interactions.
166      */
onNotifyInteraction( @onNull String requestId, @NonNull Bundle interaction)167     public abstract void onNotifyInteraction(
168             @NonNull String requestId, @NonNull Bundle interaction);
169 
wrapSelectionsCallback( ISelectionsCallback callback)170     private ContentSuggestionsManager.SelectionsCallback wrapSelectionsCallback(
171             ISelectionsCallback callback) {
172         return (statusCode, selections) -> {
173             try {
174                 callback.onContentSelectionsAvailable(statusCode, selections);
175             } catch (RemoteException e) {
176                 Slog.e(TAG, "Error sending result: " + e);
177             }
178         };
179     }
180 
181     private ContentSuggestionsManager.ClassificationsCallback wrapClassificationCallback(
182             IClassificationsCallback callback) {
183         return ((statusCode, classifications) -> {
184             try {
185                 callback.onContentClassificationsAvailable(statusCode, classifications);
186             } catch (RemoteException e) {
187                 Slog.e(TAG, "Error sending result: " + e);
188             }
189         });
190     }
191 }
192