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