Merge changes from topic "susharon-wallpapereffectsgeneration"

* changes:
  Server implementation of WallpaperEffectsService API.
  Data class and interface for wallpaper effects generation api.
This commit is contained in:
Sharon Su
2022-02-02 07:42:16 +00:00
committed by Android (Google) Code Review
24 changed files with 2237 additions and 1 deletions

View File

@@ -76,6 +76,7 @@ package android {
field public static final String BIND_TRANSLATION_SERVICE = "android.permission.BIND_TRANSLATION_SERVICE";
field public static final String BIND_TRUST_AGENT = "android.permission.BIND_TRUST_AGENT";
field public static final String BIND_TV_REMOTE_SERVICE = "android.permission.BIND_TV_REMOTE_SERVICE";
field public static final String BIND_WALLPAPER_EFFECTS_GENERATION_SERVICE = "android.permission.BIND_WALLPAPER_EFFECTS_GENERATION_SERVICE";
field public static final String BLUETOOTH_MAP = "android.permission.BLUETOOTH_MAP";
field public static final String BRICK = "android.permission.BRICK";
field public static final String BRIGHTNESS_SLIDER_USAGE = "android.permission.BRIGHTNESS_SLIDER_USAGE";
@@ -191,6 +192,7 @@ package android {
field public static final String MANAGE_USB = "android.permission.MANAGE_USB";
field public static final String MANAGE_USERS = "android.permission.MANAGE_USERS";
field public static final String MANAGE_USER_OEM_UNLOCK_STATE = "android.permission.MANAGE_USER_OEM_UNLOCK_STATE";
field public static final String MANAGE_WALLPAPER_EFFECTS_GENERATION = "android.permission.MANAGE_WALLPAPER_EFFECTS_GENERATION";
field public static final String MANAGE_WEAK_ESCROW_TOKEN = "android.permission.MANAGE_WEAK_ESCROW_TOKEN";
field public static final String MANAGE_WIFI_AUTO_JOIN = "android.permission.MANAGE_WIFI_AUTO_JOIN";
field public static final String MANAGE_WIFI_COUNTRY_CODE = "android.permission.MANAGE_WIFI_COUNTRY_CODE";
@@ -2554,6 +2556,107 @@ package android.app.usage {
}
package android.app.wallpapereffectsgeneration {
public final class CameraAttributes implements android.os.Parcelable {
method public int describeContents();
method @NonNull public float[] getAnchorPointInOutputUvSpace();
method @NonNull public float[] getAnchorPointInWorldSpace();
method public float getCameraOrbitPitchDegrees();
method public float getCameraOrbitYawDegrees();
method public float getDollyDistanceInWorldSpace();
method public float getFrustumFarInWorldSpace();
method public float getFrustumNearInWorldSpace();
method public float getVerticalFovDegrees();
method public void writeToParcel(@NonNull android.os.Parcel, int);
field @NonNull public static final android.os.Parcelable.Creator<android.app.wallpapereffectsgeneration.CameraAttributes> CREATOR;
}
public static final class CameraAttributes.Builder {
ctor public CameraAttributes.Builder(@NonNull float[], @NonNull float[]);
method @NonNull public android.app.wallpapereffectsgeneration.CameraAttributes build();
method @NonNull public android.app.wallpapereffectsgeneration.CameraAttributes.Builder setCameraOrbitPitchDegrees(@FloatRange(from=-90.0F, to=90.0f) float);
method @NonNull public android.app.wallpapereffectsgeneration.CameraAttributes.Builder setCameraOrbitYawDegrees(@FloatRange(from=-180.0F, to=180.0f) float);
method @NonNull public android.app.wallpapereffectsgeneration.CameraAttributes.Builder setDollyDistanceInWorldSpace(float);
method @NonNull public android.app.wallpapereffectsgeneration.CameraAttributes.Builder setFrustumFarInWorldSpace(@FloatRange(from=0.0f) float);
method @NonNull public android.app.wallpapereffectsgeneration.CameraAttributes.Builder setFrustumNearInWorldSpace(@FloatRange(from=0.0f) float);
method @NonNull public android.app.wallpapereffectsgeneration.CameraAttributes.Builder setVerticalFovDegrees(@FloatRange(from=0.0f, to=180.0f, fromInclusive=false) float);
}
public final class CinematicEffectRequest implements android.os.Parcelable {
ctor public CinematicEffectRequest(@NonNull String, @NonNull android.graphics.Bitmap);
method public int describeContents();
method @NonNull public android.graphics.Bitmap getBitmap();
method @NonNull public String getTaskId();
method public void writeToParcel(@NonNull android.os.Parcel, int);
field @NonNull public static final android.os.Parcelable.Creator<android.app.wallpapereffectsgeneration.CinematicEffectRequest> CREATOR;
}
public final class CinematicEffectResponse implements android.os.Parcelable {
method public int describeContents();
method @Nullable public android.app.wallpapereffectsgeneration.CameraAttributes getEndKeyFrame();
method public int getImageContentType();
method @Nullable public android.app.wallpapereffectsgeneration.CameraAttributes getStartKeyFrame();
method public int getStatusCode();
method @NonNull public String getTaskId();
method @NonNull public java.util.List<android.app.wallpapereffectsgeneration.TexturedMesh> getTexturedMeshes();
method public void writeToParcel(@NonNull android.os.Parcel, int);
field public static final int CINEMATIC_EFFECT_STATUS_ERROR = 2; // 0x2
field public static final int CINEMATIC_EFFECT_STATUS_NOT_READY = 3; // 0x3
field public static final int CINEMATIC_EFFECT_STATUS_OK = 1; // 0x1
field public static final int CINEMATIC_EFFECT_STATUS_PENDING = 4; // 0x4
field public static final int CINEMATIC_EFFECT_STATUS_TOO_MANY_REQUESTS = 5; // 0x5
field public static final int CINEMATIC_EFFECT_STATUS_UNKNOWN = 0; // 0x0
field @NonNull public static final android.os.Parcelable.Creator<android.app.wallpapereffectsgeneration.CinematicEffectResponse> CREATOR;
field public static final int IMAGE_CONTENT_TYPE_LANDSCAPE = 2; // 0x2
field public static final int IMAGE_CONTENT_TYPE_OTHER = 3; // 0x3
field public static final int IMAGE_CONTENT_TYPE_PEOPLE_PORTRAIT = 1; // 0x1
field public static final int IMAGE_CONTENT_TYPE_UNKNOWN = 0; // 0x0
}
public static final class CinematicEffectResponse.Builder {
ctor public CinematicEffectResponse.Builder(int, @NonNull String);
method @NonNull public android.app.wallpapereffectsgeneration.CinematicEffectResponse build();
method @NonNull public android.app.wallpapereffectsgeneration.CinematicEffectResponse.Builder setEndKeyFrame(@Nullable android.app.wallpapereffectsgeneration.CameraAttributes);
method @NonNull public android.app.wallpapereffectsgeneration.CinematicEffectResponse.Builder setImageContentType(int);
method @NonNull public android.app.wallpapereffectsgeneration.CinematicEffectResponse.Builder setStartKeyFrame(@Nullable android.app.wallpapereffectsgeneration.CameraAttributes);
method @NonNull public android.app.wallpapereffectsgeneration.CinematicEffectResponse.Builder setTexturedMeshes(@NonNull java.util.List<android.app.wallpapereffectsgeneration.TexturedMesh>);
}
public final class TexturedMesh implements android.os.Parcelable {
method public int describeContents();
method @NonNull public android.graphics.Bitmap getBitmap();
method @NonNull public int[] getIndices();
method @NonNull public int getIndicesLayoutType();
method @NonNull public float[] getVertices();
method @NonNull public int getVerticesLayoutType();
method public void writeToParcel(@NonNull android.os.Parcel, int);
field @NonNull public static final android.os.Parcelable.Creator<android.app.wallpapereffectsgeneration.TexturedMesh> CREATOR;
field public static final int INDICES_LAYOUT_TRIANGLES = 1; // 0x1
field public static final int INDICES_LAYOUT_UNDEFINED = 0; // 0x0
field public static final int VERTICES_LAYOUT_POSITION3_UV2 = 1; // 0x1
field public static final int VERTICES_LAYOUT_UNDEFINED = 0; // 0x0
}
public static final class TexturedMesh.Builder {
ctor public TexturedMesh.Builder(@NonNull android.graphics.Bitmap);
method @NonNull public android.app.wallpapereffectsgeneration.TexturedMesh build();
method @NonNull public android.app.wallpapereffectsgeneration.TexturedMesh.Builder setIndices(@NonNull int[]);
method @NonNull public android.app.wallpapereffectsgeneration.TexturedMesh.Builder setIndicesLayoutType(int);
method @NonNull public android.app.wallpapereffectsgeneration.TexturedMesh.Builder setVertices(@NonNull float[]);
method @NonNull public android.app.wallpapereffectsgeneration.TexturedMesh.Builder setVerticesLayoutType(int);
}
public final class WallpaperEffectsGenerationManager {
method @RequiresPermission(android.Manifest.permission.MANAGE_WALLPAPER_EFFECTS_GENERATION) public void generateCinematicEffect(@NonNull android.app.wallpapereffectsgeneration.CinematicEffectRequest, @NonNull java.util.concurrent.Executor, @NonNull android.app.wallpapereffectsgeneration.WallpaperEffectsGenerationManager.CinematicEffectListener);
}
public static interface WallpaperEffectsGenerationManager.CinematicEffectListener {
method public void onCinematicEffectGenerated(@NonNull android.app.wallpapereffectsgeneration.CinematicEffectResponse);
}
}
package android.apphibernation {
public class AppHibernationManager {
@@ -2742,6 +2845,7 @@ package android.content {
field public static final String UI_TRANSLATION_SERVICE = "ui_translation";
field public static final String UWB_SERVICE = "uwb";
field public static final String VR_SERVICE = "vrmanager";
field public static final String WALLPAPER_EFFECTS_GENERATION_SERVICE = "wallpaper_effects_generation";
field public static final String WIFI_NL80211_SERVICE = "wifinl80211";
field @Deprecated public static final String WIFI_RTT_SERVICE = "rttmanager";
field public static final String WIFI_SCANNING_SERVICE = "wifiscanner";
@@ -11715,6 +11819,17 @@ package android.service.wallpaper {
}
package android.service.wallpapereffectsgeneration {
public abstract class WallpaperEffectsGenerationService extends android.app.Service {
ctor public WallpaperEffectsGenerationService();
method @NonNull public final android.os.IBinder onBind(@NonNull android.content.Intent);
method public abstract void onGenerateCinematicEffect(@NonNull android.app.wallpapereffectsgeneration.CinematicEffectRequest);
method public final void returnCinematicEffectResponse(@NonNull android.app.wallpapereffectsgeneration.CinematicEffectResponse);
}
}
package android.service.watchdog {
public abstract class ExplicitHealthCheckService extends android.app.Service {

View File

@@ -51,6 +51,8 @@ import android.app.usage.IUsageStatsManager;
import android.app.usage.NetworkStatsManager;
import android.app.usage.StorageStatsManager;
import android.app.usage.UsageStatsManager;
import android.app.wallpapereffectsgeneration.IWallpaperEffectsGenerationManager;
import android.app.wallpapereffectsgeneration.WallpaperEffectsGenerationManager;
import android.apphibernation.AppHibernationManager;
import android.appwidget.AppWidgetManager;
import android.bluetooth.BluetoothFrameworkInitializer;
@@ -1297,6 +1299,20 @@ public final class SystemServiceRegistry {
}
});
registerService(Context.WALLPAPER_EFFECTS_GENERATION_SERVICE,
WallpaperEffectsGenerationManager.class,
new CachedServiceFetcher<WallpaperEffectsGenerationManager>() {
@Override
public WallpaperEffectsGenerationManager createService(ContextImpl ctx)
throws ServiceNotFoundException {
IBinder b = ServiceManager.getService(
Context.WALLPAPER_EFFECTS_GENERATION_SERVICE);
return b == null ? null :
new WallpaperEffectsGenerationManager(
IWallpaperEffectsGenerationManager.Stub.asInterface(b));
}
});
registerService(Context.VR_SERVICE, VrManager.class, new CachedServiceFetcher<VrManager>() {
@Override
public VrManager createService(ContextImpl ctx) throws ServiceNotFoundException {

View File

@@ -0,0 +1,301 @@
/*
* Copyright (C) 2022 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.app.wallpapereffectsgeneration;
import android.annotation.FloatRange;
import android.annotation.NonNull;
import android.annotation.SystemApi;
import android.os.Parcel;
import android.os.Parcelable;
/**
* Representing the position and other parameters of camera of a single frame.
*
* @hide
*/
@SystemApi
public final class CameraAttributes implements Parcelable {
/**
* The location of the anchor within the 3D scene.
* Expecting 3 floats representing the x, y, z coordinates
* of the anchor point.
*/
@NonNull
private float[] mAnchorPointInWorldSpace;
/**
* Where the anchor point should project to in the output image.
* Expecting 2 floats representing the u,v coordinates of the
* anchor point.
*/
@NonNull
private float[] mAnchorPointInOutputUvSpace;
/**
* Specifies the amount of yaw orbit rotation the camera should perform
* around the anchor point in world space.
*/
private float mCameraOrbitYawDegrees;
/**
* Specifies the amount of pitch orbit rotation the camera should perform
* around the anchor point in world space.
*/
private float mCameraOrbitPitchDegrees;
/**
* Specifies by how much the camera should be placed towards the anchor
* point in world space, which is also called dolly distance.
*/
private float mDollyDistanceInWorldSpace;
/**
* Specifies the vertical fov degrees of the virtual image.
*/
private float mVerticalFovDegrees;
/**
* The frustum of near plane.
*/
private float mFrustumNearInWorldSpace;
/**
* The frustum of far plane.
*/
private float mFrustumFarInWorldSpace;
private CameraAttributes(Parcel in) {
this.mCameraOrbitYawDegrees = in.readFloat();
this.mCameraOrbitPitchDegrees = in.readFloat();
this.mDollyDistanceInWorldSpace = in.readFloat();
this.mVerticalFovDegrees = in.readFloat();
this.mFrustumNearInWorldSpace = in.readFloat();
this.mFrustumFarInWorldSpace = in.readFloat();
this.mAnchorPointInWorldSpace = in.createFloatArray();
this.mAnchorPointInOutputUvSpace = in.createFloatArray();
}
private CameraAttributes(float[] anchorPointInWorldSpace, float[] anchorPointInOutputUvSpace,
float cameraOrbitYawDegrees, float cameraOrbitPitchDegrees,
float dollyDistanceInWorldSpace,
float verticalFovDegrees, float frustumNearInWorldSpace, float frustumFarInWorldSpace) {
mAnchorPointInWorldSpace = anchorPointInWorldSpace;
mAnchorPointInOutputUvSpace = anchorPointInOutputUvSpace;
mCameraOrbitYawDegrees = cameraOrbitYawDegrees;
mCameraOrbitPitchDegrees = cameraOrbitPitchDegrees;
mDollyDistanceInWorldSpace = dollyDistanceInWorldSpace;
mVerticalFovDegrees = verticalFovDegrees;
mFrustumNearInWorldSpace = frustumNearInWorldSpace;
mFrustumFarInWorldSpace = frustumFarInWorldSpace;
}
/**
* Get the location of the anchor within the 3D scene. The response float array contains
* 3 floats representing the x, y, z coordinates
*/
@NonNull
public float[] getAnchorPointInWorldSpace() {
return mAnchorPointInWorldSpace;
}
/**
* Get where the anchor point should project to in the output image. The response float
* array contains 2 floats representing the u,v coordinates of the anchor point.
*/
@NonNull
public float[] getAnchorPointInOutputUvSpace() {
return mAnchorPointInOutputUvSpace;
}
/**
* Get the camera yaw orbit rotation.
*/
public float getCameraOrbitYawDegrees() {
return mCameraOrbitYawDegrees;
}
/**
* Get the camera pitch orbit rotation.
*/
public float getCameraOrbitPitchDegrees() {
return mCameraOrbitPitchDegrees;
}
/**
* Get how many units the camera should be placed towards the anchor point in world space.
*/
public float getDollyDistanceInWorldSpace() {
return mDollyDistanceInWorldSpace;
}
/**
* Get the camera vertical fov degrees.
*/
public float getVerticalFovDegrees() {
return mVerticalFovDegrees;
}
/**
* Get the frustum in near plane.
*/
public float getFrustumNearInWorldSpace() {
return mFrustumNearInWorldSpace;
}
/**
* Get the frustum in far plane.
*/
public float getFrustumFarInWorldSpace() {
return mFrustumFarInWorldSpace;
}
@NonNull
public static final Creator<CameraAttributes> CREATOR = new Creator<CameraAttributes>() {
@Override
public CameraAttributes createFromParcel(Parcel in) {
return new CameraAttributes(in);
}
@Override
public CameraAttributes[] newArray(int size) {
return new CameraAttributes[size];
}
};
@Override
public void writeToParcel(@NonNull Parcel out, int flags) {
out.writeFloat(mCameraOrbitYawDegrees);
out.writeFloat(mCameraOrbitPitchDegrees);
out.writeFloat(mDollyDistanceInWorldSpace);
out.writeFloat(mVerticalFovDegrees);
out.writeFloat(mFrustumNearInWorldSpace);
out.writeFloat(mFrustumFarInWorldSpace);
out.writeFloatArray(mAnchorPointInWorldSpace);
out.writeFloatArray(mAnchorPointInOutputUvSpace);
}
@Override
public int describeContents() {
return 0;
}
/**
* Builder for {@link CameraAttributes}.
*
* @hide
*/
@SystemApi
public static final class Builder {
@NonNull
private float[] mAnchorPointInWorldSpace;
@NonNull
private float[] mAnchorPointInOutputUvSpace;
private float mCameraOrbitYawDegrees;
private float mCameraOrbitPitchDegrees;
private float mDollyDistanceInWorldSpace;
private float mVerticalFovDegrees;
private float mFrustumNearInWorldSpace;
private float mFrustumFarInWorldSpace;
/**
* Constructor with anchor point in world space and anchor point in output image
* space.
*
* @param anchorPointInWorldSpace the location of the anchor within the 3D scene. The
* float array contains 3 floats representing the x, y, z coordinates.
* @param anchorPointInOutputUvSpace where the anchor point should project to in the
* output image. The float array contains 2 floats representing the u,v coordinates
* of the anchor point.
*
* @hide
*/
@SystemApi
public Builder(@NonNull float[] anchorPointInWorldSpace,
@NonNull float[] anchorPointInOutputUvSpace) {
mAnchorPointInWorldSpace = anchorPointInWorldSpace;
mAnchorPointInOutputUvSpace = anchorPointInOutputUvSpace;
}
/**
* Sets the camera orbit yaw rotation.
*/
@NonNull
public Builder setCameraOrbitYawDegrees(
@FloatRange(from = -180.0f, to = 180.0f) float cameraOrbitYawDegrees) {
mCameraOrbitYawDegrees = cameraOrbitYawDegrees;
return this;
}
/**
* Sets the camera orbit pitch rotation.
*/
@NonNull
public Builder setCameraOrbitPitchDegrees(
@FloatRange(from = -90.0f, to = 90.0f) float cameraOrbitPitchDegrees) {
mCameraOrbitPitchDegrees = cameraOrbitPitchDegrees;
return this;
}
/**
* Sets the camera dolly distance.
*/
@NonNull
public Builder setDollyDistanceInWorldSpace(float dollyDistanceInWorldSpace) {
mDollyDistanceInWorldSpace = dollyDistanceInWorldSpace;
return this;
}
/**
* Sets the camera vertical fov degree.
*/
@NonNull
public Builder setVerticalFovDegrees(
@FloatRange(from = 0.0f, to = 180.0f, fromInclusive = false)
float verticalFovDegrees) {
mVerticalFovDegrees = verticalFovDegrees;
return this;
}
/**
* Sets the frustum in near plane.
*/
@NonNull
public Builder setFrustumNearInWorldSpace(
@FloatRange(from = 0.0f) float frustumNearInWorldSpace) {
mFrustumNearInWorldSpace = frustumNearInWorldSpace;
return this;
}
/**
* Sets the frustum in far plane.
*/
@NonNull
public Builder setFrustumFarInWorldSpace(
@FloatRange(from = 0.0f) float frustumFarInWorldSpace) {
mFrustumFarInWorldSpace = frustumFarInWorldSpace;
return this;
}
/**
* Builds a new {@link CameraAttributes} instance.
*/
@NonNull
public CameraAttributes build() {
return new CameraAttributes(mAnchorPointInWorldSpace,
mAnchorPointInOutputUvSpace,
mCameraOrbitYawDegrees,
mCameraOrbitPitchDegrees,
mDollyDistanceInWorldSpace,
mVerticalFovDegrees,
mFrustumNearInWorldSpace,
mFrustumFarInWorldSpace);
}
}
}

View File

@@ -0,0 +1,19 @@
/**
* Copyright (c) 2022, 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.app.wallpapereffectsgeneration;
parcelable CinematicEffectRequest;

View File

@@ -0,0 +1,115 @@
/*
* Copyright (C) 2022 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.app.wallpapereffectsgeneration;
import android.annotation.NonNull;
import android.annotation.SystemApi;
import android.graphics.Bitmap;
import android.os.Parcel;
import android.os.Parcelable;
import java.util.Objects;
/**
* A {@link CinematicEffectRequest} is the data class having all the information
* passed to wallpaper effects generation service.
*
* @hide
*/
@SystemApi
public final class CinematicEffectRequest implements Parcelable {
/**
* Unique id of a cienmatic effect generation task.
*/
@NonNull
private String mTaskId;
/**
* The bitmap to generate cinematic effect from.
*/
@NonNull
private Bitmap mBitmap;
private CinematicEffectRequest(Parcel in) {
this.mTaskId = in.readString();
this.mBitmap = Bitmap.CREATOR.createFromParcel(in);
}
/**
* Constructor with task id and bitmap.
*/
public CinematicEffectRequest(@NonNull String taskId, @NonNull Bitmap bitmap) {
mTaskId = taskId;
mBitmap = bitmap;
}
/**
* Returns the task id.
*/
@NonNull
public String getTaskId() {
return mTaskId;
}
/**
* Returns the bitmap of this request.
*/
@NonNull
public Bitmap getBitmap() {
return mBitmap;
}
@Override
public boolean equals(Object o) {
if (this == o) return true;
if (o == null || getClass() != o.getClass()) {
return false;
}
CinematicEffectRequest that = (CinematicEffectRequest) o;
return mTaskId.equals(that.mTaskId);
}
@Override
public int hashCode() {
return Objects.hash(mTaskId);
}
@Override
public int describeContents() {
return 0;
}
@Override
public void writeToParcel(@NonNull Parcel out, int flags) {
out.writeString(mTaskId);
mBitmap.writeToParcel(out, flags);
}
@NonNull
public static final Creator<CinematicEffectRequest> CREATOR =
new Creator<CinematicEffectRequest>() {
@Override
public CinematicEffectRequest createFromParcel(Parcel in) {
return new CinematicEffectRequest(in);
}
@Override
public CinematicEffectRequest[] newArray(int size) {
return new CinematicEffectRequest[size];
}
};
}

View File

@@ -0,0 +1,19 @@
/**
* Copyright (c) 2022, 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.app.wallpapereffectsgeneration;
parcelable CinematicEffectResponse;

View File

@@ -0,0 +1,299 @@
/*
* Copyright (C) 2022 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.app.wallpapereffectsgeneration;
import android.annotation.IntDef;
import android.annotation.NonNull;
import android.annotation.Nullable;
import android.annotation.SystemApi;
import android.os.Parcel;
import android.os.Parcelable;
import java.lang.annotation.Retention;
import java.lang.annotation.RetentionPolicy;
import java.util.ArrayList;
import java.util.List;
import java.util.Objects;
/**
* A {@link CinematicEffectResponse} include textured meshes
* and camera attributes of key frames.
*
* @hide
*/
@SystemApi
public final class CinematicEffectResponse implements Parcelable {
/** @hide */
@IntDef(prefix = {"CINEMATIC_EFFECT_STATUS_"},
value = {CINEMATIC_EFFECT_STATUS_UNKNOWN,
CINEMATIC_EFFECT_STATUS_OK,
CINEMATIC_EFFECT_STATUS_ERROR,
CINEMATIC_EFFECT_STATUS_NOT_READY,
CINEMATIC_EFFECT_STATUS_PENDING,
CINEMATIC_EFFECT_STATUS_TOO_MANY_REQUESTS})
@Retention(RetentionPolicy.SOURCE)
public @interface CinematicEffectStatusCode {}
/** Cinematic effect generation unknown status. */
public static final int CINEMATIC_EFFECT_STATUS_UNKNOWN = 0;
/** Cinematic effect generation success. */
public static final int CINEMATIC_EFFECT_STATUS_OK = 1;
/** Cinematic effect generation failure. */
public static final int CINEMATIC_EFFECT_STATUS_ERROR = 2;
/** Service not ready for cinematic effect generation. */
public static final int CINEMATIC_EFFECT_STATUS_NOT_READY = 3;
/** Cienmatic effect generation process is pending. */
public static final int CINEMATIC_EFFECT_STATUS_PENDING = 4;
/** Too manay requests for server to handle. */
public static final int CINEMATIC_EFFECT_STATUS_TOO_MANY_REQUESTS = 5;
/** @hide */
@IntDef(prefix = {"IMAGE_CONTENT_TYPE_"},
value = {IMAGE_CONTENT_TYPE_UNKNOWN,
IMAGE_CONTENT_TYPE_PEOPLE_PORTRAIT,
IMAGE_CONTENT_TYPE_LANDSCAPE,
IMAGE_CONTENT_TYPE_OTHER
})
@Retention(RetentionPolicy.SOURCE)
public @interface ImageContentType {}
/** Image content unknown. */
public static final int IMAGE_CONTENT_TYPE_UNKNOWN = 0;
/** Image content is people portrait. */
public static final int IMAGE_CONTENT_TYPE_PEOPLE_PORTRAIT = 1;
/** Image content is landscape. */
public static final int IMAGE_CONTENT_TYPE_LANDSCAPE = 2;
/** Image content is doesn't belong to other types. */
public static final int IMAGE_CONTENT_TYPE_OTHER = 3;
@CinematicEffectStatusCode
private int mStatusCode;
/** The id of the cinematic effect generation task. */
@NonNull
private String mTaskId;
@ImageContentType
private int mImageContentType;
/** The textured mesh required to render cinematic effect. */
@NonNull
private List<TexturedMesh> mTexturedMeshes;
/** The start camera position for animation. */
@Nullable
private CameraAttributes mStartKeyFrame;
/** The end camera position for animation. */
@Nullable
private CameraAttributes mEndKeyFrame;
private CinematicEffectResponse(Parcel in) {
mStatusCode = in.readInt();
mTaskId = in.readString();
mImageContentType = in.readInt();
mTexturedMeshes = new ArrayList<TexturedMesh>();
in.readTypedList(mTexturedMeshes, TexturedMesh.CREATOR);
mStartKeyFrame = in.readTypedObject(CameraAttributes.CREATOR);
mEndKeyFrame = in.readTypedObject(CameraAttributes.CREATOR);
}
private CinematicEffectResponse(@CinematicEffectStatusCode int statusCode,
String taskId,
@ImageContentType int imageContentType,
List<TexturedMesh> texturedMeshes,
CameraAttributes startKeyFrame,
CameraAttributes endKeyFrame) {
mStatusCode = statusCode;
mTaskId = taskId;
mImageContentType = imageContentType;
mStartKeyFrame = startKeyFrame;
mEndKeyFrame = endKeyFrame;
mTexturedMeshes = texturedMeshes;
}
/** Gets the cinematic effect generation status code. */
@CinematicEffectStatusCode
public int getStatusCode() {
return mStatusCode;
}
/** Get the task id. */
@NonNull
public String getTaskId() {
return mTaskId;
}
/**
* Get the image content type, which briefly classifies what's
* the content of image, like people portrait, landscape etc.
*/
@ImageContentType
public int getImageContentType() {
return mImageContentType;
}
/** Get the textured meshes. */
@NonNull
public List<TexturedMesh> getTexturedMeshes() {
return mTexturedMeshes;
}
/**
* Get the camera attributes (position info and other parameters, see docs of
* {@link CameraAttributes}) of the start key frame on the animation path.
*/
@Nullable
public CameraAttributes getStartKeyFrame() {
return mStartKeyFrame;
}
/**
* Get the camera attributes (position info and other parameters, see docs of
* {@link CameraAttributes}) of the end key frame on the animation path.
*/
@Nullable
public CameraAttributes getEndKeyFrame() {
return mEndKeyFrame;
}
@NonNull
public static final Creator<CinematicEffectResponse> CREATOR =
new Creator<CinematicEffectResponse>() {
@Override
public CinematicEffectResponse createFromParcel(Parcel in) {
return new CinematicEffectResponse(in);
}
@Override
public CinematicEffectResponse[] newArray(int size) {
return new CinematicEffectResponse[size];
}
};
@Override
public int describeContents() {
return 0;
}
@Override
public void writeToParcel(@NonNull Parcel out, int flags) {
out.writeInt(mStatusCode);
out.writeString(mTaskId);
out.writeInt(mImageContentType);
out.writeTypedList(mTexturedMeshes, flags);
out.writeTypedObject(mStartKeyFrame, flags);
out.writeTypedObject(mEndKeyFrame, flags);
}
@Override
public boolean equals(Object o) {
if (this == o) return true;
if (o == null || getClass() != o.getClass()) {
return false;
}
CinematicEffectResponse that = (CinematicEffectResponse) o;
return mTaskId.equals(that.mTaskId);
}
@Override
public int hashCode() {
return Objects.hash(mTaskId);
}
/**
* Builder of {@link CinematicEffectResponse}
*
* @hide
*/
@SystemApi
public static final class Builder {
@CinematicEffectStatusCode
private int mStatusCode;
@NonNull
private String mTaskId;
@ImageContentType
private int mImageContentType;
@NonNull
private List<TexturedMesh> mTexturedMeshes;
@Nullable
private CameraAttributes mStartKeyFrame;
@Nullable
private CameraAttributes mEndKeyFrame;
/**
* Constructor with task id and status code.
*
* @hide
*/
@SystemApi
public Builder(@CinematicEffectStatusCode int statusCode, @NonNull String taskId) {
mStatusCode = statusCode;
mTaskId = taskId;
}
/**
* Sets the image content type.
*/
@NonNull
public Builder setImageContentType(@ImageContentType int imageContentType) {
mImageContentType = imageContentType;
return this;
}
/**
* Sets the textured meshes.
*/
@NonNull
public Builder setTexturedMeshes(@NonNull List<TexturedMesh> texturedMeshes) {
mTexturedMeshes = texturedMeshes;
return this;
}
/**
* Sets start key frame.
*/
@NonNull
public Builder setStartKeyFrame(@Nullable CameraAttributes startKeyFrame) {
mStartKeyFrame = startKeyFrame;
return this;
}
/**
* Sets end key frame.
*/
@NonNull
public Builder setEndKeyFrame(@Nullable CameraAttributes endKeyFrame) {
mEndKeyFrame = endKeyFrame;
return this;
}
/**
* Builds a {@link CinematicEffectResponse} instance.
*/
@NonNull
public CinematicEffectResponse build() {
if (mTexturedMeshes == null) {
// Place holder because build doesn't allow list to be nullable.
mTexturedMeshes = new ArrayList<>(0);
}
return new CinematicEffectResponse(mStatusCode, mTaskId, mImageContentType,
mTexturedMeshes, mStartKeyFrame, mEndKeyFrame);
}
}
}

View File

@@ -0,0 +1,30 @@
/**
* Copyright (c) 2022, 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.app.wallpapereffectsgeneration;
import android.app.wallpapereffectsgeneration.CinematicEffectResponse;
/**
* Callback used by system server to notify invoker of {@link WallpaperEffectsGenerationMAnager}
* of the cinematic effect generation result.
*
* @hide
*/
oneway interface ICinematicEffectListener {
void onCinematicEffectGenerated(in CinematicEffectResponse response);
}

View File

@@ -0,0 +1,34 @@
/**
* Copyright (c) 2022, 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.app.wallpapereffectsgeneration;
import android.app.wallpapereffectsgeneration.CinematicEffectRequest;
import android.app.wallpapereffectsgeneration.CinematicEffectResponse;
import android.app.wallpapereffectsgeneration.ICinematicEffectListener;
/**
* Used by {@link android.app.wallpapereffectsgeneration.WallpaperEffectsGenerationManager}
* to to generate effects.
*
* @hide
*/
oneway interface IWallpaperEffectsGenerationManager {
void generateCinematicEffect(in CinematicEffectRequest request,
in ICinematicEffectListener listener);
void returnCinematicEffectResponse(in CinematicEffectResponse response);
}

View File

@@ -0,0 +1,273 @@
/*
* Copyright (C) 2022 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.app.wallpapereffectsgeneration;
import android.annotation.IntDef;
import android.annotation.NonNull;
import android.annotation.SystemApi;
import android.graphics.Bitmap;
import android.os.Parcel;
import android.os.Parcelable;
import java.lang.annotation.Retention;
import java.lang.annotation.RetentionPolicy;
/**
* The textured mesh representation, including a texture (bitmap) to sample from when rendering,
* and a mesh consisting of primitives such as triangles. The mesh is represented by an indices
* array describing the set of primitives in the mesh, and a vertices array that the indices
* refer to.
*
* @hide
*/
@SystemApi
public final class TexturedMesh implements Parcelable {
/**
* The texture to sample from when rendering mesh.
*/
@NonNull
private Bitmap mBitmap;
/**
* The set of primitives as pointers into the vertices.
*/
@NonNull
private int[] mIndices;
/**
* The specific vertices that the indices refer to.
*/
@NonNull
private float[] mVertices;
/** @hide */
@IntDef(prefix = {"INDICES_LAYOUT_"}, value = {
INDICES_LAYOUT_UNDEFINED,
INDICES_LAYOUT_TRIANGLES})
@Retention(RetentionPolicy.SOURCE)
public @interface IndicesLayoutType {
}
/** Undefined indices layout */
public static final int INDICES_LAYOUT_UNDEFINED = 0;
/**
* Indices layout is triangle. Vertices are grouped into 3 and each
* group forms a triangle.
*/
public static final int INDICES_LAYOUT_TRIANGLES = 1;
@IndicesLayoutType
private int mIndicesLayoutType;
/** @hide */
@IntDef(prefix = {"VERTICES_LAYOUT_"}, value = {
VERTICES_LAYOUT_UNDEFINED,
VERTICES_LAYOUT_POSITION3_UV2})
@Retention(RetentionPolicy.SOURCE)
public @interface VerticesLayoutType {
}
/**
* Undefined vertices layout.
*/
public static final int VERTICES_LAYOUT_UNDEFINED = 0;
/**
* The vertices array uses 5 numbers to represent a point, in the format
* of [x1, y1, z1, u1, v1, x2, y2, z2, u2, v2, ...].
*/
public static final int VERTICES_LAYOUT_POSITION3_UV2 = 1;
@VerticesLayoutType
private int mVerticesLayoutType;
private TexturedMesh(Parcel in) {
this.mIndicesLayoutType = in.readInt();
this.mVerticesLayoutType = in.readInt();
this.mBitmap = in.readTypedObject(Bitmap.CREATOR);
Parcel data = Parcel.obtain();
try {
byte[] bytes = in.readBlob();
data.unmarshall(bytes, 0, bytes.length);
data.setDataPosition(0);
this.mIndices = data.createIntArray();
this.mVertices = data.createFloatArray();
} finally {
data.recycle();
}
}
private TexturedMesh(@NonNull Bitmap bitmap, @NonNull int[] indices,
@NonNull float[] vertices, @IndicesLayoutType int indicesLayoutType,
@VerticesLayoutType int verticesLayoutType) {
mBitmap = bitmap;
mIndices = indices;
mVertices = vertices;
mIndicesLayoutType = indicesLayoutType;
mVerticesLayoutType = verticesLayoutType;
}
/** Get the bitmap, which is the texture to sample from when rendering. */
@NonNull
public Bitmap getBitmap() {
return mBitmap;
}
/**
* Get the indices as pointers to the vertices array. Depending on the getIndicesLayoutType(),
* the primitives may have different shapes. For example, with INDICES_LAYOUT_TRIANGLES,
* indices 0, 1, 2 forms a triangle, indices 3, 4, 5 form another triangle.
*/
@NonNull
public int[] getIndices() {
return mIndices;
}
/**
* Get the vertices that the index array refers to. Depending on the getVerticesLayoutType()
* result, the vertices array can represent different per-vertex coordinates. For example,
* with VERTICES_LAYOUT_POSITION3_UV2 type, vertices are in the format of
* [x1, y1, z1, u1, v1, x2, y2, z2, u2, v2, ...].
*/
@NonNull
public float[] getVertices() {
return mVertices;
}
/** Get the indices layout type. */
@IndicesLayoutType
@NonNull
public int getIndicesLayoutType() {
return mIndicesLayoutType;
}
/** Get the indices layout type. */
@VerticesLayoutType
@NonNull
public int getVerticesLayoutType() {
return mVerticesLayoutType;
}
@NonNull
public static final Creator<TexturedMesh> CREATOR = new Creator<TexturedMesh>() {
@Override
public TexturedMesh createFromParcel(Parcel in) {
return new TexturedMesh(in);
}
@Override
public TexturedMesh[] newArray(int size) {
return new TexturedMesh[size];
}
};
@Override
public void writeToParcel(@NonNull Parcel out, int flags) {
out.writeInt(mIndicesLayoutType);
out.writeInt(mVerticesLayoutType);
out.writeTypedObject(mBitmap, flags);
// Indices and vertices can reach 5MB. Write the data as a Blob,
// which will be written to ashmem if too large.
Parcel data = Parcel.obtain();
try {
data.writeIntArray(mIndices);
data.writeFloatArray(mVertices);
out.writeBlob(data.marshall());
} finally {
data.recycle();
}
}
@Override
public int describeContents() {
return 0;
}
/**
* A builder for {@link TexturedMesh}
*
* @hide
*/
@SystemApi
public static final class Builder {
private Bitmap mBitmap;
private int[] mIndices;
private float[] mVertices;
@IndicesLayoutType
private int mIndicesLayoutType;
@VerticesLayoutType
private int mVerticesLayouttype;
/**
* Constructor with bitmap.
*
* @hide
*/
@SystemApi
public Builder(@NonNull Bitmap bitmap) {
mBitmap = bitmap;
}
/**
* Set the required indices. The indices should represent the primitives. For example,
* with INDICES_LAYOUT_TRIANGLES, indices 0, 1, 2 forms a triangle, indices 3, 4, 5
* form another triangle.
*/
@NonNull
public Builder setIndices(@NonNull int[] indices) {
mIndices = indices;
return this;
}
/**
* Set the required vertices. The vertices array should represent per-vertex coordinates.
* For example, with VERTICES_LAYOUT_POSITION3_UV2 type, vertices are in the format of
* [x1, y1, z1, u1, v1, x2, y2, z2, u2, v2, ...].
*
*/
@NonNull
public Builder setVertices(@NonNull float[] vertices) {
mVertices = vertices;
return this;
}
/**
* Set the required indices layout type.
*/
@NonNull
public Builder setIndicesLayoutType(@IndicesLayoutType int indicesLayoutType) {
mIndicesLayoutType = indicesLayoutType;
return this;
}
/**
* Set the required vertices layout type.
*/
@NonNull
public Builder setVerticesLayoutType(@VerticesLayoutType int verticesLayoutype) {
mVerticesLayouttype = verticesLayoutype;
return this;
}
/** Builds a TexturedMesh based on the given parameters. */
@NonNull
public TexturedMesh build() {
return new TexturedMesh(mBitmap, mIndices, mVertices, mIndicesLayoutType,
mVerticesLayouttype);
}
}
}

View File

@@ -0,0 +1,114 @@
/*
* Copyright (C) 2022 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.app.wallpapereffectsgeneration;
import android.annotation.CallbackExecutor;
import android.annotation.NonNull;
import android.annotation.RequiresPermission;
import android.annotation.SystemApi;
import android.annotation.SystemService;
import android.content.Context;
import android.os.RemoteException;
import java.util.concurrent.Executor;
/**
* A {@link WallpaperEffectsGenerationManager} is the class that passes wallpaper effects
* generation requests to wallpaper effect generation service. For example, create a cinematic
* and render a cinematic live wallpaper with the response.
*
* Usage:
* <pre>{@code
* mWallpaperEffectsGenerationManager =
* context.getSystemService(WallpaperEffectsGenerationManager.class);
* mWallpaperEffectsGenerationManager.
* generateCinematicEffect(cinematicEffectRequest, response->{
* // proceed cinematic effect response.
* });
* }</pre>
*
* @hide
*/
@SystemApi
@SystemService(Context.WALLPAPER_EFFECTS_GENERATION_SERVICE)
public final class WallpaperEffectsGenerationManager {
/**
* Interface for the cinematic effect listener.
*/
public interface CinematicEffectListener {
/**
* Async call when the cinematic effect response is generated.
* Client needs to check the status code of {@link CinematicEffectResponse}
* to determine if the effect generation is successful.
*
* @param response The generated cinematic effect response.
*/
void onCinematicEffectGenerated(@NonNull CinematicEffectResponse response);
}
private final IWallpaperEffectsGenerationManager mService;
/** @hide */
public WallpaperEffectsGenerationManager(
@NonNull IWallpaperEffectsGenerationManager service) {
mService = service;
}
/**
* Execute a {@link android.app.wallpapereffectsgeneration.CinematicEffectRequest} from
* the given parameters to the wallpaper effects generation service. After the cinematic
* effect response is ready, the given listener is invoked by the system with the response.
* The listener may never receive a callback if unexpected error happened when proceeding
* request.
*
* @param request request to generate cinematic effect.
* @param executor where the listener is invoked.
* @param listener listener invoked when the cinematic effect response is available.
* @hide
*/
@SystemApi
@RequiresPermission(android.Manifest.permission.MANAGE_WALLPAPER_EFFECTS_GENERATION)
public void generateCinematicEffect(@NonNull CinematicEffectRequest request,
@NonNull @CallbackExecutor Executor executor,
@NonNull CinematicEffectListener listener) {
try {
mService.generateCinematicEffect(request,
new CinematicEffectListenerWrapper(listener, executor));
} catch (RemoteException e) {
throw e.rethrowFromSystemServer();
}
}
private static final class CinematicEffectListenerWrapper
extends ICinematicEffectListener.Stub {
@NonNull
private final CinematicEffectListener mListener;
@NonNull
private final Executor mExecutor;
CinematicEffectListenerWrapper(@NonNull CinematicEffectListener listener,
@NonNull Executor executor) {
mListener = listener;
mExecutor = executor;
}
@Override
public void onCinematicEffectGenerated(CinematicEffectResponse response) {
mExecutor.execute(() -> mListener.onCinematicEffectGenerated(response));
}
}
}

View File

@@ -5030,6 +5030,20 @@ public abstract class Context {
*/
public static final String SOUND_TRIGGER_MIDDLEWARE_SERVICE = "soundtrigger_middleware";
/**
* Used for getting the wallpaper effects generation service.
*
* <p><b>NOTE: </b> this service is optional; callers of
* {@code Context.getSystemServiceName(WALLPAPER_EFFECTS_GENERATION_SERVICE)} should check for
* {@code null}.
*
* @hide
* @see #getSystemService(String)
*/
@SystemApi
public static final String WALLPAPER_EFFECTS_GENERATION_SERVICE =
"wallpaper_effects_generation";
/**
* Used to access {@link MusicRecognitionManagerService}.
*

View File

@@ -0,0 +1,28 @@
/*
* Copyright (C) 2022 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.wallpapereffectsgeneration;
import android.app.wallpapereffectsgeneration.CinematicEffectRequest;
/**
* Interface from the system to WallpaperEffectsGeneration service.
*
* @hide
*/
oneway interface IWallpaperEffectsGenerationService {
void onGenerateCinematicEffect(in CinematicEffectRequest request);
}

View File

@@ -0,0 +1,142 @@
/*
* Copyright (C) 2022 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.wallpapereffectsgeneration;
import static com.android.internal.util.function.pooled.PooledLambda.obtainMessage;
import android.annotation.CallSuper;
import android.annotation.NonNull;
import android.annotation.SystemApi;
import android.app.Service;
import android.app.wallpapereffectsgeneration.CinematicEffectRequest;
import android.app.wallpapereffectsgeneration.CinematicEffectResponse;
import android.app.wallpapereffectsgeneration.IWallpaperEffectsGenerationManager;
import android.content.Context;
import android.content.Intent;
import android.os.Handler;
import android.os.IBinder;
import android.os.Looper;
import android.os.RemoteException;
import android.os.ServiceManager;
import android.util.Log;
import android.util.Slog;
/**
* A service for handling wallpaper effects generation tasks. It must implement
* (onGenerateCinematicEffect} method to generate response and call returnCinematicEffectResponse
* to send the response.
*
* <p>To extend this service, you must declare the service in your manifest file with the
* {@link android.Manifest.permission#BIND_WALLPAPER_EFFECTS_GENERATION} permission and includes
* an intent filter with the {@link #SERVICE_INTERFACE} action. For example: </p>
* <pre>
* <application>
* <service android:name=".CtsWallpaperEffectsGenerationService"
* android:exported="true"
* android:label="CtsWallpaperEffectsGenerationService"
* android:permission="android.permission.BIND_WALLPAPER_EFFECTS_GENERATION_SERVICE">
* <intent-filter>
* <action android:name="android.service.wallpapereffectsgeneration.WallpaperEffectsGenerationService"
/>
* </intent-filter>
* </service>
* <uses-library android:name="android.test.runner"/>
* </application>
* </pre>
*
* @hide
*/
@SystemApi
public abstract class WallpaperEffectsGenerationService extends Service {
/**
* The {@link Intent} that must be declared as handled by the service.
*
* <p>The service must also require the
* {@link android.permission#MANAGE_WALLPAPER_EFFECTS_GENERATION}
* permission.
*
* @hide
*/
public static final String SERVICE_INTERFACE =
"android.service.wallpapereffectsgeneration.WallpaperEffectsGenerationService";
private static final boolean DEBUG = false;
private static final String TAG = "WallpaperEffectsGenerationService";
private Handler mHandler;
private IWallpaperEffectsGenerationManager mService;
private final IWallpaperEffectsGenerationService mInterface =
new IWallpaperEffectsGenerationService.Stub() {
@Override
public void onGenerateCinematicEffect(CinematicEffectRequest request) {
mHandler.sendMessage(
obtainMessage(
WallpaperEffectsGenerationService::onGenerateCinematicEffect,
WallpaperEffectsGenerationService.this, request));
}
};
/**
* Called when the OS receives a request for generating cinematic effect. On receiving the
* request, it extract cinematic information from the input and call
* {@link #returnCinematicEffectResponse} with the textured mesh
* and metadata wrapped in CinematicEffectResponse.
*
* @param request the cinematic effect request passed from the client.
*/
public abstract void onGenerateCinematicEffect(@NonNull CinematicEffectRequest request);
/**
* Returns the cinematic effect response. Must be called when cinematic effect
* response is generated and ready to be sent back. Otherwise the response won't be
* returned.
*
* @param response the cinematic effect response returned from service provider.
*/
public final void returnCinematicEffectResponse(@NonNull CinematicEffectResponse response) {
try {
mService.returnCinematicEffectResponse(response);
} catch (RemoteException e) {
throw e.rethrowFromSystemServer();
}
}
@CallSuper
@Override
public void onCreate() {
super.onCreate();
if (DEBUG) {
Log.d(TAG, "onCreate WallpaperEffectsGenerationService");
}
mHandler = new Handler(Looper.getMainLooper(), null, true);
IBinder b = ServiceManager.getService(Context.WALLPAPER_EFFECTS_GENERATION_SERVICE);
mService = IWallpaperEffectsGenerationManager.Stub.asInterface(b);
}
@NonNull
@Override
public final IBinder onBind(@NonNull Intent intent) {
if (DEBUG) {
Log.d(TAG, "onBind WallpaperEffectsGenerationService");
}
if (SERVICE_INTERFACE.equals(intent.getAction())) {
return mInterface.asBinder();
}
Slog.w(TAG,
"Tried to bind to wrong intent (should be " + SERVICE_INTERFACE + ": " + intent);
return null;
}
}

View File

@@ -4125,6 +4125,16 @@
<permission android:name="android.permission.BIND_CONTENT_SUGGESTIONS_SERVICE"
android:protectionLevel="signature" />
<!-- Must be required by a
android.service.wallpapereffectsgeneration.WallpaperEffectsGenerationService,
to ensure that only the system can bind to it.
@SystemApi @hide This is not a third-party API (intended for OEMs and system apps).
<p>Protection level: signature
-->
<permission android:name="android.permission.BIND_WALLPAPER_EFFECTS_GENERATION_SERVICE"
android:protectionLevel="signature" />
<!-- Must be declared by a android.service.musicrecognition.MusicRecognitionService,
to ensure that only the system can bind to it.
@SystemApi @hide This is not a third-party API (intended for OEMs and system apps).
@@ -5824,6 +5834,13 @@
<permission android:name="android.permission.MANAGE_SMARTSPACE"
android:protectionLevel="signature" />
<!-- @SystemApi Allows an application to manage the wallpaper effects
generation service.
@hide <p>Not for use by third-party applications.</p> -->
<permission android:name="android.permission.MANAGE_WALLPAPER_EFFECTS_GENERATION"
android:protectionLevel="signature" />
<!-- Allows an app to set the theme overlay in /vendor/overlay
being used.
@hide <p>Not for use by third-party applications.</p> -->

View File

@@ -4173,6 +4173,15 @@
<string name="config_defaultMusicRecognitionService" translatable="false"></string>
<!-- The package name for the system's wallpaper effects generation service.
This service returns wallpaper effects results.
This service must be trusted, as it can be activated without explicit consent of the user.
If no service with the specified name exists on the device, wallpaper effects
generation service will be disabled.
Example: "com.android.intelligence/.WallpaperEffectsGenerationService"
-->
<string name="config_defaultWallpaperEffectsGenerationService" translatable="false"></string>
<!-- The package name for the default retail demo app.
This package must be trusted, as it has the permissions to query the usage stats on the
device.

View File

@@ -3673,6 +3673,7 @@
<java-symbol type="string" name="config_defaultContentSuggestionsService" />
<java-symbol type="string" name="config_defaultSearchUiService" />
<java-symbol type="string" name="config_defaultSmartspaceService" />
<java-symbol type="string" name="config_defaultWallpaperEffectsGenerationService" />
<java-symbol type="string" name="config_defaultMusicRecognitionService" />
<java-symbol type="string" name="config_defaultAttentionService" />
<java-symbol type="string" name="config_defaultRotationResolverService" />

View File

@@ -102,6 +102,7 @@ filegroup {
":services.usage-sources",
":services.usb-sources",
":services.voiceinteraction-sources",
":services.wallpapereffectsgeneration-sources",
":services.wifi-sources",
],
visibility: ["//visibility:private"],
@@ -158,6 +159,7 @@ java_library {
"services.usage",
"services.usb",
"services.voiceinteraction",
"services.wallpapereffectsgeneration",
"services.wifi",
"service-blobstore",
"service-jobscheduler",

View File

@@ -366,6 +366,8 @@ public final class SystemServer implements Dumpable {
"com.android.server.adb.AdbService$Lifecycle";
private static final String SPEECH_RECOGNITION_MANAGER_SERVICE_CLASS =
"com.android.server.speech.SpeechRecognitionManagerService";
private static final String WALLPAPER_EFFECTS_GENERATION_MANAGER_SERVICE_CLASS =
"com.android.server.wallpapereffectsgeneration.WallpaperEffectsGenerationManagerService";
private static final String APP_PREDICTION_MANAGER_SERVICE_CLASS =
"com.android.server.appprediction.AppPredictionManagerService";
private static final String CONTENT_SUGGESTIONS_SERVICE_CLASS =
@@ -1895,7 +1897,6 @@ public final class SystemServer implements Dumpable {
}
t.traceEnd();
t.traceBegin("StartIpSecService");
try {
ipSecService = IpSecService.create(context);
@@ -2126,6 +2127,14 @@ public final class SystemServer implements Dumpable {
Slog.i(TAG, "Wallpaper service disabled by config");
}
// WallpaperEffectsGeneration manager service
// TODO (b/135218095): Use deviceHasConfigString(context,
// R.string.config_defaultWallpaperEffectsGenerationService)
t.traceBegin("StartWallpaperEffectsGenerationService");
mSystemServiceManager.startService(
WALLPAPER_EFFECTS_GENERATION_MANAGER_SERVICE_CLASS);
t.traceEnd();
t.traceBegin("StartAudioService");
if (!isArc) {
mSystemServiceManager.startService(AudioService.Lifecycle.class);

View File

@@ -0,0 +1,22 @@
package {
// See: http://go/android-license-faq
// A large-scale-change added 'default_applicable_licenses' to import
// all of the 'license_kinds' from "frameworks_base_license"
// to get the below license kinds:
// SPDX-license-identifier-Apache-2.0
default_applicable_licenses: ["frameworks_base_license"],
}
filegroup {
name: "services.wallpapereffectsgeneration-sources",
srcs: ["java/**/*.java"],
path: "java",
visibility: ["//frameworks/base/services"],
}
java_library_static {
name: "services.wallpapereffectsgeneration",
defaults: ["platform_service_defaults"],
srcs: [":services.wallpapereffectsgeneration-sources"],
libs: ["services.core"],
}

View File

@@ -0,0 +1,113 @@
/*
* Copyright (C) 2022 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 com.android.server.wallpapereffectsgeneration;
import android.annotation.NonNull;
import android.content.ComponentName;
import android.content.Context;
import android.os.IBinder;
import android.service.wallpapereffectsgeneration.IWallpaperEffectsGenerationService;
import android.service.wallpapereffectsgeneration.WallpaperEffectsGenerationService;
import android.text.format.DateUtils;
import com.android.internal.infra.AbstractMultiplePendingRequestsRemoteService;
/**
* Proxy to the
* {@link android.service.wallpapereffectsgeneration.WallpaperEffectsGenerationService}
* implementation in another process.
*/
public class RemoteWallpaperEffectsGenerationService extends
AbstractMultiplePendingRequestsRemoteService<RemoteWallpaperEffectsGenerationService,
IWallpaperEffectsGenerationService> {
private static final String TAG =
RemoteWallpaperEffectsGenerationService.class.getSimpleName();
private static final long TIMEOUT_REMOTE_REQUEST_MILLIS = 2 * DateUtils.SECOND_IN_MILLIS;
private final RemoteWallpaperEffectsGenerationServiceCallback mCallback;
public RemoteWallpaperEffectsGenerationService(Context context,
ComponentName componentName, int userId,
RemoteWallpaperEffectsGenerationServiceCallback callback,
boolean bindInstantServiceAllowed,
boolean verbose) {
super(context, WallpaperEffectsGenerationService.SERVICE_INTERFACE,
componentName, userId, callback,
context.getMainThreadHandler(),
bindInstantServiceAllowed ? Context.BIND_ALLOW_INSTANT : 0,
verbose, /* initialCapacity= */ 1);
mCallback = callback;
}
@Override
protected IWallpaperEffectsGenerationService getServiceInterface(IBinder service) {
return IWallpaperEffectsGenerationService.Stub.asInterface(service);
}
@Override
protected long getTimeoutIdleBindMillis() {
return PERMANENT_BOUND_TIMEOUT_MS;
}
@Override
protected long getRemoteRequestMillis() {
return TIMEOUT_REMOTE_REQUEST_MILLIS;
}
/**
* Schedules a request to bind to the remote service.
*/
public void reconnect() {
super.scheduleBind();
}
/**
* Schedule async request on remote service.
*/
public void scheduleOnResolvedService(
@NonNull AsyncRequest<IWallpaperEffectsGenerationService> request) {
scheduleAsyncRequest(request);
}
/**
* Execute async request on remote service immediately instead of sending it to Handler queue.
*/
public void executeOnResolvedService(
@NonNull AsyncRequest<IWallpaperEffectsGenerationService> request) {
executeAsyncRequest(request);
}
/**
* Notifies server (WallpaperEffectsGenerationPerUserService) about unexpected events..
*/
public interface RemoteWallpaperEffectsGenerationServiceCallback
extends VultureCallback<RemoteWallpaperEffectsGenerationService> {
/**
* Notifies change in connected state of the remote service.
*/
void onConnectedStateChanged(boolean connected);
}
@Override // from AbstractRemoteService
protected void handleOnConnectedStateChanged(boolean connected) {
if (mCallback != null) {
mCallback.onConnectedStateChanged(connected);
}
}
}

View File

@@ -0,0 +1,185 @@
/*
* Copyright (C) 2022 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 com.android.server.wallpapereffectsgeneration;
import static android.Manifest.permission.MANAGE_WALLPAPER_EFFECTS_GENERATION;
import static android.app.ActivityManagerInternal.ALLOW_NON_FULL;
import static android.content.Context.WALLPAPER_EFFECTS_GENERATION_SERVICE;
import static android.content.pm.PackageManager.PERMISSION_GRANTED;
import android.annotation.NonNull;
import android.annotation.Nullable;
import android.annotation.UserIdInt;
import android.app.ActivityManagerInternal;
import android.app.wallpapereffectsgeneration.CinematicEffectRequest;
import android.app.wallpapereffectsgeneration.CinematicEffectResponse;
import android.app.wallpapereffectsgeneration.ICinematicEffectListener;
import android.app.wallpapereffectsgeneration.IWallpaperEffectsGenerationManager;
import android.content.Context;
import android.os.Binder;
import android.os.RemoteException;
import android.os.ResultReceiver;
import android.os.ShellCallback;
import android.util.Slog;
import com.android.server.LocalServices;
import com.android.server.infra.AbstractMasterSystemService;
import com.android.server.infra.FrameworkResourcesServiceNameResolver;
import com.android.server.wm.ActivityTaskManagerInternal;
import java.io.FileDescriptor;
import java.util.function.Consumer;
/**
* A service used to return wallpaper effect given a request.
*/
public class WallpaperEffectsGenerationManagerService extends
AbstractMasterSystemService<WallpaperEffectsGenerationManagerService,
WallpaperEffectsGenerationPerUserService> {
private static final String TAG =
WallpaperEffectsGenerationManagerService.class.getSimpleName();
private static final boolean DEBUG = false;
private static final int MAX_TEMP_SERVICE_DURATION_MS = 1_000 * 60 * 2; // 2 minutes
private final ActivityTaskManagerInternal mActivityTaskManagerInternal;
public WallpaperEffectsGenerationManagerService(Context context) {
super(context,
new FrameworkResourcesServiceNameResolver(context,
com.android.internal.R.string.config_defaultWallpaperEffectsGenerationService),
null,
PACKAGE_UPDATE_POLICY_NO_REFRESH | PACKAGE_RESTART_POLICY_NO_REFRESH);
mActivityTaskManagerInternal = LocalServices.getService(ActivityTaskManagerInternal.class);
}
@Override
protected WallpaperEffectsGenerationPerUserService newServiceLocked(int resolvedUserId,
boolean disabled) {
return new WallpaperEffectsGenerationPerUserService(this, mLock, resolvedUserId);
}
@Override
public void onStart() {
publishBinderService(WALLPAPER_EFFECTS_GENERATION_SERVICE,
new WallpaperEffectsGenerationManagerStub());
}
@Override
protected void enforceCallingPermissionForManagement() {
getContext().enforceCallingPermission(MANAGE_WALLPAPER_EFFECTS_GENERATION, TAG);
}
@Override // from AbstractMasterSystemService
protected void onServicePackageUpdatedLocked(@UserIdInt int userId) {
final WallpaperEffectsGenerationPerUserService service = peekServiceForUserLocked(userId);
if (service != null) {
service.onPackageUpdatedLocked();
}
}
@Override // from AbstractMasterSystemService
protected void onServicePackageRestartedLocked(@UserIdInt int userId) {
final WallpaperEffectsGenerationPerUserService service = peekServiceForUserLocked(userId);
if (service != null) {
service.onPackageRestartedLocked();
}
}
@Override
protected int getMaximumTemporaryServiceDurationMs() {
return MAX_TEMP_SERVICE_DURATION_MS;
}
private class WallpaperEffectsGenerationManagerStub
extends IWallpaperEffectsGenerationManager.Stub {
@Override
public void generateCinematicEffect(@NonNull CinematicEffectRequest request,
@NonNull ICinematicEffectListener listener) {
if (!runForUserLocked("generateCinematicEffect", (service) ->
service.onGenerateCinematicEffectLocked(request, listener))) {
try {
listener.onCinematicEffectGenerated(
new CinematicEffectResponse.Builder(
CinematicEffectResponse.CINEMATIC_EFFECT_STATUS_ERROR,
request.getTaskId()).build());
} catch (RemoteException e) {
if (DEBUG) {
Slog.d(TAG, "fail to invoke cinematic effect listener for task["
+ request.getTaskId() + "]");
}
}
}
}
@Override
public void returnCinematicEffectResponse(@NonNull CinematicEffectResponse response) {
runForUserLocked("returnCinematicResponse", (service) ->
service.onReturnCinematicEffectResponseLocked(response));
}
public void onShellCommand(@Nullable FileDescriptor in, @Nullable FileDescriptor out,
@Nullable FileDescriptor err,
@NonNull String[] args, @Nullable ShellCallback callback,
@NonNull ResultReceiver resultReceiver) {
new WallpaperEffectsGenerationManagerServiceShellCommand(
WallpaperEffectsGenerationManagerService.this)
.exec(this, in, out, err, args, callback, resultReceiver);
}
/**
* Execute the operation for the user locked. Return true if
* WallpaperEffectsGenerationPerUserService is found for the user.
* Otherwise return false.
*/
private boolean runForUserLocked(@NonNull final String func,
@NonNull final Consumer<WallpaperEffectsGenerationPerUserService> c) {
ActivityManagerInternal am = LocalServices.getService(ActivityManagerInternal.class);
final int userId = am.handleIncomingUser(Binder.getCallingPid(), Binder.getCallingUid(),
Binder.getCallingUserHandle().getIdentifier(), false, ALLOW_NON_FULL,
null, null);
if (DEBUG) {
Slog.d(TAG, "runForUserLocked:" + func + " from pid=" + Binder.getCallingPid()
+ ", uid=" + Binder.getCallingUid());
}
Context ctx = getContext();
if (!(ctx.checkCallingPermission(MANAGE_WALLPAPER_EFFECTS_GENERATION)
== PERMISSION_GRANTED
|| mServiceNameResolver.isTemporary(userId)
|| mActivityTaskManagerInternal.isCallerRecents(Binder.getCallingUid()))) {
String msg = "Permission Denial: Cannot call " + func + " from pid="
+ Binder.getCallingPid() + ", uid=" + Binder.getCallingUid();
Slog.w(TAG, msg);
throw new SecurityException(msg);
}
final long origId = Binder.clearCallingIdentity();
boolean accepted = false;
try {
synchronized (mLock) {
final WallpaperEffectsGenerationPerUserService service =
getServiceForUserLocked(userId);
if (service != null) {
accepted = true;
c.accept(service);
}
}
} finally {
Binder.restoreCallingIdentity(origId);
}
return accepted;
}
}
}

View File

@@ -0,0 +1,85 @@
/*
* Copyright (C) 2022 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 com.android.server.wallpapereffectsgeneration;
import android.annotation.NonNull;
import android.os.ShellCommand;
import java.io.PrintWriter;
/**
* The shell command implementation for the WallpaperEffectsGenerationService.
*/
public class WallpaperEffectsGenerationManagerServiceShellCommand extends ShellCommand {
private static final String TAG =
WallpaperEffectsGenerationManagerServiceShellCommand.class.getSimpleName();
private final WallpaperEffectsGenerationManagerService mService;
public WallpaperEffectsGenerationManagerServiceShellCommand(
@NonNull WallpaperEffectsGenerationManagerService service) {
mService = service;
}
@Override
public int onCommand(String cmd) {
if (cmd == null) {
return handleDefaultCommands(cmd);
}
final PrintWriter pw = getOutPrintWriter();
switch (cmd) {
case "set": {
final String what = getNextArgRequired();
switch (what) {
case "temporary-service": {
final int userId = Integer.parseInt(getNextArgRequired());
String serviceName = getNextArg();
if (serviceName == null) {
mService.resetTemporaryService(userId);
pw.println("WallpaperEffectsGenerationService temporarily reset. ");
return 0;
}
final int duration = Integer.parseInt(getNextArgRequired());
mService.setTemporaryService(userId, serviceName, duration);
pw.println("WallpaperEffectsGenerationService temporarily set to "
+ serviceName + " for " + duration + "ms");
break;
}
}
}
break;
default:
return handleDefaultCommands(cmd);
}
return 0;
}
@Override
public void onHelp() {
try (PrintWriter pw = getOutPrintWriter()) {
pw.println("WallpaperEffectsGenerationService commands:");
pw.println(" help");
pw.println(" Prints this help text.");
pw.println("");
pw.println(" set temporary-service USER_ID [COMPONENT_NAME DURATION]");
pw.println(" Temporarily (for DURATION ms) changes the service implemtation.");
pw.println(" To reset, call with just the USER_ID argument.");
pw.println("");
}
}
}

View File

@@ -0,0 +1,274 @@
/*
* Copyright (C) 2022 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 com.android.server.wallpapereffectsgeneration;
import android.Manifest;
import android.annotation.NonNull;
import android.annotation.Nullable;
import android.app.AppGlobals;
import android.app.wallpapereffectsgeneration.CinematicEffectRequest;
import android.app.wallpapereffectsgeneration.CinematicEffectResponse;
import android.app.wallpapereffectsgeneration.ICinematicEffectListener;
import android.content.ComponentName;
import android.content.pm.PackageManager;
import android.content.pm.PackageManager.NameNotFoundException;
import android.content.pm.ServiceInfo;
import android.os.RemoteException;
import android.util.Slog;
import com.android.internal.annotations.GuardedBy;
import com.android.server.infra.AbstractPerUserSystemService;
/**
* Per-user instance of {@link WallpaperEffectsGenerationManagerService}.
*/
public class WallpaperEffectsGenerationPerUserService extends
AbstractPerUserSystemService<WallpaperEffectsGenerationPerUserService,
WallpaperEffectsGenerationManagerService> implements
RemoteWallpaperEffectsGenerationService.RemoteWallpaperEffectsGenerationServiceCallback {
private static final String TAG =
WallpaperEffectsGenerationPerUserService.class.getSimpleName();
@GuardedBy("mLock")
private CinematicEffectListenerWrapper mCinematicEffectListenerWrapper;
@Nullable
@GuardedBy("mLock")
private RemoteWallpaperEffectsGenerationService mRemoteService;
protected WallpaperEffectsGenerationPerUserService(
WallpaperEffectsGenerationManagerService master,
Object lock, int userId) {
super(master, lock, userId);
}
@Override // from PerUserSystemService
protected ServiceInfo newServiceInfoLocked(@NonNull ComponentName serviceComponent)
throws NameNotFoundException {
ServiceInfo si;
try {
si = AppGlobals.getPackageManager().getServiceInfo(serviceComponent,
PackageManager.GET_META_DATA, mUserId);
} catch (RemoteException e) {
throw new NameNotFoundException("Could not get service for " + serviceComponent);
}
if (!Manifest.permission.BIND_WALLPAPER_EFFECTS_GENERATION_SERVICE.equals(si.permission)) {
Slog.w(TAG, "WallpaperEffectsGenerationService from '" + si.packageName
+ "' does not require permission "
+ Manifest.permission.BIND_WALLPAPER_EFFECTS_GENERATION_SERVICE);
throw new SecurityException("Service does not require permission "
+ Manifest.permission.BIND_WALLPAPER_EFFECTS_GENERATION_SERVICE);
}
return si;
}
@GuardedBy("mLock")
@Override // from PerUserSystemService
protected boolean updateLocked(boolean disabled) {
final boolean enabledChanged = super.updateLocked(disabled);
updateRemoteServiceLocked();
return enabledChanged;
}
/**
* Notifies the service of a new cinematic effect generation request.
*/
@GuardedBy("mLock")
public void onGenerateCinematicEffectLocked(
@NonNull CinematicEffectRequest cinematicEffectRequest,
@NonNull ICinematicEffectListener cinematicEffectListener) {
String newTaskId = cinematicEffectRequest.getTaskId();
// Previous request is still being processed.
if (mCinematicEffectListenerWrapper != null) {
if (mCinematicEffectListenerWrapper.mTaskId.equals(newTaskId)) {
invokeCinematicListenerAndCleanup(
new CinematicEffectResponse.Builder(
CinematicEffectResponse.CINEMATIC_EFFECT_STATUS_PENDING, newTaskId)
.build()
);
} else {
invokeCinematicListenerAndCleanup(
new CinematicEffectResponse.Builder(
CinematicEffectResponse.CINEMATIC_EFFECT_STATUS_TOO_MANY_REQUESTS,
newTaskId).build()
);
}
return;
}
RemoteWallpaperEffectsGenerationService remoteService = ensureRemoteServiceLocked();
if (remoteService != null) {
remoteService.executeOnResolvedService(
s -> s.onGenerateCinematicEffect(cinematicEffectRequest));
mCinematicEffectListenerWrapper =
new CinematicEffectListenerWrapper(newTaskId, cinematicEffectListener);
} else {
if (isDebug()) {
Slog.d(TAG, "Remote service not found");
}
try {
cinematicEffectListener.onCinematicEffectGenerated(
createErrorCinematicEffectResponse(newTaskId));
} catch (RemoteException e) {
if (isDebug()) {
Slog.d(TAG, "Failed to invoke cinematic effect listener for task [" + newTaskId
+ "]");
}
}
}
}
/**
* Notifies the service of a generated cinematic effect response.
*/
@GuardedBy("mLock")
public void onReturnCinematicEffectResponseLocked(
@NonNull CinematicEffectResponse cinematicEffectResponse) {
invokeCinematicListenerAndCleanup(cinematicEffectResponse);
}
@GuardedBy("mLock")
private void updateRemoteServiceLocked() {
if (mRemoteService != null) {
mRemoteService.destroy();
mRemoteService = null;
}
// End existing response and clean up listener for next request.
if (mCinematicEffectListenerWrapper != null) {
invokeCinematicListenerAndCleanup(
createErrorCinematicEffectResponse(mCinematicEffectListenerWrapper.mTaskId));
}
}
void onPackageUpdatedLocked() {
if (isDebug()) {
Slog.v(TAG, "onPackageUpdatedLocked()");
}
destroyAndRebindRemoteService();
}
void onPackageRestartedLocked() {
if (isDebug()) {
Slog.v(TAG, "onPackageRestartedLocked()");
}
destroyAndRebindRemoteService();
}
private void destroyAndRebindRemoteService() {
if (mRemoteService == null) {
return;
}
if (isDebug()) {
Slog.d(TAG, "Destroying the old remote service.");
}
mRemoteService.destroy();
mRemoteService = null;
mRemoteService = ensureRemoteServiceLocked();
if (mRemoteService != null) {
if (isDebug()) {
Slog.d(TAG, "Rebinding to the new remote service.");
}
mRemoteService.reconnect();
}
// Clean up listener for next request.
if (mCinematicEffectListenerWrapper != null) {
invokeCinematicListenerAndCleanup(
createErrorCinematicEffectResponse(mCinematicEffectListenerWrapper.mTaskId));
}
}
private CinematicEffectResponse createErrorCinematicEffectResponse(String taskId) {
return new CinematicEffectResponse.Builder(
CinematicEffectResponse.CINEMATIC_EFFECT_STATUS_ERROR,
taskId).build();
}
@GuardedBy("mLock")
private void invokeCinematicListenerAndCleanup(
CinematicEffectResponse cinematicEffectResponse) {
try {
if (mCinematicEffectListenerWrapper != null
&& mCinematicEffectListenerWrapper.mListener != null) {
mCinematicEffectListenerWrapper.mListener.onCinematicEffectGenerated(
cinematicEffectResponse);
} else {
if (isDebug()) {
Slog.w(TAG, "Cinematic effect listener not found for task["
+ mCinematicEffectListenerWrapper.mTaskId + "]");
}
}
} catch (RemoteException e) {
if (isDebug()) {
Slog.w(TAG, "Error invoking cinematic effect listener for task["
+ mCinematicEffectListenerWrapper.mTaskId + "]");
}
} finally {
mCinematicEffectListenerWrapper = null;
}
}
@GuardedBy("mLock")
@Nullable
private RemoteWallpaperEffectsGenerationService ensureRemoteServiceLocked() {
if (mRemoteService == null) {
final String serviceName = getComponentNameLocked();
if (serviceName == null) {
if (mMaster.verbose) {
Slog.v(TAG, "ensureRemoteServiceLocked(): not set");
}
return null;
}
ComponentName serviceComponent = ComponentName.unflattenFromString(serviceName);
mRemoteService = new RemoteWallpaperEffectsGenerationService(getContext(),
serviceComponent, mUserId, this,
mMaster.isBindInstantServiceAllowed(), mMaster.verbose);
}
return mRemoteService;
}
@Override // from RemoteWallpaperEffectsGenerationService
public void onServiceDied(RemoteWallpaperEffectsGenerationService service) {
Slog.w(TAG, "remote wallpaper effects generation service died");
updateRemoteServiceLocked();
}
@Override // from RemoteWallpaperEffectsGenerationService
public void onConnectedStateChanged(boolean connected) {
if (!connected) {
Slog.w(TAG, "remote wallpaper effects generation service disconnected");
updateRemoteServiceLocked();
}
}
private static final class CinematicEffectListenerWrapper {
@NonNull
private final String mTaskId;
@NonNull
private final ICinematicEffectListener mListener;
CinematicEffectListenerWrapper(
@NonNull final String taskId,
@NonNull final ICinematicEffectListener listener) {
mTaskId = taskId;
mListener = listener;
}
}
}