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