/* * Copyright (C) 2022 The Android Open Source Project * * Licensed under the Apache License, Version 2.0 (the "License"); * you may not use this file except in compliance with the License. * You may obtain a copy of the License at * * http://www.apache.org/licenses/LICENSE-2.0 * * Unless required by applicable law or agreed to in writing, software * distributed under the License is distributed on an "AS IS" BASIS, * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. * See the License for the specific language governing permissions and * limitations under the License. */ package com.android.server.connectivity.mdns; import android.annotation.NonNull; import android.annotation.Nullable; import android.annotation.RequiresApi; import android.os.Build; import android.os.Looper; import com.android.internal.annotations.VisibleForTesting; import com.android.net.module.util.SharedLog; import java.util.Collections; import java.util.List; /** * Sends mDns announcements when a service registration changes and at regular intervals. * * This allows maintaining other hosts' caches up-to-date. See RFC6762 8.3. */ @RequiresApi(Build.VERSION_CODES.TIRAMISU) public class MdnsAnnouncer extends MdnsPacketRepeater { private static final long ANNOUNCEMENT_INITIAL_DELAY_MS = 1000L; @VisibleForTesting static final int ANNOUNCEMENT_COUNT = 8; // Matches delay and GoodbyeCount used by the legacy implementation private static final long EXIT_DELAY_MS = 2000L; private static final int EXIT_COUNT = 3; /** Base class for announcement requests to send with {@link MdnsAnnouncer}. */ public abstract static class BaseAnnouncementInfo implements MdnsPacketRepeater.Request { private final int mServiceId; @NonNull private final MdnsPacket mPacket; protected BaseAnnouncementInfo(int serviceId, @NonNull List announcedRecords, @NonNull List additionalRecords) { mServiceId = serviceId; final int flags = 0x8400; // Response, authoritative (rfc6762 18.4) mPacket = new MdnsPacket(flags, Collections.emptyList() /* questions */, announcedRecords, Collections.emptyList() /* authorityRecords */, additionalRecords); } public int getServiceId() { return mServiceId; } @Override public MdnsPacket getPacket(int index) { return mPacket; } } /** Announcement request to send with {@link MdnsAnnouncer}. */ public static class AnnouncementInfo extends BaseAnnouncementInfo { AnnouncementInfo(int serviceId, List announcedRecords, List additionalRecords) { super(serviceId, announcedRecords, additionalRecords); } @Override public long getDelayMs(int nextIndex) { // Delay is doubled for each announcement return ANNOUNCEMENT_INITIAL_DELAY_MS << (nextIndex - 1); } @Override public int getNumSends() { return ANNOUNCEMENT_COUNT; } } /** Service exit announcement request to send with {@link MdnsAnnouncer}. */ public static class ExitAnnouncementInfo extends BaseAnnouncementInfo { ExitAnnouncementInfo(int serviceId, List announcedRecords) { super(serviceId, announcedRecords, Collections.emptyList() /* additionalRecords */); } @Override public long getDelayMs(int nextIndex) { return EXIT_DELAY_MS; } @Override public int getNumSends() { return EXIT_COUNT; } } public MdnsAnnouncer(@NonNull Looper looper, @NonNull MdnsReplySender replySender, @Nullable PacketRepeaterCallback cb, @NonNull SharedLog sharedLog) { super(looper, replySender, cb, sharedLog, MdnsAdvertiser.DBG); } // TODO: Notify MdnsRecordRepository that the records were announced for that service ID, // so it can update the last advertised timestamp of the associated records. }