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.util.Slog; 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 = Log.isLoggable(TAG, Log.DEBUG); 69 70 public static final String INPUT_CALLBACK_BINDER_KEY = "callback_binder"; 71 public static final int INPUT_CALLBACK_BINDER_CODE = IBinder.FIRST_CALL_TRANSACTION; 72 73 private final InputFilter[] mHandledKeys; 74 75 private InputBinder mInputBinder; 76 CarInputHandlingService(InputFilter[] handledKeys)77 protected CarInputHandlingService(InputFilter[] handledKeys) { 78 if (handledKeys == null) { 79 throw new IllegalArgumentException("handledKeys is null"); 80 } 81 82 mHandledKeys = new InputFilter[handledKeys.length]; 83 System.arraycopy(handledKeys, 0, mHandledKeys, 0, handledKeys.length); 84 } 85 86 @Override 87 @CallSuper onBind(Intent intent)88 public IBinder onBind(Intent intent) { 89 if (DBG) { 90 Slog.d(TAG, "onBind, intent: " + intent); 91 } 92 93 doCallbackIfPossible(intent.getExtras()); 94 95 if (mInputBinder == null) { 96 mInputBinder = new InputBinder(); 97 } 98 99 return mInputBinder; 100 } 101 doCallbackIfPossible(Bundle extras)102 private void doCallbackIfPossible(Bundle extras) { 103 if (extras == null) { 104 Slog.i(TAG, "doCallbackIfPossible: extras are null"); 105 return; 106 } 107 IBinder callbackBinder = extras.getBinder(INPUT_CALLBACK_BINDER_KEY); 108 if (callbackBinder == null) { 109 Slog.i(TAG, "doCallbackIfPossible: callback IBinder is null"); 110 return; 111 } 112 Parcel dataIn = Parcel.obtain(); 113 try { 114 dataIn.writeTypedArray(mHandledKeys, 0); 115 callbackBinder.transact(INPUT_CALLBACK_BINDER_CODE, dataIn, null, IBinder.FLAG_ONEWAY); 116 } catch (RemoteException e) { 117 Car.handleRemoteExceptionFromCarService(this, e); 118 } finally { 119 dataIn.recycle(); 120 } 121 } 122 123 /** 124 * Called when key event has been received. 125 */ 126 @MainThread onKeyEvent(KeyEvent keyEvent, int targetDisplay)127 protected abstract void onKeyEvent(KeyEvent keyEvent, int targetDisplay); 128 129 @Override dump(FileDescriptor fd, PrintWriter writer, String[] args)130 protected void dump(FileDescriptor fd, PrintWriter writer, String[] args) { 131 writer.println("**" + getClass().getSimpleName() + "**"); 132 writer.println("input binder: " + mInputBinder); 133 } 134 135 private class InputBinder extends ICarInputListener.Stub { 136 private final EventHandler mEventHandler; 137 InputBinder()138 InputBinder() { 139 mEventHandler = new EventHandler(CarInputHandlingService.this); 140 } 141 142 @Override onKeyEvent(KeyEvent keyEvent, int targetDisplay)143 public void onKeyEvent(KeyEvent keyEvent, int targetDisplay) throws RemoteException { 144 mEventHandler.doKeyEvent(keyEvent, targetDisplay); 145 } 146 } 147 148 private static class EventHandler extends Handler { 149 private static final int KEY_EVENT = 0; 150 private final WeakReference<CarInputHandlingService> mRefService; 151 EventHandler(CarInputHandlingService service)152 EventHandler(CarInputHandlingService service) { 153 mRefService = new WeakReference<>(service); 154 } 155 156 @Override handleMessage(Message msg)157 public void handleMessage(Message msg) { 158 CarInputHandlingService service = mRefService.get(); 159 if (service == null) { 160 return; 161 } 162 163 if (msg.what == KEY_EVENT) { 164 service.onKeyEvent((KeyEvent) msg.obj, msg.arg1); 165 } else { 166 throw new IllegalArgumentException("Unexpected message: " + msg); 167 } 168 } 169 doKeyEvent(KeyEvent event, int targetDisplay)170 void doKeyEvent(KeyEvent event, int targetDisplay) { 171 sendMessage(obtainMessage(KEY_EVENT, targetDisplay, 0, event)); 172 } 173 } 174 175 /** 176 * Filter for input events that are handled by custom service. 177 */ 178 public static final class InputFilter implements Parcelable { 179 public final int mKeyCode; 180 public final int mTargetDisplay; 181 InputFilter(int keyCode, int targetDisplay)182 public InputFilter(int keyCode, int targetDisplay) { 183 mKeyCode = keyCode; 184 mTargetDisplay = targetDisplay; 185 } 186 187 // Parcelling part InputFilter(Parcel in)188 InputFilter(Parcel in) { 189 mKeyCode = in.readInt(); 190 mTargetDisplay = in.readInt(); 191 } 192 193 @Override describeContents()194 public int describeContents() { 195 return 0; 196 } 197 198 @Override writeToParcel(Parcel dest, int flags)199 public void writeToParcel(Parcel dest, int flags) { 200 dest.writeInt(mKeyCode); 201 dest.writeInt(mTargetDisplay); 202 } 203 204 public static final Parcelable.Creator CREATOR = new Parcelable.Creator() { 205 public InputFilter createFromParcel(Parcel in) { 206 return new InputFilter(in); 207 } 208 209 public InputFilter[] newArray(int size) { 210 return new InputFilter[size]; 211 } 212 }; 213 } 214 } 215