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