/* * 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.ext.services.displayhash; import static android.view.displayhash.DisplayHashResultCallback.DISPLAY_HASH_ERROR_INVALID_HASH_ALGORITHM; import static android.view.displayhash.DisplayHashResultCallback.DISPLAY_HASH_ERROR_UNKNOWN; import android.graphics.Rect; import android.hardware.HardwareBuffer; import android.os.Build; import android.service.displayhash.DisplayHashParams; import android.service.displayhash.DisplayHashingService; import android.util.ArrayMap; import android.util.Log; import android.view.displayhash.DisplayHash; import android.view.displayhash.DisplayHashResultCallback; import android.view.displayhash.VerifiedDisplayHash; import androidx.annotation.NonNull; import androidx.annotation.RequiresApi; import com.google.common.annotations.VisibleForTesting; import java.util.Map; /** * The implementation service for {@link DisplayHashingService} */ @RequiresApi(Build.VERSION_CODES.S) public class DisplayHashingServiceImpl extends DisplayHashingService { static final String TAG = "DisplayHashingService"; private static final int INTERVAL_BETWEEN_REQUESTS_MILLIS = 10000; private ImageHashManager mImageHashManager = new ImageHashManager(); private final HmacKeyManager mHmacKeyManager = new HmacKeyManager(); private final ArrayMap<String, DisplayHashParams> mDisplayHashParams = new ArrayMap<>(); @Override public void onCreate() { super.onCreate(); mDisplayHashParams.put("PHASH", new DisplayHashParams.Builder() .setBufferSize(32, 32) .setGrayscaleBuffer(true).build()); } @Override public void onGenerateDisplayHash(@NonNull byte[] salt, @NonNull HardwareBuffer buffer, @NonNull Rect bounds, @NonNull String hashAlgorithm, @NonNull DisplayHashResultCallback callback) { if (salt == null) { Log.w(TAG, "Failed to generate display hash: salt was null"); callback.onDisplayHashError(DISPLAY_HASH_ERROR_UNKNOWN); return; } int hashAlgorithmIndex = getIndexForHashAlgorithm(hashAlgorithm); if (hashAlgorithmIndex < 0) { Log.w(TAG, "Failed to generate display hash: invalid hash request"); callback.onDisplayHashError(DISPLAY_HASH_ERROR_INVALID_HASH_ALGORITHM); return; } if (buffer == null) { Log.w(TAG, "Failed to generate display hash: null buffer"); callback.onDisplayHashError(DISPLAY_HASH_ERROR_UNKNOWN); return; } long timestamp = System.currentTimeMillis(); byte[] imageHash = mImageHashManager.generateHash(buffer, hashAlgorithm); if (imageHash == null) { Log.w(TAG, "Failed to generate display hash: failed to create image hash"); callback.onDisplayHashError(DISPLAY_HASH_ERROR_UNKNOWN); return; } byte[] hmac = mHmacKeyManager.generateHmac(salt, timestamp, bounds, hashAlgorithmIndex, imageHash); callback.onDisplayHashResult( new DisplayHash(timestamp, bounds, hashAlgorithm, imageHash, hmac)); } @Override public VerifiedDisplayHash onVerifyDisplayHash(@NonNull byte[] salt, @NonNull DisplayHash displayHash) { if (displayHash == null || salt == null) { Log.w(TAG, "Failed to verify display hash: invalid token or salt"); return null; } boolean verified = mHmacKeyManager.verifyHmac(salt, displayHash.getTimeMillis(), displayHash.getBoundsInWindow(), getIndexForHashAlgorithm(displayHash.getHashAlgorithm()), displayHash.getImageHash(), displayHash.getHmac()); if (verified) { return new VerifiedDisplayHash(displayHash.getTimeMillis(), displayHash.getBoundsInWindow(), displayHash.getHashAlgorithm(), displayHash.getImageHash()); } else { return null; } } @Override public int onGetIntervalBetweenRequestsMillis() { return INTERVAL_BETWEEN_REQUESTS_MILLIS; } private int getIndexForHashAlgorithm(String hashAlgorithm) { return mDisplayHashParams.indexOfKey(hashAlgorithm); } @VisibleForTesting public void setImageHashManager(ImageHashManager imageHashManager) { mImageHashManager = imageHashManager; } @NonNull @Override public Map<String, DisplayHashParams> onGetDisplayHashAlgorithms() { return mDisplayHashParams; } }