1 /* 2 * Copyright (C) 2020 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.telephony; 18 19 import android.annotation.NonNull; 20 import android.annotation.Nullable; 21 import android.annotation.RequiresPermission; 22 import android.annotation.SystemApi; 23 import android.os.Binder; 24 import android.os.IBinder; 25 import android.os.Parcel; 26 import android.os.Parcelable; 27 28 import java.util.ArrayList; 29 import java.util.Collection; 30 import java.util.Collections; 31 import java.util.Comparator; 32 import java.util.HashMap; 33 import java.util.HashSet; 34 import java.util.List; 35 import java.util.Map; 36 import java.util.Objects; 37 import java.util.Set; 38 39 /** 40 * Request used to register {@link SignalThresholdInfo} to be notified when the signal strength 41 * breach the specified thresholds. 42 */ 43 public final class SignalStrengthUpdateRequest implements Parcelable { 44 /** 45 * List of SignalThresholdInfo for the request. 46 */ 47 private final List<SignalThresholdInfo> mSignalThresholdInfos; 48 49 /** 50 * Whether the reporting is required for thresholds in the request while device is idle. 51 */ 52 private final boolean mIsReportingRequestedWhileIdle; 53 54 /** 55 * Whether the reporting requested for system thresholds while device is idle. 56 * 57 * System signal thresholds are loaded from carrier config items and mainly used for UI 58 * displaying. By default, they are ignored when device is idle. When setting the value to true, 59 * modem will continue reporting signal strength changes over the system signal thresholds even 60 * device is idle. 61 * 62 * This should only set to true by the system caller. 63 */ 64 private final boolean mIsSystemThresholdReportingRequestedWhileIdle; 65 66 /** 67 * A IBinder object as a token for server side to check if the request client is still living. 68 */ 69 private final IBinder mLiveToken; 70 SignalStrengthUpdateRequest( @ullable List<SignalThresholdInfo> signalThresholdInfos, boolean isReportingRequestedWhileIdle, boolean isSystemThresholdReportingRequestedWhileIdle)71 private SignalStrengthUpdateRequest( 72 @Nullable List<SignalThresholdInfo> signalThresholdInfos, 73 boolean isReportingRequestedWhileIdle, 74 boolean isSystemThresholdReportingRequestedWhileIdle) { 75 validate(signalThresholdInfos, isSystemThresholdReportingRequestedWhileIdle); 76 77 mSignalThresholdInfos = signalThresholdInfos; 78 mIsReportingRequestedWhileIdle = isReportingRequestedWhileIdle; 79 mIsSystemThresholdReportingRequestedWhileIdle = 80 isSystemThresholdReportingRequestedWhileIdle; 81 mLiveToken = new Binder(); 82 } 83 84 /** 85 * Builder class to create {@link SignalStrengthUpdateRequest} object. 86 */ 87 public static final class Builder { 88 private List<SignalThresholdInfo> mSignalThresholdInfos = null; 89 private boolean mIsReportingRequestedWhileIdle = false; 90 private boolean mIsSystemThresholdReportingRequestedWhileIdle = false; 91 92 /** 93 * Set the collection of SignalThresholdInfo for the builder object 94 * 95 * @param signalThresholdInfos the collection of SignalThresholdInfo 96 * 97 * @return the builder to facilitate the chaining 98 */ setSignalThresholdInfos( @onNull Collection<SignalThresholdInfo> signalThresholdInfos)99 public @NonNull Builder setSignalThresholdInfos( 100 @NonNull Collection<SignalThresholdInfo> signalThresholdInfos) { 101 Objects.requireNonNull(signalThresholdInfos, 102 "SignalThresholdInfo collection must not be null"); 103 for (SignalThresholdInfo info : signalThresholdInfos) { 104 Objects.requireNonNull(info, 105 "SignalThresholdInfo in the collection must not be null"); 106 } 107 108 mSignalThresholdInfos = new ArrayList<>(signalThresholdInfos); 109 // Sort the collection with RAN and then SignalMeasurementType ascending order, make the 110 // ordering not matter for equals 111 mSignalThresholdInfos.sort( 112 Comparator.comparingInt(SignalThresholdInfo::getRadioAccessNetworkType) 113 .thenComparing(SignalThresholdInfo::getSignalMeasurementType)); 114 return this; 115 } 116 117 /** 118 * Set the builder object if require reporting on thresholds in this request when device is 119 * idle. 120 * 121 * @param isReportingRequestedWhileIdle true if request reporting when device is idle 122 * 123 * @return the builder to facilitate the chaining 124 */ setReportingRequestedWhileIdle( boolean isReportingRequestedWhileIdle)125 public @NonNull Builder setReportingRequestedWhileIdle( 126 boolean isReportingRequestedWhileIdle) { 127 mIsReportingRequestedWhileIdle = isReportingRequestedWhileIdle; 128 return this; 129 } 130 131 /** 132 * Set the builder object if require reporting on the system thresholds when device is idle. 133 * 134 * <p>This is intended to be used by the system privileged caller only. When setting to 135 * {@code true}, signal strength update request through 136 * {@link TelephonyManager#setSignalStrengthUpdateRequest(SignalStrengthUpdateRequest)} 137 * will require permission 138 * {@link android.Manifest.permission#LISTEN_ALWAYS_REPORTED_SIGNAL_STRENGTH}. 139 * 140 * @param isSystemThresholdReportingRequestedWhileIdle true if request reporting on the 141 * system thresholds when device is idle 142 * @return the builder to facilitate the chaining 143 * @hide 144 */ 145 @SystemApi 146 @RequiresPermission(android.Manifest.permission.LISTEN_ALWAYS_REPORTED_SIGNAL_STRENGTH) setSystemThresholdReportingRequestedWhileIdle( boolean isSystemThresholdReportingRequestedWhileIdle)147 public @NonNull Builder setSystemThresholdReportingRequestedWhileIdle( 148 boolean isSystemThresholdReportingRequestedWhileIdle) { 149 mIsSystemThresholdReportingRequestedWhileIdle = 150 isSystemThresholdReportingRequestedWhileIdle; 151 return this; 152 } 153 154 /** 155 * Build a {@link SignalStrengthUpdateRequest} object. 156 * 157 * @return the SignalStrengthUpdateRequest object 158 * 159 * @throws IllegalArgumentException if the SignalThresholdInfo collection is empty size, the 160 * signal measurement type for the same RAN in the collection is not unique 161 */ build()162 public @NonNull SignalStrengthUpdateRequest build() { 163 return new SignalStrengthUpdateRequest(mSignalThresholdInfos, 164 mIsReportingRequestedWhileIdle, mIsSystemThresholdReportingRequestedWhileIdle); 165 } 166 } 167 SignalStrengthUpdateRequest(Parcel in)168 private SignalStrengthUpdateRequest(Parcel in) { 169 mSignalThresholdInfos = in.createTypedArrayList(SignalThresholdInfo.CREATOR); 170 mIsReportingRequestedWhileIdle = in.readBoolean(); 171 mIsSystemThresholdReportingRequestedWhileIdle = in.readBoolean(); 172 mLiveToken = in.readStrongBinder(); 173 } 174 175 /** 176 * Get the collection of SignalThresholdInfo in the request. 177 * 178 * @return the collection of SignalThresholdInfo 179 */ 180 @NonNull getSignalThresholdInfos()181 public Collection<SignalThresholdInfo> getSignalThresholdInfos() { 182 return Collections.unmodifiableList(mSignalThresholdInfos); 183 } 184 185 /** 186 * Get whether reporting is requested for the threshold in the request while device is idle. 187 * 188 * @return true if reporting requested while device is idle 189 */ isReportingRequestedWhileIdle()190 public boolean isReportingRequestedWhileIdle() { 191 return mIsReportingRequestedWhileIdle; 192 } 193 194 /** 195 * @return true if reporting requested for system thresholds while device is idle 196 * 197 * @hide 198 */ 199 @SystemApi isSystemThresholdReportingRequestedWhileIdle()200 public boolean isSystemThresholdReportingRequestedWhileIdle() { 201 return mIsSystemThresholdReportingRequestedWhileIdle; 202 } 203 204 /** 205 * @return the live token of the request 206 * 207 * @hide 208 */ getLiveToken()209 public @NonNull IBinder getLiveToken() { 210 return mLiveToken; 211 } 212 213 @Override describeContents()214 public int describeContents() { 215 return 0; 216 } 217 218 @Override writeToParcel(@onNull Parcel dest, int flags)219 public void writeToParcel(@NonNull Parcel dest, int flags) { 220 dest.writeTypedList(mSignalThresholdInfos); 221 dest.writeBoolean(mIsReportingRequestedWhileIdle); 222 dest.writeBoolean(mIsSystemThresholdReportingRequestedWhileIdle); 223 dest.writeStrongBinder(mLiveToken); 224 } 225 226 @Override equals(Object other)227 public boolean equals(Object other) { 228 if (this == other) return true; 229 230 if (!(other instanceof SignalStrengthUpdateRequest)) { 231 return false; 232 } 233 234 SignalStrengthUpdateRequest request = (SignalStrengthUpdateRequest) other; 235 return mSignalThresholdInfos.equals(request.mSignalThresholdInfos) 236 && mIsReportingRequestedWhileIdle == request.mIsReportingRequestedWhileIdle 237 && mIsSystemThresholdReportingRequestedWhileIdle 238 == request.mIsSystemThresholdReportingRequestedWhileIdle; 239 } 240 241 @Override hashCode()242 public int hashCode() { 243 return Objects.hash(mSignalThresholdInfos, mIsReportingRequestedWhileIdle, 244 mIsSystemThresholdReportingRequestedWhileIdle); 245 } 246 247 public static final @NonNull Parcelable.Creator<SignalStrengthUpdateRequest> CREATOR = 248 new Parcelable.Creator<SignalStrengthUpdateRequest>() { 249 @Override 250 public SignalStrengthUpdateRequest createFromParcel(Parcel source) { 251 return new SignalStrengthUpdateRequest(source); 252 } 253 254 @Override 255 public SignalStrengthUpdateRequest[] newArray(int size) { 256 return new SignalStrengthUpdateRequest[size]; 257 } 258 }; 259 260 @Override toString()261 public String toString() { 262 return new StringBuilder("SignalStrengthUpdateRequest{") 263 .append("mSignalThresholdInfos=") 264 .append(mSignalThresholdInfos) 265 .append(" mIsReportingRequestedWhileIdle=") 266 .append(mIsReportingRequestedWhileIdle) 267 .append(" mIsSystemThresholdReportingRequestedWhileIdle=") 268 .append(mIsSystemThresholdReportingRequestedWhileIdle) 269 .append(" mLiveToken") 270 .append(mLiveToken) 271 .append("}").toString(); 272 } 273 274 /** 275 * Throw IAE if SignalThresholdInfo collection is null or empty, 276 * or the SignalMeasurementType for the same RAN in the collection is not unique. 277 */ validate(Collection<SignalThresholdInfo> infos, boolean isSystemThresholdReportingRequestedWhileIdle)278 private static void validate(Collection<SignalThresholdInfo> infos, 279 boolean isSystemThresholdReportingRequestedWhileIdle) { 280 // System app (like Bluetooth) can specify the request to report system thresholds while 281 // device is idle (with permission protection). In this case, the request doesn't need to 282 // provide a non-empty list of SignalThresholdInfo which is only asked for public apps. 283 if (infos == null || (infos.isEmpty() && !isSystemThresholdReportingRequestedWhileIdle)) { 284 throw new IllegalArgumentException("SignalThresholdInfo collection is null or empty"); 285 } 286 287 // Map from RAN to set of SignalMeasurementTypes 288 Map<Integer, Set<Integer>> ranToTypes = new HashMap<>(infos.size()); 289 for (SignalThresholdInfo info : infos) { 290 final int ran = info.getRadioAccessNetworkType(); 291 final int type = info.getSignalMeasurementType(); 292 ranToTypes.putIfAbsent(ran, new HashSet<>()); 293 if (!ranToTypes.get(ran).add(type)) { 294 throw new IllegalArgumentException( 295 "SignalMeasurementType " + type + " for RAN " + ran + " is not unique"); 296 } 297 } 298 } 299 } 300