Merge "Added public APIs to generate and validate a DisplayHash." into sc-dev

This commit is contained in:
Chavi Weingarten
2021-02-19 01:47:14 +00:00
committed by Android (Google) Code Review
24 changed files with 1149 additions and 571 deletions

View File

@@ -10444,6 +10444,7 @@ package android.content {
field public static final int CONTEXT_RESTRICTED = 4; // 0x4
field public static final String CROSS_PROFILE_APPS_SERVICE = "crossprofileapps";
field public static final String DEVICE_POLICY_SERVICE = "device_policy";
field public static final String DISPLAY_HASH_SERVICE = "display_hash";
field public static final String DISPLAY_SERVICE = "display";
field public static final String DOWNLOAD_SERVICE = "download";
field public static final String DROPBOX_SERVICE = "dropbox";
@@ -48235,6 +48236,7 @@ package android.view {
method public android.view.View focusSearch(int);
method public void forceHasOverlappingRendering(boolean);
method public void forceLayout();
method @Nullable public void generateDisplayHash(@NonNull String, @Nullable android.graphics.Rect, @NonNull java.util.concurrent.Executor, @NonNull android.view.displayhash.DisplayHashResultCallback);
method public static int generateViewId();
method public CharSequence getAccessibilityClassName();
method public android.view.View.AccessibilityDelegate getAccessibilityDelegate();
@@ -51190,6 +51192,41 @@ package android.view.contentcapture {
}
package android.view.displayhash {
public final class DisplayHash implements android.os.Parcelable {
method public int describeContents();
method public void writeToParcel(@NonNull android.os.Parcel, int);
field @NonNull public static final android.os.Parcelable.Creator<android.view.displayhash.DisplayHash> CREATOR;
}
public final class DisplayHashManager {
method @NonNull public java.util.Set<java.lang.String> getSupportedHashAlgorithms();
method @Nullable public android.view.displayhash.VerifiedDisplayHash verifyDisplayHash(@NonNull android.view.displayhash.DisplayHash);
}
public interface DisplayHashResultCallback {
method public void onDisplayHashError(int);
method public void onDisplayHashResult(@NonNull android.view.displayhash.DisplayHash);
field public static final int DISPLAY_HASH_ERROR_INVALID_BOUNDS = -2; // 0xfffffffe
field public static final int DISPLAY_HASH_ERROR_MISSING_WINDOW = -3; // 0xfffffffd
field public static final int DISPLAY_HASH_ERROR_NOT_VISIBLE_ON_SCREEN = -4; // 0xfffffffc
field public static final int DISPLAY_HASH_ERROR_UNKNOWN = -1; // 0xffffffff
}
public final class VerifiedDisplayHash implements android.os.Parcelable {
ctor public VerifiedDisplayHash(long, @NonNull android.graphics.Rect, @NonNull String, @NonNull byte[]);
method public int describeContents();
method @NonNull public android.graphics.Rect getBoundsInWindow();
method @NonNull public String getHashAlgorithm();
method @NonNull public byte[] getImageHash();
method public long getTimeMillis();
method public void writeToParcel(@NonNull android.os.Parcel, int);
field @NonNull public static final android.os.Parcelable.Creator<android.view.displayhash.VerifiedDisplayHash> CREATOR;
}
}
package android.view.inputmethod {
public class BaseInputConnection implements android.view.inputmethod.InputConnection {

View File

@@ -9984,6 +9984,18 @@ package android.service.dataloader {
}
package android.service.displayhash {
public abstract class DisplayHasherService extends android.app.Service {
ctor public DisplayHasherService();
method @NonNull public final android.os.IBinder onBind(@NonNull android.content.Intent);
method @Nullable public abstract void onGenerateDisplayHash(@NonNull byte[], @NonNull android.hardware.HardwareBuffer, @NonNull android.graphics.Rect, @NonNull String, @NonNull android.view.displayhash.DisplayHashResultCallback);
method @Nullable public abstract android.view.displayhash.VerifiedDisplayHash onVerifyDisplayHash(@NonNull byte[], @NonNull android.view.displayhash.DisplayHash);
field public static final String SERVICE_INTERFACE = "android.service.displayhash.DisplayHasherService";
}
}
package android.service.euicc {
public final class DownloadSubscriptionResult implements android.os.Parcelable {
@@ -10344,30 +10356,6 @@ package android.service.rotationresolver {
}
package android.service.screenshot {
public final class ScreenshotHash implements android.os.Parcelable {
ctor public ScreenshotHash(long, @NonNull android.graphics.Rect, @NonNull String, @NonNull byte[], @NonNull byte[]);
method public int describeContents();
method @NonNull public android.graphics.Rect getBoundsInWindow();
method @NonNull public String getHashingAlgorithm();
method @NonNull public byte[] getHmac();
method @NonNull public byte[] getImageHash();
method public long getScreenshotTimeMillis();
method public void writeToParcel(@NonNull android.os.Parcel, int);
field @NonNull public static final android.os.Parcelable.Creator<android.service.screenshot.ScreenshotHash> CREATOR;
}
public abstract class ScreenshotHasherService extends android.app.Service {
ctor public ScreenshotHasherService();
method @NonNull public final android.os.IBinder onBind(@NonNull android.content.Intent);
method @Nullable public abstract android.service.screenshot.ScreenshotHash onGenerateScreenshotHash(@NonNull byte[], @NonNull android.hardware.HardwareBuffer, @NonNull android.graphics.Rect, @NonNull String);
method public abstract boolean onVerifyScreenshotHash(@NonNull byte[], @NonNull android.service.screenshot.ScreenshotHash);
field public static final String SERVICE_INTERFACE = "android.service.screenshot.ScreenshotHasherService";
}
}
package android.service.search {
public abstract class SearchUiService extends android.app.Service {
@@ -14399,6 +14387,21 @@ package android.view.contentcapture {
}
package android.view.displayhash {
public final class DisplayHash implements android.os.Parcelable {
ctor public DisplayHash(long, @NonNull android.graphics.Rect, @NonNull String, @NonNull byte[], @NonNull byte[]);
method public int describeContents();
method @NonNull public android.graphics.Rect getBoundsInWindow();
method @NonNull public String getHashAlgorithm();
method @NonNull public byte[] getHmac();
method @NonNull public byte[] getImageHash();
method public long getTimeMillis();
method public void writeToParcel(@NonNull android.os.Parcel, int);
}
}
package android.view.translation {
public final class UiTranslationManager {

View File

@@ -211,6 +211,7 @@ import android.view.autofill.AutofillManager;
import android.view.autofill.IAutoFillManager;
import android.view.contentcapture.ContentCaptureManager;
import android.view.contentcapture.IContentCaptureManager;
import android.view.displayhash.DisplayHashManager;
import android.view.inputmethod.InputMethodManager;
import android.view.textclassifier.TextClassificationManager;
import android.view.textservice.TextServicesManager;
@@ -1426,6 +1427,13 @@ public final class SystemServiceRegistry {
}
});
registerService(Context.DISPLAY_HASH_SERVICE, DisplayHashManager.class,
new CachedServiceFetcher<DisplayHashManager>() {
@Override
public DisplayHashManager createService(ContextImpl ctx) {
return new DisplayHashManager();
}});
sInitializing = true;
try {
// Note: the following functions need to be @SystemApis, once they become mainline

View File

@@ -5490,6 +5490,14 @@ public abstract class Context {
@SystemApi
public static final String DOMAIN_VERIFICATION_SERVICE = "domain_verification";
/**
* Use with {@link #getSystemService(String)} to access
* {@link android.view.displayhash.DisplayHashManager} to handle display hashes.
*
* @see #getSystemService(String)
*/
public static final String DISPLAY_HASH_SERVICE = "display_hash";
/**
* Determine whether the given permission is allowed for a particular
* process and user ID running in the system.

View File

@@ -0,0 +1,168 @@
/*
* 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;
/**
* 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 DisplayHasherService extends Service {
/** @hide **/
public static final String EXTRA_VERIFIED_DISPLAY_HASH =
"android.service.displayhash.extra.VERIFIED_DISPLAY_HASH";
/**
* Manifest metadata key for the resource string array containing the names of all hashing
* algorithms provided by the service.
*
* @hide
*/
public static final String SERVICE_META_DATA_KEY_AVAILABLE_ALGORITHMS =
"android.displayhash.available_algorithms";
/**
* 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.DisplayHasherService";
private DisplayHasherServiceWrapper mWrapper;
private Handler mHandler;
public DisplayHasherService() {
}
@Override
public void onCreate() {
super.onCreate();
mWrapper = new DisplayHasherServiceWrapper();
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 #SERVICE_META_DATA_KEY_AVAILABLE_ALGORITHMS)}.
* @param callback The callback to invoke
* {@link DisplayHashResultCallback#onDisplayHashResult(DisplayHash)}
* if successfully generated a DisplayHash or {@link
* DisplayHashResultCallback#onDisplayHashError(int)} if failed.
*/
@Nullable
public abstract void onGenerateDisplayHash(@NonNull byte[] salt,
@NonNull HardwareBuffer buffer, @NonNull Rect bounds,
@NonNull String hashAlgorithm, @NonNull DisplayHashResultCallback callback);
/**
* 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 token was generated by the system or null
* if the token cannot be verified.
*/
@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 final class DisplayHasherServiceWrapper extends IDisplayHasherService.Stub {
@Override
public void generateDisplayHash(byte[] salt, HardwareBuffer buffer, Rect bounds,
String hashAlgorithm, RemoteCallback callback) {
mHandler.sendMessage(
obtainMessage(DisplayHasherService::onGenerateDisplayHash,
DisplayHasherService.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(DisplayHasherService::verifyDisplayHash,
DisplayHasherService.this, salt, displayHash, callback));
}
}
}

View File

@@ -14,43 +14,41 @@
* limitations under the License.
*/
package android.service.screenshot;
package android.service.displayhash;
import android.graphics.Rect;
import android.hardware.HardwareBuffer;
import android.os.RemoteCallback;
import android.service.screenshot.ScreenshotHash;
import android.view.displayhash.DisplayHash;
/**
* Service used to handle ScreenshotHash requests.
* Service used to handle DisplayHash requests.
*
* @hide
*/
oneway interface IScreenshotHasherService {
oneway interface IDisplayHasherService {
/**
* Generates the ScreenshotHash token that can be used to validate that the system generated the
* token.
* 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 screenshot The screenshot to generate the hash and add to the token.
* @param bounds The size and position of the content being screenshot in the window.
* @param hashAlgorithm The String for the hashing algorithm to use based on values in
* @param buffer The buffer to generate the hash for.
* @param bounds The size and position of the content being hashed in window space.
* @param hashAlgorithm The String for the hash algorithm to use based on values in
* {@link #SERVICE_META_DATA_KEY_AVAILABLE_ALGORITHMS}.
* @param Callback The callback invoked to send back the ScreenshotHash token.
* @param Callback The callback invoked to send back the DisplayHash.
*/
void generateScreenshotHash(in byte[] salt, in HardwareBuffer screenshot, in Rect bounds,
void generateDisplayHash(in byte[] salt, in HardwareBuffer buffer, in Rect bounds,
in String hashAlgorithm, in RemoteCallback callback);
/**
* Call to verify that the ScreenshotHash passed in was generated by the system. The result
* will be sent in the callback as a boolean with the key {@link #EXTRA_VERIFICATION_STATUS}.
* Call to verify that the DisplayHash passed in was generated by the system. The result
* will be sent in the callback as a boolean with the key {@link #EXTRA_VERIFIED_DISPLAY_HASH}.
*
* @param salt The salt value to use when verifying the hmac. This should be the same value that
* was passed to {@link generateScreenshotHash()} to generate the token.
* @param screenshotHash The hash to verify that it was generated by the system.
* @param callback The callback invoked to send back the verification status.
* was passed to {@link generateDisplayHash()} to generate the DisplayHash.
* @param displayHash The hash to verify that it was generated by the system.
* @param callback The callback invoked to send back the VerifiedDisplayHash.
*/
void verifyScreenshotHash(in byte[] salt, in ScreenshotHash screenshotHash,
in RemoteCallback callback);
void verifyDisplayHash(in byte[] salt, in DisplayHash displayHash, in RemoteCallback callback);
}

View File

@@ -1,256 +0,0 @@
/*
* 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.screenshot;
import android.annotation.NonNull;
import android.annotation.SystemApi;
import android.graphics.Rect;
import android.os.Parcel;
import android.os.Parcelable;
import com.android.internal.util.DataClass;
/**
* The screenshot hash used to validate information about what was present on screen.
* @hide
*
* TODO: Remove hide and SystemAPI since this will be a public class
*/
@SystemApi
@DataClass(genToString = true, genAidl = true)
public final class ScreenshotHash implements Parcelable {
/**
* The timestamp when the screenshot was generated.
*/
private final long mScreenshotTimeMillis;
/**
* The bounds of the requested area to take the screenshot. This is in window space passed in
* by the client.
*/
@NonNull
private final Rect mBoundsInWindow;
/**
* The selected hashing algorithm that generated the image hash.
*/
@NonNull
private final String mHashingAlgorithm;
/**
* The image hash generated when creating the ScreenshotHash from the screenshot taken.
*/
@NonNull
private final byte[] mImageHash;
/**
* The hmac generated by the system and used to verify whether this token was generated by
* the system. This should only be accessed by a system process.
*/
@NonNull
private final byte[] mHmac;
/**
* The hmac generated by the system and used to verify whether this token was generated by
* the system. This should only be accessed by a system process.
*
* @hide
*/
@SystemApi
@NonNull
public byte[] getHmac() {
return mHmac;
}
// Code below generated by codegen v1.0.22.
//
// DO NOT MODIFY!
// CHECKSTYLE:OFF Generated code
//
// To regenerate run:
// $ codegen $ANDROID_BUILD_TOP/frameworks/base/core/java/android/service/screenshot
// /ScreenshotHash.java
//
// To exclude the generated code from IntelliJ auto-formatting enable (one-time):
// Settings > Editor > Code Style > Formatter Control
//@formatter:off
/**
* Creates a new ScreenshotHash.
*
* @param screenshotTimeMillis
* The timestamp when the screenshot was generated.
* @param boundsInWindow
* The bounds of the requested area to take the screenshot. This is in window space passed in
* by the client.
* @param hashingAlgorithm
* The selected hashing algorithm that generated the image hash.
* @param imageHash
* The image hash generated when creating the ScreenshotHash from the screenshot taken.
* @param hmac
* The hmac generated by the system and used to verify whether this token was generated by
* the system. This should only be accessed by a system process.
*/
@DataClass.Generated.Member
public ScreenshotHash(
long screenshotTimeMillis,
@NonNull Rect boundsInWindow,
@NonNull String hashingAlgorithm,
@NonNull byte[] imageHash,
@NonNull byte[] hmac) {
this.mScreenshotTimeMillis = screenshotTimeMillis;
this.mBoundsInWindow = boundsInWindow;
com.android.internal.util.AnnotationValidations.validate(
NonNull.class, null, mBoundsInWindow);
this.mHashingAlgorithm = hashingAlgorithm;
com.android.internal.util.AnnotationValidations.validate(
NonNull.class, null, mHashingAlgorithm);
this.mImageHash = imageHash;
com.android.internal.util.AnnotationValidations.validate(
NonNull.class, null, mImageHash);
this.mHmac = hmac;
com.android.internal.util.AnnotationValidations.validate(
NonNull.class, null, mHmac);
// onConstructed(); // You can define this method to get a callback
}
/**
* The timestamp when the screenshot was generated.
*/
@DataClass.Generated.Member
public long getScreenshotTimeMillis() {
return mScreenshotTimeMillis;
}
/**
* The bounds of the requested area to take the screenshot. This is in window space passed in
* by the client.
*/
@DataClass.Generated.Member
public @NonNull Rect getBoundsInWindow() {
return mBoundsInWindow;
}
/**
* The selected hashing algorithm that generated the image hash.
*/
@DataClass.Generated.Member
public @NonNull String getHashingAlgorithm() {
return mHashingAlgorithm;
}
/**
* The image hash generated when creating the ScreenshotHash from the screenshot taken.
*/
@DataClass.Generated.Member
public @NonNull byte[] getImageHash() {
return mImageHash;
}
@Override
@DataClass.Generated.Member
public String toString() {
// You can override field toString logic by defining methods like:
// String fieldNameToString() { ... }
return "ScreenshotHash { " +
"screenshotTimeMillis = " + mScreenshotTimeMillis + ", " +
"boundsInWindow = " + mBoundsInWindow + ", " +
"hashingAlgorithm = " + mHashingAlgorithm + ", " +
"imageHash = " + java.util.Arrays.toString(mImageHash) + ", " +
"hmac = " + java.util.Arrays.toString(mHmac) +
" }";
}
@Override
@DataClass.Generated.Member
public void writeToParcel(@NonNull Parcel dest, int flags) {
// You can override field parcelling by defining methods like:
// void parcelFieldName(Parcel dest, int flags) { ... }
dest.writeLong(mScreenshotTimeMillis);
dest.writeTypedObject(mBoundsInWindow, flags);
dest.writeString(mHashingAlgorithm);
dest.writeByteArray(mImageHash);
dest.writeByteArray(mHmac);
}
@Override
@DataClass.Generated.Member
public int describeContents() { return 0; }
/** @hide */
@SuppressWarnings({"unchecked", "RedundantCast"})
@DataClass.Generated.Member
/* package-private */ ScreenshotHash(@NonNull Parcel in) {
// You can override field unparcelling by defining methods like:
// static FieldType unparcelFieldName(Parcel in) { ... }
long screenshotTimeMillis = in.readLong();
Rect boundsInWindow = (Rect) in.readTypedObject(Rect.CREATOR);
String hashingAlgorithm = in.readString();
byte[] imageHash = in.createByteArray();
byte[] hmac = in.createByteArray();
this.mScreenshotTimeMillis = screenshotTimeMillis;
this.mBoundsInWindow = boundsInWindow;
com.android.internal.util.AnnotationValidations.validate(
NonNull.class, null, mBoundsInWindow);
this.mHashingAlgorithm = hashingAlgorithm;
com.android.internal.util.AnnotationValidations.validate(
NonNull.class, null, mHashingAlgorithm);
this.mImageHash = imageHash;
com.android.internal.util.AnnotationValidations.validate(
NonNull.class, null, mImageHash);
this.mHmac = hmac;
com.android.internal.util.AnnotationValidations.validate(
NonNull.class, null, mHmac);
// onConstructed(); // You can define this method to get a callback
}
@DataClass.Generated.Member
public static final @NonNull Parcelable.Creator<ScreenshotHash> CREATOR
= new Parcelable.Creator<ScreenshotHash>() {
@Override
public ScreenshotHash[] newArray(int size) {
return new ScreenshotHash[size];
}
@Override
public ScreenshotHash createFromParcel(@NonNull Parcel in) {
return new ScreenshotHash(in);
}
};
@DataClass.Generated(
time = 1612383172822L,
codegenVersion = "1.0.22",
sourceFile = "frameworks/base/core/java/android/service/screenshot/ScreenshotHash.java",
inputSignatures = "private final long mScreenshotTimeMillis\nprivate final @android.annotation.NonNull android.graphics.Rect mBoundsInWindow\nprivate final @android.annotation.NonNull java.lang.String mHashingAlgorithm\nprivate final @android.annotation.NonNull byte[] mImageHash\nprivate final @android.annotation.NonNull byte[] mHmac\npublic @android.annotation.SystemApi @android.annotation.NonNull byte[] getHmac()\nclass ScreenshotHash extends java.lang.Object implements [android.os.Parcelable]\n@com.android.internal.util.DataClass(genToString=true, genAidl=true)")
@Deprecated
private void __metadata() {}
//@formatter:on
// End of generated code
}

View File

@@ -1,160 +0,0 @@
/*
* 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.screenshot;
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;
/**
* A service that handles generating and verify {@link ScreenshotHash}.
*
* The service will generate a ScreenshotHash based on arguments passed in. Then later that
* same ScreenshotHash can be verified to determine that it was created by the system.
*
* @hide
*/
@SystemApi
public abstract class ScreenshotHasherService extends Service {
/** @hide **/
public static final String EXTRA_SCREENSHOT_HASH =
"android.service.screenshot.extra.SCREENSHOT_HASH";
/** @hide **/
public static final String EXTRA_VERIFICATION_STATUS =
"android.service.screenshot.extra.VERIFICATION_STATUS";
/**
* Manifest metadata key for the resource string array containing the names of all hashing
* algorithms provided by the service.
*
* @hide
*/
public static final String SERVICE_META_DATA_KEY_AVAILABLE_ALGORITHMS =
"android.screenshot.available_algorithms";
/**
* The {@link Intent} action that must be declared as handled by a service in its manifest
* for the system to recognize it as a ScreenshotHash providing service.
*
* @hide
*/
@SystemApi
public static final String SERVICE_INTERFACE =
"android.service.screenshot.ScreenshotHasherService";
private ScreenshotHasherServiceWrapper mWrapper;
private Handler mHandler;
public ScreenshotHasherService() {
}
@Override
public void onCreate() {
super.onCreate();
mWrapper = new ScreenshotHasherServiceWrapper();
mHandler = new Handler(Looper.getMainLooper(), null, true);
}
@NonNull
@Override
public final IBinder onBind(@NonNull Intent intent) {
return mWrapper;
}
/**
* Generates the ScreenshotHash 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 screenshot The screenshot buffer for the content.
* @param bounds The size and position of the content being screenshot in the window.
* @param hashAlgorithm The String for the hashing algorithm to use based values in
* {@link #SERVICE_META_DATA_KEY_AVAILABLE_ALGORITHMS)}.
* @return A ScreenshotHash that can be used to validate information about the content.
* Returns null when the arguments sent are invalid.
*/
@Nullable
public abstract ScreenshotHash onGenerateScreenshotHash(@NonNull byte[] salt,
@NonNull HardwareBuffer screenshot, @NonNull Rect bounds,
@NonNull String hashAlgorithm);
/**
* Call to verify that the ScreenshotHash 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 #onGenerateScreenshotHash(byte[],
* HardwareBuffer, Rect, String)} to
* generate the token.
* @param screenshotHash The token to verify that it was generated by the system.
* @return true if the token can be verified that it was generated by the system.
*/
public abstract boolean onVerifyScreenshotHash(@NonNull byte[] salt,
@NonNull ScreenshotHash screenshotHash);
private void generateScreenshotHash(byte[] salt, HardwareBuffer screenshot, Rect bounds,
String hashAlgorithm, RemoteCallback callback) {
ScreenshotHash screenshotHash = onGenerateScreenshotHash(salt, screenshot,
bounds,
hashAlgorithm);
final Bundle data = new Bundle();
data.putParcelable(EXTRA_SCREENSHOT_HASH, screenshotHash);
callback.sendResult(data);
}
private void verifyScreenshotHash(byte[] salt, ScreenshotHash screenshotHash,
RemoteCallback callback) {
boolean verificationStatus = onVerifyScreenshotHash(salt, screenshotHash);
final Bundle data = new Bundle();
data.putBoolean(EXTRA_VERIFICATION_STATUS, verificationStatus);
callback.sendResult(data);
}
private final class ScreenshotHasherServiceWrapper extends
IScreenshotHasherService.Stub {
@Override
public void generateScreenshotHash(byte[] salt, HardwareBuffer screenshot, Rect bounds,
String hashAlgorithm, RemoteCallback callback) {
mHandler.sendMessage(
obtainMessage(ScreenshotHasherService::generateScreenshotHash,
ScreenshotHasherService.this, salt, screenshot, bounds,
hashAlgorithm, callback));
}
@Override
public void verifyScreenshotHash(byte[] salt, ScreenshotHash screenshotHash,
RemoteCallback callback) {
mHandler.sendMessage(
obtainMessage(ScreenshotHasherService::verifyScreenshotHash,
ScreenshotHasherService.this, salt, screenshotHash,
callback));
}
}
}

View File

@@ -32,7 +32,6 @@ import android.graphics.Region;
import android.os.Bundle;
import android.os.IRemoteCallback;
import android.os.ParcelFileDescriptor;
import android.service.screenshot.ScreenshotHash;
import android.view.DisplayCutout;
import android.view.IApplicationToken;
import android.view.IAppTransitionAnimationSpecsFuture;
@@ -63,6 +62,8 @@ import android.view.AppTransitionAnimationSpec;
import android.view.WindowContentFrameStats;
import android.view.WindowManager;
import android.view.SurfaceControl;
import android.view.displayhash.DisplayHash;
import android.view.displayhash.VerifiedDisplayHash;
/**
* System private interface to the window manager.
@@ -757,23 +758,23 @@ interface IWindowManager
void holdLock(in IBinder token, in int durationMs);
/**
* Gets an array of support hashing algorithms that can be used to generate the hash of the
* screenshot. The String value of one algorithm should be used when requesting to generate
* the ScreenshotHash.
* Gets an array of support hash algorithms that can be used to generate a DisplayHash. The
* String value of one algorithm should be used when requesting to generate
* the DisplayHash.
*
* @return a String array of supported hashing algorithms.
* @return a String array of supported hash algorithms.
*/
String[] getSupportedScreenshotHashingAlgorithms();
String[] getSupportedDisplayHashAlgorithms();
/**
* Validate the ScreenshotHash was generated by the system. The ScreenshotHash passed in
* should be the token generated when calling {@link IWindowSession#generateScreenshotHash}
* Validate the DisplayHash was generated by the system. The DisplayHash passed in should be
* the object generated when calling {@link IWindowSession#generateDisplayHash}
*
* @param ScreenshotHash The token to verify that it was generated by the system.
* @return true if the token was generated by the system or false if the token cannot be
* verified.
* @param DisplayHash The hash to verify that it was generated by the system.
* @return a {@link VerifiedDisplayHash} if the hash was generated by the system or null
* if the token cannot be verified.
*/
boolean verifyScreenshotHash(in ScreenshotHash screenshotHash);
VerifiedDisplayHash verifyDisplayHash(in DisplayHash displayHash);
/**
* Registers a listener for a {@link android.app.WindowContext} to handle configuration changes

View File

@@ -22,7 +22,7 @@ import android.graphics.Point;
import android.graphics.Rect;
import android.graphics.Region;
import android.os.Bundle;
import android.service.screenshot.ScreenshotHash;
import android.os.RemoteCallback;
import android.util.MergedConfiguration;
import android.view.DisplayCutout;
import android.view.InputChannel;
@@ -337,14 +337,15 @@ interface IWindowSession {
void grantEmbeddedWindowFocus(IWindow window, in IBinder inputToken, boolean grantFocus);
/**
* Generates an ScreenshotHash that can be used to validate whether specific content was on
* Generates an DisplayHash that can be used to validate whether specific content was on
* screen.
*
* @param window The token for the window where the view to screenshot is shown.
* @param boundsInWindow The size and position of the ads view in the window
* @param hashAlgorithm The String for the hashing algorithm to use based on values returned
* from {@link IWindowManager#getSupportedHashingAlgorithms()}
* @param window The token for the window to generate the hash of.
* @param boundsInWindow The size and position in the window of where to generate the hash.
* @param hashAlgorithm The String for the hash algorithm to use based on values returned
* from {@link IWindowManager#getSupportedDisplayHashAlgorithms()}
* @param callback The callback invoked to get the results of generateDisplayHash
*/
ScreenshotHash generateScreenshotHash(IWindow window, in Rect boundsInWindow,
in String hashAlgorithm);
oneway void generateDisplayHash(IWindow window, in Rect boundsInWindow,
in String hashAlgorithm, in RemoteCallback callback);
}

View File

@@ -18,6 +18,12 @@ package android.view;
import static android.content.res.Resources.ID_NULL;
import static android.view.accessibility.AccessibilityEvent.CONTENT_CHANGE_TYPE_UNDEFINED;
import static android.view.displayhash.DisplayHashResultCallback.DISPLAY_HASH_ERROR_INVALID_BOUNDS;
import static android.view.displayhash.DisplayHashResultCallback.DISPLAY_HASH_ERROR_MISSING_WINDOW;
import static android.view.displayhash.DisplayHashResultCallback.DISPLAY_HASH_ERROR_NOT_VISIBLE_ON_SCREEN;
import static android.view.displayhash.DisplayHashResultCallback.DISPLAY_HASH_ERROR_UNKNOWN;
import static android.view.displayhash.DisplayHashResultCallback.EXTRA_DISPLAY_HASH;
import static android.view.displayhash.DisplayHashResultCallback.EXTRA_DISPLAY_HASH_ERROR_CODE;
import static com.android.internal.util.FrameworkStatsLog.TOUCH_GESTURE_CLASSIFIED__CLASSIFICATION__DEEP_PRESS;
import static com.android.internal.util.FrameworkStatsLog.TOUCH_GESTURE_CLASSIFIED__CLASSIFICATION__LONG_PRESS;
@@ -89,6 +95,7 @@ import android.os.IBinder;
import android.os.Message;
import android.os.Parcel;
import android.os.Parcelable;
import android.os.RemoteCallback;
import android.os.RemoteException;
import android.os.SystemClock;
import android.os.Trace;
@@ -135,6 +142,9 @@ import android.view.autofill.AutofillValue;
import android.view.contentcapture.ContentCaptureContext;
import android.view.contentcapture.ContentCaptureManager;
import android.view.contentcapture.ContentCaptureSession;
import android.view.displayhash.DisplayHash;
import android.view.displayhash.DisplayHashManager;
import android.view.displayhash.DisplayHashResultCallback;
import android.view.inputmethod.EditorInfo;
import android.view.inputmethod.InputConnection;
import android.view.inspector.InspectableProperty;
@@ -176,6 +186,7 @@ import java.util.Locale;
import java.util.Map;
import java.util.Queue;
import java.util.concurrent.CopyOnWriteArrayList;
import java.util.concurrent.Executor;
import java.util.concurrent.atomic.AtomicInteger;
import java.util.function.Predicate;
@@ -30707,4 +30718,71 @@ public class View implements Drawable.Callback, KeyEvent.Callback,
public void onTranslationComplete(@NonNull TranslationRequest request) {
// no-op
}
/**
* Called to generate a {@link DisplayHash} for this view.
*
* @param hashAlgorithm The hash algorithm to use when hashing the display. Must be one of
* the values returned from
* {@link DisplayHashManager#getSupportedHashAlgorithms()}
* @param bounds The bounds for the content within the View to generate the hash for. If
* bounds are null, the entire View's bounds will be used. If empty, it will
* invoke the callback
* {@link DisplayHashResultCallback#onDisplayHashError} with error
* {@link DisplayHashResultCallback#DISPLAY_HASH_ERROR_INVALID_BOUNDS}
* @param executor The executor that the callback should be invoked on.
* @param callback The callback to handle the results of generating the display hash
*/
@Nullable
public void generateDisplayHash(@NonNull String hashAlgorithm,
@Nullable Rect bounds, @NonNull Executor executor,
@NonNull DisplayHashResultCallback callback) {
IWindowSession session = getWindowSession();
if (session == null) {
callback.onDisplayHashError(DISPLAY_HASH_ERROR_MISSING_WINDOW);
return;
}
IWindow window = getWindow();
if (window == null) {
callback.onDisplayHashError(DISPLAY_HASH_ERROR_MISSING_WINDOW);
return;
}
Rect visibleBounds = new Rect();
getGlobalVisibleRect(visibleBounds);
if (bounds != null && bounds.isEmpty()) {
callback.onDisplayHashError(DISPLAY_HASH_ERROR_INVALID_BOUNDS);
return;
}
if (bounds != null) {
bounds.offset(visibleBounds.left, visibleBounds.top);
visibleBounds.intersectUnchecked(bounds);
}
if (visibleBounds.isEmpty()) {
callback.onDisplayHashError(DISPLAY_HASH_ERROR_NOT_VISIBLE_ON_SCREEN);
return;
}
RemoteCallback remoteCallback = new RemoteCallback(result ->
executor.execute(() -> {
DisplayHash displayHash = result.getParcelable(EXTRA_DISPLAY_HASH);
int errorCode = result.getInt(EXTRA_DISPLAY_HASH_ERROR_CODE,
DISPLAY_HASH_ERROR_UNKNOWN);
if (displayHash != null) {
callback.onDisplayHashResult(displayHash);
} else {
callback.onDisplayHashError(errorCode);
}
}));
try {
session.generateDisplayHash(window, visibleBounds, hashAlgorithm, remoteCallback);
} catch (RemoteException e) {
Log.e(VIEW_LOG_TAG, "Failed to call generateDisplayHash");
callback.onDisplayHashError(DISPLAY_HASH_ERROR_UNKNOWN);
}
}
}

View File

@@ -23,8 +23,8 @@ import android.graphics.Point;
import android.graphics.Rect;
import android.graphics.Region;
import android.os.IBinder;
import android.os.RemoteCallback;
import android.os.RemoteException;
import android.service.screenshot.ScreenshotHash;
import android.util.Log;
import android.util.MergedConfiguration;
import android.window.ClientWindowFrames;
@@ -487,8 +487,7 @@ public class WindowlessWindowManager implements IWindowSession {
}
@Override
public ScreenshotHash generateScreenshotHash(IWindow window, Rect boundsInWindow,
String hashAlgorithm) {
return null;
public void generateDisplayHash(IWindow window, Rect boundsInWindow, String hashAlgorithm,
RemoteCallback callback) {
}
}

View File

@@ -1,5 +1,5 @@
/*
* Copyright (C) 2020 The Android Open Source Project
* 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.
@@ -14,6 +14,6 @@
* limitations under the License.
*/
package android.service.screenshot;
package android.view.displayhash;
parcelable ScreenshotHash;
parcelable DisplayHash;

View File

@@ -0,0 +1,226 @@
/*
* 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.view.displayhash;
import android.annotation.NonNull;
import android.annotation.SystemApi;
import android.graphics.Rect;
import android.os.Parcel;
import android.os.Parcelable;
import com.android.internal.util.AnnotationValidations;
/**
* The DisplayHash used to validate information about what was present on screen.
*/
public final class DisplayHash implements Parcelable {
/**
* The timestamp when the hash was generated.
*/
private final long mTimeMillis;
/**
* The bounds of the requested area to generate the hash. This is in window space passed in
* by the client.
*/
@NonNull
private final Rect mBoundsInWindow;
/**
* The selected hash algorithm that generated the image hash.
*/
@NonNull
private final String mHashAlgorithm;
/**
* The image hash generated when creating the DisplayHash.
*/
@NonNull
private final byte[] mImageHash;
/**
* The hmac generated by the system and used to verify whether this token was generated by
* the system.
*/
@NonNull
private final byte[] mHmac;
/**
* Creates a new DisplayHash.
*
* @param timeMillis The timestamp when the hash was generated.
* @param boundsInWindow The bounds of the requested area to generate the hash. This is
* in window space passed in by the client.
* @param hashAlgorithm The selected hash algorithm that generated the image hash.
* @param imageHash The image hash generated when creating the DisplayHash.
* @param hmac The hmac generated by the system and used to verify whether this
* token was generated by
* the system. This should only be accessed by a system process.
* @hide
*/
@SystemApi
public DisplayHash(long timeMillis, @NonNull Rect boundsInWindow,
@NonNull String hashAlgorithm, @NonNull byte[] imageHash, @NonNull byte[] hmac) {
mTimeMillis = timeMillis;
mBoundsInWindow = boundsInWindow;
AnnotationValidations.validate(NonNull.class, null, mBoundsInWindow);
mHashAlgorithm = hashAlgorithm;
AnnotationValidations.validate(NonNull.class, null, mHashAlgorithm);
mImageHash = imageHash;
AnnotationValidations.validate(NonNull.class, null, mImageHash);
mHmac = hmac;
AnnotationValidations.validate(NonNull.class, null, mHmac);
}
/**
* The timestamp when the hash was generated.
*
* @hide
*/
@SystemApi
public long getTimeMillis() {
return mTimeMillis;
}
/**
* The bounds of the requested area to to generate the hash. This is in window space passed in
* by the client.
*
* @hide
*/
@SystemApi
@NonNull
public Rect getBoundsInWindow() {
return mBoundsInWindow;
}
/**
* The selected hash algorithm that generated the image hash.
*
* @hide
*/
@SystemApi
@NonNull
public String getHashAlgorithm() {
return mHashAlgorithm;
}
/**
* The image hash generated when creating the DisplayHash.
*
* @hide
*/
@SystemApi
@NonNull
public byte[] getImageHash() {
return mImageHash;
}
/**
* The hmac generated by the system and used to verify whether this token was generated by
* the system. This should only be accessed by a system process.
*
* @hide
*/
@SystemApi
@NonNull
public byte[] getHmac() {
return mHmac;
}
/** @hide **/
@Override
public String toString() {
return "DisplayHash { "
+ "timeMillis = " + mTimeMillis + ", "
+ "boundsInWindow = " + mBoundsInWindow + ", "
+ "hashAlgorithm = " + mHashAlgorithm + ", "
+ "imageHash = " + byteArrayToString(mImageHash) + ", "
+ "hmac = " + byteArrayToString(mHmac)
+ " }";
}
private String byteArrayToString(byte[] byteArray) {
if (byteArray == null) {
return "null";
}
int iMax = byteArray.length - 1;
if (iMax == -1) {
return "[]";
}
StringBuilder b = new StringBuilder();
b.append('[');
for (int i = 0; ; i++) {
String formatted = String.format("%02X", byteArray[i] & 0xFF);
b.append(formatted);
if (i == iMax) {
return b.append(']').toString();
}
b.append(", ");
}
}
/** @hide **/
@SystemApi
@Override
public void writeToParcel(@NonNull Parcel dest, int flags) {
dest.writeLong(mTimeMillis);
dest.writeTypedObject(mBoundsInWindow, flags);
dest.writeString(mHashAlgorithm);
dest.writeByteArray(mImageHash);
dest.writeByteArray(mHmac);
}
/** @hide **/
@SystemApi
@Override
public int describeContents() {
return 0;
}
private DisplayHash(@NonNull Parcel in) {
mTimeMillis = in.readLong();
Rect boundsInWindow = in.readTypedObject(Rect.CREATOR);
String hashAlgorithm = in.readString();
byte[] imageHash = in.createByteArray();
byte[] hmac = in.createByteArray();
mBoundsInWindow = boundsInWindow;
AnnotationValidations.validate(NonNull.class, null, mBoundsInWindow);
mHashAlgorithm = hashAlgorithm;
AnnotationValidations.validate(NonNull.class, null, mHashAlgorithm);
mImageHash = imageHash;
AnnotationValidations.validate(NonNull.class, null, mImageHash);
mHmac = hmac;
AnnotationValidations.validate(NonNull.class, null, mHmac);
}
@NonNull
public static final Parcelable.Creator<DisplayHash> CREATOR =
new Parcelable.Creator<DisplayHash>() {
@Override
public DisplayHash[] newArray(int size) {
return new DisplayHash[size];
}
@Override
public DisplayHash createFromParcel(@NonNull Parcel in) {
return new DisplayHash(in);
}
};
}

View File

@@ -0,0 +1,97 @@
/*
* 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.view.displayhash;
import static android.content.Context.DISPLAY_HASH_SERVICE;
import android.annotation.NonNull;
import android.annotation.Nullable;
import android.annotation.SystemService;
import android.os.RemoteException;
import android.util.ArraySet;
import android.util.Log;
import android.view.WindowManagerGlobal;
import com.android.internal.annotations.GuardedBy;
import java.util.Collections;
import java.util.Set;
/**
* Utility class for DisplayHash requests.
*/
@SystemService(DISPLAY_HASH_SERVICE)
public final class DisplayHashManager {
private static final String TAG = "DisplayHashManager";
private final Object mSupportedHashingAlgorithmLock = new Object();
@GuardedBy("mSupportedAlgorithmLock")
private static Set<String> sSupportedHashAlgorithms;
/**
* @hide
*/
public DisplayHashManager() {
}
/**
* Get a Set of DisplayHash algorithms that the device supports.
*
* @return a String Set of supported hashing algorithms. The String value of one
* algorithm should be used when requesting to generate the DisplayHash.
*/
@NonNull
public Set<String> getSupportedHashAlgorithms() {
synchronized (mSupportedHashingAlgorithmLock) {
if (sSupportedHashAlgorithms != null) {
return sSupportedHashAlgorithms;
}
try {
String[] supportedAlgorithms = WindowManagerGlobal.getWindowManagerService()
.getSupportedDisplayHashAlgorithms();
if (supportedAlgorithms == null) {
return Collections.emptySet();
}
sSupportedHashAlgorithms = new ArraySet<>(supportedAlgorithms);
return sSupportedHashAlgorithms;
} catch (RemoteException e) {
Log.e(TAG, "Failed to send request getSupportedHashingAlgorithms", e);
return Collections.emptySet();
}
}
}
/**
* Call to verify that the DisplayHash passed in was generated by the system.
*
* @param displayHash The hash to verify that it was generated by the system.
* @return a {@link VerifiedDisplayHash} if the hash was generated by the system or null
* if the hash cannot be verified.
*/
@Nullable
public VerifiedDisplayHash verifyDisplayHash(@NonNull DisplayHash displayHash) {
try {
return WindowManagerGlobal.getWindowManagerService().verifyDisplayHash(displayHash);
} catch (RemoteException e) {
Log.e(TAG, "Failed to send request verifyImpressionToken", e);
return null;
}
}
}

View File

@@ -0,0 +1,98 @@
/*
* 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.view.displayhash;
import android.annotation.IntDef;
import android.annotation.NonNull;
import android.graphics.Rect;
import android.view.View;
import java.lang.annotation.Retention;
import java.lang.annotation.RetentionPolicy;
import java.util.concurrent.Executor;
/**
* Use when calling {@link View#generateDisplayHash(String, Rect, Executor,
* DisplayHashResultCallback)}.
*
* The callback will only invoke either {@link #onDisplayHashResult} when the system successfully
* generated the {@link DisplayHash} or {@link #onDisplayHashError(int)} when it failed.
*/
public interface DisplayHashResultCallback {
/**
* @hide
*/
String EXTRA_DISPLAY_HASH = "DISPLAY_HASH";
/**
* @hide
*/
String EXTRA_DISPLAY_HASH_ERROR_CODE = "DISPLAY_HASH_ERROR_CODE";
/**
* An unknown error occurred.
*/
int DISPLAY_HASH_ERROR_UNKNOWN = -1;
/**
* The bounds used when requesting the hash hash were invalid or empty.
*/
int DISPLAY_HASH_ERROR_INVALID_BOUNDS = -2;
/**
* The window for the view that requested the hash is no longer around. This can happen if the
* window is getting torn down.
*/
int DISPLAY_HASH_ERROR_MISSING_WINDOW = -3;
/**
* The view that requested the hash is not visible on screen. This could either mean
* that the view bounds are offscreen, window bounds are offscreen, view is not visible, or
* window is not visible.
*/
int DISPLAY_HASH_ERROR_NOT_VISIBLE_ON_SCREEN = -4;
/** @hide */
@IntDef(prefix = {"DISPLAY_HASH_ERROR_"}, value = {
DISPLAY_HASH_ERROR_UNKNOWN,
DISPLAY_HASH_ERROR_INVALID_BOUNDS,
DISPLAY_HASH_ERROR_MISSING_WINDOW,
DISPLAY_HASH_ERROR_NOT_VISIBLE_ON_SCREEN
})
@Retention(RetentionPolicy.SOURCE)
@interface DisplayHashErrorCode {
}
/**
* Callback invoked when calling
* {@link android.view.View#generateDisplayHash(String, Rect, Executor,
* DisplayHashResultCallback)}
*
* @param displayHash The DisplayHash generated. If the hash cannot be generated,
* {@link #onDisplayHashError(int)} will be called instead
*/
void onDisplayHashResult(@NonNull DisplayHash displayHash);
/**
* Callback invoked when
* {@link android.view.View#generateDisplayHash(String, Rect, Executor,
* DisplayHashResultCallback)} results in an error and cannot generate a display hash.
*
* @param errorCode One of the values in {@link DisplayHashErrorCode}
*/
void onDisplayHashError(@DisplayHashErrorCode int errorCode);
}

View File

@@ -0,0 +1 @@
include /services/core/java/com/android/server/wm/OWNERS

View File

@@ -0,0 +1,19 @@
/*
* 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.view.displayhash;
parcelable VerifiedDisplayHash;

View File

@@ -0,0 +1,242 @@
/*
* 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.view.displayhash;
import android.annotation.NonNull;
import android.graphics.Rect;
import android.os.Parcelable;
import com.android.internal.util.DataClass;
/**
* The verified display hash used to validate information about what was present on screen.
*/
@DataClass(genToString = true, genAidl = true)
public final class VerifiedDisplayHash implements Parcelable {
/**
* The timestamp when the hash was generated.
*/
private final long mTimeMillis;
/**
* The bounds of the requested area to generate the hash. This is in window space passed in
* by the client.
*/
@NonNull
private final Rect mBoundsInWindow;
/**
* The selected hash algorithm that generated the image hash.
*/
@NonNull
private final String mHashAlgorithm;
/**
* The image hash generated when creating the DisplayHash.
*/
@NonNull
private final byte[] mImageHash;
private String imageHashToString() {
return byteArrayToString(mImageHash);
}
private String byteArrayToString(byte[] byteArray) {
if (byteArray == null) {
return "null";
}
int iMax = byteArray.length - 1;
if (iMax == -1) {
return "[]";
}
StringBuilder b = new StringBuilder();
b.append('[');
for (int i = 0; ; i++) {
String formatted = String.format("%02X", byteArray[i] & 0xFF);
b.append(formatted);
if (i == iMax) {
return b.append(']').toString();
}
b.append(", ");
}
}
// Code below generated by codegen v1.0.22.
//
// DO NOT MODIFY!
// CHECKSTYLE:OFF Generated code
//
// To regenerate run:
// $ codegen $ANDROID_BUILD_TOP/frameworks/base/core/java/android/view/displayhash/VerifiedDisplayHash.java
//
// To exclude the generated code from IntelliJ auto-formatting enable (one-time):
// Settings > Editor > Code Style > Formatter Control
//@formatter:off
/**
* Creates a new VerifiedDisplayHash.
*
* @param timeMillis
* The timestamp when the hash was generated.
* @param boundsInWindow
* The bounds of the requested area to generate the hash. This is in window space passed in
* by the client.
* @param hashAlgorithm
* The selected hash algorithm that generated the image hash.
* @param imageHash
* The image hash generated when creating the DisplayHash.
*/
@DataClass.Generated.Member
public VerifiedDisplayHash(
long timeMillis,
@NonNull Rect boundsInWindow,
@NonNull String hashAlgorithm,
@NonNull byte[] imageHash) {
this.mTimeMillis = timeMillis;
this.mBoundsInWindow = boundsInWindow;
com.android.internal.util.AnnotationValidations.validate(
NonNull.class, null, mBoundsInWindow);
this.mHashAlgorithm = hashAlgorithm;
com.android.internal.util.AnnotationValidations.validate(
NonNull.class, null, mHashAlgorithm);
this.mImageHash = imageHash;
com.android.internal.util.AnnotationValidations.validate(
NonNull.class, null, mImageHash);
// onConstructed(); // You can define this method to get a callback
}
/**
* The timestamp when the hash was generated.
*/
@DataClass.Generated.Member
public long getTimeMillis() {
return mTimeMillis;
}
/**
* The bounds of the requested area to generate the hash. This is in window space passed in
* by the client.
*/
@DataClass.Generated.Member
public @NonNull Rect getBoundsInWindow() {
return mBoundsInWindow;
}
/**
* The selected hash algorithm that generated the image hash.
*/
@DataClass.Generated.Member
public @NonNull String getHashAlgorithm() {
return mHashAlgorithm;
}
/**
* The image hash generated when creating the DisplayHash.
*/
@DataClass.Generated.Member
public @NonNull byte[] getImageHash() {
return mImageHash;
}
@Override
@DataClass.Generated.Member
public String toString() {
// You can override field toString logic by defining methods like:
// String fieldNameToString() { ... }
return "VerifiedDisplayHash { " +
"timeMillis = " + mTimeMillis + ", " +
"boundsInWindow = " + mBoundsInWindow + ", " +
"hashAlgorithm = " + mHashAlgorithm + ", " +
"imageHash = " + imageHashToString() +
" }";
}
@Override
@DataClass.Generated.Member
public void writeToParcel(@NonNull android.os.Parcel dest, int flags) {
// You can override field parcelling by defining methods like:
// void parcelFieldName(Parcel dest, int flags) { ... }
dest.writeLong(mTimeMillis);
dest.writeTypedObject(mBoundsInWindow, flags);
dest.writeString(mHashAlgorithm);
dest.writeByteArray(mImageHash);
}
@Override
@DataClass.Generated.Member
public int describeContents() { return 0; }
/** @hide */
@SuppressWarnings({"unchecked", "RedundantCast"})
@DataClass.Generated.Member
/* package-private */ VerifiedDisplayHash(@NonNull android.os.Parcel in) {
// You can override field unparcelling by defining methods like:
// static FieldType unparcelFieldName(Parcel in) { ... }
long timeMillis = in.readLong();
Rect boundsInWindow = (Rect) in.readTypedObject(Rect.CREATOR);
String hashAlgorithm = in.readString();
byte[] imageHash = in.createByteArray();
this.mTimeMillis = timeMillis;
this.mBoundsInWindow = boundsInWindow;
com.android.internal.util.AnnotationValidations.validate(
NonNull.class, null, mBoundsInWindow);
this.mHashAlgorithm = hashAlgorithm;
com.android.internal.util.AnnotationValidations.validate(
NonNull.class, null, mHashAlgorithm);
this.mImageHash = imageHash;
com.android.internal.util.AnnotationValidations.validate(
NonNull.class, null, mImageHash);
// onConstructed(); // You can define this method to get a callback
}
@DataClass.Generated.Member
public static final @NonNull Parcelable.Creator<VerifiedDisplayHash> CREATOR
= new Parcelable.Creator<VerifiedDisplayHash>() {
@Override
public VerifiedDisplayHash[] newArray(int size) {
return new VerifiedDisplayHash[size];
}
@Override
public VerifiedDisplayHash createFromParcel(@NonNull android.os.Parcel in) {
return new VerifiedDisplayHash(in);
}
};
@DataClass.Generated(
time = 1613168749684L,
codegenVersion = "1.0.22",
sourceFile = "frameworks/base/core/java/android/view/displayhash/VerifiedDisplayHash.java",
inputSignatures = "private final long mTimeMillis\nprivate final @android.annotation.NonNull android.graphics.Rect mBoundsInWindow\nprivate final @android.annotation.NonNull java.lang.String mHashAlgorithm\nprivate final @android.annotation.NonNull byte[] mImageHash\nprivate java.lang.String imageHashToString()\nprivate java.lang.String byteArrayToString(byte[])\nclass VerifiedDisplayHash extends java.lang.Object implements [android.os.Parcelable]\n@com.android.internal.util.DataClass(genToString=true, genAidl=true)")
@Deprecated
private void __metadata() {}
//@formatter:on
// End of generated code
}

View File

@@ -5498,11 +5498,11 @@
android:protectionLevel="signature" />
<!-- Must be required by a
{@link android.service.screenshot.ScreenshotHasherService}
{@link android.service.displayhash.DisplayHasherService}
to ensure that only the system can bind to it.
@hide This is not a third-party API (intended for OEMs and system apps).
-->
<permission android:name="android.permission.BIND_SCREENSHOT_HASHER_SERVICE"
<permission android:name="android.permission.BIND_DISPLAY_HASHER_SERVICE"
android:protectionLevel="signature" />
<!-- @hide @TestApi Allows an application to enable/disable toast rate limiting.

View File

@@ -16,9 +16,8 @@
package com.android.server.wm;
import static android.service.screenshot.ScreenshotHasherService.EXTRA_SCREENSHOT_HASH;
import static android.service.screenshot.ScreenshotHasherService.EXTRA_VERIFICATION_STATUS;
import static android.service.screenshot.ScreenshotHasherService.SERVICE_META_DATA_KEY_AVAILABLE_ALGORITHMS;
import static android.service.displayhash.DisplayHasherService.EXTRA_VERIFIED_DISPLAY_HASH;
import static android.service.displayhash.DisplayHasherService.SERVICE_META_DATA_KEY_AVAILABLE_ALGORITHMS;
import static com.android.server.wm.WindowManagerDebugConfig.TAG_WITH_CLASS_NAME;
import static com.android.server.wm.WindowManagerDebugConfig.TAG_WM;
@@ -46,11 +45,12 @@ import android.os.Message;
import android.os.RemoteCallback;
import android.os.RemoteException;
import android.os.UserHandle;
import android.service.screenshot.IScreenshotHasherService;
import android.service.screenshot.ScreenshotHash;
import android.service.screenshot.ScreenshotHasherService;
import android.service.displayhash.DisplayHasherService;
import android.service.displayhash.IDisplayHasherService;
import android.util.Slog;
import android.view.MagnificationSpec;
import android.view.displayhash.DisplayHash;
import android.view.displayhash.VerifiedDisplayHash;
import com.android.internal.annotations.GuardedBy;
@@ -61,29 +61,29 @@ import java.util.concurrent.TimeUnit;
import java.util.function.BiConsumer;
/**
* Handles requests into {@link android.service.screenshot.ScreenshotHasherService}
* Handles requests into {@link android.service.displayhash.DisplayHasherService}
*
* Do not hold the {@link WindowManagerService#mGlobalLock} when calling methods since they are
* blocking calls into another service.
*/
public class ScreenshotHashController {
private static final String TAG = TAG_WITH_CLASS_NAME ? "ScreenshotHashController" : TAG_WM;
public class DisplayHashController {
private static final String TAG = TAG_WITH_CLASS_NAME ? "DisplayHashController" : TAG_WM;
private static final boolean DEBUG = false;
private final Object mServiceConnectionLock = new Object();
@GuardedBy("mServiceConnectionLock")
private ScreenshotHasherServiceConnection mServiceConnection;
private DisplayHasherServiceConnection mServiceConnection;
private final Context mContext;
/**
* Lock used for the cached {@link #mHashingAlgorithms} array
* Lock used for the cached {@link #mHashAlgorithms} array
*/
private final Object mHashingAlgorithmsLock = new Object();
private final Object mHashAlgorithmsLock = new Object();
@GuardedBy("mHashingAlgorithmsLock")
private String[] mHashingAlgorithms;
private String[] mHashAlgorithms;
private final Handler mHandler;
@@ -94,24 +94,24 @@ public class ScreenshotHashController {
private final RectF mTmpRectF = new RectF();
private interface Command {
void run(IScreenshotHasherService service) throws RemoteException;
void run(IDisplayHasherService service) throws RemoteException;
}
ScreenshotHashController(Context context) {
DisplayHashController(Context context) {
mContext = context;
mHandler = new Handler(Looper.getMainLooper());
mSalt = UUID.randomUUID().toString().getBytes();
}
String[] getSupportedHashingAlgorithms() {
String[] getSupportedHashAlgorithms() {
// We have a separate lock for the hashing algorithm array since it doesn't need to make
// the request through the service connection. Instead, we have a lock to ensure we can
// properly cache the hashing algorithms array so we don't need to call into the
// ExtServices process for each request.
synchronized (mHashingAlgorithmsLock) {
synchronized (mHashAlgorithmsLock) {
// Already have cached values
if (mHashingAlgorithms != null) {
return mHashingAlgorithms;
if (mHashAlgorithms != null) {
return mHashAlgorithms;
}
final ServiceInfo serviceInfo = getServiceInfo();
@@ -128,55 +128,48 @@ public class ScreenshotHashController {
final int resourceId = serviceInfo.metaData.getInt(
SERVICE_META_DATA_KEY_AVAILABLE_ALGORITHMS);
mHashingAlgorithms = res.getStringArray(resourceId);
mHashAlgorithms = res.getStringArray(resourceId);
return mHashingAlgorithms;
return mHashAlgorithms;
}
}
boolean verifyScreenshotHash(ScreenshotHash screenshotHash) {
@Nullable
VerifiedDisplayHash verifyDisplayHash(DisplayHash displayHash) {
final SyncCommand syncCommand = new SyncCommand();
Bundle results = syncCommand.run((service, remoteCallback) -> {
try {
service.verifyScreenshotHash(mSalt, screenshotHash, remoteCallback);
service.verifyDisplayHash(mSalt, displayHash, remoteCallback);
} catch (RemoteException e) {
Slog.e(TAG, "Failed to invoke verifyScreenshotHash command");
Slog.e(TAG, "Failed to invoke verifyDisplayHash command");
}
});
return results.getBoolean(EXTRA_VERIFICATION_STATUS);
return results.getParcelable(EXTRA_VERIFIED_DISPLAY_HASH);
}
ScreenshotHash generateScreenshotHash(HardwareBuffer screenshot, Rect bounds,
String hashAlgorithm) {
final SyncCommand syncCommand = new SyncCommand();
Bundle results = syncCommand.run((service, remoteCallback) -> {
try {
service.generateScreenshotHash(mSalt, screenshot, bounds, hashAlgorithm,
remoteCallback);
} catch (RemoteException e) {
Slog.e(TAG, "Failed to invoke generateScreenshotHash command", e);
}
});
return results.getParcelable(EXTRA_SCREENSHOT_HASH);
void generateDisplayHash(HardwareBuffer buffer, Rect bounds,
String hashAlgorithm, RemoteCallback callback) {
connectAndRun(
service -> service.generateDisplayHash(mSalt, buffer, bounds, hashAlgorithm,
callback));
}
/**
* Calculate the bounds to take the screenshot when generating the ScreenshotHash. This
* takes into account window transform, magnification, and display bounds.
* Calculate the bounds to generate the hash for. This takes into account window transform,
* magnification, and display bounds.
*
* Call while holding {@link WindowManagerService#mGlobalLock}
*
* @param win Window that the ScreenshotHash is generated for.
* @param boundsInWindow The bounds passed in about where in the window to take the screenshot.
* @param outBounds The result of the calculated bounds
* @param win Window that the DisplayHash is generated for.
* @param boundsInWindow The bounds in the window where to generate the hash.
* @param outBounds The result of the calculated bounds
*/
void calculateScreenshotHashBoundsLocked(WindowState win, Rect boundsInWindow,
void calculateDisplayHashBoundsLocked(WindowState win, Rect boundsInWindow,
Rect outBounds) {
if (DEBUG) {
Slog.d(TAG,
"calculateScreenshotHashBoundsLocked: boundsInWindow=" + boundsInWindow);
"calculateDisplayHashBoundsLocked: boundsInWindow=" + boundsInWindow);
}
outBounds.set(boundsInWindow);
@@ -195,7 +188,7 @@ public class ScreenshotHashController {
if (DEBUG) {
Slog.d(TAG,
"calculateScreenshotHashBoundsLocked: boundsIntersectWindow=" + outBounds);
"calculateDisplayHashBoundsLocked: boundsIntersectWindow=" + outBounds);
}
if (outBounds.isEmpty()) {
@@ -210,7 +203,7 @@ public class ScreenshotHashController {
outBounds.set((int) mTmpRectF.left, (int) mTmpRectF.top, (int) mTmpRectF.right,
(int) mTmpRectF.bottom);
if (DEBUG) {
Slog.d(TAG, "calculateScreenshotHashBoundsLocked: boundsInDisplay=" + outBounds);
Slog.d(TAG, "calculateDisplayHashBoundsLocked: boundsInDisplay=" + outBounds);
}
// Apply the magnification spec values to the bounds since the content could be magnified
@@ -221,7 +214,7 @@ public class ScreenshotHashController {
}
if (DEBUG) {
Slog.d(TAG, "calculateScreenshotHashBoundsLocked: boundsWithMagnification="
Slog.d(TAG, "calculateDisplayHashBoundsLocked: boundsWithMagnification="
+ outBounds);
}
@@ -229,12 +222,12 @@ public class ScreenshotHashController {
return;
}
// Intersect with the display bounds since it shouldn't take a screenshot of content
// outside the display since it's not visible to the user.
// Intersect with the display bounds since content outside the display are not visible to
// the user.
final Rect displayBounds = displayContent.getBounds();
outBounds.intersectUnchecked(displayBounds);
if (DEBUG) {
Slog.d(TAG, "calculateScreenshotHashBoundsLocked: finalBounds=" + outBounds);
Slog.d(TAG, "calculateDisplayHashBoundsLocked: finalBounds=" + outBounds);
}
}
@@ -248,7 +241,7 @@ public class ScreenshotHashController {
if (DEBUG) Slog.v(TAG, "creating connection");
// Create the connection
mServiceConnection = new ScreenshotHasherServiceConnection();
mServiceConnection = new DisplayHasherServiceConnection();
final ComponentName component = getServiceComponentName();
if (DEBUG) Slog.v(TAG, "binding to: " + component);
@@ -279,7 +272,7 @@ public class ScreenshotHashController {
return null;
}
final Intent intent = new Intent(ScreenshotHasherService.SERVICE_INTERFACE);
final Intent intent = new Intent(DisplayHasherService.SERVICE_INTERFACE);
intent.setPackage(packageName);
final ResolveInfo resolveInfo = mContext.getPackageManager().resolveService(intent,
PackageManager.GET_SERVICES | PackageManager.GET_META_DATA);
@@ -296,10 +289,10 @@ public class ScreenshotHashController {
if (serviceInfo == null) return null;
final ComponentName name = new ComponentName(serviceInfo.packageName, serviceInfo.name);
if (!Manifest.permission.BIND_SCREENSHOT_HASHER_SERVICE
if (!Manifest.permission.BIND_DISPLAY_HASHER_SERVICE
.equals(serviceInfo.permission)) {
Slog.w(TAG, name.flattenToShortString() + " requires permission "
+ Manifest.permission.BIND_SCREENSHOT_HASHER_SERVICE);
+ Manifest.permission.BIND_DISPLAY_HASHER_SERVICE);
return null;
}
@@ -312,7 +305,7 @@ public class ScreenshotHashController {
private Bundle mResult;
private final CountDownLatch mCountDownLatch = new CountDownLatch(1);
public Bundle run(BiConsumer<IScreenshotHasherService, RemoteCallback> func) {
public Bundle run(BiConsumer<IDisplayHasherService, RemoteCallback> func) {
connectAndRun(service -> {
RemoteCallback callback = new RemoteCallback(result -> {
mResult = result;
@@ -331,9 +324,9 @@ public class ScreenshotHashController {
}
}
private class ScreenshotHasherServiceConnection implements ServiceConnection {
private class DisplayHasherServiceConnection implements ServiceConnection {
@GuardedBy("mServiceConnectionLock")
private IScreenshotHasherService mRemoteService;
private IDisplayHasherService mRemoteService;
@GuardedBy("mServiceConnectionLock")
private ArrayList<Command> mQueuedCommands;
@@ -342,7 +335,7 @@ public class ScreenshotHashController {
public void onServiceConnected(ComponentName name, IBinder service) {
if (DEBUG) Slog.v(TAG, "onServiceConnected(): " + name);
synchronized (mServiceConnectionLock) {
mRemoteService = IScreenshotHasherService.Stub.asInterface(service);
mRemoteService = IDisplayHasherService.Stub.asInterface(service);
if (mQueuedCommands != null) {
final int size = mQueuedCommands.size();
if (DEBUG) Slog.d(TAG, "running " + size + " queued commands");

View File

@@ -54,10 +54,10 @@ import android.os.Bundle;
import android.os.IBinder;
import android.os.Parcel;
import android.os.Process;
import android.os.RemoteCallback;
import android.os.RemoteException;
import android.os.Trace;
import android.os.UserHandle;
import android.service.screenshot.ScreenshotHash;
import android.text.TextUtils;
import android.util.ArraySet;
import android.util.MergedConfiguration;
@@ -850,11 +850,11 @@ class Session extends IWindowSession.Stub implements IBinder.DeathRecipient {
}
@Override
public ScreenshotHash generateScreenshotHash(IWindow window, Rect boundsInWindow,
String hashAlgorithm) {
public void generateDisplayHash(IWindow window, Rect boundsInWindow, String hashAlgorithm,
RemoteCallback callback) {
final long origId = Binder.clearCallingIdentity();
try {
return mService.generateScreenshotHash(this, window, boundsInWindow, hashAlgorithm);
mService.generateDisplayHash(this, window, boundsInWindow, hashAlgorithm, callback);
} finally {
Binder.restoreCallingIdentity(origId);
}

View File

@@ -83,6 +83,10 @@ import static android.view.WindowManagerGlobal.ADD_OKAY;
import static android.view.WindowManagerGlobal.RELAYOUT_RES_BLAST_SYNC;
import static android.view.WindowManagerGlobal.RELAYOUT_RES_SURFACE_CHANGED;
import static android.view.WindowManagerPolicyConstants.NAV_BAR_INVALID;
import static android.view.displayhash.DisplayHashResultCallback.DISPLAY_HASH_ERROR_MISSING_WINDOW;
import static android.view.displayhash.DisplayHashResultCallback.DISPLAY_HASH_ERROR_NOT_VISIBLE_ON_SCREEN;
import static android.view.displayhash.DisplayHashResultCallback.DISPLAY_HASH_ERROR_UNKNOWN;
import static android.view.displayhash.DisplayHashResultCallback.EXTRA_DISPLAY_HASH_ERROR_CODE;
import static com.android.internal.protolog.ProtoLogGroup.WM_DEBUG_ADD_REMOVE;
import static com.android.internal.protolog.ProtoLogGroup.WM_DEBUG_BOOT;
@@ -187,6 +191,7 @@ import android.os.PowerManager;
import android.os.PowerManager.ServiceType;
import android.os.PowerManagerInternal;
import android.os.PowerSaveState;
import android.os.RemoteCallback;
import android.os.RemoteException;
import android.os.ResultReceiver;
import android.os.ServiceManager;
@@ -199,7 +204,6 @@ import android.os.Trace;
import android.os.UserHandle;
import android.os.WorkSource;
import android.provider.Settings;
import android.service.screenshot.ScreenshotHash;
import android.service.vr.IVrManager;
import android.service.vr.IVrStateCallbacks;
import android.sysprop.SurfaceFlingerProperties;
@@ -261,6 +265,8 @@ import android.view.WindowManager.LayoutParams;
import android.view.WindowManager.RemoveContentMode;
import android.view.WindowManagerGlobal;
import android.view.WindowManagerPolicyConstants.PointerEventListener;
import android.view.displayhash.DisplayHash;
import android.view.displayhash.VerifiedDisplayHash;
import android.window.ClientWindowFrames;
import android.window.TaskSnapshot;
@@ -768,7 +774,8 @@ public class WindowManagerService extends IWindowManager.Stub
final EmbeddedWindowController mEmbeddedWindowController;
final AnrController mAnrController;
private final ScreenshotHashController mScreenshotHashController;
private final DisplayHashController mDisplayHashController;
@VisibleForTesting
final WindowContextListenerController mWindowContextListenerController =
new WindowContextListenerController();
@@ -1419,7 +1426,7 @@ public class WindowManagerService extends IWindowManager.Stub
mDisplayAreaPolicyProvider = DisplayAreaPolicy.Provider.fromResources(
mContext.getResources());
mScreenshotHashController = new ScreenshotHashController(mContext);
mDisplayHashController = new DisplayHashController(mContext);
setGlobalShadowSettings();
mAnrController = new AnrController(this);
mStartingSurfaceController = new StartingSurfaceController(this);
@@ -8599,39 +8606,42 @@ public class WindowManagerService extends IWindowManager.Stub
}
@Override
public String[] getSupportedScreenshotHashingAlgorithms() {
return mScreenshotHashController.getSupportedHashingAlgorithms();
public String[] getSupportedDisplayHashAlgorithms() {
return mDisplayHashController.getSupportedHashAlgorithms();
}
@Override
public boolean verifyScreenshotHash(ScreenshotHash screenshotHash) {
return mScreenshotHashController.verifyScreenshotHash(screenshotHash);
public VerifiedDisplayHash verifyDisplayHash(DisplayHash displayHash) {
return mDisplayHashController.verifyDisplayHash(displayHash);
}
ScreenshotHash generateScreenshotHash(Session session, IWindow window,
Rect boundsInWindow, String hashAlgorithm) {
void generateDisplayHash(Session session, IWindow window, Rect boundsInWindow,
String hashAlgorithm, RemoteCallback callback) {
final SurfaceControl displaySurfaceControl;
final Rect boundsInDisplay = new Rect(boundsInWindow);
synchronized (mGlobalLock) {
final WindowState win = windowForClientLocked(session, window, false);
if (win == null) {
Slog.w(TAG, "Failed to generate ScreenshotHash. Invalid window");
return null;
Slog.w(TAG, "Failed to generate DisplayHash. Invalid window");
sendDisplayHashError(callback, DISPLAY_HASH_ERROR_MISSING_WINDOW);
return;
}
DisplayContent displayContent = win.getDisplayContent();
if (displayContent == null) {
Slog.w(TAG, "Failed to generate ScreenshotHash. Window is not on a display");
return null;
Slog.w(TAG, "Failed to generate DisplayHash. Window is not on a display");
sendDisplayHashError(callback, DISPLAY_HASH_ERROR_NOT_VISIBLE_ON_SCREEN);
return;
}
displaySurfaceControl = displayContent.getSurfaceControl();
mScreenshotHashController.calculateScreenshotHashBoundsLocked(win,
boundsInWindow, boundsInDisplay);
mDisplayHashController.calculateDisplayHashBoundsLocked(win, boundsInWindow,
boundsInDisplay);
if (boundsInDisplay.isEmpty()) {
Slog.w(TAG, "Failed to generate ScreenshotHash. Bounds are not on screen");
return null;
Slog.w(TAG, "Failed to generate DisplayHash. Bounds are not on screen");
sendDisplayHashError(callback, DISPLAY_HASH_ERROR_NOT_VISIBLE_ON_SCREEN);
return;
}
}
@@ -8650,11 +8660,18 @@ public class WindowManagerService extends IWindowManager.Stub
SurfaceControl.captureLayers(args);
if (screenshotHardwareBuffer == null
|| screenshotHardwareBuffer.getHardwareBuffer() == null) {
Slog.w(TAG, "Failed to generate ScreenshotHash. Failed to take screenshot");
return null;
Slog.w(TAG, "Failed to generate DisplayHash. Couldn't capture content");
sendDisplayHashError(callback, DISPLAY_HASH_ERROR_UNKNOWN);
return;
}
return mScreenshotHashController.generateScreenshotHash(
screenshotHardwareBuffer.getHardwareBuffer(), boundsInWindow, hashAlgorithm);
mDisplayHashController.generateDisplayHash(screenshotHardwareBuffer.getHardwareBuffer(),
boundsInWindow, hashAlgorithm, callback);
}
private void sendDisplayHashError(RemoteCallback callback, int errorCode) {
Bundle bundle = new Bundle();
bundle.putInt(EXTRA_DISPLAY_HASH_ERROR_CODE, errorCode);
callback.sendResult(bundle);
}
}