1 /*
2  * Copyright (C) 2022 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.server.connectivity.mdns;
18 
19 import android.annotation.NonNull;
20 import android.annotation.Nullable;
21 import android.annotation.RequiresApi;
22 import android.os.Build;
23 import android.os.Looper;
24 
25 import com.android.internal.annotations.VisibleForTesting;
26 import com.android.net.module.util.SharedLog;
27 
28 import java.util.Collections;
29 import java.util.List;
30 
31 /**
32  * Sends mDns announcements when a service registration changes and at regular intervals.
33  *
34  * This allows maintaining other hosts' caches up-to-date. See RFC6762 8.3.
35  */
36 @RequiresApi(Build.VERSION_CODES.TIRAMISU)
37 public class MdnsAnnouncer extends MdnsPacketRepeater<MdnsAnnouncer.BaseAnnouncementInfo> {
38     private static final long ANNOUNCEMENT_INITIAL_DELAY_MS = 1000L;
39     @VisibleForTesting
40     static final int ANNOUNCEMENT_COUNT = 8;
41 
42     // Matches delay and GoodbyeCount used by the legacy implementation
43     private static final long EXIT_DELAY_MS = 2000L;
44     private static final int EXIT_COUNT = 3;
45 
46     /** Base class for announcement requests to send with {@link MdnsAnnouncer}. */
47     public abstract static class BaseAnnouncementInfo implements MdnsPacketRepeater.Request {
48         private final int mServiceId;
49         @NonNull
50         private final MdnsPacket mPacket;
51 
BaseAnnouncementInfo(int serviceId, @NonNull List<MdnsRecord> announcedRecords, @NonNull List<MdnsRecord> additionalRecords)52         protected BaseAnnouncementInfo(int serviceId, @NonNull List<MdnsRecord> announcedRecords,
53                 @NonNull List<MdnsRecord> additionalRecords) {
54             mServiceId = serviceId;
55             final int flags = 0x8400; // Response, authoritative (rfc6762 18.4)
56             mPacket = new MdnsPacket(flags,
57                     Collections.emptyList() /* questions */,
58                     announcedRecords,
59                     Collections.emptyList() /* authorityRecords */,
60                     additionalRecords);
61         }
62 
getServiceId()63         public int getServiceId() {
64             return mServiceId;
65         }
66 
67         @Override
getPacket(int index)68         public MdnsPacket getPacket(int index) {
69             return mPacket;
70         }
71     }
72 
73     /** Announcement request to send with {@link MdnsAnnouncer}. */
74     public static class AnnouncementInfo extends BaseAnnouncementInfo {
AnnouncementInfo(int serviceId, List<MdnsRecord> announcedRecords, List<MdnsRecord> additionalRecords)75         AnnouncementInfo(int serviceId, List<MdnsRecord> announcedRecords,
76                 List<MdnsRecord> additionalRecords) {
77             super(serviceId, announcedRecords, additionalRecords);
78         }
79 
80         @Override
getDelayMs(int nextIndex)81         public long getDelayMs(int nextIndex) {
82             // Delay is doubled for each announcement
83             return ANNOUNCEMENT_INITIAL_DELAY_MS << (nextIndex - 1);
84         }
85 
86         @Override
getNumSends()87         public int getNumSends() {
88             return ANNOUNCEMENT_COUNT;
89         }
90     }
91 
92     /** Service exit announcement request to send with {@link MdnsAnnouncer}. */
93     public static class ExitAnnouncementInfo extends BaseAnnouncementInfo {
ExitAnnouncementInfo(int serviceId, List<MdnsRecord> announcedRecords)94         ExitAnnouncementInfo(int serviceId, List<MdnsRecord> announcedRecords) {
95             super(serviceId, announcedRecords, Collections.emptyList() /* additionalRecords */);
96         }
97 
98         @Override
getDelayMs(int nextIndex)99         public long getDelayMs(int nextIndex) {
100             return EXIT_DELAY_MS;
101         }
102 
103         @Override
getNumSends()104         public int getNumSends() {
105             return EXIT_COUNT;
106         }
107     }
108 
MdnsAnnouncer(@onNull Looper looper, @NonNull MdnsReplySender replySender, @Nullable PacketRepeaterCallback<BaseAnnouncementInfo> cb, @NonNull SharedLog sharedLog)109     public MdnsAnnouncer(@NonNull Looper looper,
110             @NonNull MdnsReplySender replySender,
111             @Nullable PacketRepeaterCallback<BaseAnnouncementInfo> cb,
112             @NonNull SharedLog sharedLog) {
113         super(looper, replySender, cb, sharedLog, MdnsAdvertiser.DBG);
114     }
115 
116     // TODO: Notify MdnsRecordRepository that the records were announced for that service ID,
117     // so it can update the last advertised timestamp of the associated records.
118 }
119