Skip to content

Commit

Permalink
Add system captions manager service
Browse files Browse the repository at this point in the history
This service connects to a remote system captions manager service. This
service is responsible for enabling system captions when the user
requests them. As the system binds to it, this service will be
persistent.

Cherry pick from ag/6761232

Bug: 128925852
Test: Manual. I created an implementation of the service.
Merged-In: Iafde1bb68f4754d8167624f47c6833d43c0ec336
Change-Id: Iafde1bb68f4754d8167624f47c6833d43c0ec336
  • Loading branch information
robertjamesberry committed Apr 7, 2019
1 parent 02cca81 commit 835123d
Show file tree
Hide file tree
Showing 10 changed files with 424 additions and 14 deletions.
6 changes: 6 additions & 0 deletions core/res/res/values/config.xml
Original file line number Diff line number Diff line change
Expand Up @@ -3558,6 +3558,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
Expand Down
1 change: 1 addition & 0 deletions core/res/res/values/symbols.xml
Original file line number Diff line number Diff line change
Expand Up @@ -3409,6 +3409,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" />
Expand Down
1 change: 1 addition & 0 deletions services/Android.bp
Original file line number Diff line number Diff line change
Expand Up @@ -31,6 +31,7 @@ java_library {
"services.print",
"services.restrictions",
"services.startop",
"services.systemcaptions",
"services.usage",
"services.usb",
"services.voiceinteraction",
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -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));
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -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;
Expand Down Expand Up @@ -45,6 +46,8 @@
import com.android.server.SystemService;

import java.io.PrintWriter;
import java.lang.annotation.Retention;
import java.lang.annotation.RetentionPolicy;
import java.util.List;

/**
Expand Down Expand Up @@ -75,6 +78,30 @@
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
*/
Expand Down Expand Up @@ -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.
Expand All @@ -154,7 +184,7 @@ protected AbstractMasterSystemService(@NonNull Context context,
@Nullable ServiceNameResolver serviceNameResolver,
@Nullable String disallowProperty) {
this(context, serviceNameResolver, disallowProperty,
/* refreshServiceOnPackageUpdate=*/ true);
/*packageUpdatePolicy=*/ PACKAGE_UPDATE_POLICY_REFRESH_LAZY);
}

/**
Expand All @@ -167,17 +197,19 @@ protected AbstractMasterSystemService(@NonNull Context context,
* @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) {
Expand Down Expand Up @@ -645,7 +677,7 @@ protected void dumpLocked(@NonNull String prefix, @NonNull PrintWriter pw) {
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);
}
Expand Down Expand Up @@ -701,12 +733,21 @@ public void onPackageUpdateStarted(@NonNull String packageName, int uid) {
}
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 "
Expand Down
19 changes: 18 additions & 1 deletion services/java/com/android/server/SystemServer.java
Original file line number Diff line number Diff line change
Expand Up @@ -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 =
Expand Down Expand Up @@ -1232,6 +1234,8 @@ private void startOtherServices() {
startContentCaptureService(context);
startAttentionService(context);

startSystemCaptionsManagerService(context);

// App prediction manager service
traceBeginAndSlog("StartAppPredictionService");
mSystemServiceManager.startService(APP_PREDICTION_MANAGER_SERVICE_CLASS);
Expand Down Expand Up @@ -2225,6 +2229,19 @@ private void startOtherServices() {
}, 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;
Expand Down Expand Up @@ -2273,7 +2290,7 @@ private void startAttentionService(@NonNull Context context) {
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"));
Expand Down
5 changes: 5 additions & 0 deletions services/systemcaptions/Android.bp
Original file line number Diff line number Diff line change
@@ -0,0 +1,5 @@
java_library_static {
name: "services.systemcaptions",
srcs: ["java/**/*.java"],
libs: ["services.core"],
}
Original file line number Diff line number Diff line change
@@ -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;
}
}
}
}
Loading

0 comments on commit 835123d

Please sign in to comment.