NetworkScoreService.java revision 6a4b220f1263d95fdefe6361c2bc87bbb04bbed0
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 com.android.server;
18
19import android.Manifest.permission;
20import android.content.Context;
21import android.content.SharedPreferences;
22import android.content.pm.PackageManager;
23import android.net.INetworkScoreService;
24import android.net.NetworkKey;
25import android.net.NetworkScorerAppManager;
26import android.net.RssiCurve;
27import android.net.ScoredNetwork;
28import android.text.TextUtils;
29
30import com.android.internal.R;
31
32import java.io.FileDescriptor;
33import java.io.PrintWriter;
34import java.util.HashMap;
35import java.util.Map;
36
37/**
38 * Backing service for {@link android.net.NetworkScoreManager}.
39 * @hide
40 */
41public class NetworkScoreService extends INetworkScoreService.Stub {
42    private static final String TAG = "NetworkScoreService";
43
44    /** SharedPreference bit set to true after the service is first initialized. */
45    private static final String PREF_SCORING_PROVISIONED = "is_provisioned";
46
47    private final Context mContext;
48
49    // TODO: Delete this temporary class once we have a real place for scores.
50    private final Map<NetworkKey, RssiCurve> mScoredNetworks;
51
52    public NetworkScoreService(Context context) {
53        mContext = context;
54        mScoredNetworks = new HashMap<>();
55    }
56
57    /** Called when the system is ready to run third-party code but before it actually does so. */
58    void systemReady() {
59        SharedPreferences prefs = mContext.getSharedPreferences(TAG, Context.MODE_PRIVATE);
60        if (!prefs.getBoolean(PREF_SCORING_PROVISIONED, false)) {
61            // On first run, we try to initialize the scorer to the one configured at build time.
62            // This will be a no-op if the scorer isn't actually valid.
63            String defaultPackage = mContext.getResources().getString(
64                    R.string.config_defaultNetworkScorerPackageName);
65            if (!TextUtils.isEmpty(defaultPackage)) {
66                NetworkScorerAppManager.setActiveScorer(mContext, defaultPackage);
67            }
68            prefs.edit().putBoolean(PREF_SCORING_PROVISIONED, true).apply();
69        }
70    }
71
72    @Override
73    public boolean updateScores(ScoredNetwork[] networks) {
74        if (!NetworkScorerAppManager.isCallerActiveScorer(mContext, getCallingUid())) {
75            throw new SecurityException("Caller with UID " + getCallingUid() +
76                    " is not the active scorer.");
77        }
78
79        // TODO: Propagate these scores down to the network subsystem layer instead of just holding
80        // them in memory.
81        for (ScoredNetwork network : networks) {
82            mScoredNetworks.put(network.networkKey, network.rssiCurve);
83        }
84
85        return true;
86    }
87
88    @Override
89    public boolean clearScores() {
90        // Only the active scorer or the system (who can broadcast BROADCAST_SCORE_NETWORKS) should
91        // be allowed to flush all scores.
92        if (NetworkScorerAppManager.isCallerActiveScorer(mContext, getCallingUid()) ||
93                mContext.checkCallingOrSelfPermission(permission.BROADCAST_SCORE_NETWORKS) ==
94                        PackageManager.PERMISSION_GRANTED) {
95            clearInternal();
96            return true;
97        } else {
98            throw new SecurityException(
99                    "Caller is neither the active scorer nor the scorer manager.");
100        }
101    }
102
103    @Override
104    public boolean setActiveScorer(String packageName) {
105        mContext.enforceCallingOrSelfPermission(permission.BROADCAST_SCORE_NETWORKS, TAG);
106        // Preemptively clear scores even though the set operation could fail. We do this for safety
107        // as scores should never be compared across apps; in practice, Settings should only be
108        // allowing valid apps to be set as scorers, so failure here should be rare.
109        clearInternal();
110        return NetworkScorerAppManager.setActiveScorer(mContext, packageName);
111    }
112
113    /** Clear scores. Callers are responsible for checking permissions as appropriate. */
114    private void clearInternal() {
115        // TODO: Propagate the flush down to the network subsystem layer.
116        mScoredNetworks.clear();
117    }
118
119    @Override
120    protected void dump(FileDescriptor fd, PrintWriter writer, String[] args) {
121        mContext.enforceCallingOrSelfPermission(permission.DUMP, TAG);
122        String currentScorer = NetworkScorerAppManager.getActiveScorer(mContext);
123        if (currentScorer == null) {
124            writer.println("Scoring is disabled.");
125            return;
126        }
127        writer.println("Current scorer: " + currentScorer);
128        if (mScoredNetworks.isEmpty()) {
129            writer.println("No networks scored.");
130        } else {
131            for (Map.Entry<NetworkKey, RssiCurve> entry : mScoredNetworks.entrySet()) {
132                writer.println(entry.getKey() + ": " + entry.getValue());
133            }
134        }
135    }
136}
137