blob: 1aab6722dfeea10e0fe8896394d4017ea9fee5e3 [file] [log] [blame]
/*
* 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 static com.android.internal.util.function.pooled.PooledLambda.obtainMessage;
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 static final int MSG_BIND = 1;
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()");
}
scheduleBind();
}
/**
* Destroys this service.
*/
public void destroy() {
mHandler.sendMessage(
obtainMessage(RemoteSystemCaptionsManagerService::handleDestroy, this));
}
void handleDestroy() {
if (mVerbose) {
Slog.v(TAG, "handleDestroy()");
}
synchronized (mLock) {
if (mDestroyed) {
if (mVerbose) {
Slog.v(TAG, "handleDestroy(): Already destroyed");
}
return;
}
mDestroyed = true;
ensureUnboundLocked();
}
}
boolean isDestroyed() {
synchronized (mLock) {
return mDestroyed;
}
}
private void scheduleBind() {
if (mHandler.hasMessages(MSG_BIND)) {
if (mVerbose) Slog.v(TAG, "scheduleBind(): already scheduled");
return;
}
mHandler.sendMessage(
obtainMessage(RemoteSystemCaptionsManagerService::handleEnsureBound, this)
.setWhat(MSG_BIND));
}
private void handleEnsureBound() {
synchronized (mLock) {
if (mService != null || mBinding) {
return;
}
if (mVerbose) {
Slog.v(TAG, "handleEnsureBound(): binding");
}
mBinding = true;
int flags = Context.BIND_AUTO_CREATE | Context.BIND_FOREGROUND_SERVICE
| Context.BIND_INCLUDE_CAPABILITIES;
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;
}
}
}
}