/* * Copyright (C) 2021 The Android Open Source Project * * Licensed under the Apache License, Version 2.0 (the "License"); * you may not use this file except in compliance with the License. * You may obtain a copy of the License at * * http://www.apache.org/licenses/LICENSE-2.0 * * Unless required by applicable law or agreed to in writing, software * distributed under the License is distributed on an "AS IS" BASIS, * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. * See the License for the specific language governing permissions and * limitations under the License. */ package android.service.displayhash; import static com.android.internal.util.function.pooled.PooledLambda.obtainMessage; import android.annotation.NonNull; import android.annotation.Nullable; import android.annotation.SystemApi; import android.app.Service; import android.content.Intent; import android.graphics.Rect; import android.hardware.HardwareBuffer; import android.os.Bundle; import android.os.Handler; import android.os.IBinder; import android.os.Looper; import android.os.RemoteCallback; import android.view.displayhash.DisplayHash; import android.view.displayhash.DisplayHashResultCallback; import android.view.displayhash.VerifiedDisplayHash; import java.util.Map; /** * A service that handles generating and verify {@link DisplayHash}. * * The service will generate a DisplayHash based on arguments passed in. Then later that * same DisplayHash can be verified to determine that it was created by the system. * * @hide */ @SystemApi public abstract class DisplayHashingService extends Service { /** @hide **/ public static final String EXTRA_VERIFIED_DISPLAY_HASH = "android.service.displayhash.extra.VERIFIED_DISPLAY_HASH"; /** @hide **/ public static final String EXTRA_INTERVAL_BETWEEN_REQUESTS = "android.service.displayhash.extra.INTERVAL_BETWEEN_REQUESTS"; /** * The {@link Intent} action that must be declared as handled by a service in its manifest * for the system to recognize it as a DisplayHash providing service. * * @hide */ @SystemApi public static final String SERVICE_INTERFACE = "android.service.displayhash.DisplayHashingService"; private DisplayHashingServiceWrapper mWrapper; private Handler mHandler; @Override public void onCreate() { super.onCreate(); mWrapper = new DisplayHashingServiceWrapper(); mHandler = new Handler(Looper.getMainLooper(), null, true); } @NonNull @Override public final IBinder onBind(@NonNull Intent intent) { return mWrapper; } /** * Generates the DisplayHash that can be used to validate that the system generated the * token. * * @param salt The salt to use when generating the hmac. This should be unique to the * caller so the token cannot be verified by any other process. * @param buffer The buffer for the content to generate the hash for. * @param bounds The size and position of the content in window space. * @param hashAlgorithm The String for the hashing algorithm to use based values in * {@link #getDisplayHashAlgorithms(RemoteCallback)}. * @param callback The callback to invoke * {@link DisplayHashResultCallback#onDisplayHashResult(DisplayHash)} * if successfully generated a DisplayHash or {@link * DisplayHashResultCallback#onDisplayHashError(int)} if failed. */ public abstract void onGenerateDisplayHash(@NonNull byte[] salt, @NonNull HardwareBuffer buffer, @NonNull Rect bounds, @NonNull String hashAlgorithm, @NonNull DisplayHashResultCallback callback); /** * Returns a map of supported algorithms and their {@link DisplayHashParams} */ @NonNull public abstract Map onGetDisplayHashAlgorithms(); /** * Call to verify that the DisplayHash passed in was generated by the system. * * @param salt The salt value to use when verifying the hmac. This should be the * same value that was passed to * {@link #onGenerateDisplayHash(byte[], * HardwareBuffer, Rect, String, DisplayHashResultCallback)} to * generate the token. * @param displayHash The token to verify that it was generated by the system. * @return a {@link VerifiedDisplayHash} if the provided display hash was originally generated * by the system or null if the system did not generate the display hash. */ @Nullable public abstract VerifiedDisplayHash onVerifyDisplayHash(@NonNull byte[] salt, @NonNull DisplayHash displayHash); private void verifyDisplayHash(byte[] salt, DisplayHash displayHash, RemoteCallback callback) { VerifiedDisplayHash verifiedDisplayHash = onVerifyDisplayHash(salt, displayHash); final Bundle data = new Bundle(); data.putParcelable(EXTRA_VERIFIED_DISPLAY_HASH, verifiedDisplayHash); callback.sendResult(data); } private void getDisplayHashAlgorithms(RemoteCallback callback) { Map displayHashParams = onGetDisplayHashAlgorithms(); final Bundle data = new Bundle(); for (Map.Entry entry : displayHashParams.entrySet()) { data.putParcelable(entry.getKey(), entry.getValue()); } callback.sendResult(data); } /** * Call to get the interval required between display hash requests. Requests made faster than * this will be throttled. * * @return the interval value required between requests. */ public abstract int onGetIntervalBetweenRequestsMillis(); private void getDurationBetweenRequestsMillis(RemoteCallback callback) { int durationBetweenRequestMillis = onGetIntervalBetweenRequestsMillis(); Bundle data = new Bundle(); data.putInt(EXTRA_INTERVAL_BETWEEN_REQUESTS, durationBetweenRequestMillis); callback.sendResult(data); } private final class DisplayHashingServiceWrapper extends IDisplayHashingService.Stub { @Override public void generateDisplayHash(byte[] salt, HardwareBuffer buffer, Rect bounds, String hashAlgorithm, RemoteCallback callback) { mHandler.sendMessage( obtainMessage(DisplayHashingService::onGenerateDisplayHash, DisplayHashingService.this, salt, buffer, bounds, hashAlgorithm, new DisplayHashResultCallback() { @Override public void onDisplayHashResult( @NonNull DisplayHash displayHash) { Bundle result = new Bundle(); result.putParcelable(EXTRA_DISPLAY_HASH, displayHash); callback.sendResult(result); } @Override public void onDisplayHashError(int errorCode) { Bundle result = new Bundle(); result.putInt(EXTRA_DISPLAY_HASH_ERROR_CODE, errorCode); callback.sendResult(result); } })); } @Override public void verifyDisplayHash(byte[] salt, DisplayHash displayHash, RemoteCallback callback) { mHandler.sendMessage( obtainMessage(DisplayHashingService::verifyDisplayHash, DisplayHashingService.this, salt, displayHash, callback)); } @Override public void getDisplayHashAlgorithms(RemoteCallback callback) { mHandler.sendMessage(obtainMessage(DisplayHashingService::getDisplayHashAlgorithms, DisplayHashingService.this, callback)); } @Override public void getIntervalBetweenRequestsMillis(RemoteCallback callback) { mHandler.sendMessage( obtainMessage(DisplayHashingService::getDurationBetweenRequestsMillis, DisplayHashingService.this, callback)); } } }