diff options
10 files changed, 424 insertions, 14 deletions
diff --git a/core/res/res/values/config.xml b/core/res/res/values/config.xml index ec2a6ae572c0..7b0c6319c797 100644 --- a/core/res/res/values/config.xml +++ b/core/res/res/values/config.xml @@ -3564,6 +3564,12 @@ --> <string name="config_defaultSystemCaptionsService" translatable="false"></string> + <!-- The component name for the system-wide captions manager service. + This service must be trusted, as the system binds to it and keeps it running. + Example: "com.android.captions/.SystemCaptionsManagerService" + --> + <string name="config_defaultSystemCaptionsManagerService" translatable="false"></string> + <!-- The package name for the incident report approver app. This app is usually PermissionController or an app that replaces it. When a bugreport or incident report with EXPLICT-level sharing flags is going to be diff --git a/core/res/res/values/symbols.xml b/core/res/res/values/symbols.xml index 3e23640ed872..e4556259ad64 100644 --- a/core/res/res/values/symbols.xml +++ b/core/res/res/values/symbols.xml @@ -3411,6 +3411,7 @@ <java-symbol type="string" name="config_defaultContentSuggestionsService" /> <java-symbol type="string" name="config_defaultAttentionService" /> <java-symbol type="string" name="config_defaultSystemCaptionsService" /> + <java-symbol type="string" name="config_defaultSystemCaptionsManagerService" /> <java-symbol type="string" name="notification_channel_foreground_service" /> <java-symbol type="string" name="foreground_service_app_in_background" /> diff --git a/services/Android.bp b/services/Android.bp index 567efac5753c..b08d1a8095db 100644 --- a/services/Android.bp +++ b/services/Android.bp @@ -31,6 +31,7 @@ java_library { "services.print", "services.restrictions", "services.startop", + "services.systemcaptions", "services.usage", "services.usb", "services.voiceinteraction", diff --git a/services/contentcapture/java/com/android/server/contentcapture/ContentCaptureManagerService.java b/services/contentcapture/java/com/android/server/contentcapture/ContentCaptureManagerService.java index 9b02c4e72cfc..757c2dc7f6f1 100644 --- a/services/contentcapture/java/com/android/server/contentcapture/ContentCaptureManagerService.java +++ b/services/contentcapture/java/com/android/server/contentcapture/ContentCaptureManagerService.java @@ -129,7 +129,8 @@ public final class ContentCaptureManagerService extends public ContentCaptureManagerService(@NonNull Context context) { super(context, new FrameworkResourcesServiceNameResolver(context, com.android.internal.R.string.config_defaultContentCaptureService), - UserManager.DISALLOW_CONTENT_CAPTURE, /* refreshServiceOnPackageUpdate= */ false); + UserManager.DISALLOW_CONTENT_CAPTURE, + /*packageUpdatePolicy=*/ PACKAGE_UPDATE_POLICY_NO_REFRESH); DeviceConfig.addOnPropertyChangedListener(DeviceConfig.NAMESPACE_CONTENT_CAPTURE, ActivityThread.currentApplication().getMainExecutor(), (namespace, key, value) -> onDeviceConfigChange(key, value)); diff --git a/services/core/java/com/android/server/infra/AbstractMasterSystemService.java b/services/core/java/com/android/server/infra/AbstractMasterSystemService.java index 098b0e9d4d39..9782f30d3074 100644 --- a/services/core/java/com/android/server/infra/AbstractMasterSystemService.java +++ b/services/core/java/com/android/server/infra/AbstractMasterSystemService.java @@ -15,6 +15,7 @@ */ package com.android.server.infra; +import android.annotation.IntDef; import android.annotation.NonNull; import android.annotation.Nullable; import android.annotation.UserIdInt; @@ -45,6 +46,8 @@ import com.android.server.LocalServices; import com.android.server.SystemService; import java.io.PrintWriter; +import java.lang.annotation.Retention; +import java.lang.annotation.RetentionPolicy; import java.util.List; /** @@ -75,6 +78,30 @@ import java.util.List; public abstract class AbstractMasterSystemService<M extends AbstractMasterSystemService<M, S>, S extends AbstractPerUserSystemService<S, M>> extends SystemService { + /** On a package update, does not refresh the per-user service in the cache. */ + public static final int PACKAGE_UPDATE_POLICY_NO_REFRESH = 0; + + /** + * On a package update, removes any existing per-user services in the cache. + * + * <p>This does not immediately recreate these services. It is assumed they will be recreated + * for the next user request. + */ + public static final int PACKAGE_UPDATE_POLICY_REFRESH_LAZY = 1; + + /** + * On a package update, removes and recreates any existing per-user services in the cache. + */ + public static final int PACKAGE_UPDATE_POLICY_REFRESH_EAGER = 2; + + @IntDef(flag = true, prefix = { "PACKAGE_UPDATE_POLICY_" }, value = { + PACKAGE_UPDATE_POLICY_NO_REFRESH, + PACKAGE_UPDATE_POLICY_REFRESH_LAZY, + PACKAGE_UPDATE_POLICY_REFRESH_EAGER + }) + @Retention(RetentionPolicy.SOURCE) + public @interface PackageUpdatePolicy {} + /** * Log tag */ @@ -127,8 +154,11 @@ public abstract class AbstractMasterSystemService<M extends AbstractMasterSystem /** * Whether the per-user service should be removed from the cache when its apk is updated. + * + * <p>One of {@link #PACKAGE_UPDATE_POLICY_NO_REFRESH}, + * {@link #PACKAGE_UPDATE_POLICY_REFRESH_LAZY} or {@link #PACKAGE_UPDATE_POLICY_REFRESH_EAGER}. */ - private final boolean mRefreshServiceOnPackageUpdate; + private final @PackageUpdatePolicy int mPackageUpdatePolicy; /** * Name of the service packages whose APK are being updated, keyed by user id. @@ -154,7 +184,7 @@ public abstract class AbstractMasterSystemService<M extends AbstractMasterSystem @Nullable ServiceNameResolver serviceNameResolver, @Nullable String disallowProperty) { this(context, serviceNameResolver, disallowProperty, - /* refreshServiceOnPackageUpdate=*/ true); + /*packageUpdatePolicy=*/ PACKAGE_UPDATE_POLICY_REFRESH_LAZY); } /** @@ -167,17 +197,19 @@ public abstract class AbstractMasterSystemService<M extends AbstractMasterSystem * @param disallowProperty when not {@code null}, defines a {@link UserManager} restriction that * disables the service. <b>NOTE: </b> you'll also need to add it to * {@code UserRestrictionsUtils.USER_RESTRICTIONS}. - * @param refreshServiceOnPackageUpdate when {@code true}, the - * {@link AbstractPerUserSystemService} is removed from the cache (and re-added) when the - * service package is updated; when {@code false}, the service is untouched during the - * update. + * @param packageUpdatePolicy when {@link #PACKAGE_UPDATE_POLICY_REFRESH_LAZY}, the + * {@link AbstractPerUserSystemService} is removed from the cache when the service + * package is updated; when {@link #PACKAGE_UPDATE_POLICY_REFRESH_EAGER}, the + * {@link AbstractPerUserSystemService} is removed from the cache and immediately + * re-added when the service package is updated; when + * {@link #PACKAGE_UPDATE_POLICY_NO_REFRESH}, the service is untouched during the update. */ protected AbstractMasterSystemService(@NonNull Context context, @Nullable ServiceNameResolver serviceNameResolver, - @Nullable String disallowProperty, boolean refreshServiceOnPackageUpdate) { + @Nullable String disallowProperty, @PackageUpdatePolicy int packageUpdatePolicy) { super(context); - mRefreshServiceOnPackageUpdate = refreshServiceOnPackageUpdate; + mPackageUpdatePolicy = packageUpdatePolicy; mServiceNameResolver = serviceNameResolver; if (mServiceNameResolver != null) { @@ -645,7 +677,7 @@ public abstract class AbstractMasterSystemService<M extends AbstractMasterSystem final int size = mServicesCache.size(); pw.print(prefix); pw.print("Debug: "); pw.print(realDebug); pw.print(" Verbose: "); pw.println(realVerbose); - pw.print("Refresh on package update: "); pw.println(mRefreshServiceOnPackageUpdate); + pw.print("Refresh on package update: "); pw.println(mPackageUpdatePolicy); if (mUpdatingPackageNames != null) { pw.print("Packages being updated: "); pw.println(mUpdatingPackageNames); } @@ -701,12 +733,21 @@ public abstract class AbstractMasterSystemService<M extends AbstractMasterSystem } mUpdatingPackageNames.put(userId, packageName); onServicePackageUpdatingLocked(userId); - if (mRefreshServiceOnPackageUpdate) { + if (mPackageUpdatePolicy != PACKAGE_UPDATE_POLICY_NO_REFRESH) { if (debug) { - Slog.d(mTag, "Removing service for user " + userId + " because package " - + activePackageName + " is being updated"); + Slog.d(mTag, "Removing service for user " + userId + + " because package " + activePackageName + + " is being updated"); } removeCachedServiceLocked(userId); + + if (mPackageUpdatePolicy == PACKAGE_UPDATE_POLICY_REFRESH_EAGER) { + if (debug) { + Slog.d(mTag, "Eagerly recreating service for user " + + userId); + } + getServiceForUserLocked(userId); + } } else { if (debug) { Slog.d(mTag, "Holding service for user " + userId + " while package " diff --git a/services/java/com/android/server/SystemServer.java b/services/java/com/android/server/SystemServer.java index 9d09c4c3637b..106e64222c00 100644 --- a/services/java/com/android/server/SystemServer.java +++ b/services/java/com/android/server/SystemServer.java @@ -254,6 +254,8 @@ public final class SystemServer { "com.android.server.autofill.AutofillManagerService"; private static final String CONTENT_CAPTURE_MANAGER_SERVICE_CLASS = "com.android.server.contentcapture.ContentCaptureManagerService"; + private static final String SYSTEM_CAPTIONS_MANAGER_SERVICE_CLASS = + "com.android.server.systemcaptions.SystemCaptionsManagerService"; private static final String TIME_ZONE_RULES_MANAGER_SERVICE_CLASS = "com.android.server.timezone.RulesManagerService$Lifecycle"; private static final String IOT_SERVICE_CLASS = @@ -1232,6 +1234,8 @@ public final class SystemServer { startContentCaptureService(context); startAttentionService(context); + startSystemCaptionsManagerService(context); + // App prediction manager service traceBeginAndSlog("StartAppPredictionService"); mSystemServiceManager.startService(APP_PREDICTION_MANAGER_SERVICE_CLASS); @@ -2225,6 +2229,19 @@ public final class SystemServer { }, BOOT_TIMINGS_TRACE_LOG); } + private void startSystemCaptionsManagerService(@NonNull Context context) { + String serviceName = context.getString( + com.android.internal.R.string.config_defaultSystemCaptionsManagerService); + if (TextUtils.isEmpty(serviceName)) { + Slog.d(TAG, "SystemCaptionsManagerService disabled because resource is not overlaid"); + return; + } + + traceBeginAndSlog("StartSystemCaptionsManagerService"); + mSystemServiceManager.startService(SYSTEM_CAPTIONS_MANAGER_SERVICE_CLASS); + traceEnd(); + } + private void startContentCaptureService(@NonNull Context context) { // First check if it was explicitly enabled by DeviceConfig boolean explicitlyEnabled = false; @@ -2273,7 +2290,7 @@ public final class SystemServer { traceEnd(); } - static final void startSystemUi(Context context, WindowManagerService windowManager) { + private static void startSystemUi(Context context, WindowManagerService windowManager) { Intent intent = new Intent(); intent.setComponent(new ComponentName("com.android.systemui", "com.android.systemui.SystemUIService")); diff --git a/services/systemcaptions/Android.bp b/services/systemcaptions/Android.bp new file mode 100644 index 000000000000..4e190b639b2c --- /dev/null +++ b/services/systemcaptions/Android.bp @@ -0,0 +1,5 @@ +java_library_static { + name: "services.systemcaptions", + srcs: ["java/**/*.java"], + libs: ["services.core"], +} diff --git a/services/systemcaptions/java/com/android/server/systemcaptions/RemoteSystemCaptionsManagerService.java b/services/systemcaptions/java/com/android/server/systemcaptions/RemoteSystemCaptionsManagerService.java new file mode 100644 index 000000000000..5480b6ced4e1 --- /dev/null +++ b/services/systemcaptions/java/com/android/server/systemcaptions/RemoteSystemCaptionsManagerService.java @@ -0,0 +1,164 @@ +/* + * Copyright (C) 2019 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.systemcaptions; + +import android.annotation.Nullable; +import android.content.ComponentName; +import android.content.Context; +import android.content.Intent; +import android.content.ServiceConnection; +import android.os.Handler; +import android.os.IBinder; +import android.os.Looper; +import android.os.UserHandle; +import android.util.Slog; + +import com.android.internal.annotations.GuardedBy; + +/** Manages the connection to the remote system captions manager service. */ +final class RemoteSystemCaptionsManagerService { + + private static final String TAG = RemoteSystemCaptionsManagerService.class.getSimpleName(); + + private static final String SERVICE_INTERFACE = + "android.service.systemcaptions.SystemCaptionsManagerService"; + + private final Object mLock = new Object(); + + private final Context mContext; + private final Intent mIntent; + private final ComponentName mComponentName; + private final int mUserId; + private final boolean mVerbose; + private final Handler mHandler; + + private final RemoteServiceConnection mServiceConnection = new RemoteServiceConnection(); + + @GuardedBy("mLock") + @Nullable private IBinder mService; + + @GuardedBy("mLock") + private boolean mBinding = false; + + @GuardedBy("mLock") + private boolean mDestroyed = false; + + RemoteSystemCaptionsManagerService( + Context context, ComponentName componentName, int userId, boolean verbose) { + mContext = context; + mComponentName = componentName; + mUserId = userId; + mVerbose = verbose; + mIntent = new Intent(SERVICE_INTERFACE).setComponent(componentName); + mHandler = new Handler(Looper.getMainLooper()); + } + + void initialize() { + if (mVerbose) { + Slog.v(TAG, "initialize()"); + } + ensureBound(); + } + + void destroy() { + if (mVerbose) { + Slog.v(TAG, "destroy()"); + } + + synchronized (mLock) { + if (mDestroyed) { + if (mVerbose) { + Slog.v(TAG, "destroy(): Already destroyed"); + } + return; + } + mDestroyed = true; + ensureUnboundLocked(); + } + } + + boolean isDestroyed() { + synchronized (mLock) { + return mDestroyed; + } + } + + private void ensureBound() { + synchronized (mLock) { + if (mService != null || mBinding) { + return; + } + + if (mVerbose) { + Slog.v(TAG, "ensureBound(): binding"); + } + mBinding = true; + + int flags = Context.BIND_AUTO_CREATE | Context.BIND_FOREGROUND_SERVICE; + boolean willBind = mContext.bindServiceAsUser(mIntent, mServiceConnection, flags, + mHandler, new UserHandle(mUserId)); + if (!willBind) { + Slog.w(TAG, "Could not bind to " + mIntent + " with flags " + flags); + mBinding = false; + mService = null; + } + } + } + + @GuardedBy("mLock") + private void ensureUnboundLocked() { + if (mService == null && !mBinding) { + return; + } + + mBinding = false; + mService = null; + + if (mVerbose) { + Slog.v(TAG, "ensureUnbound(): unbinding"); + } + mContext.unbindService(mServiceConnection); + } + + private class RemoteServiceConnection implements ServiceConnection { + @Override + public void onServiceConnected(ComponentName name, IBinder service) { + synchronized (mLock) { + if (mVerbose) { + Slog.v(TAG, "onServiceConnected()"); + } + if (mDestroyed || !mBinding) { + Slog.wtf(TAG, "onServiceConnected() dispatched after unbindService"); + return; + } + mBinding = false; + mService = service; + } + } + + @Override + public void onServiceDisconnected(ComponentName name) { + synchronized (mLock) { + if (mVerbose) { + Slog.v(TAG, "onServiceDisconnected()"); + } + mBinding = true; + mService = null; + } + } + } +} diff --git a/services/systemcaptions/java/com/android/server/systemcaptions/SystemCaptionsManagerPerUserService.java b/services/systemcaptions/java/com/android/server/systemcaptions/SystemCaptionsManagerPerUserService.java new file mode 100644 index 000000000000..b503670e9dcd --- /dev/null +++ b/services/systemcaptions/java/com/android/server/systemcaptions/SystemCaptionsManagerPerUserService.java @@ -0,0 +1,113 @@ +/* + * Copyright (C) 2019 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.systemcaptions; + +import android.annotation.NonNull; +import android.annotation.Nullable; +import android.annotation.UserIdInt; +import android.app.AppGlobals; +import android.content.ComponentName; +import android.content.pm.PackageManager; +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; + +/** Manages the captions manager service on a per-user basis. */ +final class SystemCaptionsManagerPerUserService extends + AbstractPerUserSystemService<SystemCaptionsManagerPerUserService, + SystemCaptionsManagerService> { + + private static final String TAG = SystemCaptionsManagerPerUserService.class.getSimpleName(); + + @Nullable + @GuardedBy("mLock") + private RemoteSystemCaptionsManagerService mRemoteService; + + SystemCaptionsManagerPerUserService( + @NonNull SystemCaptionsManagerService master, + @NonNull Object lock, boolean disabled, @UserIdInt int userId) { + super(master, lock, userId); + } + + @Override + @NonNull + protected ServiceInfo newServiceInfoLocked( + @SuppressWarnings("unused") @NonNull ComponentName serviceComponent) + throws PackageManager.NameNotFoundException { + try { + return AppGlobals.getPackageManager().getServiceInfo(serviceComponent, + PackageManager.GET_META_DATA, mUserId); + } catch (RemoteException e) { + throw new PackageManager.NameNotFoundException( + "Could not get service for " + serviceComponent); + } + } + + @GuardedBy("mLock") + void initializeLocked() { + if (mMaster.verbose) { + Slog.v(TAG, "initialize()"); + } + + RemoteSystemCaptionsManagerService service = getRemoteServiceLocked(); + if (service == null && mMaster.verbose) { + Slog.v(TAG, "initialize(): Failed to init remote server"); + } + } + + @GuardedBy("mLock") + void destroyLocked() { + if (mMaster.verbose) { + Slog.v(TAG, "destroyLocked()"); + } + + if (mRemoteService != null) { + mRemoteService.destroy(); + mRemoteService = null; + } + } + + @GuardedBy("mLock") + @Nullable + private RemoteSystemCaptionsManagerService getRemoteServiceLocked() { + if (mRemoteService == null) { + String serviceName = getComponentNameLocked(); + if (serviceName == null) { + if (mMaster.verbose) { + Slog.v(TAG, "getRemoteServiceLocked(): Not set"); + } + return null; + } + + ComponentName serviceComponent = ComponentName.unflattenFromString(serviceName); + mRemoteService = new RemoteSystemCaptionsManagerService( + getContext(), + serviceComponent, + mUserId, + mMaster.verbose); + if (mMaster.verbose) { + Slog.v(TAG, "getRemoteServiceLocked(): initialize for user " + mUserId); + } + mRemoteService.initialize(); + } + + return mRemoteService; + } +} diff --git a/services/systemcaptions/java/com/android/server/systemcaptions/SystemCaptionsManagerService.java b/services/systemcaptions/java/com/android/server/systemcaptions/SystemCaptionsManagerService.java new file mode 100644 index 000000000000..27a116c8043e --- /dev/null +++ b/services/systemcaptions/java/com/android/server/systemcaptions/SystemCaptionsManagerService.java @@ -0,0 +1,61 @@ +/* + * Copyright (C) 2019 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.systemcaptions; + +import android.annotation.NonNull; +import android.annotation.UserIdInt; +import android.content.Context; + +import com.android.server.infra.AbstractMasterSystemService; +import com.android.server.infra.FrameworkResourcesServiceNameResolver; + +/** A system service to bind to a remote system captions manager service. */ +public final class SystemCaptionsManagerService extends + AbstractMasterSystemService<SystemCaptionsManagerService, + SystemCaptionsManagerPerUserService> { + + public SystemCaptionsManagerService(@NonNull Context context) { + super(context, + new FrameworkResourcesServiceNameResolver( + context, + com.android.internal.R.string.config_defaultSystemCaptionsManagerService), + /*disallowProperty=*/ null, + /*packageUpdatePolicy=*/ PACKAGE_UPDATE_POLICY_REFRESH_EAGER); + } + + @Override + public void onStart() { + // Do nothing. This service does not publish any local or system services. + } + + @Override + protected SystemCaptionsManagerPerUserService newServiceLocked( + @UserIdInt int resolvedUserId, boolean disabled) { + SystemCaptionsManagerPerUserService perUserService = + new SystemCaptionsManagerPerUserService(this, mLock, disabled, resolvedUserId); + perUserService.initializeLocked(); + return perUserService; + } + + @Override + protected void onServiceRemoved( + SystemCaptionsManagerPerUserService service, @UserIdInt int userId) { + synchronized (mLock) { + service.destroyLocked(); + } + } +} |