2 * Copyright (c) 2016, The Android Open Source Project
4 * Licensed under the Apache License, Version 2.0 (the "License");
5 * you may not use this file except in compliance with the License.
6 * You may obtain a copy of the License at
8 * http://www.apache.org/licenses/LICENSE-2.0
10 * Unless required by applicable law or agreed to in writing, software
11 * distributed under the License is distributed on an "AS IS" BASIS,
12 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
13 * See the License for the specific language governing permissions and
14 * limitations under the License.
16 package com.android.server.utils;
18 import android.annotation.NonNull;
19 import android.app.PendingIntent;
20 import android.content.ComponentName;
21 import android.content.Context;
22 import android.content.Intent;
23 import android.content.ServiceConnection;
24 import android.os.IBinder;
25 import android.os.IBinder.DeathRecipient;
26 import android.os.IInterface;
27 import android.os.RemoteException;
28 import android.os.UserHandle;
29 import android.util.Slog;
31 import java.util.Objects;
34 * Manages the lifecycle of an application-provided service bound from system server.
38 public class ManagedApplicationService {
39 private final String TAG = getClass().getSimpleName();
41 private final Context mContext;
42 private final int mUserId;
43 private final ComponentName mComponent;
44 private final int mClientLabel;
45 private final String mSettingsAction;
46 private final BinderChecker mChecker;
48 private final DeathRecipient mDeathRecipient = new DeathRecipient() {
50 public void binderDied() {
51 synchronized (mLock) {
52 mBoundInterface = null;
57 private final Object mLock = new Object();
59 // State protected by mLock
60 private ServiceConnection mPendingConnection;
61 private ServiceConnection mConnection;
62 private IInterface mBoundInterface;
65 private ManagedApplicationService(final Context context, final ComponentName component,
66 final int userId, int clientLabel, String settingsAction,
67 BinderChecker binderChecker) {
69 mComponent = component;
71 mClientLabel = clientLabel;
72 mSettingsAction = settingsAction;
73 mChecker = binderChecker;
77 * Implement to validate returned IBinder instance.
79 public interface BinderChecker {
80 IInterface asInterface(IBinder binder);
81 boolean checkType(IInterface service);
85 * Create a new ManagedApplicationService object but do not yet bind to the user service.
87 * @param context a Context to use for binding the application service.
88 * @param component the {@link ComponentName} of the application service to bind.
89 * @param userId the user ID of user to bind the application service as.
90 * @param clientLabel the resource ID of a label displayed to the user indicating the
92 * @param settingsAction an action that can be used to open the Settings UI to enable/disable
93 * binding to these services.
94 * @param binderChecker an interface used to validate the returned binder object.
95 * @return a ManagedApplicationService instance.
97 public static ManagedApplicationService build(@NonNull final Context context,
98 @NonNull final ComponentName component, final int userId, @NonNull int clientLabel,
99 @NonNull String settingsAction, @NonNull BinderChecker binderChecker) {
100 return new ManagedApplicationService(context, component, userId, clientLabel,
101 settingsAction, binderChecker);
105 * @return the user ID of the user that owns the bound service.
107 public int getUserId() {
112 * @return the component of the bound service.
114 public ComponentName getComponent() {
119 * Asynchronously unbind from the application service if the bound service component and user
120 * does not match the given signature.
122 * @param componentName the component that must match.
123 * @param userId the user ID that must match.
124 * @return {@code true} if not matching.
126 public boolean disconnectIfNotMatching(final ComponentName componentName, final int userId) {
127 if (matches(componentName, userId)) {
135 * Asynchronously unbind from the application service if bound.
137 public void disconnect() {
138 synchronized (mLock) {
139 // Wipe out pending connections
140 mPendingConnection = null;
142 // Unbind existing connection, if it exists
143 if (mConnection != null) {
144 mContext.unbindService(mConnection);
148 mBoundInterface = null;
153 * Asynchronously bind to the application service if not bound.
155 public void connect() {
156 synchronized (mLock) {
157 if (mConnection != null || mPendingConnection != null) {
158 // We're already connected or are trying to connect
162 final PendingIntent pendingIntent = PendingIntent.getActivity(
163 mContext, 0, new Intent(mSettingsAction), 0);
164 final Intent intent = new Intent().setComponent(mComponent).
165 putExtra(Intent.EXTRA_CLIENT_LABEL, mClientLabel).
166 putExtra(Intent.EXTRA_CLIENT_INTENT, pendingIntent);
168 final ServiceConnection serviceConnection = new ServiceConnection() {
170 public void onServiceConnected(ComponentName componentName, IBinder iBinder) {
171 synchronized (mLock) {
172 if (mPendingConnection == this) {
173 // No longer pending, remove from pending connection
174 mPendingConnection = null;
177 // Service connection wasn't pending, must have been disconnected
178 mContext.unbindService(this);
182 iBinder.linkToDeath(mDeathRecipient, 0);
183 mBoundInterface = mChecker.asInterface(iBinder);
184 if (!mChecker.checkType(mBoundInterface)) {
185 // Received an invalid binder, disconnect
186 mContext.unbindService(this);
187 mBoundInterface = null;
189 } catch (RemoteException e) {
191 Slog.w(TAG, "Unable to bind service: " + intent, e);
192 mBoundInterface = null;
198 public void onServiceDisconnected(ComponentName componentName) {
199 Slog.w(TAG, "Service disconnected: " + intent);
203 mPendingConnection = serviceConnection;
206 if (!mContext.bindServiceAsUser(intent, serviceConnection,
207 Context.BIND_AUTO_CREATE | Context.BIND_FOREGROUND_SERVICE,
208 new UserHandle(mUserId))) {
209 Slog.w(TAG, "Unable to bind service: " + intent);
211 } catch (SecurityException e) {
212 Slog.w(TAG, "Unable to bind service: " + intent, e);
217 private boolean matches(final ComponentName component, final int userId) {
218 return Objects.equals(mComponent, component) && mUserId == userId;