Loading core/java/android/net/NetworkScorerAppManager.java +32 −5 Original line number Diff line number Diff line Loading @@ -19,11 +19,9 @@ package android.net; import android.Manifest; import android.Manifest.permission; import android.annotation.Nullable; import android.app.AppOpsManager; import android.content.Context; import android.content.Intent; import android.content.pm.ActivityInfo; import android.content.pm.ApplicationInfo; import android.content.pm.PackageManager; import android.content.pm.ResolveInfo; import android.os.UserHandle; Loading Loading @@ -69,12 +67,32 @@ public final class NetworkScorerAppManager { */ public final String mConfigurationActivityClassName; /** * Optional class name of the scoring service we can bind to. Null if none is set. */ public final String mScoringServiceClassName; public NetworkScorerAppData(String packageName, int packageUid, CharSequence scorerName, @Nullable String configurationActivityClassName) { @Nullable String configurationActivityClassName, @Nullable String scoringServiceClassName) { mScorerName = scorerName; mPackageName = packageName; mPackageUid = packageUid; mConfigurationActivityClassName = configurationActivityClassName; mScoringServiceClassName = scoringServiceClassName; } @Override public String toString() { final StringBuilder sb = new StringBuilder("NetworkScorerAppData{"); sb.append("mPackageName='").append(mPackageName).append('\''); sb.append(", mPackageUid=").append(mPackageUid); sb.append(", mScorerName=").append(mScorerName); sb.append(", mConfigurationActivityClassName='").append(mConfigurationActivityClassName) .append('\''); sb.append(", mScoringServiceClassName='").append(mScoringServiceClassName).append('\''); sb.append('}'); return sb.toString(); } } Loading Loading @@ -128,18 +146,27 @@ public final class NetworkScorerAppManager { Intent intent = new Intent(NetworkScoreManager.ACTION_CUSTOM_ENABLE); intent.setPackage(receiverInfo.packageName); List<ResolveInfo> configActivities = pm.queryIntentActivities(intent, 0 /* flags */); if (!configActivities.isEmpty()) { if (configActivities != null && !configActivities.isEmpty()) { ActivityInfo activityInfo = configActivities.get(0).activityInfo; if (activityInfo != null) { configurationActivityClassName = activityInfo.name; } } // Find the scoring service class we can bind to, if any. String scoringServiceClassName = null; Intent serviceIntent = new Intent(NetworkScoreManager.ACTION_SCORE_NETWORKS); serviceIntent.setPackage(receiverInfo.packageName); ResolveInfo resolveServiceInfo = pm.resolveService(serviceIntent, 0 /* flags */); if (resolveServiceInfo != null && resolveServiceInfo.serviceInfo != null) { scoringServiceClassName = resolveServiceInfo.serviceInfo.name; } // NOTE: loadLabel will attempt to load the receiver's label and fall back to the // app label if none is present. scorers.add(new NetworkScorerAppData(receiverInfo.packageName, receiverInfo.applicationInfo.uid, receiverInfo.loadLabel(pm), configurationActivityClassName)); configurationActivityClassName, scoringServiceClassName)); } return scorers; Loading core/tests/coretests/src/android/net/NetworkScorerAppManagerTest.java +63 −19 Original line number Diff line number Diff line Loading @@ -23,10 +23,10 @@ import android.content.pm.ActivityInfo; import android.content.pm.ApplicationInfo; import android.content.pm.PackageManager; import android.content.pm.ResolveInfo; import android.content.pm.ServiceInfo; import android.net.NetworkScorerAppManager.NetworkScorerAppData; import android.os.UserHandle; import android.test.InstrumentationTestCase; import android.util.Pair; import org.mockito.ArgumentMatcher; import org.mockito.Mock; Loading Loading @@ -58,25 +58,26 @@ public class NetworkScorerAppManagerTest extends InstrumentationTestCase { public void testGetAllValidScorers() throws Exception { // Package 1 - Valid scorer. Pair<ResolveInfo, ResolveInfo> package1 = buildResolveInfo("package1", 1, true, true, false); ResolveInfoHolder package1 = buildResolveInfo("package1", 1, true, true, false, false); // Package 2 - Receiver does not have BROADCAST_NETWORK_PRIVILEGED permission. Pair<ResolveInfo, ResolveInfo> package2 = buildResolveInfo("package2", 2, false, true, false); ResolveInfoHolder package2 = buildResolveInfo("package2", 2, false, true, false, false); // Package 3 - App does not have SCORE_NETWORKS permission. Pair<ResolveInfo, ResolveInfo> package3 = buildResolveInfo("package3", 3, true, false, false); ResolveInfoHolder package3 = buildResolveInfo("package3", 3, true, false, false, false); // Package 4 - Valid scorer w/ optional config activity. Pair<ResolveInfo, ResolveInfo> package4 = buildResolveInfo("package4", 4, true, true, true); ResolveInfoHolder package4 = buildResolveInfo("package4", 4, true, true, true, false); List<Pair<ResolveInfo, ResolveInfo>> scorers = new ArrayList<>(); // Package 5 - Valid scorer w/ optional service to bind to. ResolveInfoHolder package5 = buildResolveInfo("package5", 5, true, true, false, true); List<ResolveInfoHolder> scorers = new ArrayList<>(); scorers.add(package1); scorers.add(package2); scorers.add(package3); scorers.add(package4); scorers.add(package5); setScorers(scorers); Iterator<NetworkScorerAppData> result = Loading @@ -94,14 +95,20 @@ public class NetworkScorerAppManagerTest extends InstrumentationTestCase { assertEquals(4, next.mPackageUid); assertEquals(".ConfigActivity", next.mConfigurationActivityClassName); assertTrue(result.hasNext()); next = result.next(); assertEquals("package5", next.mPackageName); assertEquals(5, next.mPackageUid); assertEquals(".ScoringService", next.mScoringServiceClassName); assertFalse(result.hasNext()); } private void setScorers(List<Pair<ResolveInfo, ResolveInfo>> scorers) { private void setScorers(List<ResolveInfoHolder> scorers) { List<ResolveInfo> receivers = new ArrayList<>(); for (final Pair<ResolveInfo, ResolveInfo> scorer : scorers) { receivers.add(scorer.first); if (scorer.second != null) { for (final ResolveInfoHolder scorer : scorers) { receivers.add(scorer.scorerResolveInfo); if (scorer.configActivityResolveInfo != null) { // This scorer has a config activity. Mockito.when(mMockPm.queryIntentActivities( Mockito.argThat(new ArgumentMatcher<Intent>() { Loading @@ -110,10 +117,26 @@ public class NetworkScorerAppManagerTest extends InstrumentationTestCase { Intent intent = (Intent) object; return NetworkScoreManager.ACTION_CUSTOM_ENABLE.equals( intent.getAction()) && scorer.first.activityInfo.packageName.equals( && scorer.scorerResolveInfo.activityInfo.packageName.equals( intent.getPackage()); } }), Mockito.eq(0))).thenReturn( Collections.singletonList(scorer.configActivityResolveInfo)); } if (scorer.serviceResolveInfo != null) { // This scorer has a service to bind to Mockito.when(mMockPm.resolveService( Mockito.argThat(new ArgumentMatcher<Intent>() { @Override public boolean matches(Object object) { Intent intent = (Intent) object; return NetworkScoreManager.ACTION_SCORE_NETWORKS.equals( intent.getAction()) && scorer.scorerResolveInfo.activityInfo.packageName.equals( intent.getPackage()); } }), Mockito.eq(0))).thenReturn(Collections.singletonList(scorer.second)); }), Mockito.eq(0))).thenReturn(scorer.serviceResolveInfo); } } Loading @@ -128,9 +151,9 @@ public class NetworkScorerAppManagerTest extends InstrumentationTestCase { .thenReturn(receivers); } private Pair<ResolveInfo, ResolveInfo> buildResolveInfo(String packageName, int packageUid, boolean hasReceiverPermission, boolean hasScorePermission, boolean hasConfigActivity) throws Exception { private ResolveInfoHolder buildResolveInfo(String packageName, int packageUid, boolean hasReceiverPermission, boolean hasScorePermission, boolean hasConfigActivity, boolean hasServiceInfo) throws Exception { Mockito.when(mMockPm.checkPermission(permission.SCORE_NETWORKS, packageName)) .thenReturn(hasScorePermission ? PackageManager.PERMISSION_GRANTED : PackageManager.PERMISSION_DENIED); Loading @@ -150,6 +173,27 @@ public class NetworkScorerAppManagerTest extends InstrumentationTestCase { configActivityInfo.activityInfo = new ActivityInfo(); configActivityInfo.activityInfo.name = ".ConfigActivity"; } return Pair.create(resolveInfo, configActivityInfo); ResolveInfo serviceInfo = null; if (hasServiceInfo) { serviceInfo = new ResolveInfo(); serviceInfo.serviceInfo = new ServiceInfo(); serviceInfo.serviceInfo.name = ".ScoringService"; } return new ResolveInfoHolder(resolveInfo, configActivityInfo, serviceInfo); } private static class ResolveInfoHolder { final ResolveInfo scorerResolveInfo; final ResolveInfo configActivityResolveInfo; final ResolveInfo serviceResolveInfo; public ResolveInfoHolder(ResolveInfo scorerResolveInfo, ResolveInfo configActivityResolveInfo, ResolveInfo serviceResolveInfo) { this.scorerResolveInfo = scorerResolveInfo; this.configActivityResolveInfo = configActivityResolveInfo; this.serviceResolveInfo = serviceResolveInfo; } } } services/core/java/com/android/server/NetworkScoreService.java +125 −12 Original line number Diff line number Diff line Loading @@ -18,10 +18,12 @@ package com.android.server; import android.Manifest.permission; import android.content.BroadcastReceiver; import android.content.ComponentName; import android.content.ContentResolver; import android.content.Context; import android.content.Intent; import android.content.IntentFilter; import android.content.ServiceConnection; import android.content.pm.PackageManager; import android.net.INetworkScoreCache; import android.net.INetworkScoreService; Loading @@ -30,6 +32,7 @@ import android.net.NetworkScorerAppManager; import android.net.NetworkScorerAppManager.NetworkScorerAppData; import android.net.ScoredNetwork; import android.os.Binder; import android.os.IBinder; import android.os.PatternMatcher; import android.os.RemoteException; import android.os.UserHandle; Loading @@ -55,17 +58,17 @@ import java.util.Set; */ public class NetworkScoreService extends INetworkScoreService.Stub { private static final String TAG = "NetworkScoreService"; private static final boolean DBG = false; private final Context mContext; private final Map<Integer, INetworkScoreCache> mScoreCaches; /** Lock used to update mReceiver when scorer package changes occur. */ private Object mReceiverLock = new Object[0]; private final Object mReceiverLock = new Object[0]; /** Clears scores when the active scorer package is no longer valid. */ @GuardedBy("mReceiverLock") private ScorerChangedReceiver mReceiver; private ScoringServiceConnection mServiceConnection; private class ScorerChangedReceiver extends BroadcastReceiver { final String mRegisteredPackage; Loading @@ -77,14 +80,23 @@ public class NetworkScoreService extends INetworkScoreService.Stub { @Override public void onReceive(Context context, Intent intent) { String action = intent.getAction(); if ((Intent.ACTION_PACKAGE_CHANGED.equals(action) || Intent.ACTION_PACKAGE_REPLACED.equals(action) || Intent.ACTION_PACKAGE_FULLY_REMOVED.equals(action)) && NetworkScorerAppManager.getActiveScorer(mContext) == null) { if (Intent.ACTION_PACKAGE_CHANGED.equals(action) || Intent.ACTION_PACKAGE_REPLACED.equals(action) || Intent.ACTION_PACKAGE_FULLY_REMOVED.equals(action)) { NetworkScorerAppData activeScorer = NetworkScorerAppManager.getActiveScorer(mContext); if (activeScorer == null) { // Package change has invalidated a scorer. Log.i(TAG, "Package " + mRegisteredPackage + " is no longer valid, disabling scoring"); " is no longer valid, disabling scoring."); setScorerInternal(null); } else if (activeScorer.mScoringServiceClassName == null) { // The scoring service is not available, make sure it's unbound. unbindFromScoringServiceIfNeeded(); } else { // The scoring service may have changed or been added. bindToScoringServiceIfNeeded(activeScorer); } } } } Loading @@ -96,6 +108,7 @@ public class NetworkScoreService extends INetworkScoreService.Stub { /** Called when the system is ready to run third-party code but before it actually does so. */ void systemReady() { if (DBG) Log.d(TAG, "systemReady"); ContentResolver cr = mContext.getContentResolver(); if (Settings.Global.getInt(cr, Settings.Global.NETWORK_SCORING_PROVISIONED, 0) == 0) { // On first run, we try to initialize the scorer to the one configured at build time. Loading @@ -111,7 +124,14 @@ public class NetworkScoreService extends INetworkScoreService.Stub { registerPackageReceiverIfNeeded(); } /** Called when the system is ready for us to start third-party code. */ void systemRunning() { if (DBG) Log.d(TAG, "systemRunning"); bindToScoringServiceIfNeeded(); } private void registerPackageReceiverIfNeeded() { if (DBG) Log.d(TAG, "registerPackageReceiverIfNeeded"); NetworkScorerAppData scorer = NetworkScorerAppManager.getActiveScorer(mContext); synchronized (mReceiverLock) { // Unregister the receiver if the current scorer has changed since last registration. Loading Loading @@ -142,6 +162,41 @@ public class NetworkScoreService extends INetworkScoreService.Stub { } } private void bindToScoringServiceIfNeeded() { if (DBG) Log.d(TAG, "bindToScoringServiceIfNeeded"); NetworkScorerAppData scorerData = NetworkScorerAppManager.getActiveScorer(mContext); bindToScoringServiceIfNeeded(scorerData); } private void bindToScoringServiceIfNeeded(NetworkScorerAppData scorerData) { if (DBG) Log.d(TAG, "bindToScoringServiceIfNeeded(" + scorerData + ")"); if (scorerData != null && scorerData.mScoringServiceClassName != null) { ComponentName componentName = new ComponentName(scorerData.mPackageName, scorerData.mScoringServiceClassName); // If we're connected to a different component then drop it. if (mServiceConnection != null && !mServiceConnection.mComponentName.equals(componentName)) { unbindFromScoringServiceIfNeeded(); } // If we're not connected at all then create a new connection. if (mServiceConnection == null) { mServiceConnection = new ScoringServiceConnection(componentName); } // Make sure the connection is connected (idempotent) mServiceConnection.connect(mContext); } } private void unbindFromScoringServiceIfNeeded() { if (DBG) Log.d(TAG, "unbindFromScoringServiceIfNeeded"); if (mServiceConnection != null) { mServiceConnection.disconnect(mContext); } mServiceConnection = null; } @Override public boolean updateScores(ScoredNetwork[] networks) { if (!NetworkScorerAppManager.isCallerActiveScorer(mContext, getCallingUid())) { Loading Loading @@ -228,8 +283,10 @@ public class NetworkScoreService extends INetworkScoreService.Stub { /** Set the active scorer. Callers are responsible for checking permissions as appropriate. */ private boolean setScorerInternal(String packageName) { if (DBG) Log.d(TAG, "setScorerInternal(" + packageName + ")"); long token = Binder.clearCallingIdentity(); try { unbindFromScoringServiceIfNeeded(); // Preemptively clear scores even though the set operation could fail. We do this for // safety as scores should never be compared across apps; in practice, Settings should // only be allowing valid apps to be set as scorers, so failure here should be rare. Loading @@ -237,8 +294,13 @@ public class NetworkScoreService extends INetworkScoreService.Stub { // Get the scorer that is about to be replaced, if any, so we can notify it directly. NetworkScorerAppData prevScorer = NetworkScorerAppManager.getActiveScorer(mContext); boolean result = NetworkScorerAppManager.setActiveScorer(mContext, packageName); // Unconditionally attempt to bind to the current scorer. If setActiveScorer() failed // then we'll attempt to restore the previous binding (if any), otherwise an attempt // will be made to bind to the new scorer. bindToScoringServiceIfNeeded(); if (result) { // new scorer successfully set registerPackageReceiverIfNeeded(); Intent intent = new Intent(NetworkScoreManager.ACTION_SCORER_CHANGED); if (prevScorer != null) { // Directly notify the old scorer. intent.setPackage(prevScorer.mPackageName); Loading Loading @@ -295,7 +357,6 @@ public class NetworkScoreService extends INetworkScoreService.Stub { return; } writer.println("Current scorer: " + currentScorer.mPackageName); writer.flush(); for (INetworkScoreCache scoreCache : getScoreCaches()) { try { Loading @@ -307,6 +368,12 @@ public class NetworkScoreService extends INetworkScoreService.Stub { } } } if (mServiceConnection != null) { mServiceConnection.dump(fd, writer, args); } else { writer.println("ScoringServiceConnection: null"); } writer.flush(); } /** Loading @@ -320,4 +387,50 @@ public class NetworkScoreService extends INetworkScoreService.Stub { return new HashSet<>(mScoreCaches.values()); } } private static class ScoringServiceConnection implements ServiceConnection { private final ComponentName mComponentName; private boolean mBound = false; ScoringServiceConnection(ComponentName componentName) { mComponentName = componentName; } void connect(Context context) { disconnect(context); Intent service = new Intent(); service.setComponent(mComponentName); mBound = context.bindServiceAsUser(service, this, Context.BIND_AUTO_CREATE | Context.BIND_FOREGROUND_SERVICE, UserHandle.SYSTEM); if (!mBound) { Log.w(TAG, "Bind call failed for " + service); } } void disconnect(Context context) { try { if (mBound) { mBound = false; context.unbindService(this); } } catch (RuntimeException e) { Log.e(TAG, "Unbind failed.", e); } } @Override public void onServiceConnected(ComponentName name, IBinder service) { if (DBG) Log.d(TAG, "ScoringServiceConnection: " + name.flattenToString()); } @Override public void onServiceDisconnected(ComponentName name) { if (DBG) Log.d(TAG, "ScoringServiceConnection, disconnected: " + name.flattenToString()); } public void dump(FileDescriptor fd, PrintWriter writer, String[] args) { writer.println("ScoringServiceConnection: " + mComponentName + ", bound: " + mBound); } } } services/java/com/android/server/SystemServer.java +6 −0 Original line number Diff line number Diff line Loading @@ -1424,6 +1424,12 @@ public final class SystemServer { } catch (Throwable e) { reportWtf("Notifying MmsService running", e); } try { if (networkScoreF != null) networkScoreF.systemRunning(); } catch (Throwable e) { reportWtf("Notifying NetworkScoreService running", e); } Trace.traceEnd(Trace.TRACE_TAG_SYSTEM_SERVER); } }); Loading Loading
core/java/android/net/NetworkScorerAppManager.java +32 −5 Original line number Diff line number Diff line Loading @@ -19,11 +19,9 @@ package android.net; import android.Manifest; import android.Manifest.permission; import android.annotation.Nullable; import android.app.AppOpsManager; import android.content.Context; import android.content.Intent; import android.content.pm.ActivityInfo; import android.content.pm.ApplicationInfo; import android.content.pm.PackageManager; import android.content.pm.ResolveInfo; import android.os.UserHandle; Loading Loading @@ -69,12 +67,32 @@ public final class NetworkScorerAppManager { */ public final String mConfigurationActivityClassName; /** * Optional class name of the scoring service we can bind to. Null if none is set. */ public final String mScoringServiceClassName; public NetworkScorerAppData(String packageName, int packageUid, CharSequence scorerName, @Nullable String configurationActivityClassName) { @Nullable String configurationActivityClassName, @Nullable String scoringServiceClassName) { mScorerName = scorerName; mPackageName = packageName; mPackageUid = packageUid; mConfigurationActivityClassName = configurationActivityClassName; mScoringServiceClassName = scoringServiceClassName; } @Override public String toString() { final StringBuilder sb = new StringBuilder("NetworkScorerAppData{"); sb.append("mPackageName='").append(mPackageName).append('\''); sb.append(", mPackageUid=").append(mPackageUid); sb.append(", mScorerName=").append(mScorerName); sb.append(", mConfigurationActivityClassName='").append(mConfigurationActivityClassName) .append('\''); sb.append(", mScoringServiceClassName='").append(mScoringServiceClassName).append('\''); sb.append('}'); return sb.toString(); } } Loading Loading @@ -128,18 +146,27 @@ public final class NetworkScorerAppManager { Intent intent = new Intent(NetworkScoreManager.ACTION_CUSTOM_ENABLE); intent.setPackage(receiverInfo.packageName); List<ResolveInfo> configActivities = pm.queryIntentActivities(intent, 0 /* flags */); if (!configActivities.isEmpty()) { if (configActivities != null && !configActivities.isEmpty()) { ActivityInfo activityInfo = configActivities.get(0).activityInfo; if (activityInfo != null) { configurationActivityClassName = activityInfo.name; } } // Find the scoring service class we can bind to, if any. String scoringServiceClassName = null; Intent serviceIntent = new Intent(NetworkScoreManager.ACTION_SCORE_NETWORKS); serviceIntent.setPackage(receiverInfo.packageName); ResolveInfo resolveServiceInfo = pm.resolveService(serviceIntent, 0 /* flags */); if (resolveServiceInfo != null && resolveServiceInfo.serviceInfo != null) { scoringServiceClassName = resolveServiceInfo.serviceInfo.name; } // NOTE: loadLabel will attempt to load the receiver's label and fall back to the // app label if none is present. scorers.add(new NetworkScorerAppData(receiverInfo.packageName, receiverInfo.applicationInfo.uid, receiverInfo.loadLabel(pm), configurationActivityClassName)); configurationActivityClassName, scoringServiceClassName)); } return scorers; Loading
core/tests/coretests/src/android/net/NetworkScorerAppManagerTest.java +63 −19 Original line number Diff line number Diff line Loading @@ -23,10 +23,10 @@ import android.content.pm.ActivityInfo; import android.content.pm.ApplicationInfo; import android.content.pm.PackageManager; import android.content.pm.ResolveInfo; import android.content.pm.ServiceInfo; import android.net.NetworkScorerAppManager.NetworkScorerAppData; import android.os.UserHandle; import android.test.InstrumentationTestCase; import android.util.Pair; import org.mockito.ArgumentMatcher; import org.mockito.Mock; Loading Loading @@ -58,25 +58,26 @@ public class NetworkScorerAppManagerTest extends InstrumentationTestCase { public void testGetAllValidScorers() throws Exception { // Package 1 - Valid scorer. Pair<ResolveInfo, ResolveInfo> package1 = buildResolveInfo("package1", 1, true, true, false); ResolveInfoHolder package1 = buildResolveInfo("package1", 1, true, true, false, false); // Package 2 - Receiver does not have BROADCAST_NETWORK_PRIVILEGED permission. Pair<ResolveInfo, ResolveInfo> package2 = buildResolveInfo("package2", 2, false, true, false); ResolveInfoHolder package2 = buildResolveInfo("package2", 2, false, true, false, false); // Package 3 - App does not have SCORE_NETWORKS permission. Pair<ResolveInfo, ResolveInfo> package3 = buildResolveInfo("package3", 3, true, false, false); ResolveInfoHolder package3 = buildResolveInfo("package3", 3, true, false, false, false); // Package 4 - Valid scorer w/ optional config activity. Pair<ResolveInfo, ResolveInfo> package4 = buildResolveInfo("package4", 4, true, true, true); ResolveInfoHolder package4 = buildResolveInfo("package4", 4, true, true, true, false); List<Pair<ResolveInfo, ResolveInfo>> scorers = new ArrayList<>(); // Package 5 - Valid scorer w/ optional service to bind to. ResolveInfoHolder package5 = buildResolveInfo("package5", 5, true, true, false, true); List<ResolveInfoHolder> scorers = new ArrayList<>(); scorers.add(package1); scorers.add(package2); scorers.add(package3); scorers.add(package4); scorers.add(package5); setScorers(scorers); Iterator<NetworkScorerAppData> result = Loading @@ -94,14 +95,20 @@ public class NetworkScorerAppManagerTest extends InstrumentationTestCase { assertEquals(4, next.mPackageUid); assertEquals(".ConfigActivity", next.mConfigurationActivityClassName); assertTrue(result.hasNext()); next = result.next(); assertEquals("package5", next.mPackageName); assertEquals(5, next.mPackageUid); assertEquals(".ScoringService", next.mScoringServiceClassName); assertFalse(result.hasNext()); } private void setScorers(List<Pair<ResolveInfo, ResolveInfo>> scorers) { private void setScorers(List<ResolveInfoHolder> scorers) { List<ResolveInfo> receivers = new ArrayList<>(); for (final Pair<ResolveInfo, ResolveInfo> scorer : scorers) { receivers.add(scorer.first); if (scorer.second != null) { for (final ResolveInfoHolder scorer : scorers) { receivers.add(scorer.scorerResolveInfo); if (scorer.configActivityResolveInfo != null) { // This scorer has a config activity. Mockito.when(mMockPm.queryIntentActivities( Mockito.argThat(new ArgumentMatcher<Intent>() { Loading @@ -110,10 +117,26 @@ public class NetworkScorerAppManagerTest extends InstrumentationTestCase { Intent intent = (Intent) object; return NetworkScoreManager.ACTION_CUSTOM_ENABLE.equals( intent.getAction()) && scorer.first.activityInfo.packageName.equals( && scorer.scorerResolveInfo.activityInfo.packageName.equals( intent.getPackage()); } }), Mockito.eq(0))).thenReturn( Collections.singletonList(scorer.configActivityResolveInfo)); } if (scorer.serviceResolveInfo != null) { // This scorer has a service to bind to Mockito.when(mMockPm.resolveService( Mockito.argThat(new ArgumentMatcher<Intent>() { @Override public boolean matches(Object object) { Intent intent = (Intent) object; return NetworkScoreManager.ACTION_SCORE_NETWORKS.equals( intent.getAction()) && scorer.scorerResolveInfo.activityInfo.packageName.equals( intent.getPackage()); } }), Mockito.eq(0))).thenReturn(Collections.singletonList(scorer.second)); }), Mockito.eq(0))).thenReturn(scorer.serviceResolveInfo); } } Loading @@ -128,9 +151,9 @@ public class NetworkScorerAppManagerTest extends InstrumentationTestCase { .thenReturn(receivers); } private Pair<ResolveInfo, ResolveInfo> buildResolveInfo(String packageName, int packageUid, boolean hasReceiverPermission, boolean hasScorePermission, boolean hasConfigActivity) throws Exception { private ResolveInfoHolder buildResolveInfo(String packageName, int packageUid, boolean hasReceiverPermission, boolean hasScorePermission, boolean hasConfigActivity, boolean hasServiceInfo) throws Exception { Mockito.when(mMockPm.checkPermission(permission.SCORE_NETWORKS, packageName)) .thenReturn(hasScorePermission ? PackageManager.PERMISSION_GRANTED : PackageManager.PERMISSION_DENIED); Loading @@ -150,6 +173,27 @@ public class NetworkScorerAppManagerTest extends InstrumentationTestCase { configActivityInfo.activityInfo = new ActivityInfo(); configActivityInfo.activityInfo.name = ".ConfigActivity"; } return Pair.create(resolveInfo, configActivityInfo); ResolveInfo serviceInfo = null; if (hasServiceInfo) { serviceInfo = new ResolveInfo(); serviceInfo.serviceInfo = new ServiceInfo(); serviceInfo.serviceInfo.name = ".ScoringService"; } return new ResolveInfoHolder(resolveInfo, configActivityInfo, serviceInfo); } private static class ResolveInfoHolder { final ResolveInfo scorerResolveInfo; final ResolveInfo configActivityResolveInfo; final ResolveInfo serviceResolveInfo; public ResolveInfoHolder(ResolveInfo scorerResolveInfo, ResolveInfo configActivityResolveInfo, ResolveInfo serviceResolveInfo) { this.scorerResolveInfo = scorerResolveInfo; this.configActivityResolveInfo = configActivityResolveInfo; this.serviceResolveInfo = serviceResolveInfo; } } }
services/core/java/com/android/server/NetworkScoreService.java +125 −12 Original line number Diff line number Diff line Loading @@ -18,10 +18,12 @@ package com.android.server; import android.Manifest.permission; import android.content.BroadcastReceiver; import android.content.ComponentName; import android.content.ContentResolver; import android.content.Context; import android.content.Intent; import android.content.IntentFilter; import android.content.ServiceConnection; import android.content.pm.PackageManager; import android.net.INetworkScoreCache; import android.net.INetworkScoreService; Loading @@ -30,6 +32,7 @@ import android.net.NetworkScorerAppManager; import android.net.NetworkScorerAppManager.NetworkScorerAppData; import android.net.ScoredNetwork; import android.os.Binder; import android.os.IBinder; import android.os.PatternMatcher; import android.os.RemoteException; import android.os.UserHandle; Loading @@ -55,17 +58,17 @@ import java.util.Set; */ public class NetworkScoreService extends INetworkScoreService.Stub { private static final String TAG = "NetworkScoreService"; private static final boolean DBG = false; private final Context mContext; private final Map<Integer, INetworkScoreCache> mScoreCaches; /** Lock used to update mReceiver when scorer package changes occur. */ private Object mReceiverLock = new Object[0]; private final Object mReceiverLock = new Object[0]; /** Clears scores when the active scorer package is no longer valid. */ @GuardedBy("mReceiverLock") private ScorerChangedReceiver mReceiver; private ScoringServiceConnection mServiceConnection; private class ScorerChangedReceiver extends BroadcastReceiver { final String mRegisteredPackage; Loading @@ -77,14 +80,23 @@ public class NetworkScoreService extends INetworkScoreService.Stub { @Override public void onReceive(Context context, Intent intent) { String action = intent.getAction(); if ((Intent.ACTION_PACKAGE_CHANGED.equals(action) || Intent.ACTION_PACKAGE_REPLACED.equals(action) || Intent.ACTION_PACKAGE_FULLY_REMOVED.equals(action)) && NetworkScorerAppManager.getActiveScorer(mContext) == null) { if (Intent.ACTION_PACKAGE_CHANGED.equals(action) || Intent.ACTION_PACKAGE_REPLACED.equals(action) || Intent.ACTION_PACKAGE_FULLY_REMOVED.equals(action)) { NetworkScorerAppData activeScorer = NetworkScorerAppManager.getActiveScorer(mContext); if (activeScorer == null) { // Package change has invalidated a scorer. Log.i(TAG, "Package " + mRegisteredPackage + " is no longer valid, disabling scoring"); " is no longer valid, disabling scoring."); setScorerInternal(null); } else if (activeScorer.mScoringServiceClassName == null) { // The scoring service is not available, make sure it's unbound. unbindFromScoringServiceIfNeeded(); } else { // The scoring service may have changed or been added. bindToScoringServiceIfNeeded(activeScorer); } } } } Loading @@ -96,6 +108,7 @@ public class NetworkScoreService extends INetworkScoreService.Stub { /** Called when the system is ready to run third-party code but before it actually does so. */ void systemReady() { if (DBG) Log.d(TAG, "systemReady"); ContentResolver cr = mContext.getContentResolver(); if (Settings.Global.getInt(cr, Settings.Global.NETWORK_SCORING_PROVISIONED, 0) == 0) { // On first run, we try to initialize the scorer to the one configured at build time. Loading @@ -111,7 +124,14 @@ public class NetworkScoreService extends INetworkScoreService.Stub { registerPackageReceiverIfNeeded(); } /** Called when the system is ready for us to start third-party code. */ void systemRunning() { if (DBG) Log.d(TAG, "systemRunning"); bindToScoringServiceIfNeeded(); } private void registerPackageReceiverIfNeeded() { if (DBG) Log.d(TAG, "registerPackageReceiverIfNeeded"); NetworkScorerAppData scorer = NetworkScorerAppManager.getActiveScorer(mContext); synchronized (mReceiverLock) { // Unregister the receiver if the current scorer has changed since last registration. Loading Loading @@ -142,6 +162,41 @@ public class NetworkScoreService extends INetworkScoreService.Stub { } } private void bindToScoringServiceIfNeeded() { if (DBG) Log.d(TAG, "bindToScoringServiceIfNeeded"); NetworkScorerAppData scorerData = NetworkScorerAppManager.getActiveScorer(mContext); bindToScoringServiceIfNeeded(scorerData); } private void bindToScoringServiceIfNeeded(NetworkScorerAppData scorerData) { if (DBG) Log.d(TAG, "bindToScoringServiceIfNeeded(" + scorerData + ")"); if (scorerData != null && scorerData.mScoringServiceClassName != null) { ComponentName componentName = new ComponentName(scorerData.mPackageName, scorerData.mScoringServiceClassName); // If we're connected to a different component then drop it. if (mServiceConnection != null && !mServiceConnection.mComponentName.equals(componentName)) { unbindFromScoringServiceIfNeeded(); } // If we're not connected at all then create a new connection. if (mServiceConnection == null) { mServiceConnection = new ScoringServiceConnection(componentName); } // Make sure the connection is connected (idempotent) mServiceConnection.connect(mContext); } } private void unbindFromScoringServiceIfNeeded() { if (DBG) Log.d(TAG, "unbindFromScoringServiceIfNeeded"); if (mServiceConnection != null) { mServiceConnection.disconnect(mContext); } mServiceConnection = null; } @Override public boolean updateScores(ScoredNetwork[] networks) { if (!NetworkScorerAppManager.isCallerActiveScorer(mContext, getCallingUid())) { Loading Loading @@ -228,8 +283,10 @@ public class NetworkScoreService extends INetworkScoreService.Stub { /** Set the active scorer. Callers are responsible for checking permissions as appropriate. */ private boolean setScorerInternal(String packageName) { if (DBG) Log.d(TAG, "setScorerInternal(" + packageName + ")"); long token = Binder.clearCallingIdentity(); try { unbindFromScoringServiceIfNeeded(); // Preemptively clear scores even though the set operation could fail. We do this for // safety as scores should never be compared across apps; in practice, Settings should // only be allowing valid apps to be set as scorers, so failure here should be rare. Loading @@ -237,8 +294,13 @@ public class NetworkScoreService extends INetworkScoreService.Stub { // Get the scorer that is about to be replaced, if any, so we can notify it directly. NetworkScorerAppData prevScorer = NetworkScorerAppManager.getActiveScorer(mContext); boolean result = NetworkScorerAppManager.setActiveScorer(mContext, packageName); // Unconditionally attempt to bind to the current scorer. If setActiveScorer() failed // then we'll attempt to restore the previous binding (if any), otherwise an attempt // will be made to bind to the new scorer. bindToScoringServiceIfNeeded(); if (result) { // new scorer successfully set registerPackageReceiverIfNeeded(); Intent intent = new Intent(NetworkScoreManager.ACTION_SCORER_CHANGED); if (prevScorer != null) { // Directly notify the old scorer. intent.setPackage(prevScorer.mPackageName); Loading Loading @@ -295,7 +357,6 @@ public class NetworkScoreService extends INetworkScoreService.Stub { return; } writer.println("Current scorer: " + currentScorer.mPackageName); writer.flush(); for (INetworkScoreCache scoreCache : getScoreCaches()) { try { Loading @@ -307,6 +368,12 @@ public class NetworkScoreService extends INetworkScoreService.Stub { } } } if (mServiceConnection != null) { mServiceConnection.dump(fd, writer, args); } else { writer.println("ScoringServiceConnection: null"); } writer.flush(); } /** Loading @@ -320,4 +387,50 @@ public class NetworkScoreService extends INetworkScoreService.Stub { return new HashSet<>(mScoreCaches.values()); } } private static class ScoringServiceConnection implements ServiceConnection { private final ComponentName mComponentName; private boolean mBound = false; ScoringServiceConnection(ComponentName componentName) { mComponentName = componentName; } void connect(Context context) { disconnect(context); Intent service = new Intent(); service.setComponent(mComponentName); mBound = context.bindServiceAsUser(service, this, Context.BIND_AUTO_CREATE | Context.BIND_FOREGROUND_SERVICE, UserHandle.SYSTEM); if (!mBound) { Log.w(TAG, "Bind call failed for " + service); } } void disconnect(Context context) { try { if (mBound) { mBound = false; context.unbindService(this); } } catch (RuntimeException e) { Log.e(TAG, "Unbind failed.", e); } } @Override public void onServiceConnected(ComponentName name, IBinder service) { if (DBG) Log.d(TAG, "ScoringServiceConnection: " + name.flattenToString()); } @Override public void onServiceDisconnected(ComponentName name) { if (DBG) Log.d(TAG, "ScoringServiceConnection, disconnected: " + name.flattenToString()); } public void dump(FileDescriptor fd, PrintWriter writer, String[] args) { writer.println("ScoringServiceConnection: " + mComponentName + ", bound: " + mBound); } } }
services/java/com/android/server/SystemServer.java +6 −0 Original line number Diff line number Diff line Loading @@ -1424,6 +1424,12 @@ public final class SystemServer { } catch (Throwable e) { reportWtf("Notifying MmsService running", e); } try { if (networkScoreF != null) networkScoreF.systemRunning(); } catch (Throwable e) { reportWtf("Notifying NetworkScoreService running", e); } Trace.traceEnd(Trace.TRACE_TAG_SYSTEM_SERVER); } }); Loading