1/*
2 * Copyright (C) 2011 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
17package android.net;
18
19import android.os.Parcel;
20import android.os.Parcelable;
21import android.util.BackupUtils;
22import android.util.Pair;
23import android.util.RecurrenceRule;
24
25import com.android.internal.util.Preconditions;
26
27import java.io.ByteArrayOutputStream;
28import java.io.DataInputStream;
29import java.io.DataOutputStream;
30import java.io.IOException;
31import java.time.ZoneId;
32import java.time.ZonedDateTime;
33import java.util.Iterator;
34import java.util.Objects;
35
36/**
37 * Policy for networks matching a {@link NetworkTemplate}, including usage cycle
38 * and limits to be enforced.
39 *
40 * @hide
41 */
42public class NetworkPolicy implements Parcelable, Comparable<NetworkPolicy> {
43    private static final int VERSION_INIT = 1;
44    private static final int VERSION_RULE = 2;
45
46    public static final int CYCLE_NONE = -1;
47    public static final long WARNING_DISABLED = -1;
48    public static final long LIMIT_DISABLED = -1;
49    public static final long SNOOZE_NEVER = -1;
50
51    public NetworkTemplate template;
52    public RecurrenceRule cycleRule;
53    public long warningBytes = WARNING_DISABLED;
54    public long limitBytes = LIMIT_DISABLED;
55    public long lastWarningSnooze = SNOOZE_NEVER;
56    public long lastLimitSnooze = SNOOZE_NEVER;
57    @Deprecated public boolean metered = true;
58    public boolean inferred = false;
59
60    private static final long DEFAULT_MTU = 1500;
61
62    public static RecurrenceRule buildRule(int cycleDay, ZoneId cycleTimezone) {
63        if (cycleDay != NetworkPolicy.CYCLE_NONE) {
64            return RecurrenceRule.buildRecurringMonthly(cycleDay, cycleTimezone);
65        } else {
66            return RecurrenceRule.buildNever();
67        }
68    }
69
70    @Deprecated
71    public NetworkPolicy(NetworkTemplate template, int cycleDay, String cycleTimezone,
72            long warningBytes, long limitBytes, boolean metered) {
73        this(template, cycleDay, cycleTimezone, warningBytes, limitBytes, SNOOZE_NEVER,
74                SNOOZE_NEVER, metered, false);
75    }
76
77    @Deprecated
78    public NetworkPolicy(NetworkTemplate template, int cycleDay, String cycleTimezone,
79            long warningBytes, long limitBytes, long lastWarningSnooze, long lastLimitSnooze,
80            boolean metered, boolean inferred) {
81        this(template, buildRule(cycleDay, ZoneId.of(cycleTimezone)), warningBytes,
82                limitBytes, lastWarningSnooze, lastLimitSnooze, metered, inferred);
83    }
84
85    public NetworkPolicy(NetworkTemplate template, RecurrenceRule cycleRule, long warningBytes,
86            long limitBytes, long lastWarningSnooze, long lastLimitSnooze, boolean metered,
87            boolean inferred) {
88        this.template = Preconditions.checkNotNull(template, "missing NetworkTemplate");
89        this.cycleRule = Preconditions.checkNotNull(cycleRule, "missing RecurrenceRule");
90        this.warningBytes = warningBytes;
91        this.limitBytes = limitBytes;
92        this.lastWarningSnooze = lastWarningSnooze;
93        this.lastLimitSnooze = lastLimitSnooze;
94        this.metered = metered;
95        this.inferred = inferred;
96    }
97
98    private NetworkPolicy(Parcel source) {
99        template = source.readParcelable(null);
100        cycleRule = source.readParcelable(null);
101        warningBytes = source.readLong();
102        limitBytes = source.readLong();
103        lastWarningSnooze = source.readLong();
104        lastLimitSnooze = source.readLong();
105        metered = source.readInt() != 0;
106        inferred = source.readInt() != 0;
107    }
108
109    @Override
110    public void writeToParcel(Parcel dest, int flags) {
111        dest.writeParcelable(template, flags);
112        dest.writeParcelable(cycleRule, flags);
113        dest.writeLong(warningBytes);
114        dest.writeLong(limitBytes);
115        dest.writeLong(lastWarningSnooze);
116        dest.writeLong(lastLimitSnooze);
117        dest.writeInt(metered ? 1 : 0);
118        dest.writeInt(inferred ? 1 : 0);
119    }
120
121    @Override
122    public int describeContents() {
123        return 0;
124    }
125
126    public Iterator<Pair<ZonedDateTime, ZonedDateTime>> cycleIterator() {
127        return cycleRule.cycleIterator();
128    }
129
130    /**
131     * Test if given measurement is over {@link #warningBytes}.
132     */
133    public boolean isOverWarning(long totalBytes) {
134        return warningBytes != WARNING_DISABLED && totalBytes >= warningBytes;
135    }
136
137    /**
138     * Test if given measurement is near enough to {@link #limitBytes} to be
139     * considered over-limit.
140     */
141    public boolean isOverLimit(long totalBytes) {
142        // over-estimate, since kernel will trigger limit once first packet
143        // trips over limit.
144        totalBytes += 2 * DEFAULT_MTU;
145        return limitBytes != LIMIT_DISABLED && totalBytes >= limitBytes;
146    }
147
148    /**
149     * Clear any existing snooze values, setting to {@link #SNOOZE_NEVER}.
150     */
151    public void clearSnooze() {
152        lastWarningSnooze = SNOOZE_NEVER;
153        lastLimitSnooze = SNOOZE_NEVER;
154    }
155
156    /**
157     * Test if this policy has a cycle defined, after which usage should reset.
158     */
159    public boolean hasCycle() {
160        return cycleRule.cycleIterator().hasNext();
161    }
162
163    @Override
164    public int compareTo(NetworkPolicy another) {
165        if (another == null || another.limitBytes == LIMIT_DISABLED) {
166            // other value is missing or disabled; we win
167            return -1;
168        }
169        if (limitBytes == LIMIT_DISABLED || another.limitBytes < limitBytes) {
170            // we're disabled or other limit is smaller; they win
171            return 1;
172        }
173        return 0;
174    }
175
176    @Override
177    public int hashCode() {
178        return Objects.hash(template, cycleRule, warningBytes, limitBytes,
179                lastWarningSnooze, lastLimitSnooze, metered, inferred);
180    }
181
182    @Override
183    public boolean equals(Object obj) {
184        if (obj instanceof NetworkPolicy) {
185            final NetworkPolicy other = (NetworkPolicy) obj;
186            return warningBytes == other.warningBytes
187                    && limitBytes == other.limitBytes
188                    && lastWarningSnooze == other.lastWarningSnooze
189                    && lastLimitSnooze == other.lastLimitSnooze && metered == other.metered
190                    && inferred == other.inferred
191                    && Objects.equals(template, other.template)
192                    && Objects.equals(cycleRule, other.cycleRule);
193        }
194        return false;
195    }
196
197    @Override
198    public String toString() {
199        return new StringBuilder("NetworkPolicy{")
200                .append("template=").append(template)
201                .append(" cycleRule=").append(cycleRule)
202                .append(" warningBytes=").append(warningBytes)
203                .append(" limitBytes=").append(limitBytes)
204                .append(" lastWarningSnooze=").append(lastWarningSnooze)
205                .append(" lastLimitSnooze=").append(lastLimitSnooze)
206                .append(" metered=").append(metered)
207                .append(" inferred=").append(inferred)
208                .append("}").toString();
209    }
210
211    public static final Creator<NetworkPolicy> CREATOR = new Creator<NetworkPolicy>() {
212        @Override
213        public NetworkPolicy createFromParcel(Parcel in) {
214            return new NetworkPolicy(in);
215        }
216
217        @Override
218        public NetworkPolicy[] newArray(int size) {
219            return new NetworkPolicy[size];
220        }
221    };
222
223    public byte[] getBytesForBackup() throws IOException {
224        ByteArrayOutputStream baos = new ByteArrayOutputStream();
225        DataOutputStream out = new DataOutputStream(baos);
226
227        out.writeInt(VERSION_RULE);
228        out.write(template.getBytesForBackup());
229        cycleRule.writeToStream(out);
230        out.writeLong(warningBytes);
231        out.writeLong(limitBytes);
232        out.writeLong(lastWarningSnooze);
233        out.writeLong(lastLimitSnooze);
234        out.writeInt(metered ? 1 : 0);
235        out.writeInt(inferred ? 1 : 0);
236        return baos.toByteArray();
237    }
238
239    public static NetworkPolicy getNetworkPolicyFromBackup(DataInputStream in) throws IOException,
240            BackupUtils.BadVersionException {
241        final int version = in.readInt();
242        switch (version) {
243            case VERSION_INIT: {
244                NetworkTemplate template = NetworkTemplate.getNetworkTemplateFromBackup(in);
245                int cycleDay = in.readInt();
246                String cycleTimeZone = BackupUtils.readString(in);
247                long warningBytes = in.readLong();
248                long limitBytes = in.readLong();
249                long lastWarningSnooze = in.readLong();
250                long lastLimitSnooze = in.readLong();
251                boolean metered = in.readInt() == 1;
252                boolean inferred = in.readInt() == 1;
253                return new NetworkPolicy(template, cycleDay, cycleTimeZone, warningBytes,
254                        limitBytes, lastWarningSnooze, lastLimitSnooze, metered, inferred);
255            }
256            case VERSION_RULE: {
257                NetworkTemplate template = NetworkTemplate.getNetworkTemplateFromBackup(in);
258                RecurrenceRule cycleRule = new RecurrenceRule(in);
259                long warningBytes = in.readLong();
260                long limitBytes = in.readLong();
261                long lastWarningSnooze = in.readLong();
262                long lastLimitSnooze = in.readLong();
263                boolean metered = in.readInt() == 1;
264                boolean inferred = in.readInt() == 1;
265                return new NetworkPolicy(template, cycleRule, warningBytes,
266                        limitBytes, lastWarningSnooze, lastLimitSnooze, metered, inferred);
267            }
268            default: {
269                throw new BackupUtils.BadVersionException("Unknown backup version: " + version);
270            }
271        }
272    }
273}
274