1 /*
2  * Copyright (C) 2021 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.displayhash;
18 
19 import static com.android.internal.util.function.pooled.PooledLambda.obtainMessage;
20 
21 import android.annotation.NonNull;
22 import android.annotation.Nullable;
23 import android.annotation.SystemApi;
24 import android.app.Service;
25 import android.content.Intent;
26 import android.graphics.Rect;
27 import android.hardware.HardwareBuffer;
28 import android.os.Bundle;
29 import android.os.Handler;
30 import android.os.IBinder;
31 import android.os.Looper;
32 import android.os.RemoteCallback;
33 import android.view.displayhash.DisplayHash;
34 import android.view.displayhash.DisplayHashResultCallback;
35 import android.view.displayhash.VerifiedDisplayHash;
36 
37 import java.util.Map;
38 
39 /**
40  * A service that handles generating and verify {@link DisplayHash}.
41  *
42  * The service will generate a DisplayHash based on arguments passed in. Then later that
43  * same DisplayHash can be verified to determine that it was created by the system.
44  *
45  * @hide
46  */
47 @SystemApi
48 public abstract class DisplayHashingService extends Service {
49 
50     /** @hide **/
51     public static final String EXTRA_VERIFIED_DISPLAY_HASH =
52             "android.service.displayhash.extra.VERIFIED_DISPLAY_HASH";
53 
54     /** @hide **/
55     public static final String EXTRA_INTERVAL_BETWEEN_REQUESTS =
56             "android.service.displayhash.extra.INTERVAL_BETWEEN_REQUESTS";
57 
58     /**
59      * The {@link Intent} action that must be declared as handled by a service in its manifest
60      * for the system to recognize it as a DisplayHash providing service.
61      *
62      * @hide
63      */
64     @SystemApi
65     public static final String SERVICE_INTERFACE =
66             "android.service.displayhash.DisplayHashingService";
67 
68     private DisplayHashingServiceWrapper mWrapper;
69     private Handler mHandler;
70 
71     @Override
onCreate()72     public void onCreate() {
73         super.onCreate();
74         mWrapper = new DisplayHashingServiceWrapper();
75         mHandler = new Handler(Looper.getMainLooper(), null, true);
76     }
77 
78     @NonNull
79     @Override
onBind(@onNull Intent intent)80     public final IBinder onBind(@NonNull Intent intent) {
81         return mWrapper;
82     }
83 
84     /**
85      * Generates the DisplayHash that can be used to validate that the system generated the
86      * token.
87      *
88      * @param salt          The salt to use when generating the hmac. This should be unique to the
89      *                      caller so the token cannot be verified by any other process.
90      * @param buffer        The buffer for the content to generate the hash for.
91      * @param bounds        The size and position of the content in window space.
92      * @param hashAlgorithm The String for the hashing algorithm to use based values in
93      *                      {@link #getDisplayHashAlgorithms(RemoteCallback)}.
94      * @param callback      The callback to invoke
95      *                      {@link DisplayHashResultCallback#onDisplayHashResult(DisplayHash)}
96      *                      if successfully generated a DisplayHash or {@link
97      *                      DisplayHashResultCallback#onDisplayHashError(int)} if failed.
98      */
onGenerateDisplayHash(@onNull byte[] salt, @NonNull HardwareBuffer buffer, @NonNull Rect bounds, @NonNull String hashAlgorithm, @NonNull DisplayHashResultCallback callback)99     public abstract void onGenerateDisplayHash(@NonNull byte[] salt,
100             @NonNull HardwareBuffer buffer, @NonNull Rect bounds,
101             @NonNull String hashAlgorithm, @NonNull DisplayHashResultCallback callback);
102 
103     /**
104      * Returns a map of supported algorithms and their {@link DisplayHashParams}
105      */
106     @NonNull
onGetDisplayHashAlgorithms()107     public abstract Map<String, DisplayHashParams> onGetDisplayHashAlgorithms();
108 
109     /**
110      * Call to verify that the DisplayHash passed in was generated by the system.
111      *
112      * @param salt        The salt value to use when verifying the hmac. This should be the
113      *                    same value that was passed to
114      *                    {@link #onGenerateDisplayHash(byte[],
115      *                    HardwareBuffer, Rect, String, DisplayHashResultCallback)} to
116      *                    generate the token.
117      * @param displayHash The token to verify that it was generated by the system.
118      * @return a {@link VerifiedDisplayHash} if the provided display hash was originally generated
119      * by the system or null if the system did not generate the display hash.
120      */
121     @Nullable
onVerifyDisplayHash(@onNull byte[] salt, @NonNull DisplayHash displayHash)122     public abstract VerifiedDisplayHash onVerifyDisplayHash(@NonNull byte[] salt,
123             @NonNull DisplayHash displayHash);
124 
verifyDisplayHash(byte[] salt, DisplayHash displayHash, RemoteCallback callback)125     private void verifyDisplayHash(byte[] salt, DisplayHash displayHash,
126             RemoteCallback callback) {
127         VerifiedDisplayHash verifiedDisplayHash = onVerifyDisplayHash(salt,
128                 displayHash);
129         final Bundle data = new Bundle();
130         data.putParcelable(EXTRA_VERIFIED_DISPLAY_HASH, verifiedDisplayHash);
131         callback.sendResult(data);
132     }
133 
getDisplayHashAlgorithms(RemoteCallback callback)134     private void getDisplayHashAlgorithms(RemoteCallback callback) {
135         Map<String, DisplayHashParams> displayHashParams = onGetDisplayHashAlgorithms();
136         final Bundle data = new Bundle();
137         for (Map.Entry<String, DisplayHashParams> entry : displayHashParams.entrySet()) {
138             data.putParcelable(entry.getKey(), entry.getValue());
139         }
140         callback.sendResult(data);
141     }
142 
143     /**
144      * Call to get the interval required between display hash requests. Requests made faster than
145      * this will be throttled.
146      *
147      * @return the interval value required between requests.
148      */
onGetIntervalBetweenRequestsMillis()149     public abstract int onGetIntervalBetweenRequestsMillis();
150 
getDurationBetweenRequestsMillis(RemoteCallback callback)151     private void getDurationBetweenRequestsMillis(RemoteCallback callback) {
152         int durationBetweenRequestMillis = onGetIntervalBetweenRequestsMillis();
153         Bundle data = new Bundle();
154         data.putInt(EXTRA_INTERVAL_BETWEEN_REQUESTS, durationBetweenRequestMillis);
155         callback.sendResult(data);
156     }
157 
158     private final class DisplayHashingServiceWrapper extends IDisplayHashingService.Stub {
159         @Override
generateDisplayHash(byte[] salt, HardwareBuffer buffer, Rect bounds, String hashAlgorithm, RemoteCallback callback)160         public void generateDisplayHash(byte[] salt, HardwareBuffer buffer, Rect bounds,
161                 String hashAlgorithm, RemoteCallback callback) {
162             mHandler.sendMessage(
163                     obtainMessage(DisplayHashingService::onGenerateDisplayHash,
164                             DisplayHashingService.this, salt, buffer, bounds,
165                             hashAlgorithm, new DisplayHashResultCallback() {
166                                 @Override
167                                 public void onDisplayHashResult(
168                                         @NonNull DisplayHash displayHash) {
169                                     Bundle result = new Bundle();
170                                     result.putParcelable(EXTRA_DISPLAY_HASH, displayHash);
171                                     callback.sendResult(result);
172                                 }
173 
174                                 @Override
175                                 public void onDisplayHashError(int errorCode) {
176                                     Bundle result = new Bundle();
177                                     result.putInt(EXTRA_DISPLAY_HASH_ERROR_CODE, errorCode);
178                                     callback.sendResult(result);
179                                 }
180                             }));
181         }
182 
183         @Override
verifyDisplayHash(byte[] salt, DisplayHash displayHash, RemoteCallback callback)184         public void verifyDisplayHash(byte[] salt, DisplayHash displayHash,
185                 RemoteCallback callback) {
186             mHandler.sendMessage(
187                     obtainMessage(DisplayHashingService::verifyDisplayHash,
188                             DisplayHashingService.this, salt, displayHash, callback));
189         }
190 
191         @Override
getDisplayHashAlgorithms(RemoteCallback callback)192         public void getDisplayHashAlgorithms(RemoteCallback callback) {
193             mHandler.sendMessage(obtainMessage(DisplayHashingService::getDisplayHashAlgorithms,
194                     DisplayHashingService.this, callback));
195         }
196 
197         @Override
getIntervalBetweenRequestsMillis(RemoteCallback callback)198         public void getIntervalBetweenRequestsMillis(RemoteCallback callback) {
199             mHandler.sendMessage(
200                     obtainMessage(DisplayHashingService::getDurationBetweenRequestsMillis,
201                             DisplayHashingService.this, callback));
202         }
203     }
204 }
205