blob: 23d5af55641c77420e043a306ae03a33dd23366a [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;
Jeff Davidsonb6646a82014-06-27 16:24:42 -070020import android.annotation.Nullable;
Jeremy Joslinb8418ac82016-12-06 07:42:38 -080021import android.content.ContentResolver;
Jeff Davidsondd6fd1e2014-04-14 15:14:30 -070022import android.content.Context;
23import android.content.Intent;
Jeff Davidsondd6fd1e2014-04-14 15:14:30 -070024import android.content.pm.PackageManager;
25import android.content.pm.ResolveInfo;
Jeff Davidsonac7285d2014-08-08 15:12:47 -070026import android.os.UserHandle;
Jeff Davidsondd6fd1e2014-04-14 15:14:30 -070027import android.provider.Settings;
Jeff Davidsondd6fd1e2014-04-14 15:14:30 -070028import android.text.TextUtils;
Jeff Davidson6a4b2202014-04-16 17:29:40 -070029import android.util.Log;
Jeremy Joslin9b442fa2017-01-09 16:22:20 -080030
Jeremy Joslinb8418ac82016-12-06 07:42:38 -080031import com.android.internal.R;
Jeremy Joslin9b442fa2017-01-09 16:22:20 -080032
Jeff Davidsondd6fd1e2014-04-14 15:14:30 -070033import java.util.ArrayList;
Jeremy Joslin5b294b42015-12-17 17:38:04 -080034import java.util.Collections;
Jeff Davidsondd6fd1e2014-04-14 15:14:30 -070035import java.util.List;
36
37/**
Jeremy Joslinb8418ac82016-12-06 07:42:38 -080038 * Internal class for discovering and managing the network scorer/recommendation application.
Jeff Davidsonc7415532014-06-23 18:15:34 -070039 *
Jeff Davidsondd6fd1e2014-04-14 15:14:30 -070040 * @hide
41 */
Amin Shaikhaa09aa02016-11-21 17:27:53 -080042public class NetworkScorerAppManager {
Jeff Davidson6a4b2202014-04-16 17:29:40 -070043 private static final String TAG = "NetworkScorerAppManager";
Jeremy Joslinb8418ac82016-12-06 07:42:38 -080044 private static final boolean DEBUG = Log.isLoggable(TAG, Log.DEBUG);
45 private static final boolean VERBOSE = Log.isLoggable(TAG, Log.VERBOSE);
Amin Shaikhaa09aa02016-11-21 17:27:53 -080046 private final Context mContext;
47
48 public NetworkScorerAppManager(Context context) {
49 mContext = context;
50 }
Jeff Davidsondd6fd1e2014-04-14 15:14:30 -070051
Jeremy Joslinb8418ac82016-12-06 07:42:38 -080052 /**
53 * Holds metadata about a discovered network scorer/recommendation application.
54 */
Jeff Davidsonc7415532014-06-23 18:15:34 -070055 public static class NetworkScorerAppData {
56 /** Package name of this scorer app. */
Jeremy Joslinb8418ac82016-12-06 07:42:38 -080057 public final String packageName;
Jeff Davidsonc7415532014-06-23 18:15:34 -070058
Shirish Kalele4cab12d2015-08-06 12:34:22 -070059 /** UID of the scorer app. */
Jeremy Joslinb8418ac82016-12-06 07:42:38 -080060 public final int packageUid;
Jeff Davidsonc7415532014-06-23 18:15:34 -070061
Jeff Davidsonb6646a82014-06-27 16:24:42 -070062 /**
Jeremy Joslinb8418ac82016-12-06 07:42:38 -080063 * Name of the recommendation service we can bind to.
Jeff Davidsonb6646a82014-06-27 16:24:42 -070064 */
Jeremy Joslinb8418ac82016-12-06 07:42:38 -080065 public final String recommendationServiceClassName;
Jeff Davidsonb6646a82014-06-27 16:24:42 -070066
Jeremy Joslinb8418ac82016-12-06 07:42:38 -080067 public NetworkScorerAppData(String packageName, int packageUid,
68 String recommendationServiceClassName) {
69 this.packageName = packageName;
70 this.packageUid = packageUid;
71 this.recommendationServiceClassName = recommendationServiceClassName;
Jeremy Joslindd251ef2016-03-14 11:17:41 -070072 }
73
74 @Override
75 public String toString() {
76 final StringBuilder sb = new StringBuilder("NetworkScorerAppData{");
Jeremy Joslinb8418ac82016-12-06 07:42:38 -080077 sb.append("mPackageName='").append(packageName).append('\'');
78 sb.append(", packageUid=").append(packageUid);
79 sb.append(", recommendationServiceClassName='")
80 .append(recommendationServiceClassName).append('\'');
Jeremy Joslindd251ef2016-03-14 11:17:41 -070081 sb.append('}');
82 return sb.toString();
Jeff Davidsonc7415532014-06-23 18:15:34 -070083 }
84 }
85
Jeff Davidsondd6fd1e2014-04-14 15:14:30 -070086 /**
Jeremy Joslinb8418ac82016-12-06 07:42:38 -080087 * @return A {@link NetworkScorerAppData} instance containing information about the
88 * best configured network recommendation provider installed or {@code null}
89 * if none of the configured packages can recommend networks.
Jeff Davidsondd6fd1e2014-04-14 15:14:30 -070090 *
Jeremy Joslinb8418ac82016-12-06 07:42:38 -080091 * <p>A network recommendation provider is any application which:
Jeff Davidsondd6fd1e2014-04-14 15:14:30 -070092 * <ul>
Jeremy Joslinb8418ac82016-12-06 07:42:38 -080093 * <li>Is listed in the <code>config_networkRecommendationPackageNames</code> config.
Jeff Davidsondd6fd1e2014-04-14 15:14:30 -070094 * <li>Declares the {@link android.Manifest.permission#SCORE_NETWORKS} permission.
Jeremy Joslinb8418ac82016-12-06 07:42:38 -080095 * <li>Includes a Service for {@link NetworkScoreManager#ACTION_RECOMMEND_NETWORKS}.
Jeff Davidsondd6fd1e2014-04-14 15:14:30 -070096 * </ul>
Jeff Davidsondd6fd1e2014-04-14 15:14:30 -070097 */
Jeremy Joslinb8418ac82016-12-06 07:42:38 -080098 public NetworkScorerAppData getNetworkRecommendationProviderData() {
99 // Network recommendation apps can only run as the primary user right now.
100 // http://b/23422763
Jeremy Joslin2ceac5a2016-01-08 09:30:44 -0800101 if (UserHandle.getCallingUserId() != UserHandle.USER_SYSTEM) {
Jeremy Joslinb8418ac82016-12-06 07:42:38 -0800102 return null;
103 }
104
105 final List<String> potentialPkgs = getPotentialRecommendationProviderPackages();
106 if (potentialPkgs.isEmpty()) {
107 if (DEBUG) {
108 Log.d(TAG, "No Network Recommendation Providers specified.");
109 }
110 return null;
111 }
112
113 final PackageManager pm = mContext.getPackageManager();
114 for (int i = 0; i < potentialPkgs.size(); i++) {
115 final String potentialPkg = potentialPkgs.get(i);
116
117 // Look for the recommendation service class and required receiver.
118 final ResolveInfo resolveServiceInfo = findRecommendationService(potentialPkg);
119 if (resolveServiceInfo != null) {
120 return new NetworkScorerAppData(potentialPkg,
121 resolveServiceInfo.serviceInfo.applicationInfo.uid,
122 resolveServiceInfo.serviceInfo.name);
123 } else {
124 if (DEBUG) {
125 Log.d(TAG, potentialPkg + " does not have the required components, skipping.");
126 }
127 }
128 }
129
130 // None of the configured packages are valid.
131 return null;
132 }
133
134 /**
135 * @return A priority order list of package names that have been granted the
136 * permission needed for them to act as a network recommendation provider.
137 * The packages in the returned list may not contain the other required
138 * network recommendation provider components so additional checks are required
139 * before making a package the network recommendation provider.
140 */
141 public List<String> getPotentialRecommendationProviderPackages() {
142 final String[] packageArray = mContext.getResources().getStringArray(
143 R.array.config_networkRecommendationPackageNames);
144 if (packageArray == null || packageArray.length == 0) {
145 if (DEBUG) {
146 Log.d(TAG, "No Network Recommendation Providers specified.");
147 }
Jeremy Joslin5b294b42015-12-17 17:38:04 -0800148 return Collections.emptyList();
149 }
Jeff Davidsondd6fd1e2014-04-14 15:14:30 -0700150
Jeremy Joslinb8418ac82016-12-06 07:42:38 -0800151 if (VERBOSE) {
152 Log.d(TAG, "Configured packages: " + TextUtils.join(", ", packageArray));
Jeff Davidsondd6fd1e2014-04-14 15:14:30 -0700153 }
154
Jeremy Joslinb8418ac82016-12-06 07:42:38 -0800155 List<String> packages = new ArrayList<>();
156 final PackageManager pm = mContext.getPackageManager();
157 for (String potentialPkg : packageArray) {
158 if (pm.checkPermission(permission.SCORE_NETWORKS, potentialPkg)
159 == PackageManager.PERMISSION_GRANTED) {
160 packages.add(potentialPkg);
161 } else {
162 if (DEBUG) {
163 Log.d(TAG, potentialPkg + " has not been granted " + permission.SCORE_NETWORKS
164 + ", skipping.");
165 }
166 }
167 }
168
169 return packages;
170 }
171
172 private ResolveInfo findRecommendationService(String packageName) {
173 final PackageManager pm = mContext.getPackageManager();
174 final int resolveFlags = 0;
175
176 final Intent serviceIntent = new Intent(NetworkScoreManager.ACTION_RECOMMEND_NETWORKS);
177 serviceIntent.setPackage(packageName);
178 final ResolveInfo resolveServiceInfo =
179 pm.resolveService(serviceIntent, resolveFlags);
180
181 if (VERBOSE) {
182 Log.d(TAG, "Resolved " + serviceIntent + " to " + resolveServiceInfo);
183 }
184
185 if (resolveServiceInfo != null && resolveServiceInfo.serviceInfo != null) {
186 return resolveServiceInfo;
187 }
188
189 if (VERBOSE) {
190 Log.v(TAG, packageName + " does not have a service for " + serviceIntent);
191 }
192 return null;
Jeff Davidsondd6fd1e2014-04-14 15:14:30 -0700193 }
194
195 /**
Jeff Davidsonc7415532014-06-23 18:15:34 -0700196 * Get the application to use for scoring networks.
Jeff Davidsondd6fd1e2014-04-14 15:14:30 -0700197 *
Jeff Davidsonc7415532014-06-23 18:15:34 -0700198 * @return the scorer app info or null if scoring is disabled (including if no scorer was ever
Jeff Davidsondd6fd1e2014-04-14 15:14:30 -0700199 * selected) or if the previously-set scorer is no longer a valid scorer app (e.g. because
200 * it was disabled or uninstalled).
201 */
Jeremy Joslinb8418ac82016-12-06 07:42:38 -0800202 @Nullable
Amin Shaikhaa09aa02016-11-21 17:27:53 -0800203 public NetworkScorerAppData getActiveScorer() {
Jeremy Joslinb8418ac82016-12-06 07:42:38 -0800204 if (isNetworkRecommendationsDisabled()) {
205 // If recommendations are disabled then there can't be an active scorer.
206 return null;
207 }
208
209 // Otherwise return the recommendation provider (which may be null).
210 return getNetworkRecommendationProviderData();
Jeff Davidsondd6fd1e2014-04-14 15:14:30 -0700211 }
212
213 /**
214 * Set the specified package as the default scorer application.
215 *
Jeff Davidsonc7415532014-06-23 18:15:34 -0700216 * <p>The caller must have permission to write to {@link android.provider.Settings.Global}.
Jeff Davidsondd6fd1e2014-04-14 15:14:30 -0700217 *
Jeff Davidsondd6fd1e2014-04-14 15:14:30 -0700218 * @param packageName the packageName of the new scorer to use. If null, scoring will be
219 * disabled. Otherwise, the scorer will only be set if it is a valid scorer application.
Jeremy Joslinb8418ac82016-12-06 07:42:38 -0800220 * @return true if the scorer was changed, or false if the package is not a valid scorer or
221 * a valid network recommendation provider exists.
222 * @deprecated Scorers are now selected from a configured list.
Jeff Davidsondd6fd1e2014-04-14 15:14:30 -0700223 */
Jeremy Joslinb8418ac82016-12-06 07:42:38 -0800224 @Deprecated
Amin Shaikhaa09aa02016-11-21 17:27:53 -0800225 public boolean setActiveScorer(String packageName) {
Jeremy Joslinb8418ac82016-12-06 07:42:38 -0800226 return false;
Jeff Davidsondd6fd1e2014-04-14 15:14:30 -0700227 }
228
229 /** Determine whether the application with the given UID is the enabled scorer. */
Jeremy Joslin9b442fa2017-01-09 16:22:20 -0800230 @Deprecated // Use NetworkScoreManager.isCallerActiveScorer()
Amin Shaikhaa09aa02016-11-21 17:27:53 -0800231 public boolean isCallerActiveScorer(int callingUid) {
232 NetworkScorerAppData defaultApp = getActiveScorer();
Jeff Davidsondd6fd1e2014-04-14 15:14:30 -0700233 if (defaultApp == null) {
234 return false;
235 }
Amin Shaikh2cd918a2017-01-05 14:28:47 -0800236 return callingUid == defaultApp.packageUid;
Jeff Davidsondd6fd1e2014-04-14 15:14:30 -0700237 }
238
Jeremy Joslinb8418ac82016-12-06 07:42:38 -0800239 private boolean isNetworkRecommendationsDisabled() {
240 final ContentResolver cr = mContext.getContentResolver();
241 // A value of 1 indicates enabled.
242 return Settings.Global.getInt(cr, Settings.Global.NETWORK_RECOMMENDATIONS_ENABLED, 0) != 1;
Jeff Davidsondd6fd1e2014-04-14 15:14:30 -0700243 }
244}