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 private PendingEvent mPendingEvent; 64 ManagedApplicationService(final Context context, final ComponentName component, final int userId, int clientLabel, String settingsAction, BinderChecker binderChecker)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 { asInterface(IBinder binder)80 IInterface asInterface(IBinder binder); checkType(IInterface service)81 boolean checkType(IInterface service); 82 } 83 84 /** 85 * Implement to call IInterface methods after service is connected. 86 */ 87 public interface PendingEvent { runEvent(IInterface service)88 void runEvent(IInterface service) throws RemoteException; 89 } 90 91 /** 92 * Create a new ManagedApplicationService object but do not yet bind to the user service. 93 * 94 * @param context a Context to use for binding the application service. 95 * @param component the {@link ComponentName} of the application service to bind. 96 * @param userId the user ID of user to bind the application service as. 97 * @param clientLabel the resource ID of a label displayed to the user indicating the 98 * binding service. 99 * @param settingsAction an action that can be used to open the Settings UI to enable/disable 100 * binding to these services. 101 * @param binderChecker an interface used to validate the returned binder object. 102 * @return a ManagedApplicationService instance. 103 */ build(@onNull final Context context, @NonNull final ComponentName component, final int userId, @NonNull int clientLabel, @NonNull String settingsAction, @NonNull BinderChecker binderChecker)104 public static ManagedApplicationService build(@NonNull final Context context, 105 @NonNull final ComponentName component, final int userId, @NonNull int clientLabel, 106 @NonNull String settingsAction, @NonNull BinderChecker binderChecker) { 107 return new ManagedApplicationService(context, component, userId, clientLabel, 108 settingsAction, binderChecker); 109 } 110 111 /** 112 * @return the user ID of the user that owns the bound service. 113 */ getUserId()114 public int getUserId() { 115 return mUserId; 116 } 117 118 /** 119 * @return the component of the bound service. 120 */ getComponent()121 public ComponentName getComponent() { 122 return mComponent; 123 } 124 125 /** 126 * Asynchronously unbind from the application service if the bound service component and user 127 * does not match the given signature. 128 * 129 * @param componentName the component that must match. 130 * @param userId the user ID that must match. 131 * @return {@code true} if not matching. 132 */ disconnectIfNotMatching(final ComponentName componentName, final int userId)133 public boolean disconnectIfNotMatching(final ComponentName componentName, final int userId) { 134 if (matches(componentName, userId)) { 135 return false; 136 } 137 disconnect(); 138 return true; 139 } 140 141 142 /** 143 * Send an event to run as soon as the binder interface is available. 144 * 145 * @param event a {@link PendingEvent} to send. 146 */ sendEvent(@onNull PendingEvent event)147 public void sendEvent(@NonNull PendingEvent event) { 148 IInterface iface; 149 synchronized (mLock) { 150 iface = mBoundInterface; 151 if (iface == null) { 152 mPendingEvent = event; 153 } 154 } 155 156 if (iface != null) { 157 try { 158 event.runEvent(iface); 159 } catch (RuntimeException | RemoteException ex) { 160 Slog.e(TAG, "Received exception from user service: ", ex); 161 } 162 } 163 } 164 165 /** 166 * Asynchronously unbind from the application service if bound. 167 */ disconnect()168 public void disconnect() { 169 synchronized (mLock) { 170 // Wipe out pending connections 171 mPendingConnection = null; 172 173 // Unbind existing connection, if it exists 174 if (mConnection != null) { 175 mContext.unbindService(mConnection); 176 mConnection = null; 177 } 178 179 mBoundInterface = null; 180 } 181 } 182 183 /** 184 * Asynchronously bind to the application service if not bound. 185 */ connect()186 public void connect() { 187 synchronized (mLock) { 188 if (mConnection != null || mPendingConnection != null) { 189 // We're already connected or are trying to connect 190 return; 191 } 192 193 final PendingIntent pendingIntent = PendingIntent.getActivity( 194 mContext, 0, new Intent(mSettingsAction), 0); 195 final Intent intent = new Intent().setComponent(mComponent). 196 putExtra(Intent.EXTRA_CLIENT_LABEL, mClientLabel). 197 putExtra(Intent.EXTRA_CLIENT_INTENT, pendingIntent); 198 199 final ServiceConnection serviceConnection = new ServiceConnection() { 200 @Override 201 public void onServiceConnected(ComponentName componentName, IBinder iBinder) { 202 IInterface iface = null; 203 PendingEvent pendingEvent = null; 204 synchronized (mLock) { 205 if (mPendingConnection == this) { 206 // No longer pending, remove from pending connection 207 mPendingConnection = null; 208 mConnection = this; 209 } else { 210 // Service connection wasn't pending, must have been disconnected 211 mContext.unbindService(this); 212 return; 213 } 214 215 try { 216 iBinder.linkToDeath(mDeathRecipient, 0); 217 mBoundInterface = mChecker.asInterface(iBinder); 218 if (!mChecker.checkType(mBoundInterface)) { 219 // Received an invalid binder, disconnect 220 mContext.unbindService(this); 221 mBoundInterface = null; 222 } 223 iface = mBoundInterface; 224 pendingEvent = mPendingEvent; 225 mPendingEvent = null; 226 } catch (RemoteException e) { 227 // DOA 228 Slog.w(TAG, "Unable to bind service: " + intent, e); 229 mBoundInterface = null; 230 } 231 } 232 if (iface != null && pendingEvent != null) { 233 try { 234 pendingEvent.runEvent(iface); 235 } catch (RuntimeException | RemoteException ex) { 236 Slog.e(TAG, "Received exception from user service: ", ex); 237 } 238 } 239 } 240 241 @Override 242 public void onServiceDisconnected(ComponentName componentName) { 243 Slog.w(TAG, "Service disconnected: " + intent); 244 mConnection = null; 245 mBoundInterface = null; 246 } 247 }; 248 249 mPendingConnection = serviceConnection; 250 251 try { 252 if (!mContext.bindServiceAsUser(intent, serviceConnection, 253 Context.BIND_AUTO_CREATE | Context.BIND_FOREGROUND_SERVICE, 254 new UserHandle(mUserId))) { 255 Slog.w(TAG, "Unable to bind service: " + intent); 256 } 257 } catch (SecurityException e) { 258 Slog.w(TAG, "Unable to bind service: " + intent, e); 259 } 260 } 261 } 262 matches(final ComponentName component, final int userId)263 private boolean matches(final ComponentName component, final int userId) { 264 return Objects.equals(mComponent, component) && mUserId == userId; 265 } 266 } 267