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 android.car.input; 17 18 import static com.android.car.internal.ExcludeFromCodeCoverageGeneratedReport.DEPRECATED_CODE; 19 20 import android.annotation.CallSuper; 21 import android.annotation.MainThread; 22 import android.annotation.SystemApi; 23 import android.app.Service; 24 import android.car.Car; 25 import android.car.CarLibLog; 26 import android.car.annotation.AddedInOrBefore; 27 import android.content.Intent; 28 import android.os.Bundle; 29 import android.os.Handler; 30 import android.os.IBinder; 31 import android.os.Message; 32 import android.os.Parcel; 33 import android.os.Parcelable; 34 import android.os.RemoteException; 35 import android.util.Log; 36 import android.view.KeyEvent; 37 38 import com.android.car.internal.ExcludeFromCodeCoverageGeneratedReport; 39 40 import java.io.FileDescriptor; 41 import java.io.PrintWriter; 42 import java.lang.ref.WeakReference; 43 44 /** 45 * A service that is used for handling of input events. 46 * 47 * <p>To extend this class, you must declare the service in your manifest file with 48 * the {@code android.car.permission.BIND_CAR_INPUT_SERVICE} permission 49 * <pre> 50 * <service android:name=".MyCarInputService" 51 * android:permission="android.car.permission.BIND_CAR_INPUT_SERVICE"> 52 * </service></pre> 53 * <p>Also, you will need to register this service in the following configuration file: 54 * {@code packages/services/Car/service/res/values/config.xml} 55 * 56 * @deprecated No longer needed after the new Car Input API was introduced (see 57 * {@link CarInputManager} for more details). 58 * 59 * @removed 60 * 61 * @hide 62 */ 63 @SystemApi 64 @Deprecated 65 @ExcludeFromCodeCoverageGeneratedReport(reason = DEPRECATED_CODE) 66 public abstract class CarInputHandlingService extends Service { 67 private static final String TAG = CarLibLog.TAG_INPUT; 68 private static final boolean DBG = false; 69 70 @AddedInOrBefore(majorVersion = 33) 71 public static final String INPUT_CALLBACK_BINDER_KEY = "callback_binder"; 72 @AddedInOrBefore(majorVersion = 33) 73 public static final int INPUT_CALLBACK_BINDER_CODE = IBinder.FIRST_CALL_TRANSACTION; 74 75 private final InputFilter[] mHandledKeys; 76 77 private InputBinder mInputBinder; 78 CarInputHandlingService(InputFilter[] handledKeys)79 protected CarInputHandlingService(InputFilter[] handledKeys) { 80 if (handledKeys == null) { 81 throw new IllegalArgumentException("handledKeys is null"); 82 } 83 84 mHandledKeys = new InputFilter[handledKeys.length]; 85 System.arraycopy(handledKeys, 0, mHandledKeys, 0, handledKeys.length); 86 } 87 88 @Override 89 @CallSuper 90 @AddedInOrBefore(majorVersion = 33) onBind(Intent intent)91 public IBinder onBind(Intent intent) { 92 if (DBG) { 93 Log.d(TAG, "onBind, intent: " + intent); 94 } 95 96 doCallbackIfPossible(intent.getExtras()); 97 98 if (mInputBinder == null) { 99 mInputBinder = new InputBinder(); 100 } 101 102 return mInputBinder; 103 } 104 doCallbackIfPossible(Bundle extras)105 private void doCallbackIfPossible(Bundle extras) { 106 if (extras == null) { 107 Log.i(TAG, "doCallbackIfPossible: extras are null"); 108 return; 109 } 110 IBinder callbackBinder = extras.getBinder(INPUT_CALLBACK_BINDER_KEY); 111 if (callbackBinder == null) { 112 Log.i(TAG, "doCallbackIfPossible: callback IBinder is null"); 113 return; 114 } 115 Parcel dataIn = Parcel.obtain(); 116 try { 117 dataIn.writeTypedArray(mHandledKeys, 0); 118 callbackBinder.transact(INPUT_CALLBACK_BINDER_CODE, dataIn, null, IBinder.FLAG_ONEWAY); 119 } catch (RemoteException e) { 120 Car.handleRemoteExceptionFromCarService(this, e); 121 } finally { 122 dataIn.recycle(); 123 } 124 } 125 126 /** 127 * Called when key event has been received. 128 */ 129 @MainThread 130 @AddedInOrBefore(majorVersion = 33) onKeyEvent(KeyEvent keyEvent, int targetDisplay)131 protected abstract void onKeyEvent(KeyEvent keyEvent, int targetDisplay); 132 133 @Override 134 @AddedInOrBefore(majorVersion = 33) dump(FileDescriptor fd, PrintWriter writer, String[] args)135 protected void dump(FileDescriptor fd, PrintWriter writer, String[] args) { 136 writer.println("**" + getClass().getSimpleName() + "**"); 137 writer.println("input binder: " + mInputBinder); 138 } 139 140 private class InputBinder extends ICarInputListener.Stub { 141 private final EventHandler mEventHandler; 142 InputBinder()143 InputBinder() { 144 mEventHandler = new EventHandler(CarInputHandlingService.this); 145 } 146 147 @Override onKeyEvent(KeyEvent keyEvent, int targetDisplay)148 public void onKeyEvent(KeyEvent keyEvent, int targetDisplay) throws RemoteException { 149 mEventHandler.doKeyEvent(keyEvent, targetDisplay); 150 } 151 } 152 153 private static class EventHandler extends Handler { 154 private static final int KEY_EVENT = 0; 155 private final WeakReference<CarInputHandlingService> mRefService; 156 EventHandler(CarInputHandlingService service)157 EventHandler(CarInputHandlingService service) { 158 mRefService = new WeakReference<>(service); 159 } 160 161 @Override handleMessage(Message msg)162 public void handleMessage(Message msg) { 163 CarInputHandlingService service = mRefService.get(); 164 if (service == null) { 165 return; 166 } 167 168 if (msg.what == KEY_EVENT) { 169 service.onKeyEvent((KeyEvent) msg.obj, msg.arg1); 170 } else { 171 throw new IllegalArgumentException("Unexpected message: " + msg); 172 } 173 } 174 doKeyEvent(KeyEvent event, int targetDisplay)175 void doKeyEvent(KeyEvent event, int targetDisplay) { 176 sendMessage(obtainMessage(KEY_EVENT, targetDisplay, 0, event)); 177 } 178 } 179 180 /** 181 * Filter for input events that are handled by custom service. 182 */ 183 public static final class InputFilter implements Parcelable { 184 @AddedInOrBefore(majorVersion = 33) 185 public final int mKeyCode; 186 @AddedInOrBefore(majorVersion = 33) 187 public final int mTargetDisplay; 188 InputFilter(int keyCode, int targetDisplay)189 public InputFilter(int keyCode, int targetDisplay) { 190 mKeyCode = keyCode; 191 mTargetDisplay = targetDisplay; 192 } 193 194 // Parcelling part InputFilter(Parcel in)195 InputFilter(Parcel in) { 196 mKeyCode = in.readInt(); 197 mTargetDisplay = in.readInt(); 198 } 199 200 @Override 201 @AddedInOrBefore(majorVersion = 33) describeContents()202 public int describeContents() { 203 return 0; 204 } 205 206 @Override 207 @AddedInOrBefore(majorVersion = 33) writeToParcel(Parcel dest, int flags)208 public void writeToParcel(Parcel dest, int flags) { 209 dest.writeInt(mKeyCode); 210 dest.writeInt(mTargetDisplay); 211 } 212 213 @AddedInOrBefore(majorVersion = 33) 214 public static final Parcelable.Creator CREATOR = new Parcelable.Creator() { 215 public InputFilter createFromParcel(Parcel in) { 216 return new InputFilter(in); 217 } 218 219 public InputFilter[] newArray(int size) { 220 return new InputFilter[size]; 221 } 222 }; 223 } 224 } 225