1 /* 2 * Copyright (C) 2024 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 android.app.appsearch.functions; 17 18 import android.annotation.NonNull; 19 import android.content.ComponentName; 20 import android.content.Context; 21 import android.content.Intent; 22 import android.content.ServiceConnection; 23 import android.os.Handler; 24 import android.os.IBinder; 25 import android.os.Looper; 26 import android.os.UserHandle; 27 import android.util.Log; 28 29 import java.util.concurrent.Executor; 30 import java.util.function.Function; 31 32 /** 33 * An implementation of {@link ServiceCallHelper} that that is based on {@link Context#bindService}. 34 * 35 * @hide 36 */ 37 public class ServiceCallHelperImpl<T> implements ServiceCallHelper<T> { 38 private static final String TAG = "AppSearchAppFunction"; 39 40 @NonNull private final Context mContext; 41 @NonNull private final Function<IBinder, T> mInterfaceConverter; 42 private final Handler mHandler = new Handler(Looper.getMainLooper()); 43 private final Executor mExecutor; 44 45 /** 46 * @param interfaceConverter A function responsible for converting an IBinder object into the 47 * desired service interface. 48 * @param executor An Executor instance to dispatch callback. 49 * @param context The system context. 50 */ ServiceCallHelperImpl( @onNull Context context, @NonNull Function<IBinder, T> interfaceConverter, @NonNull Executor executor)51 public ServiceCallHelperImpl( 52 @NonNull Context context, 53 @NonNull Function<IBinder, T> interfaceConverter, 54 @NonNull Executor executor) { 55 mContext = context; 56 mInterfaceConverter = interfaceConverter; 57 mExecutor = executor; 58 } 59 60 @Override runServiceCall( @onNull Intent intent, int bindFlags, long timeoutInMillis, @NonNull UserHandle userHandle, @NonNull RunServiceCallCallback<T> callback)61 public boolean runServiceCall( 62 @NonNull Intent intent, 63 int bindFlags, 64 long timeoutInMillis, 65 @NonNull UserHandle userHandle, 66 @NonNull RunServiceCallCallback<T> callback) { 67 OneOffServiceConnection serviceConnection = 68 new OneOffServiceConnection( 69 intent, bindFlags, timeoutInMillis, userHandle, callback); 70 71 return serviceConnection.bindAndRun(); 72 } 73 74 private class OneOffServiceConnection 75 implements ServiceConnection, ServiceUsageCompleteListener { 76 private final Intent mIntent; 77 private final int mFlags; 78 private final long mTimeoutMillis; 79 private final UserHandle mUserHandle; 80 private final RunServiceCallCallback<T> mCallback; 81 private final Runnable mTimeoutCallback; 82 OneOffServiceConnection( @onNull Intent intent, int flags, long timeoutMillis, @NonNull UserHandle userHandle, @NonNull RunServiceCallCallback<T> callback)83 OneOffServiceConnection( 84 @NonNull Intent intent, 85 int flags, 86 long timeoutMillis, 87 @NonNull UserHandle userHandle, 88 @NonNull RunServiceCallCallback<T> callback) { 89 mIntent = intent; 90 mFlags = flags; 91 mTimeoutMillis = timeoutMillis; 92 mCallback = callback; 93 mTimeoutCallback = 94 () -> 95 mExecutor.execute( 96 () -> { 97 safeUnbind(); 98 mCallback.onTimedOut(); 99 }); 100 mUserHandle = userHandle; 101 } 102 bindAndRun()103 public boolean bindAndRun() { 104 boolean bindServiceResult = 105 mContext.bindServiceAsUser(mIntent, this, mFlags, mUserHandle); 106 107 if (bindServiceResult) { 108 mHandler.postDelayed(mTimeoutCallback, mTimeoutMillis); 109 } else { 110 safeUnbind(); 111 } 112 113 return bindServiceResult; 114 } 115 116 @Override onServiceConnected(ComponentName name, IBinder service)117 public void onServiceConnected(ComponentName name, IBinder service) { 118 T serviceInterface = mInterfaceConverter.apply(service); 119 120 mExecutor.execute(() -> mCallback.onServiceConnected(serviceInterface, this)); 121 } 122 123 @Override onServiceDisconnected(ComponentName name)124 public void onServiceDisconnected(ComponentName name) { 125 safeUnbind(); 126 mExecutor.execute(mCallback::onFailedToConnect); 127 } 128 129 @Override onBindingDied(ComponentName name)130 public void onBindingDied(ComponentName name) { 131 safeUnbind(); 132 mExecutor.execute(mCallback::onFailedToConnect); 133 } 134 135 @Override onNullBinding(ComponentName name)136 public void onNullBinding(ComponentName name) { 137 safeUnbind(); 138 mExecutor.execute(mCallback::onFailedToConnect); 139 } 140 safeUnbind()141 private void safeUnbind() { 142 try { 143 mHandler.removeCallbacks(mTimeoutCallback); 144 mContext.unbindService(this); 145 } catch (Exception ex) { 146 Log.w(TAG, "Failed to unbind", ex); 147 } 148 } 149 150 @Override onCompleted()151 public void onCompleted() { 152 safeUnbind(); 153 } 154 } 155 } 156