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 package com.android.server.wifi.entitlement; 17 18 import android.annotation.NonNull; 19 20 import com.android.internal.annotations.VisibleForTesting; 21 22 import java.time.Duration; 23 import java.time.Instant; 24 25 /** The Imsi Pseudonym information*/ 26 public class PseudonymInfo { 27 @VisibleForTesting 28 static final long DEFAULT_PSEUDONYM_TTL_IN_MILLIS = Duration.ofDays(2).toMillis(); 29 @VisibleForTesting 30 static final long REFRESH_AHEAD_TIME_IN_MILLIS = Duration.ofMinutes(30).toMillis(); 31 static final long MINIMUM_REFRESH_INTERVAL_IN_MILLIS = Duration.ofHours(12).toMillis(); 32 33 private final String mImsi; 34 private final String mPseudonym; 35 36 /* 37 * The number of milliseconds from the epoch of 1970-01-01T00:00:00Z when the pseudonym is 38 * received. 39 */ 40 private final long mTimeStamp; 41 42 /* 43 * Time To Live in milliseconds from the pseudonym is received. This is the maximum lifetime of 44 * a pseudonym. The pseudonym remains valid from the time it is received, until the mTtlInMillis 45 * has elapsed. 46 */ 47 private final long mTtlInMillis; 48 49 /* 50 * Age To Refresh in milliseconds from the pseudonym is received. When a pseudonym is expiring, 51 * we should refresh it ahead of time. For example, we refresh a pseudonym half an hour before 52 * it expires. The TTL is 24 hours. So we should refresh this pseudonym when it is 23.5 hours 53 * old. 54 */ 55 private final long mAtrInMillis; 56 57 /* 58 * Minimum Age To Refresh in milliseconds from the pseudonym is received. We should not 59 * refresh the pseudonym too frequently. 60 */ 61 62 private final long mMinAtrInMillis; 63 PseudonymInfo(@onNull String pseudonym, @NonNull String imsi)64 public PseudonymInfo(@NonNull String pseudonym, @NonNull String imsi) { 65 this(pseudonym, imsi, DEFAULT_PSEUDONYM_TTL_IN_MILLIS); 66 } 67 PseudonymInfo(@onNull String pseudonym, @NonNull String imsi, long ttlInMillis)68 public PseudonymInfo(@NonNull String pseudonym, @NonNull String imsi, long ttlInMillis) { 69 this(pseudonym, imsi, ttlInMillis, Instant.now().toEpochMilli()); 70 } 71 72 @VisibleForTesting PseudonymInfo(@onNull String pseudonym, @NonNull String imsi, long ttlInMillis, long timeStamp)73 public PseudonymInfo(@NonNull String pseudonym, @NonNull String imsi, long ttlInMillis, 74 long timeStamp) { 75 mPseudonym = pseudonym; 76 mImsi = imsi; 77 mTimeStamp = timeStamp; 78 mTtlInMillis = ttlInMillis; 79 mAtrInMillis = ttlInMillis - Math.min(ttlInMillis / 2, REFRESH_AHEAD_TIME_IN_MILLIS); 80 mMinAtrInMillis = Math.min(mAtrInMillis, MINIMUM_REFRESH_INTERVAL_IN_MILLIS); 81 } 82 getPseudonym()83 public String getPseudonym() { 84 return mPseudonym; 85 } 86 getImsi()87 public String getImsi() { 88 return mImsi; 89 } 90 91 /** 92 * Returns the Time To Live in milliseconds. 93 */ getTtlInMillis()94 public long getTtlInMillis() { 95 return mTtlInMillis; 96 } 97 98 /* 99 * Returns the Age To Refresh in milliseconds. 100 */ getAtrInMillis()101 public long getAtrInMillis() { 102 return mAtrInMillis; 103 } 104 105 /** 106 * Returns whether the pseudonym has expired or not. 107 */ hasExpired()108 public boolean hasExpired() { 109 return Instant.now().toEpochMilli() - mTimeStamp >= mTtlInMillis; 110 } 111 112 /** 113 * Returns whether the pseudonym should be refreshed. A pseudonym should be refreshed before 114 * it expires. 115 */ shouldBeRefreshed()116 public boolean shouldBeRefreshed() { 117 return (Instant.now().toEpochMilli() - mTimeStamp) >= mAtrInMillis; 118 } 119 120 /** 121 * Returns whether the pseudonym is old enough to refresh. To prevent DOS attack, the pseudonym 122 * should not be refreshed too frequently. 123 */ isOldEnoughToRefresh()124 public boolean isOldEnoughToRefresh() { 125 return Instant.now().toEpochMilli() - mTimeStamp >= mMinAtrInMillis; 126 } 127 128 @Override toString()129 public String toString() { 130 // Mask the pseudonym and IMSI which are two kinds of PII. 131 return " mPseudonym=" 132 + (mPseudonym.length() >= 7 ? (mPseudonym.substring(0, 7) + "***") : mPseudonym) 133 + " mImsi=***" 134 + (mImsi.length() >= 3 ? mImsi.substring(mImsi.length() - 3) : mImsi) 135 + " mTimeStamp=" + mTimeStamp 136 + " mTtlInMillis=" + mTtlInMillis 137 + " mTtrInMillis=" + mAtrInMillis 138 + " mMinTtrInMillis=" + mMinAtrInMillis; 139 } 140 } 141