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