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 
17 package android.net.metrics;
18 
19 import android.annotation.IntDef;
20 import android.annotation.NonNull;
21 import android.annotation.Nullable;
22 import android.annotation.SystemApi;
23 import android.annotation.TestApi;
24 import android.os.Parcel;
25 import android.os.Parcelable;
26 import android.util.SparseArray;
27 
28 import com.android.internal.util.MessageUtils;
29 
30 import java.lang.annotation.Retention;
31 import java.lang.annotation.RetentionPolicy;
32 
33 /**
34  * An event recorded by NetworkMonitor when sending a probe for finding captive portals.
35  * {@hide}
36  */
37 @SystemApi
38 @TestApi
39 public final class ValidationProbeEvent implements IpConnectivityLog.Event {
40 
41     public static final int PROBE_DNS       = 0;
42     public static final int PROBE_HTTP      = 1;
43     public static final int PROBE_HTTPS     = 2;
44     public static final int PROBE_PAC       = 3;
45     public static final int PROBE_FALLBACK  = 4;
46     public static final int PROBE_PRIVDNS   = 5;
47 
48     public static final int DNS_FAILURE = 0;
49     public static final int DNS_SUCCESS = 1;
50 
51     private static final int FIRST_VALIDATION  = 1 << 8;
52     private static final int REVALIDATION      = 2 << 8;
53 
54     /** @hide */
55     @IntDef(value = {DNS_FAILURE, DNS_SUCCESS})
56     @Retention(RetentionPolicy.SOURCE)
57     public @interface ReturnCode {}
58 
59     /** @hide */
60     public final long durationMs;
61     // probeType byte format (MSB to LSB):
62     // byte 0: unused
63     // byte 1: unused
64     // byte 2: 0 = UNKNOWN, 1 = FIRST_VALIDATION, 2 = REVALIDATION
65     // byte 3: PROBE_* constant
66     /** @hide */
67     public final int probeType;
68     /** @hide */
69     public final @ReturnCode int returnCode;
70 
ValidationProbeEvent(long durationMs, int probeType, int returnCode)71     private ValidationProbeEvent(long durationMs, int probeType, int returnCode) {
72         this.durationMs = durationMs;
73         this.probeType = probeType;
74         this.returnCode = returnCode;
75     }
76 
ValidationProbeEvent(Parcel in)77     private ValidationProbeEvent(Parcel in) {
78         durationMs = in.readLong();
79         probeType = in.readInt();
80         returnCode = in.readInt();
81     }
82 
83     /**
84      * Utility to create an instance of {@link ValidationProbeEvent}.
85      */
86     public static final class Builder {
87         private long mDurationMs;
88         private int mProbeType;
89         private int mReturnCode;
90 
91         /**
92          * Set the duration of the probe in milliseconds.
93          */
94         @NonNull
setDurationMs(long durationMs)95         public Builder setDurationMs(long durationMs) {
96             mDurationMs = durationMs;
97             return this;
98         }
99 
100         /**
101          * Set the probe type based on whether it was the first validation.
102          */
103         @NonNull
setProbeType(int probeType, boolean firstValidation)104         public Builder setProbeType(int probeType, boolean firstValidation) {
105             mProbeType = makeProbeType(probeType, firstValidation);
106             return this;
107         }
108 
109         /**
110          * Set the return code of the probe.
111          */
112         @NonNull
setReturnCode(int returnCode)113         public Builder setReturnCode(int returnCode) {
114             mReturnCode = returnCode;
115             return this;
116         }
117 
118         /**
119          * Create a new {@link ValidationProbeEvent}.
120          */
121         @NonNull
build()122         public ValidationProbeEvent build() {
123             return new ValidationProbeEvent(mDurationMs, mProbeType, mReturnCode);
124         }
125     }
126 
127     /** @hide */
128     @Override
writeToParcel(Parcel out, int flags)129     public void writeToParcel(Parcel out, int flags) {
130         out.writeLong(durationMs);
131         out.writeInt(probeType);
132         out.writeInt(returnCode);
133     }
134 
135     /** @hide */
136     @Override
describeContents()137     public int describeContents() {
138         return 0;
139     }
140 
141     /** @hide */
142     public static final @android.annotation.NonNull Parcelable.Creator<ValidationProbeEvent> CREATOR
143         = new Parcelable.Creator<ValidationProbeEvent>() {
144         public ValidationProbeEvent createFromParcel(Parcel in) {
145             return new ValidationProbeEvent(in);
146         }
147 
148         public ValidationProbeEvent[] newArray(int size) {
149             return new ValidationProbeEvent[size];
150         }
151     };
152 
makeProbeType(int probeType, boolean firstValidation)153     private static int makeProbeType(int probeType, boolean firstValidation) {
154         return (probeType & 0xff) | (firstValidation ? FIRST_VALIDATION : REVALIDATION);
155     }
156 
157     /**
158      * Get the name of a probe specified by its probe type.
159      */
getProbeName(int probeType)160     public static @NonNull String getProbeName(int probeType) {
161         return Decoder.constants.get(probeType & 0xff, "PROBE_???");
162     }
163 
getValidationStage(int probeType)164     private static @NonNull String getValidationStage(int probeType) {
165         return Decoder.constants.get(probeType & 0xff00, "UNKNOWN");
166     }
167 
168     @NonNull
169     @Override
toString()170     public String toString() {
171         return String.format("ValidationProbeEvent(%s:%d %s, %dms)",
172                 getProbeName(probeType), returnCode, getValidationStage(probeType), durationMs);
173     }
174 
175     @Override
equals(@ullable Object obj)176     public boolean equals(@Nullable Object obj) {
177         if (obj == null || !(obj.getClass().equals(ValidationProbeEvent.class))) return false;
178         final ValidationProbeEvent other = (ValidationProbeEvent) obj;
179         return durationMs == other.durationMs
180                 && probeType == other.probeType
181                 && returnCode == other.returnCode;
182     }
183 
184     final static class Decoder {
185         static final SparseArray<String> constants = MessageUtils.findMessageNames(
186                 new Class[]{ValidationProbeEvent.class},
187                 new String[]{"PROBE_", "FIRST_", "REVALIDATION"});
188     }
189 }
190