blob: b137ad31ebf0816ac2139e7fe5818124d031aa51 [file] [log] [blame]
Jeff Davidsondd6fd1e2014-04-14 15:14:30 -07001/*
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.app.AppOpsManager;
21import android.content.Context;
22import android.content.Intent;
23import android.content.pm.ActivityInfo;
24import android.content.pm.PackageManager;
25import android.content.pm.ResolveInfo;
26import android.provider.Settings;
27import android.provider.Settings.Global;
28import android.text.TextUtils;
29
30import java.util.ArrayList;
31import java.util.Collection;
32import java.util.List;
33
34/**
35 * Internal class for managing the primary network scorer application.
36 *
37 * @hide
38 */
39public final class NetworkScorerApplication {
40
41 private static final Intent SCORE_INTENT =
42 new Intent(NetworkScoreManager.ACTION_SCORE_NETWORKS);
43
44 /** This class cannot be instantiated. */
45 private NetworkScorerApplication() {}
46
47 /**
48 * Returns the list of available scorer app package names.
49 *
50 * <p>A network scorer is any application which:
51 * <ul>
52 * <li>Declares the {@link android.Manifest.permission#SCORE_NETWORKS} permission.
53 * <li>Includes a receiver for {@link NetworkScoreManager#ACTION_SCORE_NETWORKS} guarded by the
54 * {@link android.Manifest.permission#BROADCAST_SCORE_NETWORKS} permission.
55 * </ul>
56 *
57 * @return the list of scorers, or the empty list if there are no valid scorers.
58 */
59 public static Collection<String> getAllValidScorers(Context context) {
60 List<String> scorers = new ArrayList<>();
61
62 PackageManager pm = context.getPackageManager();
63 List<ResolveInfo> receivers = pm.queryBroadcastReceivers(SCORE_INTENT, 0 /* flags */);
64 for (ResolveInfo receiver : receivers) {
65 // This field is a misnomer, see android.content.pm.ResolveInfo#activityInfo
66 final ActivityInfo receiverInfo = receiver.activityInfo;
67 if (receiverInfo == null) {
68 // Should never happen with queryBroadcastReceivers, but invalid nonetheless.
69 continue;
70 }
71 if (!permission.BROADCAST_SCORE_NETWORKS.equals(receiverInfo.permission)) {
72 // Receiver doesn't require the BROADCAST_SCORE_NETWORKS permission, which means
73 // anyone could trigger network scoring and flood the framework with score requests.
74 continue;
75 }
76 if (pm.checkPermission(permission.SCORE_NETWORKS, receiverInfo.packageName) !=
77 PackageManager.PERMISSION_GRANTED) {
78 // Application doesn't hold the SCORE_NETWORKS permission, so the user never
79 // approved it as a network scorer.
80 continue;
81 }
82 scorers.add(receiverInfo.packageName);
83 }
84
85 return scorers;
86 }
87
88 /**
89 * Get the application package name to use for scoring networks.
90 *
91 * @return the scorer package or null if scoring is disabled (including if no scorer was ever
92 * selected) or if the previously-set scorer is no longer a valid scorer app (e.g. because
93 * it was disabled or uninstalled).
94 */
95 public static String getActiveScorer(Context context) {
96 String scorerPackage = Settings.Global.getString(context.getContentResolver(),
97 Global.NETWORK_SCORER_APP);
98 Collection<String> applications = getAllValidScorers(context);
99 if (isPackageValidScorer(applications, scorerPackage)) {
100 return scorerPackage;
101 } else {
102 return null;
103 }
104 }
105
106 /**
107 * Set the specified package as the default scorer application.
108 *
109 * <p>The caller must have permission to write to {@link Settings.Global}.
110 *
111 * @param context the context of the calling application
112 * @param packageName the packageName of the new scorer to use. If null, scoring will be
113 * disabled. Otherwise, the scorer will only be set if it is a valid scorer application.
114 */
115 public static void setActiveScorer(Context context, String packageName) {
116 String oldPackageName = Settings.Global.getString(context.getContentResolver(),
117 Settings.Global.NETWORK_SCORER_APP);
118 if (TextUtils.equals(oldPackageName, packageName)) {
119 // No change.
120 return;
121 }
122
123 if (packageName == null) {
124 Settings.Global.putString(context.getContentResolver(), Global.NETWORK_SCORER_APP,
125 null);
126 } else {
127 // We only make the change if the new package is valid.
128 Collection<String> applications = getAllValidScorers(context);
129 if (isPackageValidScorer(applications, packageName)) {
130 Settings.Global.putString(context.getContentResolver(),
131 Settings.Global.NETWORK_SCORER_APP, packageName);
132 }
133 }
134 }
135
136 /** Determine whether the application with the given UID is the enabled scorer. */
137 public static boolean isCallerDefaultScorer(Context context, int callingUid) {
138 String defaultApp = getActiveScorer(context);
139 if (defaultApp == null) {
140 return false;
141 }
142 AppOpsManager appOpsMgr = (AppOpsManager) context.getSystemService(Context.APP_OPS_SERVICE);
143 try {
144 appOpsMgr.checkPackage(callingUid, defaultApp);
145 return true;
146 } catch (SecurityException e) {
147 return false;
148 }
149 }
150
151 /** Returns true if the given package is a valid scorer. */
152 private static boolean isPackageValidScorer(Collection<String> scorerPackageNames,
153 String packageName) {
154 return packageName != null && scorerPackageNames.contains(packageName);
155 }
156}