1 /* 2 * Copyright (C) 2023 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 com.android.tv.mdnsoffloadcmd; 18 19 import android.app.Notification; 20 import android.app.NotificationChannel; 21 import android.app.NotificationManager; 22 import android.app.Service; 23 import android.content.BroadcastReceiver; 24 import android.content.ComponentName; 25 import android.content.Context; 26 import android.content.Intent; 27 import android.content.IntentFilter; 28 import android.content.ServiceConnection; 29 import android.os.Binder; 30 import android.os.Handler; 31 import android.os.HandlerThread; 32 import android.os.IBinder; 33 import android.os.RemoteException; 34 import android.widget.Toast; 35 import android.util.Log; 36 37 import androidx.annotation.Nullable; 38 import androidx.core.app.NotificationCompat; 39 import androidx.core.content.ContextCompat; 40 41 import java.util.HexFormat; 42 43 import device.google.atv.mdns_offload.IMdnsOffloadManager; 44 45 public class MdnsOffloadCmdService extends Service { 46 private static final String TAG = MdnsOffloadCmdService.class.getSimpleName(); 47 48 private static final String ACTION_OFFLOAD_COMMAND = 49 "com.android.tv.mdnsoffloadcmd.OFFLOAD_COMMAND"; 50 51 private static final String CHANNEL_ID = "MdnsOffloadCmdService"; 52 53 private IMdnsOffloadManager mMdnsOffloadManagerService; 54 private IBinder mBinder; 55 56 @Nullable 57 @Override onBind(Intent intent)58 public IBinder onBind(Intent intent) { 59 //We don't want any app to bind to this service. 60 return null; 61 } 62 63 @Override onCreate()64 public void onCreate() { 65 super.onCreate(); 66 mBinder = new Binder(); 67 setupCommandBroadcastReceiver(); 68 } 69 70 @Override onStartCommand(Intent intent, int flags, int startId)71 public int onStartCommand(Intent intent, int flags, int startId) { 72 createNotificationChannel(); 73 74 Notification notification = new NotificationCompat.Builder(this, CHANNEL_ID) 75 .setContentTitle("Foreground Service") 76 .setSmallIcon(R.drawable.notification_template_icon_low_bg) 77 .build(); 78 79 bindMdnsOffloadManager(); 80 81 startForeground(1, notification); 82 return START_NOT_STICKY; 83 } 84 createNotificationChannel()85 private void createNotificationChannel() { 86 NotificationChannel serviceChannel = new NotificationChannel( 87 CHANNEL_ID, 88 "Foreground Service Channel", 89 NotificationManager.IMPORTANCE_DEFAULT 90 ); 91 92 NotificationManager manager = getSystemService(NotificationManager.class); 93 manager.createNotificationChannel(serviceChannel); 94 } 95 registerProtocolResponse(String rawHexPacket, String iface)96 private void registerProtocolResponse(String rawHexPacket, String iface) { 97 Log.d(TAG, "Registering on iface{" + iface + "} :" + rawHexPacket); 98 IMdnsOffloadManager.OffloadServiceInfo info = 99 new IMdnsOffloadManager.OffloadServiceInfo(); 100 info.rawOffloadPacket = HexFormat.of().parseHex(rawHexPacket); 101 try { 102 if (mMdnsOffloadManagerService == null) { 103 Log.e(TAG, "Offload Manager not connected"); 104 return; 105 } 106 int recordKey = mMdnsOffloadManagerService.addProtocolResponses(iface, info, mBinder); 107 Log.d(TAG, "Packet offloaded with recordKey=" + recordKey); 108 } catch (RemoteException e) { 109 Log.e(TAG, "Error while registering debug packet", e); 110 } 111 } 112 registerPassthrough(String qname, String iface)113 private void registerPassthrough(String qname, String iface) { 114 Log.d(TAG, "Registering on iface{" + iface + "} passthrough:" + qname); 115 try { 116 if (mMdnsOffloadManagerService == null) { 117 Log.e(TAG, "Offload Manager not connected"); 118 return; 119 } 120 mMdnsOffloadManagerService.addToPassthroughList(iface, qname, mBinder); 121 } catch (RemoteException e) { 122 Log.e(TAG, "Error while adding passthrough qname", e); 123 } 124 } 125 removeProtocolResponse(int recordKey)126 private void removeProtocolResponse(int recordKey) { 127 IMdnsOffloadManager.OffloadServiceInfo info = 128 new IMdnsOffloadManager.OffloadServiceInfo(); 129 try { 130 if (mMdnsOffloadManagerService == null) { 131 Log.e(TAG, "Offload Manager not connected"); 132 return; 133 } 134 mMdnsOffloadManagerService.removeProtocolResponses(recordKey, mBinder); 135 Log.d(TAG, "Removed record " + recordKey); 136 } catch (RemoteException e) { 137 Log.e(TAG, "Error while registering debug packet", e); 138 } 139 } 140 removePassthrough(String qname, String iface)141 private void removePassthrough(String qname, String iface) { 142 Log.d(TAG, "Removing passthrough:" + qname + " on iface{" + iface + "}"); 143 try { 144 if (mMdnsOffloadManagerService == null) { 145 Log.e(TAG, "Offload Manager not connected"); 146 return; 147 } 148 mMdnsOffloadManagerService.removeFromPassthroughList(iface, qname, mBinder); 149 } catch (RemoteException e) { 150 Log.e(TAG, "Error while removing passthrough qname", e); 151 } 152 } 153 setupCommandBroadcastReceiver()154 private void setupCommandBroadcastReceiver() { 155 BroadcastReceiver receiver = new CommandBroadcastReceiver(); 156 IntentFilter filter = new IntentFilter(); 157 filter.addAction(ACTION_OFFLOAD_COMMAND); 158 registerReceiver(receiver, filter, ContextCompat.RECEIVER_EXPORTED); 159 } 160 161 private class CommandBroadcastReceiver extends BroadcastReceiver { 162 @Override onReceive(Context context, Intent intent)163 public void onReceive(Context context, Intent intent) { 164 String action = intent.getStringExtra("action"); 165 switch (action) { 166 case "ADD_OFFLOAD": { 167 String iface = intent.getStringExtra("iface"); 168 String rawHexPacket = intent.getStringExtra("raw_hex_packet"); 169 if (rawHexPacket != null && iface != null) { 170 registerProtocolResponse(rawHexPacket, iface); 171 } else { 172 Log.d(TAG, "Bad parameters for ADD_OFFLOAD command"); 173 } 174 break; 175 } 176 case "REMOVE_OFFLOAD": { 177 int recordKey = intent.getIntExtra("recordKey", -1); 178 if (recordKey >= 0) { 179 removeProtocolResponse(recordKey); 180 } else { 181 Log.d(TAG, "Bad parameters for REMOVE_OFFLOAD command"); 182 } 183 break; 184 } 185 case "ADD_PASSTHROUGH": { 186 String iface = intent.getStringExtra("iface"); 187 String qname = intent.getStringExtra("qname"); 188 if (iface != null && qname != null) { 189 registerPassthrough(qname, iface); 190 } else { 191 Log.d(TAG, "Bad parameters for ADD_PASSTHROUGH command"); 192 } 193 break; 194 } 195 case "REMOVE_PASSTHROUGH": { 196 String iface = intent.getStringExtra("iface"); 197 String qname = intent.getStringExtra("qname"); 198 if (iface != null && qname != null) { 199 removePassthrough(qname, iface); 200 } else { 201 Log.d(TAG, "Bad parameters for REMOVE_PASSTHROUGH command"); 202 } 203 break; 204 } 205 } 206 } 207 } 208 209 bindMdnsOffloadManager()210 private void bindMdnsOffloadManager() { 211 ComponentName componentName = ComponentName.unflattenFromString( 212 "com.android.tv.mdnsoffloadmanager/.MdnsOffloadManagerService"); 213 Intent explicitIntent = new Intent(); 214 explicitIntent.setComponent(componentName); 215 boolean bindingSuccessful = bindService(explicitIntent, 216 mMdnsOffloadManagerServiceConnection, Context.BIND_AUTO_CREATE); 217 if (!bindingSuccessful) { 218 String msg = "Failed to bind MdnsOffloadManager."; 219 Log.e(TAG, msg); 220 throw new IllegalStateException(msg); 221 } 222 } 223 224 225 private final ServiceConnection mMdnsOffloadManagerServiceConnection = new ServiceConnection() { 226 @Override 227 public void onServiceConnected(ComponentName className, IBinder service) { 228 Log.i(TAG, "IMdnsOffloadManager service bound successfully."); 229 mMdnsOffloadManagerService = IMdnsOffloadManager.Stub.asInterface(service); 230 } 231 232 public void onServiceDisconnected(ComponentName className) { 233 Log.e(TAG, "IMdnsOffloadManager service has unexpectedly disconnected."); 234 mMdnsOffloadManagerService = null; 235 } 236 }; 237 238 } 239