From a5afd3736ab7ab659b0e741a0501fe51e2ad6369 Mon Sep 17 00:00:00 2001 From: Sharon Su Date: Tue, 1 Feb 2022 22:09:36 +0000 Subject: [PATCH] Server implementation of WallpaperEffectsService API. Test: cts tests Bug: 213238425 CTS-Coverage-Bug: 213238425 Change-Id: Id94764483b80a62a9fcaf8b2205b63e5cb66473c --- services/Android.bp | 2 + .../java/com/android/server/SystemServer.java | 11 +- .../wallpapereffectsgeneration/Android.bp | 22 ++ ...moteWallpaperEffectsGenerationService.java | 113 ++++++++ ...lpaperEffectsGenerationManagerService.java | 185 ++++++++++++ ...sGenerationManagerServiceShellCommand.java | 85 ++++++ ...lpaperEffectsGenerationPerUserService.java | 274 ++++++++++++++++++ 7 files changed, 691 insertions(+), 1 deletion(-) create mode 100644 services/wallpapereffectsgeneration/Android.bp create mode 100644 services/wallpapereffectsgeneration/java/com/android/server/wallpapereffectsgeneration/RemoteWallpaperEffectsGenerationService.java create mode 100644 services/wallpapereffectsgeneration/java/com/android/server/wallpapereffectsgeneration/WallpaperEffectsGenerationManagerService.java create mode 100644 services/wallpapereffectsgeneration/java/com/android/server/wallpapereffectsgeneration/WallpaperEffectsGenerationManagerServiceShellCommand.java create mode 100644 services/wallpapereffectsgeneration/java/com/android/server/wallpapereffectsgeneration/WallpaperEffectsGenerationPerUserService.java diff --git a/services/Android.bp b/services/Android.bp index af70692a88e5e..b0a5c66b53dd1 100644 --- a/services/Android.bp +++ b/services/Android.bp @@ -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", diff --git a/services/java/com/android/server/SystemServer.java b/services/java/com/android/server/SystemServer.java index 74e04ed6e58b3..1a54496e6bd55 100644 --- a/services/java/com/android/server/SystemServer.java +++ b/services/java/com/android/server/SystemServer.java @@ -369,6 +369,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 = @@ -1891,7 +1893,6 @@ public final class SystemServer implements Dumpable { } t.traceEnd(); - t.traceBegin("StartIpSecService"); try { ipSecService = IpSecService.create(context); @@ -2132,6 +2133,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); diff --git a/services/wallpapereffectsgeneration/Android.bp b/services/wallpapereffectsgeneration/Android.bp new file mode 100644 index 0000000000000..4dbb0fd82db18 --- /dev/null +++ b/services/wallpapereffectsgeneration/Android.bp @@ -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"], +} diff --git a/services/wallpapereffectsgeneration/java/com/android/server/wallpapereffectsgeneration/RemoteWallpaperEffectsGenerationService.java b/services/wallpapereffectsgeneration/java/com/android/server/wallpapereffectsgeneration/RemoteWallpaperEffectsGenerationService.java new file mode 100644 index 0000000000000..c228dafcee8e0 --- /dev/null +++ b/services/wallpapereffectsgeneration/java/com/android/server/wallpapereffectsgeneration/RemoteWallpaperEffectsGenerationService.java @@ -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 { + + 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 request) { + scheduleAsyncRequest(request); + } + + /** + * Execute async request on remote service immediately instead of sending it to Handler queue. + */ + public void executeOnResolvedService( + @NonNull AsyncRequest request) { + executeAsyncRequest(request); + } + + /** + * Notifies server (WallpaperEffectsGenerationPerUserService) about unexpected events.. + */ + public interface RemoteWallpaperEffectsGenerationServiceCallback + extends VultureCallback { + /** + * 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); + } + } +} diff --git a/services/wallpapereffectsgeneration/java/com/android/server/wallpapereffectsgeneration/WallpaperEffectsGenerationManagerService.java b/services/wallpapereffectsgeneration/java/com/android/server/wallpapereffectsgeneration/WallpaperEffectsGenerationManagerService.java new file mode 100644 index 0000000000000..0d0b3e0987502 --- /dev/null +++ b/services/wallpapereffectsgeneration/java/com/android/server/wallpapereffectsgeneration/WallpaperEffectsGenerationManagerService.java @@ -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 { + 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 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; + } + } +} diff --git a/services/wallpapereffectsgeneration/java/com/android/server/wallpapereffectsgeneration/WallpaperEffectsGenerationManagerServiceShellCommand.java b/services/wallpapereffectsgeneration/java/com/android/server/wallpapereffectsgeneration/WallpaperEffectsGenerationManagerServiceShellCommand.java new file mode 100644 index 0000000000000..fc6f75fc254e5 --- /dev/null +++ b/services/wallpapereffectsgeneration/java/com/android/server/wallpapereffectsgeneration/WallpaperEffectsGenerationManagerServiceShellCommand.java @@ -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(""); + } + } +} diff --git a/services/wallpapereffectsgeneration/java/com/android/server/wallpapereffectsgeneration/WallpaperEffectsGenerationPerUserService.java b/services/wallpapereffectsgeneration/java/com/android/server/wallpapereffectsgeneration/WallpaperEffectsGenerationPerUserService.java new file mode 100644 index 0000000000000..d541051e5ab28 --- /dev/null +++ b/services/wallpapereffectsgeneration/java/com/android/server/wallpapereffectsgeneration/WallpaperEffectsGenerationPerUserService.java @@ -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 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; + } + } +}