Donate to e Foundation | Murena handsets with /e/OS | Own a part of Murena! Learn more

Commit 5d942c13 authored by Treehugger Robot's avatar Treehugger Robot Committed by Gerrit Code Review
Browse files

Merge changes I6ff80178,I03875cf1,I8d210b68

* changes:
  New setting for recommendation request timeout.
  Implemented the async recommendation request call.
  Async network recommendation requests.
parents a5f96880 44e2b84b
Loading
Loading
Loading
Loading
+13 −0
Original line number Original line Diff line number Diff line
@@ -21,6 +21,7 @@ import android.net.NetworkKey;
import android.net.RecommendationRequest;
import android.net.RecommendationRequest;
import android.net.RecommendationResult;
import android.net.RecommendationResult;
import android.net.ScoredNetwork;
import android.net.ScoredNetwork;
import android.os.RemoteCallback;


/**
/**
 * A service for updating network scores from a network scorer application.
 * A service for updating network scores from a network scorer application.
@@ -117,4 +118,16 @@ interface INetworkScoreService
     *         scorer.
     *         scorer.
     */
     */
    String getActiveScorerPackage();
    String getActiveScorerPackage();
    
    /**
     * Request a recommendation for the best network to connect to
     * taking into account the inputs from the {@link RecommendationRequest}.
     *
     * @param request a {@link RecommendationRequest} instance containing the details of the request
     * @param remoteCallback a {@link RemoteCallback} instance to invoke when the recommendation
     *                       is available.
     * @throws SecurityException if the caller is not the system
     */
    oneway void requestRecommendationAsync(in RecommendationRequest request,
                                           in RemoteCallback remoteCallback);
}
}
+56 −0
Original line number Original line Diff line number Diff line
@@ -16,17 +16,24 @@


package android.net;
package android.net;


import static android.net.NetworkRecommendationProvider.EXTRA_RECOMMENDATION_RESULT;

import android.Manifest;
import android.Manifest;
import android.annotation.IntDef;
import android.annotation.IntDef;
import android.annotation.NonNull;
import android.annotation.SdkConstant;
import android.annotation.SdkConstant;
import android.annotation.SdkConstant.SdkConstantType;
import android.annotation.SdkConstant.SdkConstantType;
import android.annotation.SystemApi;
import android.annotation.SystemApi;
import android.content.Context;
import android.content.Context;
import android.content.Intent;
import android.content.Intent;
import android.net.NetworkScorerAppManager.NetworkScorerAppData;
import android.net.NetworkScorerAppManager.NetworkScorerAppData;
import android.os.Bundle;
import android.os.Handler;
import android.os.IBinder;
import android.os.IBinder;
import android.os.RemoteCallback;
import android.os.RemoteException;
import android.os.RemoteException;
import android.os.ServiceManager;
import android.os.ServiceManager;
import com.android.internal.util.Preconditions;


import java.lang.annotation.Retention;
import java.lang.annotation.Retention;
import java.lang.annotation.RetentionPolicy;
import java.lang.annotation.RetentionPolicy;
@@ -356,4 +363,53 @@ public class NetworkScoreManager {
            throw e.rethrowFromSystemServer();
            throw e.rethrowFromSystemServer();
        }
        }
    }
    }

    /**
     * Request a recommendation for which network to connect to.
     *
     * <p>The callback will be run on the thread associated with provided {@link Handler}.
     *
     * @param request a {@link RecommendationRequest} instance containing additional
     *                request details
     * @param callback a {@link RecommendationCallback} instance that will be invoked when
     *                 the {@link RecommendationResult} is available
     * @param handler a {@link Handler} instance representing the thread to run the callback on.
     * @throws SecurityException
     * @hide
     */
    public void requestRecommendation(
            final @NonNull RecommendationRequest request,
            final @NonNull RecommendationCallback callback,
            final @NonNull Handler handler) {
        Preconditions.checkNotNull(request, "RecommendationRequest cannot be null.");
        Preconditions.checkNotNull(callback, "RecommendationCallback cannot be null.");
        Preconditions.checkNotNull(handler, "Handler cannot be null.");

        RemoteCallback remoteCallback = new RemoteCallback(new RemoteCallback.OnResultListener() {
            @Override
            public void onResult(Bundle data) {
                RecommendationResult result = data.getParcelable(EXTRA_RECOMMENDATION_RESULT);
                callback.onRecommendationAvailable(result);
            }
        }, handler);

        try {
            mService.requestRecommendationAsync(request, remoteCallback);
        } catch (RemoteException e) {
            throw e.rethrowFromSystemServer();
        }
    }

    /**
     * Callers of {@link #requestRecommendation(RecommendationRequest, RecommendationCallback, Handler)}
     * must pass in an implementation of this class.
     * @hide
     */
    public abstract static class RecommendationCallback {
        /**
         * Invoked when a {@link RecommendationResult} is available.
         * @param result a {@link RecommendationResult} instance.
         */
        public abstract void onRecommendationAvailable(RecommendationResult result);
    }
}
}
+10 −0
Original line number Original line Diff line number Diff line
@@ -7672,6 +7672,16 @@ public final class Settings {
        public static final String NETWORK_RECOMMENDATIONS_ENABLED =
        public static final String NETWORK_RECOMMENDATIONS_ENABLED =
                "network_recommendations_enabled";
                "network_recommendations_enabled";


        /**
         * The number of milliseconds the {@link com.android.server.NetworkScoreService}
         * will give a recommendation request to complete before returning a default response.
         *
         * Type: long
         * @hide
         */
        public static final String NETWORK_RECOMMENDATION_REQUEST_TIMEOUT_MS =
                "network_recommendation_request_timeout_ms";

       /**
       /**
        * Settings to allow BLE scans to be enabled even when Bluetooth is turned off for
        * Settings to allow BLE scans to be enabled even when Bluetooth is turned off for
        * connectivity.
        * connectivity.
+194 −16
Original line number Original line Diff line number Diff line
@@ -42,15 +42,22 @@ import android.net.RecommendationResult;
import android.net.ScoredNetwork;
import android.net.ScoredNetwork;
import android.net.Uri;
import android.net.Uri;
import android.os.Binder;
import android.os.Binder;
import android.os.Build;
import android.os.Bundle;
import android.os.Bundle;
import android.os.Handler;
import android.os.IBinder;
import android.os.IBinder;
import android.os.IRemoteCallback;
import android.os.IRemoteCallback;
import android.os.Looper;
import android.os.Message;
import android.os.RemoteCallback;
import android.os.RemoteCallbackList;
import android.os.RemoteCallbackList;
import android.os.RemoteException;
import android.os.RemoteException;
import android.os.UserHandle;
import android.os.UserHandle;
import android.provider.Settings;
import android.provider.Settings.Global;
import android.provider.Settings.Global;
import android.util.ArrayMap;
import android.util.ArrayMap;
import android.util.Log;
import android.util.Log;
import android.util.Pair;
import android.util.TimedRemoteCaller;
import android.util.TimedRemoteCaller;


import com.android.internal.annotations.GuardedBy;
import com.android.internal.annotations.GuardedBy;
@@ -67,6 +74,7 @@ import java.util.Collections;
import java.util.List;
import java.util.List;
import java.util.Map;
import java.util.Map;
import java.util.concurrent.TimeoutException;
import java.util.concurrent.TimeoutException;
import java.util.concurrent.atomic.AtomicBoolean;
import java.util.function.Consumer;
import java.util.function.Consumer;


/**
/**
@@ -75,7 +83,7 @@ import java.util.function.Consumer;
 */
 */
public class NetworkScoreService extends INetworkScoreService.Stub {
public class NetworkScoreService extends INetworkScoreService.Stub {
    private static final String TAG = "NetworkScoreService";
    private static final String TAG = "NetworkScoreService";
    private static final boolean DBG = Log.isLoggable(TAG, Log.DEBUG);
    private static final boolean DBG = Build.IS_DEBUGGABLE && Log.isLoggable(TAG, Log.DEBUG);


    private final Context mContext;
    private final Context mContext;
    private final NetworkScorerAppManager mNetworkScorerAppManager;
    private final NetworkScorerAppManager mNetworkScorerAppManager;
@@ -83,13 +91,16 @@ public class NetworkScoreService extends INetworkScoreService.Stub {
    @GuardedBy("mScoreCaches")
    @GuardedBy("mScoreCaches")
    private final Map<Integer, RemoteCallbackList<INetworkScoreCache>> mScoreCaches;
    private final Map<Integer, RemoteCallbackList<INetworkScoreCache>> mScoreCaches;
    /** Lock used to update mPackageMonitor when scorer package changes occur. */
    /** Lock used to update mPackageMonitor when scorer package changes occur. */
    private final Object mPackageMonitorLock = new Object[0];
    private final Object mPackageMonitorLock = new Object();
    private final Object mServiceConnectionLock = new Object[0];
    private final Object mServiceConnectionLock = new Object();
    private final Handler mHandler;
    private final DispatchingContentObserver mContentObserver;


    @GuardedBy("mPackageMonitorLock")
    @GuardedBy("mPackageMonitorLock")
    private NetworkScorerPackageMonitor mPackageMonitor;
    private NetworkScorerPackageMonitor mPackageMonitor;
    @GuardedBy("mServiceConnectionLock")
    @GuardedBy("mServiceConnectionLock")
    private ScoringServiceConnection mServiceConnection;
    private ScoringServiceConnection mServiceConnection;
    private volatile long mRecommendationRequestTimeoutMs;


    private BroadcastReceiver mUserIntentReceiver = new BroadcastReceiver() {
    private BroadcastReceiver mUserIntentReceiver = new BroadcastReceiver() {
        @Override
        @Override
@@ -185,12 +196,25 @@ public class NetworkScoreService extends INetworkScoreService.Stub {
    }
    }


    /**
    /**
     * Reevaluates the service binding when the Settings toggle is changed.
     * Dispatches observed content changes to a handler for further processing.
     */
     */
    private class SettingsObserver extends ContentObserver {
    @VisibleForTesting
    public static class DispatchingContentObserver extends ContentObserver {
        final private Map<Uri, Integer> mUriEventMap;
        final private Context mContext;
        final private Handler mHandler;


        public SettingsObserver() {
        public DispatchingContentObserver(Context context, Handler handler) {
            super(null /*handler*/);
            super(handler);
            mContext = context;
            mHandler = handler;
            mUriEventMap = new ArrayMap<>();
        }

        void observe(Uri uri, int what) {
            mUriEventMap.put(uri, what);
            final ContentResolver resolver = mContext.getContentResolver();
            resolver.registerContentObserver(uri, false /*notifyForDescendants*/, this);
        }
        }


        @Override
        @Override
@@ -201,16 +225,22 @@ public class NetworkScoreService extends INetworkScoreService.Stub {
        @Override
        @Override
        public void onChange(boolean selfChange, Uri uri) {
        public void onChange(boolean selfChange, Uri uri) {
            if (DBG) Log.d(TAG, String.format("onChange(%s, %s)", selfChange, uri));
            if (DBG) Log.d(TAG, String.format("onChange(%s, %s)", selfChange, uri));
            bindToScoringServiceIfNeeded();
            final Integer what = mUriEventMap.get(uri);
            if (what != null) {
                mHandler.obtainMessage(what).sendToTarget();
            } else {
                Log.w(TAG, "No matching event to send for URI = " + uri);
            }
        }
        }
    }
    }


    public NetworkScoreService(Context context) {
    public NetworkScoreService(Context context) {
      this(context, new NetworkScorerAppManager(context));
      this(context, new NetworkScorerAppManager(context), Looper.myLooper());
    }
    }


    @VisibleForTesting
    @VisibleForTesting
    NetworkScoreService(Context context, NetworkScorerAppManager networkScoreAppManager) {
    NetworkScoreService(Context context, NetworkScorerAppManager networkScoreAppManager,
            Looper looper) {
        mContext = context;
        mContext = context;
        mNetworkScorerAppManager = networkScoreAppManager;
        mNetworkScorerAppManager = networkScoreAppManager;
        mScoreCaches = new ArrayMap<>();
        mScoreCaches = new ArrayMap<>();
@@ -219,16 +249,19 @@ public class NetworkScoreService extends INetworkScoreService.Stub {
        mContext.registerReceiverAsUser(
        mContext.registerReceiverAsUser(
                mUserIntentReceiver, UserHandle.SYSTEM, filter, null /* broadcastPermission*/,
                mUserIntentReceiver, UserHandle.SYSTEM, filter, null /* broadcastPermission*/,
                null /* scheduler */);
                null /* scheduler */);
        // TODO(jjoslin): 12/15/16 - Make timeout configurable.
        mRequestRecommendationCaller =
        mRequestRecommendationCaller =
            new RequestRecommendationCaller(TimedRemoteCaller.DEFAULT_CALL_TIMEOUT_MILLIS);
            new RequestRecommendationCaller(TimedRemoteCaller.DEFAULT_CALL_TIMEOUT_MILLIS);
        mRecommendationRequestTimeoutMs = TimedRemoteCaller.DEFAULT_CALL_TIMEOUT_MILLIS;
        mHandler = new ServiceHandler(looper);
        mContentObserver = new DispatchingContentObserver(context, mHandler);
    }
    }


    /** Called when the system is ready to run third-party code but before it actually does so. */
    /** Called when the system is ready to run third-party code but before it actually does so. */
    void systemReady() {
    void systemReady() {
        if (DBG) Log.d(TAG, "systemReady");
        if (DBG) Log.d(TAG, "systemReady");
        registerPackageMonitorIfNeeded();
        registerPackageMonitorIfNeeded();
        registerRecommendationSettingObserverIfNeeded();
        registerRecommendationSettingsObserver();
        refreshRecommendationRequestTimeoutMs();
    }
    }


    /** Called when the system is ready for us to start third-party code. */
    /** Called when the system is ready for us to start third-party code. */
@@ -242,14 +275,18 @@ public class NetworkScoreService extends INetworkScoreService.Stub {
        bindToScoringServiceIfNeeded();
        bindToScoringServiceIfNeeded();
    }
    }


    private void registerRecommendationSettingObserverIfNeeded() {
    private void registerRecommendationSettingsObserver() {
        final List<String> providerPackages =
        final List<String> providerPackages =
            mNetworkScorerAppManager.getPotentialRecommendationProviderPackages();
            mNetworkScorerAppManager.getPotentialRecommendationProviderPackages();
        if (!providerPackages.isEmpty()) {
        if (!providerPackages.isEmpty()) {
            final ContentResolver resolver = mContext.getContentResolver();
            final Uri enabledUri = Global.getUriFor(Global.NETWORK_RECOMMENDATIONS_ENABLED);
            final Uri uri = Global.getUriFor(Global.NETWORK_RECOMMENDATIONS_ENABLED);
            mContentObserver.observe(enabledUri,
            resolver.registerContentObserver(uri, false, new SettingsObserver());
                    ServiceHandler.MSG_RECOMMENDATIONS_ENABLED_CHANGED);
        }
        }

        final Uri timeoutUri = Global.getUriFor(Global.NETWORK_RECOMMENDATION_REQUEST_TIMEOUT_MS);
        mContentObserver.observe(timeoutUri,
                ServiceHandler.MSG_RECOMMENDATION_REQUEST_TIMEOUT_CHANGED);
    }
    }


    private void registerPackageMonitorIfNeeded() {
    private void registerPackageMonitorIfNeeded() {
@@ -553,6 +590,56 @@ public class NetworkScoreService extends INetworkScoreService.Stub {
        }
        }
    }
    }


    /**
     * Request a recommendation for the best network to connect to
     * taking into account the inputs from the {@link RecommendationRequest}.
     *
     * @param request a {@link RecommendationRequest} instance containing the details of the request
     * @param remoteCallback a {@link IRemoteCallback} instance to invoke when the recommendation
     *                       is available.
     * @throws SecurityException if the caller is not the system
     */
    @Override
    public void requestRecommendationAsync(RecommendationRequest request,
            RemoteCallback remoteCallback) {
        mContext.enforceCallingOrSelfPermission(permission.BROADCAST_NETWORK_PRIVILEGED, TAG);

        final OneTimeCallback oneTimeCallback = new OneTimeCallback(remoteCallback);
        final Pair<RecommendationRequest, OneTimeCallback> pair =
                Pair.create(request, oneTimeCallback);
        final Message timeoutMsg = mHandler.obtainMessage(
                ServiceHandler.MSG_RECOMMENDATION_REQUEST_TIMEOUT, pair);
        final INetworkRecommendationProvider provider = getRecommendationProvider();
        final long token = Binder.clearCallingIdentity();
        try {
            if (provider != null) {
                try {
                    mHandler.sendMessageDelayed(timeoutMsg, mRecommendationRequestTimeoutMs);
                    provider.requestRecommendation(request, new IRemoteCallback.Stub() {
                        @Override
                        public void sendResult(Bundle data) throws RemoteException {
                            // Remove the timeout message
                            mHandler.removeMessages(timeoutMsg.what, pair);
                            oneTimeCallback.sendResult(data);
                        }
                    }, 0 /*sequence*/);
                    return;
                } catch (RemoteException e) {
                    Log.w(TAG, "Failed to request a recommendation.", e);
                    // TODO(jjoslin): 12/15/16 - Keep track of failures.
                    // Remove the timeout message
                    mHandler.removeMessages(timeoutMsg.what, pair);
                    // Will fall through and send back the default recommendation.
                }
            }
        } finally {
            Binder.restoreCallingIdentity(token);
        }

        // Else send back the default recommendation.
        sendDefaultRecommendationResponse(request, oneTimeCallback);
    }

    @Override
    @Override
    public boolean requestScores(NetworkKey[] networks) {
    public boolean requestScores(NetworkKey[] networks) {
        mContext.enforceCallingOrSelfPermission(permission.REQUEST_NETWORK_SCORES, TAG);
        mContext.enforceCallingOrSelfPermission(permission.REQUEST_NETWORK_SCORES, TAG);
@@ -651,6 +738,18 @@ public class NetworkScoreService extends INetworkScoreService.Stub {
        return null;
        return null;
    }
    }


    @VisibleForTesting
    public void refreshRecommendationRequestTimeoutMs() {
        final ContentResolver cr = mContext.getContentResolver();
        long timeoutMs = Settings.Global.getLong(cr,
                Global.NETWORK_RECOMMENDATION_REQUEST_TIMEOUT_MS, -1L /*default*/);
        if (timeoutMs < 0) {
            timeoutMs = TimedRemoteCaller.DEFAULT_CALL_TIMEOUT_MILLIS;
        }
        if (DBG) Log.d(TAG, "Updating the recommendation request timeout to " + timeoutMs + " ms");
        mRecommendationRequestTimeoutMs = timeoutMs;
    }

    private static class ScoringServiceConnection implements ServiceConnection {
    private static class ScoringServiceConnection implements ServiceConnection {
        private final ComponentName mComponentName;
        private final ComponentName mComponentName;
        private final int mScoringAppUid;
        private final int mScoringAppUid;
@@ -756,4 +855,83 @@ public class NetworkScoreService extends INetworkScoreService.Stub {
            return getResultTimed(sequence);
            return getResultTimed(sequence);
        }
        }
    }
    }

    /**
     * A wrapper around {@link RemoteCallback} that guarantees
     * {@link RemoteCallback#sendResult(Bundle)} will be invoked at most once.
     */
    @VisibleForTesting
    public static final class OneTimeCallback {
        private final RemoteCallback mRemoteCallback;
        private final AtomicBoolean mCallbackRun;

        public OneTimeCallback(RemoteCallback remoteCallback) {
            mRemoteCallback = remoteCallback;
            mCallbackRun = new AtomicBoolean(false);
        }

        public void sendResult(Bundle data) {
            if (mCallbackRun.compareAndSet(false, true)) {
                mRemoteCallback.sendResult(data);
            }
        }
    }

    private static void sendDefaultRecommendationResponse(RecommendationRequest request,
            OneTimeCallback remoteCallback) {
        if (DBG) {
            Log.d(TAG, "Returning the default network recommendation.");
        }

        final RecommendationResult result;
        if (request != null && request.getCurrentSelectedConfig() != null) {
            result = RecommendationResult.createConnectRecommendation(
                    request.getCurrentSelectedConfig());
        } else {
            result = RecommendationResult.createDoNotConnectRecommendation();
        }

        final Bundle data = new Bundle();
        data.putParcelable(EXTRA_RECOMMENDATION_RESULT, result);
        remoteCallback.sendResult(data);
    }

    @VisibleForTesting
    public final class ServiceHandler extends Handler {
        public static final int MSG_RECOMMENDATION_REQUEST_TIMEOUT = 1;
        public static final int MSG_RECOMMENDATIONS_ENABLED_CHANGED = 2;
        public static final int MSG_RECOMMENDATION_REQUEST_TIMEOUT_CHANGED = 3;

        public ServiceHandler(Looper looper) {
            super(looper);
        }

        @Override
        public void handleMessage(Message msg) {
            final int what = msg.what;
            switch (what) {
                case MSG_RECOMMENDATION_REQUEST_TIMEOUT:
                    if (DBG) {
                        Log.d(TAG, "Network recommendation request timed out.");
                    }
                    final Pair<RecommendationRequest, OneTimeCallback> pair =
                            (Pair<RecommendationRequest, OneTimeCallback>) msg.obj;
                    final RecommendationRequest request = pair.first;
                    final OneTimeCallback remoteCallback = pair.second;
                    sendDefaultRecommendationResponse(request, remoteCallback);
                    break;

                case MSG_RECOMMENDATIONS_ENABLED_CHANGED:
                    bindToScoringServiceIfNeeded();
                    break;

                case MSG_RECOMMENDATION_REQUEST_TIMEOUT_CHANGED:
                    refreshRecommendationRequestTimeoutMs();
                    break;

                default:
                    Log.w(TAG,"Unknown message: " + what);
            }
        }
    }
}
}
+165 −1

File changed.

Preview size limit exceeded, changes collapsed.