/* * Copyright (c) 2009-2011 The Linux Foundation. All rights reserved. * * Redistribution and use in source and binary forms, with or without * modification, are permitted provided that the following conditions are met: * * Redistributions of source code must retain the above copyright * notice, this list of conditions and the following disclaimer. * * Redistributions in binary form must reproduce the above copyright * notice, this list of conditions and the following disclaimer in the * documentation and/or other materials provided with the distribution. * * Neither the name of The Linux Foundation nor * the names of its contributors may be used to endorse or promote * products derived from this software without specific prior written * permission. * * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" * AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE * IMPLIED WARRANTIES OF MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND * NON-INFRINGEMENT ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR * CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, * EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, * PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; * OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, * WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR * OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF * ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. */ package android.net; import android.net.IConnectivityManager; import android.os.RemoteException; import android.net.LinkInfo; import android.os.ServiceManager; import android.os.IBinder; import android.util.Log; import android.os.Binder; import android.os.Handler; import android.os.Message; import android.os.Looper; import android.os.SystemProperties; import java.util.concurrent.locks.Lock; import java.util.concurrent.locks.Condition; import java.util.concurrent.locks.ReentrantLock; import java.util.Map; /** {@hide} * This class provides a means for applications to specify their * requirements and request for a link. Apps can also report their satisfaction * with the assigned link and switch links when a new link is available. */ public class LinkProvider { static final String LOG_TAG = "LinkProvider"; static final boolean DBG = false; /** {@hide} * Default Role Id, applies to any packet data traffic pattern that * doesn't have another role defined explicitly. */ public static final int ROLE_DEFAULT = 0; /* * role that the app wants to register. */ private int mRole; /* * link requirements of the app for the registered role. */ private Map mLinkReqs = null; /* * Apps process id */ private int mPid; /* * LinkNotifier object to provide notification to the app. */ private LinkNotifier mLinkNotifier; /* * handle to connectivity service obj */ private IConnectivityManager mService; private Handler mHandler; private Looper mLooper; private static final int ON_LINK_AVAIL = 1; private static final int ON_BETTER_LINK_AVAIL = 2; private static final int ON_LINK_LOST = 3; private static final int ON_GET_LINK_FAILURE = 4; private Lock mLock; private Condition mHandlerAvail; public enum LinkRequirement { FW_LINK_BW, REV_LINK_BW, } /** {@hide} * This constructor can be used by apps to specify a role and * optional requirements and a link notifier object to receive * notifications. * * @param role * Role that the app wants to register * @param reqs * Requirements of the app for that role * @param notifier * LinkNotifier object to provide notification to the app */ public LinkProvider(int role, Map reqs, LinkNotifier notifier) throws InterruptedException { mRole = role; mLinkReqs = reqs; mLinkNotifier = notifier; /* get handle to connectivity service */ IBinder b = ServiceManager.getService("connectivity"); mService = IConnectivityManager.Stub.asInterface(b); /* check for mservice to be null and throw a exception */ if (mService == null) { throw new IllegalStateException("mService can not be null"); } mLock = new ReentrantLock(); mHandlerAvail = mLock.newCondition(); } /** {@hide} * This function will be used by apps to request a system after they * have specified their role and/or requirements. * * @return {@code true}if the request has been accepted. {@code false} * otherwise. A return value of true does NOT mean that a link is * available for the app to use. That will delivered via the * LinkNotifier. */ public boolean getLink() { if(SystemProperties.getBoolean("persist.cne.fmc.mode", false)) { Log.w(LOG_TAG,"getLink: FMC is enabled. This API is invalid."); return false; } try { if (mHandler == null) { try { init(); } catch (InterruptedException ex) { if (DBG) Log.d(LOG_TAG, "Interrupted exception!"); return false; } } else { if (DBG) Log.d(LOG_TAG, "getLink called before release is called!!"); return false; } ConSvcEventListener listener = (ConSvcEventListener) IConSvcEventListener.Stub .asInterface(new ConSvcEventListener()); mPid = Binder.getCallingPid(); if (DBG) Log.d(LOG_TAG, "GetLink called with role=" + mRole + "pid=" + mPid); return mService.getLink_LP(mRole, mLinkReqs, mPid, listener); } catch (RemoteException e) { if (DBG) Log.d(LOG_TAG, "ConSvc throwed remoteExcept'n on startConn call"); return false; } } private void init() throws InterruptedException { (new NotificationsThread()).start(); /* block until mHandler gets created. */ try { mLock.lock(); if (mHandler == null) { mHandlerAvail.await(); } } finally { mLock.unlock(); } } private void deInit() { mLooper.quit(); mHandler = null; } /** {@hide} * This function will be used by apps to report to CnE whether they * are satisfied or dissatisfied with the link that was assigned to them. * * @param info * {@code LinkInfo} about the Link assigned to the app. The app * needs to pass back the same LinkInfo object it received via * the {@code LinkNotifier} * @param isSatisfied * whether the app is satisfied with the link or not * @param isNotifyBetterLink * whether the app wants to be notified when another link is * available which CnE believes is "better" for the app * @return {@code true} if the request has been accepted by Android * framework, {@code false} otherwise. */ public boolean reportLinkSatisfaction(LinkInfo info, boolean isSatisfied, boolean isNotifyBetterLink) { try { return mService.reportLinkSatisfaction_LP(mRole, mPid, info, isSatisfied, isNotifyBetterLink); } catch (RemoteException e) { if (DBG) Log.d(LOG_TAG, "ConSvc throwed remoteExcept'n on reportConnSatis call"); return false; } } /** {@hide} * When a "better link available" notification is delivered to the * app, the app has a choice on whether to switch to the new link or * continue with the old one. The app needs to call this API if it wants to * switch to the new link. * * @param info * {@code LinkInfo} about the new link provided to the app * @param isNotifyBetterLink * Whether the app wants to be notified if a "better" network * for its role is available * @return {@code true}if the request has been accepted by Android * framework, {@code false} otherwise. */ public boolean switchLink(LinkInfo info, boolean isNotifyBetterLink) { try { return mService.switchLink_LP(mRole, mPid, info, isNotifyBetterLink); } catch (RemoteException e) { if (DBG) Log.d(LOG_TAG, "ConSvc throwed remoteExcept'n on reportConnSatis call"); return false; } } /** {@hide} * When a "better link available" notification is delivered to the * app, the app has a choice on whether to switch to the new link or * continue with the old one. The app needs to call this API if it wants to * stay with the old link. * * @param info * {@code LinkInfo} about the new link provided to the app * @param isnotifyBetterLink * whether the app wants to be notified if a "better" network * for its role is available * @return {@code true} if the request has been accepted by Android * framework, {@code false} otherwise. */ public boolean rejectSwitch(LinkInfo info, boolean isNotifyBetterLink) { try { return mService.rejectSwitch_LP(mRole, mPid, info, isNotifyBetterLink); } catch (RemoteException e) { if (DBG) Log.d(LOG_TAG, "ConSvc throwed remoteExcept'n on reportConnSatis call"); return false; } } /** {@hide} * This function will be used by apps to release the network * assigned to them for a given role. * * @return {@code true} if the request has been accepted by Android * framework, {@code false} otherwise. */ public boolean releaseLink() { try { boolean retVal = mService.releaseLink_LP(mRole, mPid); deInit(); return retVal; } catch (RemoteException e) { if (DBG) Log.d(LOG_TAG, "ConSvc throwed remoteExcept'n on releaseLink call"); return false; } } /** {@hide} */ /* * This class has the remoted function call backs that get called when the * ConSvc has to notify things to the app */ private class ConSvcEventListener extends IConSvcEventListener.Stub { public void onLinkAvail(LinkInfo info) { if (DBG) Log.v(LOG_TAG, "Sending OnLinkAvail with nwId=" + info.getNwId() + "to App"); Message msg; msg = mHandler.obtainMessage(ON_LINK_AVAIL, info); msg.setTarget(mHandler); msg.sendToTarget(); return; } public void onBetterLinkAvail(LinkInfo info) { if (DBG) Log.v(LOG_TAG, "Sending onBetterLinkAvail with nwId=" + info.getNwId() + "to App"); Message msg; msg = mHandler.obtainMessage(ON_BETTER_LINK_AVAIL, info); msg.setTarget(mHandler); msg.sendToTarget(); return; } public void onLinkLost(LinkInfo info) { if (DBG) Log.v(LOG_TAG, "Sending onLinkLost with nwId=" + info.getNwId() + "to App"); Message msg; msg = mHandler.obtainMessage(ON_LINK_LOST, info); msg.setTarget(mHandler); msg.sendToTarget(); return; } public void onGetLinkFailure(int reason) { if (DBG) Log.v(LOG_TAG, "Sending onGetLinkFailure with reason=" + reason + "to App"); Message msg; msg = mHandler.obtainMessage(ON_GET_LINK_FAILURE, reason); msg.setTarget(mHandler); msg.sendToTarget(); return; } }; private class NotificationsThread extends Thread { public void run() { Looper.prepare(); mLooper = Looper.myLooper(); mHandler = new Handler() { @Override public void handleMessage(Message msg) { if (DBG) Log.v(LOG_TAG, "handle Message called for msg = " + msg.what); switch (msg.what) { case ON_LINK_AVAIL: { LinkInfo info = (LinkInfo) msg.obj; if (mLinkNotifier != null) { mLinkNotifier.onLinkAvail(info); } break; } case ON_BETTER_LINK_AVAIL: { LinkInfo info = (LinkInfo) msg.obj; if (mLinkNotifier != null) { mLinkNotifier.onBetterLinkAvail(info); } break; } case ON_LINK_LOST: { LinkInfo info = (LinkInfo) msg.obj; if (mLinkNotifier != null) { mLinkNotifier.onLinkLost(info); } break; } case ON_GET_LINK_FAILURE: { int reason = (int) msg.arg1; if (mLinkNotifier != null) { mLinkNotifier.onGetLinkFailure(reason); } break; } default: if (DBG) Log.d(LOG_TAG, "Unhandled Message msg = " + msg.what); } } }; mLock.lock(); mHandlerAvail.signal(); mLock.unlock(); Looper.loop(); } }; }