1 /* 2 * Copyright (C) 2019 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 17 package android.car; 18 19 import static com.android.internal.util.Preconditions.checkNotNull; 20 21 import android.annotation.IntDef; 22 import android.annotation.MainThread; 23 import android.annotation.NonNull; 24 import android.annotation.SystemApi; 25 import android.app.Service; 26 import android.content.Intent; 27 import android.hardware.usb.UsbDevice; 28 import android.os.Bundle; 29 import android.os.Handler; 30 import android.os.IBinder; 31 import android.os.Looper; 32 import android.os.Message; 33 import android.os.Messenger; 34 import android.os.RemoteException; 35 import android.util.Log; 36 37 import java.io.FileDescriptor; 38 import java.io.PrintWriter; 39 import java.lang.annotation.Retention; 40 import java.lang.annotation.RetentionPolicy; 41 import java.lang.ref.WeakReference; 42 43 /** 44 * The service that must be implemented by USB AOAP handler system apps. The app must hold the 45 * following permission: {@code android.car.permission.CAR_HANDLE_USB_AOAP_DEVICE}. 46 * 47 * <p>This service gets bound by the framework and the service needs to be protected by 48 * {@code android.permission.MANAGE_USB} permission to ensure nobody else can 49 * bind to the service. At most only one client should be bound at a time. 50 * 51 * @hide 52 */ 53 @SystemApi 54 public abstract class AoapService extends Service { 55 private static final String TAG = AoapService.class.getSimpleName(); 56 57 /** Indicates success or confirmation. */ 58 public static final int RESULT_OK = 0; 59 60 /** 61 * Indicates that the device is not supported by this service and system shouldn't associate 62 * given device with this service. 63 */ 64 public static final int RESULT_DEVICE_NOT_SUPPORTED = 1; 65 66 /** 67 * Indicates that device shouldn't be switch to AOAP mode at this time. 68 */ 69 public static final int RESULT_DO_NOT_SWITCH_TO_AOAP = 2; 70 71 /** @hide */ 72 @IntDef(value = { 73 RESULT_OK, RESULT_DEVICE_NOT_SUPPORTED, RESULT_DO_NOT_SWITCH_TO_AOAP 74 }) 75 @Retention(RetentionPolicy.SOURCE) 76 public @interface Result {} 77 78 79 /** 80 * A message sent from the system USB handler service to AOAP handler service to check if the 81 * device is supported. The message must have {@link #KEY_DEVICE} with {@link UsbDevice} object. 82 * 83 * @hide 84 */ 85 public static final int MSG_NEW_DEVICE_ATTACHED = 1; 86 87 /** 88 * A response message for {@link #MSG_NEW_DEVICE_ATTACHED}. Must contain {@link #KEY_RESULT} 89 * with one of the {@code RESULT_*} constant. 90 * 91 * @hide */ 92 public static final int MSG_NEW_DEVICE_ATTACHED_RESPONSE = 2; 93 94 /** 95 * A message sent from the system USB handler service to AOAP handler service to check if the 96 * device can be switched to AOAP mode. The message must have {@link #KEY_DEVICE} with 97 * {@link UsbDevice} object. 98 * 99 * @hide 100 */ 101 public static final int MSG_CAN_SWITCH_TO_AOAP = 3; 102 103 /** 104 * A response message for {@link #MSG_CAN_SWITCH_TO_AOAP}. Must contain {@link #KEY_RESULT} 105 * with one of the {@code RESULT_*} constant. 106 * 107 * @hide */ 108 public static final int MSG_CAN_SWITCH_TO_AOAP_RESPONSE = 4; 109 110 /** @hide */ 111 public static final String KEY_DEVICE = "usb-device"; 112 113 /** @hide */ 114 public static final String KEY_RESULT = "result"; 115 116 117 /** 118 * Returns {@code true} if the given USB device is supported by this service. 119 * 120 * <p>The device is not expected to be in AOAP mode when this method is called. The purpose of 121 * this method is just to give the service a chance to tell whether based on the information 122 * provided in {@link UsbDevice} class (e.g. PID/VID) this service supports or doesn't support 123 * given device. 124 * 125 * <p>The method must return one of the following status: {@link #RESULT_OK} or 126 * {@link #RESULT_DEVICE_NOT_SUPPORTED} 127 */ 128 @MainThread isDeviceSupported(@onNull UsbDevice device)129 public abstract @Result int isDeviceSupported(@NonNull UsbDevice device); 130 131 /** 132 * This method will be called at least once per connection session before switching device into 133 * AOAP mode. 134 * 135 * <p>The device is connected, but not in AOAP mode yet. Implementors of this method may ask 136 * the framework to ignore this device for now and do not switch to AOAP. This may make sense if 137 * a connection to the device has been established through other means, and switching the device 138 * to AOAP would break that connection. 139 * 140 * <p>Note: the method may be called only if this device was claimed to be supported in 141 * {@link #isDeviceSupported(UsbDevice)} method, and this app has been chosen to handle the 142 * device. 143 * 144 * <p>The method must return one of the following status: {@link #RESULT_OK}, 145 * {@link #RESULT_DEVICE_NOT_SUPPORTED} or {@link #RESULT_DO_NOT_SWITCH_TO_AOAP} 146 */ 147 @MainThread canSwitchToAoap(@onNull UsbDevice device)148 public @Result int canSwitchToAoap(@NonNull UsbDevice device) { 149 return RESULT_OK; 150 } 151 152 private Messenger mMessenger; 153 private boolean mBound; 154 155 @Override onCreate()156 public void onCreate() { 157 super.onCreate(); 158 mMessenger = new Messenger(new IncomingHandler(this)); 159 } 160 161 @Override onBind(Intent intent)162 public IBinder onBind(Intent intent) { 163 if (mBound) { 164 Log.w(TAG, "Received onBind event when the service was already bound"); 165 } 166 mBound = true; 167 return mMessenger.getBinder(); 168 } 169 170 @Override onUnbind(Intent intent)171 public boolean onUnbind(Intent intent) { 172 mBound = false; 173 return super.onUnbind(intent); 174 } 175 176 @Override dump(FileDescriptor fd, PrintWriter writer, String[] args)177 protected void dump(FileDescriptor fd, PrintWriter writer, String[] args) { 178 writer.write("Bound: " + mBound); 179 } 180 181 private static class IncomingHandler extends Handler { 182 private final WeakReference<AoapService> mServiceRef; 183 IncomingHandler(AoapService service)184 IncomingHandler(AoapService service) { 185 super(Looper.getMainLooper()); 186 mServiceRef = new WeakReference<>(service); 187 } 188 189 @Override handleMessage(Message msg)190 public void handleMessage(Message msg) { 191 AoapService service = mServiceRef.get(); 192 if (service == null) { 193 return; 194 } 195 Bundle data = msg.getData(); 196 if (data == null) { 197 Log.e(TAG, "Ignoring message " + msg.what + " without data"); 198 return; 199 } 200 201 Log.i(TAG, "Message received: " + msg.what); 202 203 switch (msg.what) { 204 case MSG_NEW_DEVICE_ATTACHED: { 205 int res = service.isDeviceSupported( 206 checkNotNull(data.getParcelable(KEY_DEVICE))); 207 if (res != RESULT_OK && res != RESULT_DEVICE_NOT_SUPPORTED) { 208 throw new IllegalArgumentException("Result can not be " + res); 209 } 210 sendResponse(msg.replyTo, MSG_NEW_DEVICE_ATTACHED_RESPONSE, res); 211 break; 212 } 213 214 case MSG_CAN_SWITCH_TO_AOAP: { 215 int res = service.canSwitchToAoap( 216 checkNotNull(data.getParcelable(KEY_DEVICE))); 217 if (res != RESULT_OK && res != RESULT_DEVICE_NOT_SUPPORTED 218 && res != RESULT_DO_NOT_SWITCH_TO_AOAP) { 219 throw new IllegalArgumentException("Result can not be " + res); 220 } 221 sendResponse(msg.replyTo, MSG_CAN_SWITCH_TO_AOAP_RESPONSE, res); 222 break; 223 } 224 225 default: 226 Log.e(TAG, "Unknown message received: " + msg.what); 227 break; 228 } 229 } 230 sendResponse(Messenger messenger, int msg, int result)231 private void sendResponse(Messenger messenger, int msg, int result) { 232 try { 233 messenger.send(createResponseMessage(msg, result)); 234 } catch (RemoteException e) { 235 Log.e(TAG, "Failed to send message", e); 236 } 237 } 238 createResponseMessage(int msg, int result)239 private Message createResponseMessage(int msg, int result) { 240 Message response = Message.obtain(null, msg); 241 Bundle data = new Bundle(); 242 data.putInt(KEY_RESULT, result); 243 response.setData(data); 244 return response; 245 } 246 } 247 } 248