OSDN Git Service

Add a VR listener service.
[android-x86/frameworks-base.git] / services / core / java / com / android / server / utils / ManagedApplicationService.java
1 /**
2  * Copyright (c) 2016, The Android Open Source Project
3  *
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
7  *
8  *     http://www.apache.org/licenses/LICENSE-2.0
9  *
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.
15  */
16 package com.android.server.utils;
17
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;
30
31 import java.util.Objects;
32
33 /**
34  * Manages the lifecycle of an application-provided service bound from system server.
35  *
36  * @hide
37  */
38 public class ManagedApplicationService {
39     private final String TAG = getClass().getSimpleName();
40
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;
47
48     private final DeathRecipient mDeathRecipient = new DeathRecipient() {
49         @Override
50         public void binderDied() {
51             synchronized (mLock) {
52                 mBoundInterface = null;
53             }
54         }
55     };
56
57     private final Object mLock = new Object();
58
59     // State protected by mLock
60     private ServiceConnection mPendingConnection;
61     private ServiceConnection mConnection;
62     private IInterface mBoundInterface;
63
64
65     private ManagedApplicationService(final Context context, final ComponentName component,
66             final int userId, int clientLabel, String settingsAction,
67             BinderChecker binderChecker) {
68         mContext = context;
69         mComponent = component;
70         mUserId = userId;
71         mClientLabel = clientLabel;
72         mSettingsAction = settingsAction;
73         mChecker = binderChecker;
74     }
75
76     /**
77      * Implement to validate returned IBinder instance.
78      */
79     public interface BinderChecker {
80         IInterface asInterface(IBinder binder);
81         boolean checkType(IInterface service);
82     }
83
84     /**
85      * Create a new ManagedApplicationService object but do not yet bind to the user service.
86      *
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
91      *      binding service.
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.
96      */
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);
102     }
103
104     /**
105      * @return the user ID of the user that owns the bound service.
106      */
107     public int getUserId() {
108         return mUserId;
109     }
110
111     /**
112      * @return the component of the bound service.
113      */
114     public ComponentName getComponent() {
115         return mComponent;
116     }
117
118     /**
119      * Asynchronously unbind from the application service if the bound service component and user
120      * does not match the given signature.
121      *
122      * @param componentName the component that must match.
123      * @param userId the user ID that must match.
124      * @return {@code true} if not matching.
125      */
126     public boolean disconnectIfNotMatching(final ComponentName componentName, final int userId) {
127         if (matches(componentName, userId)) {
128             return false;
129         }
130         disconnect();
131         return true;
132     }
133
134     /**
135      * Asynchronously unbind from the application service if bound.
136      */
137     public void disconnect() {
138         synchronized (mLock) {
139             // Wipe out pending connections
140             mPendingConnection = null;
141
142             // Unbind existing connection, if it exists
143             if (mConnection != null) {
144                 mContext.unbindService(mConnection);
145                 mConnection = null;
146             }
147
148             mBoundInterface = null;
149         }
150     }
151
152     /**
153      * Asynchronously bind to the application service if not bound.
154      */
155     public void connect() {
156         synchronized (mLock) {
157             if (mConnection != null || mPendingConnection != null) {
158                 // We're already connected or are trying to connect
159                 return;
160             }
161
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);
167
168             final ServiceConnection serviceConnection = new ServiceConnection() {
169                 @Override
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;
175                             mConnection = this;
176                         } else {
177                             // Service connection wasn't pending, must have been disconnected
178                             mContext.unbindService(this);
179                         }
180
181                         try {
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;
188                             }
189                         } catch (RemoteException e) {
190                             // DOA
191                             Slog.w(TAG, "Unable to bind service: " + intent, e);
192                             mBoundInterface = null;
193                         }
194                     }
195                 }
196
197                 @Override
198                 public void onServiceDisconnected(ComponentName componentName) {
199                     Slog.w(TAG, "Service disconnected: " + intent);
200                 }
201             };
202
203             mPendingConnection = serviceConnection;
204
205             try {
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);
210                 }
211             } catch (SecurityException e) {
212                 Slog.w(TAG, "Unable to bind service: " + intent, e);
213             }
214         }
215     }
216
217     private boolean matches(final ComponentName component, final int userId) {
218         return Objects.equals(mComponent, component) && mUserId == userId;
219     }
220 }