| /* |
| * 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; |
| } |
| } |
| } |
| } |