1 /*
2  * Copyright (C) 2014 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.net;
18 
19 import android.Manifest.permission;
20 import android.annotation.CallbackExecutor;
21 import android.annotation.IntDef;
22 import android.annotation.NonNull;
23 import android.annotation.Nullable;
24 import android.annotation.RequiresPermission;
25 import android.annotation.SdkConstant;
26 import android.annotation.SdkConstant.SdkConstantType;
27 import android.annotation.SuppressLint;
28 import android.annotation.SystemApi;
29 import android.annotation.SystemService;
30 import android.content.Context;
31 import android.os.Binder;
32 import android.os.RemoteException;
33 import android.os.ServiceManager;
34 import android.os.ServiceManager.ServiceNotFoundException;
35 import android.util.Log;
36 
37 import java.lang.annotation.Retention;
38 import java.lang.annotation.RetentionPolicy;
39 import java.util.List;
40 import java.util.concurrent.Executor;
41 
42 /**
43  * Class that manages communication between network subsystems and a network scorer.
44  *
45  * <p>A network scorer is any application which:
46  * <ul>
47  * <li>Is granted the {@link permission#SCORE_NETWORKS} permission.
48  * <li>Is granted the {@link permission#ACCESS_COARSE_LOCATION} permission.
49  * <li>Include a Service for the {@link #ACTION_RECOMMEND_NETWORKS} action
50  *     protected by the {@link permission#BIND_NETWORK_RECOMMENDATION_SERVICE}
51  *     permission.
52  * </ul>
53  *
54  * @hide
55  */
56 @SystemApi
57 @SystemService(Context.NETWORK_SCORE_SERVICE)
58 public class NetworkScoreManager {
59     private static final String TAG = "NetworkScoreManager";
60 
61     /**
62      * Activity action: ask the user to change the active network scorer. This will show a dialog
63      * that asks the user whether they want to replace the current active scorer with the one
64      * specified in {@link #EXTRA_PACKAGE_NAME}. The activity will finish with RESULT_OK if the
65      * active scorer was changed or RESULT_CANCELED if it failed for any reason.
66      * @deprecated No longer sent.
67      */
68     @Deprecated
69     @SdkConstant(SdkConstantType.ACTIVITY_INTENT_ACTION)
70     public static final String ACTION_CHANGE_ACTIVE = "android.net.scoring.CHANGE_ACTIVE";
71 
72     /**
73      * Extra used with {@link #ACTION_CHANGE_ACTIVE} to specify the new scorer package. Set with
74      * {@link android.content.Intent#putExtra(String, String)}.
75      * @deprecated No longer sent.
76      */
77     @Deprecated
78     public static final String EXTRA_PACKAGE_NAME = "packageName";
79 
80     /**
81      * Broadcast action: new network scores are being requested. This intent will only be delivered
82      * to the current active scorer app. That app is responsible for scoring the networks and
83      * calling {@link #updateScores} when complete. The networks to score are specified in
84      * {@link #EXTRA_NETWORKS_TO_SCORE}, and will generally consist of all networks which have been
85      * configured by the user as well as any open networks.
86      *
87      * <p class="note">This is a protected intent that can only be sent by the system.
88      * @deprecated Use {@link #ACTION_RECOMMEND_NETWORKS} to bind scorer app instead.
89      */
90     @Deprecated
91     @SdkConstant(SdkConstantType.BROADCAST_INTENT_ACTION)
92     public static final String ACTION_SCORE_NETWORKS = "android.net.scoring.SCORE_NETWORKS";
93 
94     /**
95      * Extra used with {@link #ACTION_SCORE_NETWORKS} to specify the networks to be scored, as an
96      * array of {@link NetworkKey}s. Can be obtained with
97      * {@link android.content.Intent#getParcelableArrayExtra(String)}}.
98      * @deprecated Use {@link #ACTION_RECOMMEND_NETWORKS} to bind scorer app instead.
99      */
100     @Deprecated
101     public static final String EXTRA_NETWORKS_TO_SCORE = "networksToScore";
102 
103     /**
104      * Activity action: launch an activity for configuring a provider for the feature that connects
105      * and secures open wifi networks available before enabling it. Applications that enable this
106      * feature must provide an activity for this action. The framework will launch this activity
107      * which must return RESULT_OK if the feature should be enabled.
108      */
109     @SdkConstant(SdkConstantType.ACTIVITY_INTENT_ACTION)
110     public static final String ACTION_CUSTOM_ENABLE = "android.net.scoring.CUSTOM_ENABLE";
111 
112     /**
113      * Meta-data specified on a {@link NetworkRecommendationProvider} that provides a user-visible
114      * label of the recommendation service.
115      * @hide
116      */
117     public static final String RECOMMENDATION_SERVICE_LABEL_META_DATA =
118             "android.net.scoring.recommendation_service_label";
119 
120     /**
121      * Meta-data specified on a {@link NetworkRecommendationProvider} that specified the package
122      * name of the application that connects and secures open wifi networks automatically. The
123      * specified package must provide an Activity for {@link #ACTION_CUSTOM_ENABLE}.
124      * @hide
125      */
126     public static final String USE_OPEN_WIFI_PACKAGE_META_DATA =
127             "android.net.wifi.use_open_wifi_package";
128 
129     /**
130      * Meta-data specified on a {@link NetworkRecommendationProvider} that specifies the
131      * {@link android.app.NotificationChannel} ID used to post open network notifications.
132      * @hide
133      */
134     public static final String NETWORK_AVAILABLE_NOTIFICATION_CHANNEL_ID_META_DATA =
135             "android.net.wifi.notification_channel_id_network_available";
136 
137     /**
138      * Broadcast action: the active scorer has been changed. Scorer apps may listen to this to
139      * perform initialization once selected as the active scorer, or clean up unneeded resources
140      * if another scorer has been selected. This is an explicit broadcast only sent to the
141      * previous scorer and new scorer. Note that it is unnecessary to clear existing scores as
142      * this is handled by the system.
143      *
144      * <p>The new scorer will be specified in {@link #EXTRA_NEW_SCORER}.
145      *
146      * <p class="note">This is a protected intent that can only be sent by the system.
147      */
148     @SdkConstant(SdkConstantType.BROADCAST_INTENT_ACTION)
149     public static final String ACTION_SCORER_CHANGED = "android.net.scoring.SCORER_CHANGED";
150 
151     /**
152      * Service action: Used to discover and bind to a network recommendation provider.
153      * Implementations should return {@link NetworkRecommendationProvider#getBinder()} from
154      * their <code>onBind()</code> method.
155      */
156     @SdkConstant(SdkConstantType.SERVICE_ACTION)
157     public static final String ACTION_RECOMMEND_NETWORKS = "android.net.action.RECOMMEND_NETWORKS";
158 
159     /**
160      * Extra used with {@link #ACTION_SCORER_CHANGED} to specify the newly selected scorer's package
161      * name. Will be null if scoring was disabled. Can be obtained with
162      * {@link android.content.Intent#getStringExtra(String)}.
163      */
164     public static final String EXTRA_NEW_SCORER = "newScorer";
165 
166     /** @hide */
167     @IntDef({SCORE_FILTER_NONE, SCORE_FILTER_CURRENT_NETWORK, SCORE_FILTER_SCAN_RESULTS})
168     @Retention(RetentionPolicy.SOURCE)
169     public @interface ScoreUpdateFilter {}
170 
171     /**
172      * Do not filter updates sent to the {@link NetworkScoreCallback}].
173      */
174     public static final int SCORE_FILTER_NONE = 0;
175 
176     /**
177      * Only send updates to the {@link NetworkScoreCallback} when the network matches the connected
178      * network.
179      */
180     public static final int SCORE_FILTER_CURRENT_NETWORK = 1;
181 
182     /**
183      * Only send updates to the {@link NetworkScoreCallback} when the network is part of the
184      * current scan result set.
185      */
186     public static final int SCORE_FILTER_SCAN_RESULTS = 2;
187 
188     /** @hide */
189     @IntDef({RECOMMENDATIONS_ENABLED_FORCED_OFF, RECOMMENDATIONS_ENABLED_OFF,
190             RECOMMENDATIONS_ENABLED_ON})
191     @Retention(RetentionPolicy.SOURCE)
192     public @interface RecommendationsEnabledSetting {}
193 
194     /**
195      * Recommendations have been forced off.
196      * <p>
197      * This value is never set by any of the NetworkScore classes, it must be set via other means.
198      * This state is also "sticky" and we won't transition out of this state once entered. To move
199      * to a different state this value has to be explicitly set to a different value via
200      * other means.
201      * @hide
202      */
203     public static final int RECOMMENDATIONS_ENABLED_FORCED_OFF = -1;
204 
205     /**
206      * Recommendations are not enabled.
207      * <p>
208      * This is a transient state that can be entered when the default recommendation app is enabled
209      * but no longer valid. This state will transition to RECOMMENDATIONS_ENABLED_ON when a valid
210      * recommendation app is enabled.
211      * @hide
212      */
213     public static final int RECOMMENDATIONS_ENABLED_OFF = 0;
214 
215     /**
216      * Recommendations are enabled.
217      * <p>
218      * This is a transient state that means a valid recommendation app is active. This state will
219      * transition to RECOMMENDATIONS_ENABLED_OFF if the current and default recommendation apps
220      * become invalid.
221      * @hide
222      */
223     public static final int RECOMMENDATIONS_ENABLED_ON = 1;
224 
225     private final Context mContext;
226     private final INetworkScoreService mService;
227 
228     /** @hide */
NetworkScoreManager(Context context)229     public NetworkScoreManager(Context context) throws ServiceNotFoundException {
230         mContext = context;
231         mService = INetworkScoreService.Stub
232                 .asInterface(ServiceManager.getServiceOrThrow(Context.NETWORK_SCORE_SERVICE));
233     }
234 
235     /**
236      * Obtain the package name of the current active network scorer.
237      *
238      * <p>At any time, only one scorer application will receive {@link #ACTION_SCORE_NETWORKS}
239      * broadcasts and be allowed to call {@link #updateScores}. Applications may use this method to
240      * determine the current scorer and offer the user the ability to select a different scorer via
241      * the {@link #ACTION_CHANGE_ACTIVE} intent.
242      * @return the full package name of the current active scorer, or null if there is no active
243      *         scorer.
244      * @throws SecurityException if the caller doesn't hold either {@link permission#SCORE_NETWORKS}
245      *                           or {@link permission#REQUEST_NETWORK_SCORES} permissions.
246      */
247     @RequiresPermission(anyOf = {android.Manifest.permission.SCORE_NETWORKS,
248                                  android.Manifest.permission.REQUEST_NETWORK_SCORES})
getActiveScorerPackage()249     public String getActiveScorerPackage() {
250         try {
251             return mService.getActiveScorerPackage();
252         } catch (RemoteException e) {
253             throw e.rethrowFromSystemServer();
254         }
255     }
256 
257     /**
258      * Returns metadata about the active scorer or <code>null</code> if there is no active scorer.
259      *
260      * @throws SecurityException if the caller does not hold the
261      *         {@link permission#REQUEST_NETWORK_SCORES} permission.
262      * @hide
263      */
264     @Nullable
265     @RequiresPermission(android.Manifest.permission.REQUEST_NETWORK_SCORES)
getActiveScorer()266     public NetworkScorerAppData getActiveScorer() {
267         try {
268             return mService.getActiveScorer();
269         } catch (RemoteException e) {
270             throw e.rethrowFromSystemServer();
271         }
272     }
273 
274     /**
275      * Returns the list of available scorer apps. The list will be empty if there are
276      * no valid scorers.
277      *
278      * @throws SecurityException if the caller does not hold the
279      *         {@link permission#REQUEST_NETWORK_SCORES} permission.
280      * @hide
281      */
282     @RequiresPermission(android.Manifest.permission.REQUEST_NETWORK_SCORES)
getAllValidScorers()283     public List<NetworkScorerAppData> getAllValidScorers() {
284         try {
285             return mService.getAllValidScorers();
286         } catch (RemoteException e) {
287             throw e.rethrowFromSystemServer();
288         }
289     }
290 
291     /**
292      * Update network scores.
293      *
294      * <p>This may be called at any time to re-score active networks. Scores will generally be
295      * updated quickly, but if this method is called too frequently, the scores may be held and
296      * applied at a later time.
297      *
298      * @param networks the networks which have been scored by the scorer.
299      * @return whether the update was successful.
300      * @throws SecurityException if the caller is not the active scorer.
301      */
302     @RequiresPermission(android.Manifest.permission.SCORE_NETWORKS)
updateScores(@onNull ScoredNetwork[] networks)303     public boolean updateScores(@NonNull ScoredNetwork[] networks) throws SecurityException {
304         try {
305             return mService.updateScores(networks);
306         } catch (RemoteException e) {
307             throw e.rethrowFromSystemServer();
308         }
309     }
310 
311     /**
312      * Clear network scores.
313      *
314      * <p>Should be called when all scores need to be invalidated, i.e. because the scoring
315      * algorithm has changed and old scores can no longer be compared to future scores.
316      *
317      * <p>Note that scores will be cleared automatically when the active scorer changes, as scores
318      * from one scorer cannot be compared to those from another scorer.
319      *
320      * @return whether the clear was successful.
321      * @throws SecurityException if the caller is not the active scorer or if the caller doesn't
322      *                           hold the {@link permission#REQUEST_NETWORK_SCORES} permission.
323      */
324     @RequiresPermission(anyOf = {android.Manifest.permission.SCORE_NETWORKS,
325                                  android.Manifest.permission.REQUEST_NETWORK_SCORES})
clearScores()326     public boolean clearScores() throws SecurityException {
327         try {
328             return mService.clearScores();
329         } catch (RemoteException e) {
330             throw e.rethrowFromSystemServer();
331         }
332     }
333 
334     /**
335      * Set the active scorer to a new package and clear existing scores.
336      *
337      * <p>Should never be called directly without obtaining user consent. This can be done by using
338      * the {@link #ACTION_CHANGE_ACTIVE} broadcast, or using a custom configuration activity.
339      *
340      * @return true if the operation succeeded, or false if the new package is not a valid scorer.
341      * @throws SecurityException if the caller doesn't hold either {@link permission#SCORE_NETWORKS}
342      *                           or {@link permission#REQUEST_NETWORK_SCORES} permissions.
343      * @hide
344      */
345     @SystemApi
346     @RequiresPermission(anyOf = {android.Manifest.permission.SCORE_NETWORKS,
347                                  android.Manifest.permission.REQUEST_NETWORK_SCORES})
setActiveScorer(String packageName)348     public boolean setActiveScorer(String packageName) throws SecurityException {
349         try {
350             return mService.setActiveScorer(packageName);
351         } catch (RemoteException e) {
352             throw e.rethrowFromSystemServer();
353         }
354     }
355 
356     /**
357      * Turn off network scoring.
358      *
359      * <p>May only be called by the current scorer app, or the system.
360      *
361      * @throws SecurityException if the caller is not the active scorer or if the caller doesn't
362      *                           hold the {@link permission#REQUEST_NETWORK_SCORES} permission.
363      */
364     @RequiresPermission(anyOf = {android.Manifest.permission.SCORE_NETWORKS,
365                                  android.Manifest.permission.REQUEST_NETWORK_SCORES})
disableScoring()366     public void disableScoring() throws SecurityException {
367         try {
368             mService.disableScoring();
369         } catch (RemoteException e) {
370             throw e.rethrowFromSystemServer();
371         }
372     }
373 
374     /**
375      * Request scoring for networks.
376      *
377      * <p>
378      * Note: The results (i.e scores) for these networks, when available will be provided via the
379      * callback registered with {@link #registerNetworkScoreCallback(int, int, Executor,
380      * NetworkScoreCallback)}. The calling module is responsible for registering a callback to
381      * receive the results before requesting new scores via this API.
382      *
383      * @return true if the request was successfully sent, or false if there is no active scorer.
384      * @throws SecurityException if the caller does not hold the
385      *         {@link permission#REQUEST_NETWORK_SCORES} permission.
386      *
387      * @hide
388      */
389     @SystemApi
390     @RequiresPermission(android.Manifest.permission.REQUEST_NETWORK_SCORES)
requestScores(@onNull NetworkKey[] networks)391     public boolean requestScores(@NonNull NetworkKey[] networks) throws SecurityException {
392         try {
393             return mService.requestScores(networks);
394         } catch (RemoteException e) {
395             throw e.rethrowFromSystemServer();
396         }
397     }
398 
399     /**
400      * Register a network score cache.
401      *
402      * @param networkType the type of network this cache can handle. See {@link NetworkKey#type}.
403      * @param scoreCache implementation of {@link INetworkScoreCache} to store the scores.
404      * @throws SecurityException if the caller does not hold the
405      *         {@link permission#REQUEST_NETWORK_SCORES} permission.
406      * @throws IllegalArgumentException if a score cache is already registered for this type.
407      * @deprecated equivalent to registering for cache updates with {@link #SCORE_FILTER_NONE}.
408      * @hide
409      */
410     @RequiresPermission(android.Manifest.permission.REQUEST_NETWORK_SCORES)
411     @Deprecated // migrate to registerNetworkScoreCache(int, INetworkScoreCache, int)
registerNetworkScoreCache(int networkType, INetworkScoreCache scoreCache)412     public void registerNetworkScoreCache(int networkType, INetworkScoreCache scoreCache) {
413         registerNetworkScoreCache(networkType, scoreCache, SCORE_FILTER_NONE);
414     }
415 
416     /**
417      * Register a network score cache.
418      *
419      * @param networkType the type of network this cache can handle. See {@link NetworkKey#type}
420      * @param scoreCache implementation of {@link INetworkScoreCache} to store the scores
421      * @param filterType the {@link ScoreUpdateFilter} to apply
422      * @throws SecurityException if the caller does not hold the
423      *         {@link permission#REQUEST_NETWORK_SCORES} permission.
424      * @throws IllegalArgumentException if a score cache is already registered for this type.
425      * @hide
426      */
427     @RequiresPermission(android.Manifest.permission.REQUEST_NETWORK_SCORES)
registerNetworkScoreCache(int networkType, INetworkScoreCache scoreCache, @ScoreUpdateFilter int filterType)428     public void registerNetworkScoreCache(int networkType, INetworkScoreCache scoreCache,
429             @ScoreUpdateFilter int filterType) {
430         try {
431             mService.registerNetworkScoreCache(networkType, scoreCache, filterType);
432         } catch (RemoteException e) {
433             throw e.rethrowFromSystemServer();
434         }
435     }
436 
437     /**
438      * Unregister a network score cache.
439      *
440      * @param networkType the type of network this cache can handle. See {@link NetworkKey#type}.
441      * @param scoreCache implementation of {@link INetworkScoreCache} to store the scores.
442      * @throws SecurityException if the caller does not hold the
443      *         {@link permission#REQUEST_NETWORK_SCORES} permission.
444      * @throws IllegalArgumentException if a score cache is already registered for this type.
445      * @hide
446      */
447     @RequiresPermission(android.Manifest.permission.REQUEST_NETWORK_SCORES)
unregisterNetworkScoreCache(int networkType, INetworkScoreCache scoreCache)448     public void unregisterNetworkScoreCache(int networkType, INetworkScoreCache scoreCache) {
449         try {
450             mService.unregisterNetworkScoreCache(networkType, scoreCache);
451         } catch (RemoteException e) {
452             throw e.rethrowFromSystemServer();
453         }
454     }
455 
456     /**
457      * Base class for network score cache callback. Should be extended by applications and set
458      * when calling {@link #registerNetworkScoreCallback(int, int, NetworkScoreCallback,
459      * Executor)}
460      *
461      * @hide
462      */
463     @SystemApi
464     public interface NetworkScoreCallback {
465         /**
466          * Called when a new set of network scores are available.
467          * This is triggered in response when the client invokes
468          * {@link #requestScores(NetworkKey[])} to score a new set of networks.
469          *
470          * @param networks List of {@link ScoredNetwork} containing updated scores.
471          */
472         @SuppressLint("CallbackMethodName")
updateScores(@onNull List<ScoredNetwork> networks)473         void updateScores(@NonNull List<ScoredNetwork> networks);
474 
475         /**
476          * Invokes when all the previously provided scores are no longer valid.
477          */
478         @SuppressLint("CallbackMethodName")
clearScores()479         void clearScores();
480     }
481 
482     /**
483      * Callback proxy for {@link NetworkScoreCallback} objects.
484      */
485     private class NetworkScoreCallbackProxy extends INetworkScoreCache.Stub {
486         private final Executor mExecutor;
487         private final NetworkScoreCallback mCallback;
488 
NetworkScoreCallbackProxy(Executor executor, NetworkScoreCallback callback)489         NetworkScoreCallbackProxy(Executor executor, NetworkScoreCallback callback) {
490             mExecutor = executor;
491             mCallback = callback;
492         }
493 
494         @Override
updateScores(@onNull List<ScoredNetwork> networks)495         public void updateScores(@NonNull List<ScoredNetwork> networks) {
496             Binder.clearCallingIdentity();
497             mExecutor.execute(() -> {
498                 mCallback.updateScores(networks);
499             });
500         }
501 
502         @Override
clearScores()503         public void clearScores() {
504             Binder.clearCallingIdentity();
505             mExecutor.execute(() -> {
506                 mCallback.clearScores();
507             });
508         }
509     }
510 
511     /**
512      * Register a network score callback.
513      *
514      * @param networkType the type of network this cache can handle. See {@link NetworkKey#type}
515      * @param filterType the {@link ScoreUpdateFilter} to apply
516      * @param callback implementation of {@link NetworkScoreCallback} that will be invoked when the
517      *                 scores change.
518      * @param executor The executor on which to execute the callbacks.
519      * @throws SecurityException if the caller does not hold the
520      *         {@link permission#REQUEST_NETWORK_SCORES} permission.
521      * @throws IllegalArgumentException if a callback is already registered for this type.
522      * @hide
523      */
524     @SystemApi
525     @RequiresPermission(android.Manifest.permission.REQUEST_NETWORK_SCORES)
registerNetworkScoreCallback(@etworkKey.NetworkType int networkType, @ScoreUpdateFilter int filterType, @NonNull @CallbackExecutor Executor executor, @NonNull NetworkScoreCallback callback)526     public void registerNetworkScoreCallback(@NetworkKey.NetworkType int networkType,
527             @ScoreUpdateFilter int filterType,
528             @NonNull @CallbackExecutor Executor executor,
529             @NonNull NetworkScoreCallback callback) throws SecurityException {
530         if (callback == null || executor == null) {
531             throw new IllegalArgumentException("callback / executor cannot be null");
532         }
533         Log.v(TAG, "registerNetworkScoreCallback: callback=" + callback + ", executor="
534                 + executor);
535         // Use the @hide method.
536         registerNetworkScoreCache(
537                 networkType, new NetworkScoreCallbackProxy(executor, callback), filterType);
538     }
539 
540     /**
541      * Determine whether the application with the given UID is the enabled scorer.
542      *
543      * @param callingUid the UID to check
544      * @return true if the provided UID is the active scorer, false otherwise.
545      * @throws SecurityException if the caller does not hold the
546      *         {@link permission#REQUEST_NETWORK_SCORES} permission.
547      * @hide
548      */
549     @RequiresPermission(android.Manifest.permission.REQUEST_NETWORK_SCORES)
isCallerActiveScorer(int callingUid)550     public boolean isCallerActiveScorer(int callingUid) {
551         try {
552             return mService.isCallerActiveScorer(callingUid);
553         } catch (RemoteException e) {
554             throw e.rethrowFromSystemServer();
555         }
556     }
557 }
558