summaryrefslogtreecommitdiff
diff options
context:
space:
mode:
-rw-r--r--core/java/android/bluetooth/le/BluetoothLeAdvertiser.java2
-rw-r--r--core/java/android/os/Process.java3
-rw-r--r--core/jni/android_os_HwBinder.cpp58
-rw-r--r--core/jni/android_os_HwBinder.h9
-rw-r--r--core/jni/android_os_HwParcel.cpp6
-rw-r--r--core/jni/android_os_HwParcel.h1
-rw-r--r--core/jni/android_os_HwRemoteBinder.cpp4
-rw-r--r--core/jni/android_os_HwRemoteBinder.h1
-rw-r--r--services/core/java/com/android/server/connectivity/Tethering.java8
-rw-r--r--services/core/java/com/android/server/connectivity/tethering/IControlsTethering.java10
-rw-r--r--services/core/java/com/android/server/connectivity/tethering/OffloadController.java3
-rw-r--r--services/core/java/com/android/server/connectivity/tethering/TetherInterfaceStateMachine.java111
-rw-r--r--telephony/java/android/telephony/MbmsDownloadManager.java293
-rw-r--r--telephony/java/android/telephony/MbmsStreamingManager.java144
-rw-r--r--telephony/java/android/telephony/mbms/DownloadRequest.java24
-rw-r--r--telephony/java/android/telephony/mbms/FileInfo.java38
-rw-r--r--telephony/java/android/telephony/mbms/FileServiceInfo.java9
-rwxr-xr-xtelephony/java/android/telephony/mbms/IMbmsDownloadManagerCallback.aidl14
-rwxr-xr-xtelephony/java/android/telephony/mbms/IMbmsStreamingManagerCallback.aidl23
-rw-r--r--telephony/java/android/telephony/mbms/MbmsDownloadManagerCallback.java13
-rw-r--r--telephony/java/android/telephony/mbms/MbmsDownloadReceiver.java70
-rw-r--r--telephony/java/android/telephony/mbms/MbmsException.java3
-rw-r--r--telephony/java/android/telephony/mbms/MbmsStreamingManagerCallback.java13
-rw-r--r--telephony/java/android/telephony/mbms/MbmsTempFileProvider.java45
-rw-r--r--telephony/java/android/telephony/mbms/MbmsUtils.java30
-rwxr-xr-xtelephony/java/android/telephony/mbms/vendor/IMbmsDownloadService.aidl1
-rw-r--r--telephony/java/android/telephony/mbms/vendor/MbmsDownloadServiceBase.java12
-rw-r--r--tests/net/java/com/android/server/connectivity/tethering/OffloadControllerTest.java3
-rw-r--r--tests/net/java/com/android/server/connectivity/tethering/TetherInterfaceStateMachineTest.java48
29 files changed, 629 insertions, 370 deletions
diff --git a/core/java/android/bluetooth/le/BluetoothLeAdvertiser.java b/core/java/android/bluetooth/le/BluetoothLeAdvertiser.java
index 67d56d5060e9..dfd5996c6a00 100644
--- a/core/java/android/bluetooth/le/BluetoothLeAdvertiser.java
+++ b/core/java/android/bluetooth/le/BluetoothLeAdvertiser.java
@@ -208,6 +208,8 @@ public final class BluetoothLeAdvertiser {
if (wrapper == null) return;
stopAdvertisingSet(wrapper);
+
+ mLegacyAdvertisers.remove(callback);
}
}
diff --git a/core/java/android/os/Process.java b/core/java/android/os/Process.java
index d6688e3c00f7..d5e240a8e211 100644
--- a/core/java/android/os/Process.java
+++ b/core/java/android/os/Process.java
@@ -740,7 +740,8 @@ public class Process {
/**
* Return the current priority of a thread, based on Linux priorities.
*
- * @param tid The identifier of the thread/process to change.
+ * @param tid The identifier of the thread/process. If tid equals zero, the priority of the
+ * calling process/thread will be returned.
*
* @return Returns the current priority, as a Linux priority level,
* from -20 for highest scheduling priority to 19 for lowest scheduling
diff --git a/core/jni/android_os_HwBinder.cpp b/core/jni/android_os_HwBinder.cpp
index b5c881537c41..a271aad0eb47 100644
--- a/core/jni/android_os_HwBinder.cpp
+++ b/core/jni/android_os_HwBinder.cpp
@@ -58,6 +58,29 @@ static struct fields_t {
jmethodID onTransactID;
} gFields;
+struct JHwBinderHolder : public RefBase {
+ JHwBinderHolder() {}
+
+ sp<JHwBinder> get(JNIEnv *env, jobject obj) {
+ Mutex::Autolock autoLock(mLock);
+
+ sp<JHwBinder> binder = mBinder.promote();
+
+ if (binder == NULL) {
+ binder = new JHwBinder(env, obj);
+ mBinder = binder;
+ }
+
+ return binder;
+ }
+
+private:
+ Mutex mLock;
+ wp<JHwBinder> mBinder;
+
+ DISALLOW_COPY_AND_ASSIGN(JHwBinderHolder);
+};
+
// static
void JHwBinder::InitClass(JNIEnv *env) {
ScopedLocalRef<jclass> clazz(
@@ -75,10 +98,10 @@ void JHwBinder::InitClass(JNIEnv *env) {
}
// static
-sp<JHwBinder> JHwBinder::SetNativeContext(
- JNIEnv *env, jobject thiz, const sp<JHwBinder> &context) {
- sp<JHwBinder> old =
- (JHwBinder *)env->GetLongField(thiz, gFields.contextID);
+sp<JHwBinderHolder> JHwBinder::SetNativeContext(
+ JNIEnv *env, jobject thiz, const sp<JHwBinderHolder> &context) {
+ sp<JHwBinderHolder> old =
+ (JHwBinderHolder *)env->GetLongField(thiz, gFields.contextID);
if (context != NULL) {
context->incStrong(NULL /* id */);
@@ -94,27 +117,27 @@ sp<JHwBinder> JHwBinder::SetNativeContext(
}
// static
-sp<JHwBinder> JHwBinder::GetNativeContext(
+sp<JHwBinder> JHwBinder::GetNativeBinder(
JNIEnv *env, jobject thiz) {
- return (JHwBinder *)env->GetLongField(thiz, gFields.contextID);
+ JHwBinderHolder *holder =
+ reinterpret_cast<JHwBinderHolder *>(
+ env->GetLongField(thiz, gFields.contextID));
+
+ return holder->get(env, thiz);
}
JHwBinder::JHwBinder(JNIEnv *env, jobject thiz) {
jclass clazz = env->GetObjectClass(thiz);
CHECK(clazz != NULL);
- mClass = (jclass)env->NewGlobalRef(clazz);
- mObject = env->NewWeakGlobalRef(thiz);
+ mObject = env->NewGlobalRef(thiz);
}
JHwBinder::~JHwBinder() {
JNIEnv *env = AndroidRuntime::getJNIEnv();
- env->DeleteWeakGlobalRef(mObject);
+ env->DeleteGlobalRef(mObject);
mObject = NULL;
-
- env->DeleteGlobalRef(mClass);
- mClass = NULL;
}
status_t JHwBinder::onTransact(
@@ -201,10 +224,10 @@ status_t JHwBinder::onTransact(
using namespace android;
static void releaseNativeContext(void *nativeContext) {
- sp<JHwBinder> binder = (JHwBinder *)nativeContext;
+ sp<JHwBinderHolder> context = static_cast<JHwBinderHolder *>(nativeContext);
- if (binder != NULL) {
- binder->decStrong(NULL /* id */);
+ if (context != NULL) {
+ context->decStrong(NULL /* id */);
}
}
@@ -215,8 +238,7 @@ static jlong JHwBinder_native_init(JNIEnv *env) {
}
static void JHwBinder_native_setup(JNIEnv *env, jobject thiz) {
- sp<JHwBinder> context = new JHwBinder(env, thiz);
-
+ sp<JHwBinderHolder> context = new JHwBinderHolder;
JHwBinder::SetNativeContext(env, thiz, context);
}
@@ -244,7 +266,7 @@ static void JHwBinder_native_registerService(
return; // XXX exception already pending?
}
- sp<hardware::IBinder> binder = JHwBinder::GetNativeContext(env, thiz);
+ sp<hardware::IBinder> binder = JHwBinder::GetNativeBinder(env, thiz);
/* TODO(b/33440494) this is not right */
sp<hidl::base::V1_0::IBase> base = new hidl::base::V1_0::BpHwBase(binder);
diff --git a/core/jni/android_os_HwBinder.h b/core/jni/android_os_HwBinder.h
index fa8fe01d6e93..5352f1e607c2 100644
--- a/core/jni/android_os_HwBinder.h
+++ b/core/jni/android_os_HwBinder.h
@@ -24,13 +24,15 @@
namespace android {
+struct JHwBinderHolder;
+
struct JHwBinder : public hardware::BHwBinder {
static void InitClass(JNIEnv *env);
- static sp<JHwBinder> SetNativeContext(
- JNIEnv *env, jobject thiz, const sp<JHwBinder> &context);
+ static sp<JHwBinderHolder> SetNativeContext(
+ JNIEnv *env, jobject thiz, const sp<JHwBinderHolder> &context);
- static sp<JHwBinder> GetNativeContext(JNIEnv *env, jobject thiz);
+ static sp<JHwBinder> GetNativeBinder(JNIEnv *env, jobject thiz);
JHwBinder(JNIEnv *env, jobject thiz);
@@ -45,7 +47,6 @@ protected:
TransactCallback callback);
private:
- jclass mClass;
jobject mObject;
DISALLOW_COPY_AND_ASSIGN(JHwBinder);
diff --git a/core/jni/android_os_HwParcel.cpp b/core/jni/android_os_HwParcel.cpp
index b21ea828f2a4..6ea809aa95ad 100644
--- a/core/jni/android_os_HwParcel.cpp
+++ b/core/jni/android_os_HwParcel.cpp
@@ -169,7 +169,6 @@ JHwParcel::JHwParcel(JNIEnv *env, jobject thiz)
jclass clazz = env->GetObjectClass(thiz);
CHECK(clazz != NULL);
- mClass = (jclass)env->NewGlobalRef(clazz);
mObject = env->NewWeakGlobalRef(thiz);
}
@@ -182,9 +181,6 @@ JHwParcel::~JHwParcel() {
env->DeleteWeakGlobalRef(mObject);
mObject = NULL;
-
- env->DeleteGlobalRef(mClass);
- mClass = NULL;
}
hardware::Parcel *JHwParcel::getParcel() {
@@ -542,7 +538,7 @@ static void JHwParcel_native_writeStrongBinder(
env, FindClassOrDie(env, PACKAGE_PATH "/HwRemoteBinder"));
if (env->IsInstanceOf(binderObj, hwBinderKlass.get())) {
- binder = JHwBinder::GetNativeContext(env, binderObj);
+ binder = JHwBinder::GetNativeBinder(env, binderObj);
} else if (env->IsInstanceOf(binderObj, hwRemoteBinderKlass.get())) {
binder = JHwRemoteBinder::GetNativeContext(
env, binderObj)->getBinder();
diff --git a/core/jni/android_os_HwParcel.h b/core/jni/android_os_HwParcel.h
index f81de9bf30b7..f6e61004b0e3 100644
--- a/core/jni/android_os_HwParcel.h
+++ b/core/jni/android_os_HwParcel.h
@@ -53,7 +53,6 @@ protected:
virtual ~JHwParcel();
private:
- jclass mClass;
jobject mObject;
hardware::Parcel *mParcel;
diff --git a/core/jni/android_os_HwRemoteBinder.cpp b/core/jni/android_os_HwRemoteBinder.cpp
index f2f8e52db9f5..9c2ee9cfec45 100644
--- a/core/jni/android_os_HwRemoteBinder.cpp
+++ b/core/jni/android_os_HwRemoteBinder.cpp
@@ -272,7 +272,6 @@ JHwRemoteBinder::JHwRemoteBinder(
jclass clazz = env->GetObjectClass(thiz);
CHECK(clazz != NULL);
- mClass = (jclass)env->NewGlobalRef(clazz);
mObject = env->NewWeakGlobalRef(thiz);
}
@@ -281,9 +280,6 @@ JHwRemoteBinder::~JHwRemoteBinder() {
env->DeleteWeakGlobalRef(mObject);
mObject = NULL;
-
- env->DeleteGlobalRef(mClass);
- mClass = NULL;
}
sp<hardware::IBinder> JHwRemoteBinder::getBinder() const {
diff --git a/core/jni/android_os_HwRemoteBinder.h b/core/jni/android_os_HwRemoteBinder.h
index 77a02784926d..63aad0ab2923 100644
--- a/core/jni/android_os_HwRemoteBinder.h
+++ b/core/jni/android_os_HwRemoteBinder.h
@@ -68,7 +68,6 @@ protected:
virtual ~JHwRemoteBinder();
private:
- jclass mClass;
jobject mObject;
sp<hardware::IBinder> mBinder;
diff --git a/services/core/java/com/android/server/connectivity/Tethering.java b/services/core/java/com/android/server/connectivity/Tethering.java
index 4a5129d6b8e2..0f9d881472cc 100644
--- a/services/core/java/com/android/server/connectivity/Tethering.java
+++ b/services/core/java/com/android/server/connectivity/Tethering.java
@@ -1258,10 +1258,10 @@ public class Tethering extends BaseNetworkObserver {
sendMessageDelayed(CMD_RETRY_UPSTREAM, UPSTREAM_SETTLE_TIME_MS);
}
}
- setUpstreamByType(ns);
+ setUpstreamNetwork(ns);
}
- protected void setUpstreamByType(NetworkState ns) {
+ protected void setUpstreamNetwork(NetworkState ns) {
String iface = null;
if (ns != null && ns.linkProperties != null) {
// Find the interface with the default IPv4 route. It may be the
@@ -1786,7 +1786,9 @@ public class Tethering extends BaseNetworkObserver {
}
}
- mLog.log(String.format("OBSERVED LinkProperties update iface=%s state=%s", iface, state));
+ mLog.log(String.format(
+ "OBSERVED LinkProperties update iface=%s state=%s lp=%s",
+ iface, IControlsTethering.getStateString(state), newLp));
final int which = TetherMasterSM.EVENT_IFACE_UPDATE_LINKPROPERTIES;
mTetherMasterSM.sendMessage(which, state, 0, newLp);
}
diff --git a/services/core/java/com/android/server/connectivity/tethering/IControlsTethering.java b/services/core/java/com/android/server/connectivity/tethering/IControlsTethering.java
index aaa63b1613df..2b813475222f 100644
--- a/services/core/java/com/android/server/connectivity/tethering/IControlsTethering.java
+++ b/services/core/java/com/android/server/connectivity/tethering/IControlsTethering.java
@@ -33,6 +33,16 @@ public class IControlsTethering {
public static final int STATE_TETHERED = 2;
public static final int STATE_LOCAL_ONLY = 3;
+ public static String getStateString(int state) {
+ switch (state) {
+ case STATE_UNAVAILABLE: return "UNAVAILABLE";
+ case STATE_AVAILABLE: return "AVAILABLE";
+ case STATE_TETHERED: return "TETHERED";
+ case STATE_LOCAL_ONLY: return "LOCAL_ONLY";
+ }
+ return "UNKNOWN: " + state;
+ }
+
/**
* Notify that |who| has changed its tethering state.
*
diff --git a/services/core/java/com/android/server/connectivity/tethering/OffloadController.java b/services/core/java/com/android/server/connectivity/tethering/OffloadController.java
index ce6b8be77d44..08deef84f3ae 100644
--- a/services/core/java/com/android/server/connectivity/tethering/OffloadController.java
+++ b/services/core/java/com/android/server/connectivity/tethering/OffloadController.java
@@ -85,13 +85,16 @@ public class OffloadController {
mLog.i("tethering offload control not supported");
stop();
}
+ mLog.log("tethering offload started");
}
public void stop() {
+ final boolean wasStarted = started();
mUpstreamLinkProperties = null;
mHwInterface.stopOffloadControl();
mControlInitialized = false;
mConfigInitialized = false;
+ if (wasStarted) mLog.log("tethering offload stopped");
}
public void setUpstreamLinkProperties(LinkProperties lp) {
diff --git a/services/core/java/com/android/server/connectivity/tethering/TetherInterfaceStateMachine.java b/services/core/java/com/android/server/connectivity/tethering/TetherInterfaceStateMachine.java
index bff13d4cf8a6..86b255128d6d 100644
--- a/services/core/java/com/android/server/connectivity/tethering/TetherInterfaceStateMachine.java
+++ b/services/core/java/com/android/server/connectivity/tethering/TetherInterfaceStateMachine.java
@@ -56,9 +56,10 @@ import java.util.Objects;
import java.util.Random;
/**
- * @hide
+ * Provides the interface to IP-layer serving functionality for a given network
+ * interface, e.g. for tethering or "local-only hotspot" mode.
*
- * Tracks the eligibility of a given network interface for tethering.
+ * @hide
*/
public class TetherInterfaceStateMachine extends StateMachine {
private static final IpPrefix LINK_LOCAL_PREFIX = new IpPrefix("fe80::/64");
@@ -117,6 +118,12 @@ public class TetherInterfaceStateMachine extends StateMachine {
private String mMyUpstreamIfaceName; // may change over time
private NetworkInterface mNetworkInterface;
private byte[] mHwAddr;
+ // TODO: De-duplicate this with mLinkProperties above. Currently, these link
+ // properties are those selected by the IPv6TetheringCoordinator and relayed
+ // to us. By comparison, mLinkProperties contains the addresses and directly
+ // connected routes that have been formed from these properties iff. we have
+ // succeeded in configuring them and are able to announce them within Router
+ // Advertisements (otherwise, we do not add them to mLinkProperties at all).
private LinkProperties mLastIPv6LinkProperties;
private RouterAdvertisementDaemon mRaDaemon;
private RaParams mLastRaParams;
@@ -133,7 +140,7 @@ public class TetherInterfaceStateMachine extends StateMachine {
mIfaceName = ifaceName;
mInterfaceType = interfaceType;
mLinkProperties = new LinkProperties();
- mLinkProperties.setInterfaceName(mIfaceName);
+ resetLinkProperties();
mLastError = ConnectivityManager.TETHER_ERROR_NO_ERROR;
mInitialState = new InitialState();
@@ -162,10 +169,15 @@ public class TetherInterfaceStateMachine extends StateMachine {
* Internals.
*/
- // configured when we start tethering and unconfig'd on error or conclusion
- private boolean configureIfaceIp(boolean enabled) {
- if (VDBG) Log.d(TAG, "configureIfaceIp(" + enabled + ")");
+ private boolean startIPv4() { return configureIPv4(true); }
+ private void stopIPv4() { configureIPv4(false); }
+
+ private boolean configureIPv4(boolean enabled) {
+ if (VDBG) Log.d(TAG, "configureIPv4(" + enabled + ")");
+
+ // TODO: Replace this hard-coded information with dynamically selected
+ // config passed down to us by a higher layer IP-coordinating element.
String ipAsString = null;
int prefixLen = 0;
if (mInterfaceType == ConnectivityManager.TETHERING_USB) {
@@ -179,32 +191,45 @@ public class TetherInterfaceStateMachine extends StateMachine {
return true;
}
- InterfaceConfiguration ifcg = null;
+ final LinkAddress linkAddr;
try {
- ifcg = mNMService.getInterfaceConfig(mIfaceName);
- if (ifcg != null) {
- InetAddress addr = NetworkUtils.numericToInetAddress(ipAsString);
- ifcg.setLinkAddress(new LinkAddress(addr, prefixLen));
- if (mInterfaceType == ConnectivityManager.TETHERING_WIFI) {
- // The WiFi stack has ownership of the interface up/down state.
- // It is unclear whether the bluetooth or USB stacks will manage their own
- // state.
- ifcg.ignoreInterfaceUpDownStatus();
+ final InterfaceConfiguration ifcg = mNMService.getInterfaceConfig(mIfaceName);
+ if (ifcg == null) {
+ mLog.e("Received null interface config");
+ return false;
+ }
+
+ InetAddress addr = NetworkUtils.numericToInetAddress(ipAsString);
+ linkAddr = new LinkAddress(addr, prefixLen);
+ ifcg.setLinkAddress(linkAddr);
+ if (mInterfaceType == ConnectivityManager.TETHERING_WIFI) {
+ // The WiFi stack has ownership of the interface up/down state.
+ // It is unclear whether the Bluetooth or USB stacks will manage their own
+ // state.
+ ifcg.ignoreInterfaceUpDownStatus();
+ } else {
+ if (enabled) {
+ ifcg.setInterfaceUp();
} else {
- if (enabled) {
- ifcg.setInterfaceUp();
- } else {
- ifcg.setInterfaceDown();
- }
+ ifcg.setInterfaceDown();
}
- ifcg.clearFlag("running");
- mNMService.setInterfaceConfig(mIfaceName, ifcg);
}
+ ifcg.clearFlag("running");
+ mNMService.setInterfaceConfig(mIfaceName, ifcg);
} catch (Exception e) {
mLog.e("Error configuring interface " + e);
return false;
}
+ // Directly-connected route.
+ final RouteInfo route = new RouteInfo(linkAddr);
+ if (enabled) {
+ mLinkProperties.addLinkAddress(linkAddr);
+ mLinkProperties.addRoute(route);
+ } else {
+ mLinkProperties.removeLinkAddress(linkAddr);
+ mLinkProperties.removeRoute(route);
+ }
return true;
}
@@ -294,7 +319,7 @@ public class TetherInterfaceStateMachine extends StateMachine {
mLastIPv6LinkProperties = v6only;
}
- private void configureLocalRoutes(
+ private void configureLocalIPv6Routes(
HashSet<IpPrefix> deprecatedPrefixes, HashSet<IpPrefix> newPrefixes) {
// [1] Remove the routes that are deprecated.
if (!deprecatedPrefixes.isEmpty()) {
@@ -309,6 +334,8 @@ public class TetherInterfaceStateMachine extends StateMachine {
} catch (RemoteException e) {
mLog.e("Failed to remove IPv6 routes from local table: " + e);
}
+
+ for (RouteInfo route : toBeRemoved) mLinkProperties.removeRoute(route);
}
// [2] Add only the routes that have not previously been added.
@@ -340,11 +367,13 @@ public class TetherInterfaceStateMachine extends StateMachine {
} catch (RemoteException e) {
mLog.e("Failed to add IPv6 routes to local table: " + e);
}
+
+ for (RouteInfo route : toBeAdded) mLinkProperties.addRoute(route);
}
}
}
- private void configureLocalDns(
+ private void configureLocalIPv6Dns(
HashSet<Inet6Address> deprecatedDnses, HashSet<Inet6Address> newDnses) {
final INetd netd = NetdService.getInstance();
if (netd == null) {
@@ -362,6 +391,8 @@ public class TetherInterfaceStateMachine extends StateMachine {
} catch (ServiceSpecificException | RemoteException e) {
mLog.e("Failed to remove local dns IP " + dnsString + ": " + e);
}
+
+ mLinkProperties.removeLinkAddress(new LinkAddress(dns, RFC7421_PREFIX_LENGTH));
}
}
@@ -380,6 +411,8 @@ public class TetherInterfaceStateMachine extends StateMachine {
mLog.e("Failed to add local dns IP " + dnsString + ": " + e);
newDnses.remove(dns);
}
+
+ mLinkProperties.addLinkAddress(new LinkAddress(dns, RFC7421_PREFIX_LENGTH));
}
}
@@ -396,10 +429,10 @@ public class TetherInterfaceStateMachine extends StateMachine {
final RaParams deprecatedParams =
RaParams.getDeprecatedRaParams(mLastRaParams, newParams);
- configureLocalRoutes(deprecatedParams.prefixes,
+ configureLocalIPv6Routes(deprecatedParams.prefixes,
(newParams != null) ? newParams.prefixes : null);
- configureLocalDns(deprecatedParams.dnses,
+ configureLocalIPv6Dns(deprecatedParams.dnses,
(newParams != null) ? newParams.dnses : null);
mRaDaemon.buildNewRa(deprecatedParams, newParams);
@@ -419,12 +452,19 @@ public class TetherInterfaceStateMachine extends StateMachine {
private void sendInterfaceState(int newInterfaceState) {
mTetherController.updateInterfaceState(
TetherInterfaceStateMachine.this, newInterfaceState, mLastError);
- // TODO: Populate mLinkProperties correctly, and send more sensible
- // updates more frequently (not just here).
+ sendLinkProperties();
+ }
+
+ private void sendLinkProperties() {
mTetherController.updateLinkProperties(
TetherInterfaceStateMachine.this, new LinkProperties(mLinkProperties));
}
+ private void resetLinkProperties() {
+ mLinkProperties.clear();
+ mLinkProperties.setInterfaceName(mIfaceName);
+ }
+
class InitialState extends State {
@Override
public void enter() {
@@ -464,7 +504,7 @@ public class TetherInterfaceStateMachine extends StateMachine {
class BaseServingState extends State {
@Override
public void enter() {
- if (!configureIfaceIp(true)) {
+ if (!startIPv4()) {
mLastError = ConnectivityManager.TETHER_ERROR_IFACE_CFG_ERROR;
return;
}
@@ -498,7 +538,9 @@ public class TetherInterfaceStateMachine extends StateMachine {
mLog.e("Failed to untether interface: " + e);
}
- configureIfaceIp(false);
+ stopIPv4();
+
+ resetLinkProperties();
}
@Override
@@ -515,6 +557,7 @@ public class TetherInterfaceStateMachine extends StateMachine {
break;
case CMD_IPV6_TETHER_UPDATE:
updateUpstreamIPv6LinkProperties((LinkProperties) message.obj);
+ sendLinkProperties();
break;
case CMD_IP_FORWARDING_ENABLE_ERROR:
case CMD_IP_FORWARDING_DISABLE_ERROR:
@@ -625,7 +668,6 @@ public class TetherInterfaceStateMachine extends StateMachine {
if (super.processMessage(message)) return true;
maybeLogMessage(this, message.what);
- boolean retValue = true;
switch (message.what) {
case CMD_TETHER_REQUESTED:
mLog.e("CMD_TETHER_REQUESTED while already tethering.");
@@ -655,10 +697,9 @@ public class TetherInterfaceStateMachine extends StateMachine {
mMyUpstreamIfaceName = newUpstreamIfaceName;
break;
default:
- retValue = false;
- break;
+ return false;
}
- return retValue;
+ return true;
}
}
diff --git a/telephony/java/android/telephony/MbmsDownloadManager.java b/telephony/java/android/telephony/MbmsDownloadManager.java
index c8c6d014a059..79ee37a168d4 100644
--- a/telephony/java/android/telephony/MbmsDownloadManager.java
+++ b/telephony/java/android/telephony/MbmsDownloadManager.java
@@ -16,10 +16,13 @@
package android.telephony;
+import android.annotation.NonNull;
import android.content.ComponentName;
import android.content.Context;
import android.content.Intent;
import android.content.ServiceConnection;
+import android.content.SharedPreferences;
+import android.content.pm.ResolveInfo;
import android.net.Uri;
import android.os.IBinder;
import android.os.RemoteException;
@@ -27,13 +30,18 @@ import android.telephony.mbms.IDownloadCallback;
import android.telephony.mbms.DownloadRequest;
import android.telephony.mbms.DownloadStatus;
import android.telephony.mbms.IMbmsDownloadManagerCallback;
+import android.telephony.mbms.MbmsDownloadManagerCallback;
+import android.telephony.mbms.MbmsDownloadReceiver;
import android.telephony.mbms.MbmsException;
+import android.telephony.mbms.MbmsTempFileProvider;
import android.telephony.mbms.MbmsUtils;
import android.telephony.mbms.vendor.IMbmsDownloadService;
import android.util.Log;
+import java.io.File;
+import java.io.IOException;
import java.util.List;
-import java.util.concurrent.CountDownLatch;
+import java.util.concurrent.atomic.AtomicReference;
import static android.telephony.SubscriptionManager.INVALID_SUBSCRIPTION_ID;
@@ -47,7 +55,7 @@ public class MbmsDownloadManager {
* The MBMS middleware should send this when a download of single file has completed or
* failed. Mandatory extras are
* {@link #EXTRA_RESULT}
- * {@link #EXTRA_INFO}
+ * {@link #EXTRA_FILE_INFO}
* {@link #EXTRA_REQUEST}
* {@link #EXTRA_TEMP_LIST}
* {@link #EXTRA_FINAL_URI}
@@ -93,7 +101,7 @@ public class MbmsDownloadManager {
* is for. Must not be null.
* TODO: future systemapi (here and and all extras) except the two for the app intent
*/
- public static final String EXTRA_INFO = "android.telephony.mbms.extra.INFO";
+ public static final String EXTRA_FILE_INFO = "android.telephony.mbms.extra.FILE_INFO";
/**
* Extra containing the {@link DownloadRequest} for which the download result or file
@@ -144,6 +152,14 @@ public class MbmsDownloadManager {
"android.telephony.mbms.extra.PAUSED_URI_LIST";
/**
+ * Extra containing a string that points to the middleware's knowledge of where the temp file
+ * root for the app is. The path should be a canonical path as returned by
+ * {@link File#getCanonicalPath()}
+ */
+ public static final String EXTRA_TEMP_FILE_ROOT =
+ "android.telephony.mbms.extra.TEMP_FILE_ROOT";
+
+ /**
* Extra containing a list of {@link Uri}s indicating temp files which the middleware is
* still using.
*/
@@ -165,12 +181,10 @@ public class MbmsDownloadManager {
public static final int RESULT_EXPIRED = 3;
// TODO - more results!
- private static final long BIND_TIMEOUT_MS = 3000;
-
private final Context mContext;
- private int mSubId = INVALID_SUBSCRIPTION_ID;
+ private int mSubscriptionId = INVALID_SUBSCRIPTION_ID;
- private IMbmsDownloadService mService;
+ private AtomicReference<IMbmsDownloadService> mService = new AtomicReference<>(null);
private final IMbmsDownloadManagerCallback mCallback;
private final String mDownloadAppName;
@@ -179,116 +193,221 @@ public class MbmsDownloadManager {
mContext = context;
mCallback = callback;
mDownloadAppName = downloadAppName;
- mSubId = subId;
+ mSubscriptionId = subId;
}
/**
* Create a new MbmsDownloadManager using the system default data subscription ID.
- *
- * Note that this call will bind a remote service and that may take a bit. This
- * may throw an Illegal ArgumentException or RemoteException.
+ * See {@link #create(Context, IMbmsDownloadManagerCallback, String, int)}
*
* @hide
*/
- public static MbmsDownloadManager createManager(Context context,
+ public static MbmsDownloadManager create(Context context,
IMbmsDownloadManagerCallback listener, String downloadAppName)
throws MbmsException {
- MbmsDownloadManager mdm = new MbmsDownloadManager(context, listener, downloadAppName,
+ return create(context, listener, downloadAppName,
SubscriptionManager.getDefaultSubscriptionId());
- mdm.bindAndInitialize();
- return mdm;
}
/**
* Create a new MbmsDownloadManager using the given subscription ID.
*
- * Note that this call will bind a remote service and that may take a bit. This
- * may throw an Illegal ArgumentException or RemoteException.
+ * Note that this call will bind a remote service and that may take a bit. The instance of
+ * {@link MbmsDownloadManager} that is returned will not be ready for use until
+ * {@link IMbmsDownloadManagerCallback#middlewareReady()} is called on the provided callback.
+ * If you attempt to use the manager before it is ready, a {@link MbmsException} will be thrown.
+ *
+ * This also may throw an {@link IllegalArgumentException} or a {@link MbmsException}.
*
+ * @param context The instance of {@link Context} to use
+ * @param listener A callback to get asynchronous error messages and file service updates.
+ * @param downloadAppName The app name, as negotiated with the eMBMS provider
+ * @param subscriptionId The data subscription ID to use
* @hide
*/
-
- public static MbmsDownloadManager createManager(Context context,
- IMbmsDownloadManagerCallback listener, String downloadAppName, int subId)
+ public static MbmsDownloadManager create(Context context,
+ IMbmsDownloadManagerCallback listener, String downloadAppName, int subscriptionId)
throws MbmsException {
MbmsDownloadManager mdm = new MbmsDownloadManager(context, listener, downloadAppName,
- subId);
+ subscriptionId);
mdm.bindAndInitialize();
return mdm;
}
private void bindAndInitialize() throws MbmsException {
- // TODO: fold binding for download and streaming into a common utils class.
- final CountDownLatch latch = new CountDownLatch(1);
- ServiceConnection bindListener = new ServiceConnection() {
- @Override
- public void onServiceConnected(ComponentName name, IBinder service) {
- mService = IMbmsDownloadService.Stub.asInterface(service);
- latch.countDown();
- }
-
- @Override
- public void onServiceDisconnected(ComponentName name) {
- mService = null;
- }
- };
-
- Intent bindIntent = new Intent();
- bindIntent.setComponent(MbmsUtils.toComponentName(
- MbmsUtils.getMiddlewareServiceInfo(mContext, MBMS_DOWNLOAD_SERVICE_ACTION)));
-
- // Kick off the binding, and synchronously wait until binding is complete
- mContext.bindService(bindIntent, bindListener, Context.BIND_AUTO_CREATE);
-
- MbmsUtils.waitOnLatchWithTimeout(latch, BIND_TIMEOUT_MS);
-
- // TODO: initialize
+ MbmsUtils.startBinding(mContext, MBMS_DOWNLOAD_SERVICE_ACTION,
+ new ServiceConnection() {
+ @Override
+ public void onServiceConnected(ComponentName name, IBinder service) {
+ IMbmsDownloadService downloadService =
+ IMbmsDownloadService.Stub.asInterface(service);
+ try {
+ downloadService.initialize(
+ mDownloadAppName, mSubscriptionId, mCallback);
+ } catch (RemoteException e) {
+ Log.e(LOG_TAG, "Service died before initialization");
+ return;
+ }
+ mService.set(downloadService);
+ }
+
+ @Override
+ public void onServiceDisconnected(ComponentName name) {
+ mService.set(null);
+ }
+ });
}
/**
- * Gets the list of files published for download.
- * They may occur at times far in the future.
- * servicesClasses lets the app filter on types of files and is opaque data between
- * the app and the carrier
+ * An inspection API to retrieve the list of available
+ * {@link android.telephony.mbms.FileServiceInfo}s currently being advertised.
+ * The results are returned asynchronously via a call to
+ * {@link IMbmsDownloadManagerCallback#fileServicesUpdated(List)}
*
- * Multiple calls replace trhe list of serviceClasses of interest.
+ * The serviceClasses argument lets the app filter on types of programming and is opaque data
+ * negotiated beforehand between the app and the carrier.
*
- * May throw an IllegalArgumentException or RemoteException.
+ * Multiple calls replace the list of serviceClasses of interest.
*
- * Synchronous responses include
- * <li>SUCCESS</li>
- * <li>ERROR_MSDC_CONCURRENT_SERVICE_LIMIT_REACHED</li>
+ * This may throw an {@link MbmsException} containing one of the following errors:
+ * {@link MbmsException#ERROR_MIDDLEWARE_NOT_BOUND}
+ * {@link MbmsException#ERROR_CONCURRENT_SERVICE_LIMIT_REACHED}
+ * {@link MbmsException#ERROR_SERVICE_LOST}
*
- * Asynchronous errors through the listener include any of the errors except
- * <li>ERROR_MSDC_UNABLE_TO_)START_SERVICE</li>
- * <li>ERROR_MSDC_INVALID_SERVICE_ID</li>
- * <li>ERROR_MSDC_END_OF_SESSION</li>
+ * Asynchronous error codes via the {@link MbmsDownloadManagerCallback#error(int, String)}
+ * callback can include any of the errors except:
+ * {@link MbmsException#ERROR_UNABLE_TO_START_SERVICE}
+ * {@link MbmsException#ERROR_END_OF_SESSION}
*/
- public int getFileServices(List<String> serviceClasses) {
- return 0;
+ public void getFileServices(List<String> classList) throws MbmsException {
+ IMbmsDownloadService downloadService = mService.get();
+ if (downloadService == null) {
+ throw new MbmsException(MbmsException.ERROR_MIDDLEWARE_NOT_BOUND);
+ }
+ try {
+ int returnCode = downloadService.getFileServices(
+ mDownloadAppName, mSubscriptionId, classList);
+ if (returnCode != MbmsException.SUCCESS) {
+ throw new MbmsException(returnCode);
+ }
+ } catch (RemoteException e) {
+ Log.w(LOG_TAG, "Remote process died");
+ mService.set(null);
+ throw new MbmsException(MbmsException.ERROR_SERVICE_LOST);
+ }
}
+ /**
+ * Sets the temp file root for downloads.
+ * All temp files created for the middleware to write to will be contained in the specified
+ * directory. Applications that wish to specify a location only need to call this method once
+ * as long their data is persisted in storage -- the argument will be stored both in a
+ * local instance of {@link android.content.SharedPreferences} and by the middleware.
+ *
+ * If this method is not called at least once before calling
+ * {@link #download(DownloadRequest, IDownloadCallback)}, the framework
+ * will default to a directory formed by the concatenation of the app's files directory and
+ * {@link android.telephony.mbms.MbmsTempFileProvider#DEFAULT_TOP_LEVEL_TEMP_DIRECTORY}.
+ *
+ * This method may not be called while any download requests are still active. If this is
+ * the case, an {@link MbmsException} will be thrown with code
+ * {@link MbmsException#ERROR_CANNOT_CHANGE_TEMP_FILE_ROOT}
+ *
+ * The {@link File} supplied as a root temp file directory must already exist. If not, an
+ * {@link IllegalArgumentException} will be thrown.
+ * @param tempFileRootDirectory A directory to place temp files in.
+ */
+ public void setTempFileRootDirectory(@NonNull File tempFileRootDirectory)
+ throws MbmsException {
+ IMbmsDownloadService downloadService = mService.get();
+ if (downloadService == null) {
+ throw new MbmsException(MbmsException.ERROR_MIDDLEWARE_NOT_BOUND);
+ }
+ if (!tempFileRootDirectory.exists()) {
+ throw new IllegalArgumentException("Provided directory does not exist");
+ }
+ if (!tempFileRootDirectory.isDirectory()) {
+ throw new IllegalArgumentException("Provided File is not a directory");
+ }
+ String filePath;
+ try {
+ filePath = tempFileRootDirectory.getCanonicalPath();
+ } catch (IOException e) {
+ throw new IllegalArgumentException("Unable to canonicalize the provided path: " + e);
+ }
+
+ try {
+ int result = downloadService.setTempFileRootDirectory(
+ mDownloadAppName, mSubscriptionId, filePath);
+ if (result != MbmsException.SUCCESS) {
+ throw new MbmsException(result);
+ }
+ } catch (RemoteException e) {
+ mService.set(null);
+ throw new MbmsException(MbmsException.ERROR_SERVICE_LOST);
+ }
+
+ SharedPreferences prefs = mContext.getSharedPreferences(
+ MbmsTempFileProvider.TEMP_FILE_ROOT_PREF_FILE_NAME, 0);
+ prefs.edit().putString(MbmsTempFileProvider.TEMP_FILE_ROOT_PREF_NAME, filePath).apply();
+ }
/**
- * Requests a future download.
- * returns a token which may be used to cancel a download.
+ * Requests a download of a file that is available via multicast.
+ *
* downloadListener is an optional callback object which can be used to get progress reports
* of a currently occuring download. Note this can only run while the calling app
* is running, so future downloads will simply result in resultIntents being sent
* for completed or errored-out downloads. A NULL indicates no callbacks are needed.
*
- * May throw an IllegalArgumentException or RemoteExcpetion.
+ * May throw an {@link IllegalArgumentException}
+ *
+ * If {@link #setTempFileRootDirectory(File)} has not called after the app has been installed,
+ * this method will create a directory at the default location defined at
+ * {@link MbmsTempFileProvider#DEFAULT_TOP_LEVEL_TEMP_DIRECTORY} and store that as the temp
+ * file root directory.
*
* Asynchronous errors through the listener include any of the errors
+ *
+ * @param request The request that specifies what should be downloaded
+ * @param callback Optional callback that will provide progress updates if the app is running.
*/
- public DownloadRequest download(DownloadRequest request, IDownloadCallback listener) {
+ public void download(DownloadRequest request, IDownloadCallback callback)
+ throws MbmsException {
+ IMbmsDownloadService downloadService = mService.get();
+ if (downloadService == null) {
+ throw new MbmsException(MbmsException.ERROR_MIDDLEWARE_NOT_BOUND);
+ }
+
+ // Check to see whether the app's set a temp root dir yet, and set it if not.
+ SharedPreferences prefs = mContext.getSharedPreferences(
+ MbmsTempFileProvider.TEMP_FILE_ROOT_PREF_FILE_NAME, 0);
+ if (prefs.getString(MbmsTempFileProvider.TEMP_FILE_ROOT_PREF_NAME, null) == null) {
+ File tempRootDirectory = new File(mContext.getFilesDir(),
+ MbmsTempFileProvider.DEFAULT_TOP_LEVEL_TEMP_DIRECTORY);
+ tempRootDirectory.mkdirs();
+ setTempFileRootDirectory(tempRootDirectory);
+ }
+
request.setAppName(mDownloadAppName);
+ // Check if the request is a multipart download. If so, validate that the destination is
+ // a directory that exists.
+ // TODO: figure out what qualifies a request as a multipart download request.
+ if (request.getSourceUri().getLastPathSegment() != null &&
+ request.getSourceUri().getLastPathSegment().contains("*")) {
+ File toFile = new File(request.getDestinationUri().getSchemeSpecificPart());
+ if (!toFile.isDirectory()) {
+ throw new IllegalArgumentException("Multipart download must specify valid " +
+ "destination directory.");
+ }
+ }
+ // TODO: check to make sure destination is clear
+ // TODO: write download request token
try {
- mService.download(request, listener);
+ downloadService.download(request, callback);
} catch (RemoteException e) {
- mService = null;
+ mService.set(null);
}
- return request;
}
/**
@@ -355,13 +474,45 @@ public class MbmsDownloadManager {
return 0;
}
+ /**
+ * Retrieves the {@link ComponentName} for the {@link android.content.BroadcastReceiver} that
+ * the various intents from the middleware should be targeted towards.
+ * @param uid The uid of the frontend app.
+ * @return The component name of the receiver that the middleware should send its intents to,
+ * or null if the app didn't declare it in the manifest.
+ *
+ * @hide
+ * future systemapi
+ */
+ public static ComponentName getAppReceiverFromUid(Context context, int uid) {
+ String[] packageNames = context.getPackageManager().getPackagesForUid(uid);
+ if (packageNames == null) {
+ return null;
+ }
+
+ for (String packageName : packageNames) {
+ ComponentName candidate = new ComponentName(packageName,
+ MbmsDownloadReceiver.class.getCanonicalName());
+ Intent queryIntent = new Intent();
+ queryIntent.setComponent(candidate);
+ List<ResolveInfo> receivers =
+ context.getPackageManager().queryBroadcastReceivers(queryIntent, 0);
+ if (receivers != null && receivers.size() > 0) {
+ return candidate;
+ }
+ }
+ return null;
+ }
+
public void dispose() {
try {
- if (mService != null) {
- mService.dispose(mDownloadAppName, mSubId);
- } else {
+ IMbmsDownloadService downloadService = mService.get();
+ if (downloadService == null) {
Log.i(LOG_TAG, "Service already dead");
+ return;
}
+ downloadService.dispose(mDownloadAppName, mSubscriptionId);
+ mService.set(null);
} catch (RemoteException e) {
// Ignore
Log.i(LOG_TAG, "Remote exception while disposing of service");
diff --git a/telephony/java/android/telephony/MbmsStreamingManager.java b/telephony/java/android/telephony/MbmsStreamingManager.java
index f68e2439971f..af7f333390d8 100644
--- a/telephony/java/android/telephony/MbmsStreamingManager.java
+++ b/telephony/java/android/telephony/MbmsStreamingManager.java
@@ -18,11 +18,7 @@ package android.telephony;
import android.content.ComponentName;
import android.content.Context;
-import android.content.Intent;
import android.content.ServiceConnection;
-import android.content.pm.PackageManager;
-import android.content.pm.ResolveInfo;
-import android.os.DeadObjectException;
import android.os.IBinder;
import android.os.RemoteException;
import android.telephony.mbms.MbmsException;
@@ -34,56 +30,18 @@ import android.telephony.mbms.StreamingServiceInfo;
import android.telephony.mbms.vendor.IMbmsStreamingService;
import android.util.Log;
-import java.util.LinkedList;
import java.util.List;
-import java.util.concurrent.CountDownLatch;
-import java.util.concurrent.TimeUnit;
+import java.util.concurrent.atomic.AtomicReference;
import static android.telephony.SubscriptionManager.INVALID_SUBSCRIPTION_ID;
/** @hide */
public class MbmsStreamingManager {
- private interface ServiceListener {
- void onServiceConnected();
- void onServiceDisconnected();
- }
-
private static final String LOG_TAG = "MbmsStreamingManager";
public static final String MBMS_STREAMING_SERVICE_ACTION =
"android.telephony.action.EmbmsStreaming";
- private static final boolean DEBUG = true;
- private static final int BIND_TIMEOUT_MS = 3000;
-
- private IMbmsStreamingService mService;
- private ServiceConnection mServiceConnection = new ServiceConnection() {
- @Override
- public void onServiceConnected(ComponentName name, IBinder service) {
- if (service != null) {
- Log.i(LOG_TAG, String.format("Connected to service %s", name));
- synchronized (MbmsStreamingManager.this) {
- mService = IMbmsStreamingService.Stub.asInterface(service);
- for (ServiceListener l : mServiceListeners) {
- l.onServiceConnected();
- }
- }
- }
- }
-
- @Override
- public void onServiceDisconnected(ComponentName name) {
- Log.i(LOG_TAG, String.format("Disconnected from service %s", name));
- synchronized (MbmsStreamingManager.this) {
- mService = null;
- for (ServiceListener l : mServiceListeners) {
- l.onServiceDisconnected();
- }
- }
- }
- };
-
- private List<ServiceListener> mServiceListeners = new LinkedList<>();
-
+ private AtomicReference<IMbmsStreamingService> mService = new AtomicReference<>(null);
private MbmsStreamingManagerCallback mCallbackToApp;
private final String mAppName;
@@ -128,28 +86,26 @@ public class MbmsStreamingManager {
public static MbmsStreamingManager create(Context context,
MbmsStreamingManagerCallback listener, String streamingAppName)
throws MbmsException {
- int subId = SubscriptionManager.getDefaultSubscriptionId();
- MbmsStreamingManager manager = new MbmsStreamingManager(context, listener,
- streamingAppName, subId);
- manager.bindAndInitialize();
- return manager;
+ return create(context, listener, streamingAppName,
+ SubscriptionManager.getDefaultSubscriptionId());
}
/**
* Terminates this instance, ending calls to the registered listener. Also terminates
* any streaming services spawned from this instance.
*/
- public synchronized void dispose() {
- if (mService == null) {
+ public void dispose() {
+ IMbmsStreamingService streamingService = mService.get();
+ if (streamingService == null) {
// Ignore and return, assume already disposed.
return;
}
try {
- mService.dispose(mAppName, mSubscriptionId);
+ streamingService.dispose(mAppName, mSubscriptionId);
} catch (RemoteException e) {
// Ignore for now
}
- mService = null;
+ mService.set(null);
}
/**
@@ -171,17 +127,19 @@ public class MbmsStreamingManager {
* {@link MbmsException#ERROR_END_OF_SESSION}
*/
public void getStreamingServices(List<String> classList) throws MbmsException {
- if (mService == null) {
+ IMbmsStreamingService streamingService = mService.get();
+ if (streamingService == null) {
throw new MbmsException(MbmsException.ERROR_MIDDLEWARE_NOT_BOUND);
}
try {
- int returnCode = mService.getStreamingServices(mAppName, mSubscriptionId, classList);
+ int returnCode = streamingService.getStreamingServices(
+ mAppName, mSubscriptionId, classList);
if (returnCode != MbmsException.SUCCESS) {
throw new MbmsException(returnCode);
}
} catch (RemoteException e) {
Log.w(LOG_TAG, "Remote process died");
- mService = null;
+ mService.set(null);
throw new MbmsException(MbmsException.ERROR_SERVICE_LOST);
}
}
@@ -190,7 +148,7 @@ public class MbmsStreamingManager {
* Starts streaming a requested service, reporting status to the indicated listener.
* Returns an object used to control that stream. The stream may not be ready for consumption
* immediately upon return from this method -- wait until the streaming state has been
- * reported via {@link android.telephony.mbms.StreamingServiceCallback#streamStateChanged(int)}.
+ * reported via {@link android.telephony.mbms.StreamingServiceCallback#streamStateUpdated(int)}
*
* May throw an {@link MbmsException} containing any of the following error codes:
* {@link MbmsException#ERROR_MIDDLEWARE_NOT_BOUND}
@@ -203,71 +161,47 @@ public class MbmsStreamingManager {
*/
public StreamingService startStreaming(StreamingServiceInfo serviceInfo,
StreamingServiceCallback listener) throws MbmsException {
- if (mService == null) {
+ IMbmsStreamingService streamingService = mService.get();
+ if (streamingService == null) {
throw new MbmsException(MbmsException.ERROR_MIDDLEWARE_NOT_BOUND);
}
try {
- int returnCode = mService.startStreaming(
+ int returnCode = streamingService.startStreaming(
mAppName, mSubscriptionId, serviceInfo.getServiceId(), listener);
if (returnCode != MbmsException.SUCCESS) {
throw new MbmsException(returnCode);
}
} catch (RemoteException e) {
Log.w(LOG_TAG, "Remote process died");
- mService = null;
+ mService.set(null);
throw new MbmsException(MbmsException.ERROR_SERVICE_LOST);
}
return new StreamingService(
- mAppName, mSubscriptionId, mService, serviceInfo, listener);
+ mAppName, mSubscriptionId, streamingService, serviceInfo, listener);
}
private void bindAndInitialize() throws MbmsException {
- // Kick off the binding, and synchronously wait until binding is complete
- final CountDownLatch latch = new CountDownLatch(1);
- ServiceListener bindListener = new ServiceListener() {
- @Override
- public void onServiceConnected() {
- latch.countDown();
- }
-
- @Override
- public void onServiceDisconnected() {
- }
- };
-
- synchronized (this) {
- mServiceListeners.add(bindListener);
- }
-
- Intent bindIntent = new Intent();
- bindIntent.setComponent(MbmsUtils.toComponentName(
- MbmsUtils.getMiddlewareServiceInfo(mContext, MBMS_STREAMING_SERVICE_ACTION)));
-
- mContext.bindService(bindIntent, mServiceConnection, Context.BIND_AUTO_CREATE);
-
- MbmsUtils.waitOnLatchWithTimeout(latch, BIND_TIMEOUT_MS);
-
- // Remove the listener and call the initialization method through the interface.
- synchronized (this) {
- mServiceListeners.remove(bindListener);
-
- if (mService == null) {
- throw new MbmsException(MbmsException.ERROR_BIND_TIMEOUT_OR_FAILURE);
- }
+ MbmsUtils.startBinding(mContext, MBMS_STREAMING_SERVICE_ACTION,
+ new ServiceConnection() {
+ @Override
+ public void onServiceConnected(ComponentName name, IBinder service) {
+ IMbmsStreamingService streamingService =
+ IMbmsStreamingService.Stub.asInterface(service);
+ try {
+ streamingService.initialize(mCallbackToApp, mAppName, mSubscriptionId);
+ } catch (RemoteException e) {
+ Log.e(LOG_TAG, "Service died before initialization");
+ return;
+ }
+ mService.set(null);
+ }
- try {
- int returnCode = mService.initialize(mCallbackToApp, mAppName, mSubscriptionId);
- if (returnCode != MbmsException.SUCCESS) {
- throw new MbmsException(returnCode);
- }
- } catch (RemoteException e) {
- mService = null;
- Log.e(LOG_TAG, "Service died before initialization");
- throw new MbmsException(MbmsException.ERROR_SERVICE_LOST);
- }
- }
+ @Override
+ public void onServiceDisconnected(ComponentName name) {
+ mService.set(null);
+ }
+ });
}
-
}
diff --git a/telephony/java/android/telephony/mbms/DownloadRequest.java b/telephony/java/android/telephony/mbms/DownloadRequest.java
index f3ca05840da2..c561741cc80c 100644
--- a/telephony/java/android/telephony/mbms/DownloadRequest.java
+++ b/telephony/java/android/telephony/mbms/DownloadRequest.java
@@ -35,9 +35,8 @@ public class DownloadRequest implements Parcelable {
private FileServiceInfo serviceInfo;
private Uri source;
private Uri dest;
- private int sub;
+ private int subscriptionId;
private String appIntent;
- private String appName; // not the Android app Name, the embms app Name
public Builder setId(int id) {
this.id = id;
@@ -59,8 +58,8 @@ public class DownloadRequest implements Parcelable {
return this;
}
- public Builder setSub(int sub) {
- this.sub = sub;
+ public Builder setSubscriptionId(int sub) {
+ this.subscriptionId = sub;
return this;
}
@@ -70,7 +69,8 @@ public class DownloadRequest implements Parcelable {
}
public DownloadRequest build() {
- return new DownloadRequest(id, serviceInfo, source, dest, sub, appIntent, appName);
+ return new DownloadRequest(id, serviceInfo, source, dest,
+ subscriptionId, appIntent, null);
}
}
@@ -78,7 +78,7 @@ public class DownloadRequest implements Parcelable {
private final FileServiceInfo fileServiceInfo;
private final Uri sourceUri;
private final Uri destinationUri;
- private final int subId;
+ private final int subscriptionId;
private final String serializedResultIntentForApp;
private String appName; // not the Android app Name, the embms app name
@@ -89,7 +89,7 @@ public class DownloadRequest implements Parcelable {
fileServiceInfo = serviceInfo;
sourceUri = source;
destinationUri = dest;
- subId = sub;
+ subscriptionId = sub;
serializedResultIntentForApp = appIntent;
appName = name;
}
@@ -103,7 +103,7 @@ public class DownloadRequest implements Parcelable {
fileServiceInfo = dr.fileServiceInfo;
sourceUri = dr.sourceUri;
destinationUri = dr.destinationUri;
- subId = dr.subId;
+ subscriptionId = dr.subscriptionId;
serializedResultIntentForApp = dr.serializedResultIntentForApp;
appName = dr.appName;
}
@@ -113,7 +113,7 @@ public class DownloadRequest implements Parcelable {
fileServiceInfo = in.readParcelable(getClass().getClassLoader());
sourceUri = in.readParcelable(getClass().getClassLoader());
destinationUri = in.readParcelable(getClass().getClassLoader());
- subId = in.readInt();
+ subscriptionId = in.readInt();
serializedResultIntentForApp = in.readString();
appName = in.readString();
}
@@ -127,7 +127,7 @@ public class DownloadRequest implements Parcelable {
out.writeParcelable(fileServiceInfo, flags);
out.writeParcelable(sourceUri, flags);
out.writeParcelable(destinationUri, flags);
- out.writeInt(subId);
+ out.writeInt(subscriptionId);
out.writeString(serializedResultIntentForApp);
out.writeString(appName);
}
@@ -148,8 +148,8 @@ public class DownloadRequest implements Parcelable {
return destinationUri;
}
- public int getSubId() {
- return subId;
+ public int getSubscriptionId() {
+ return subscriptionId;
}
public Intent getIntentForApp() {
diff --git a/telephony/java/android/telephony/mbms/FileInfo.java b/telephony/java/android/telephony/mbms/FileInfo.java
index d3888bdd2c9a..1b873938a3f2 100644
--- a/telephony/java/android/telephony/mbms/FileInfo.java
+++ b/telephony/java/android/telephony/mbms/FileInfo.java
@@ -31,29 +31,22 @@ public class FileInfo implements Parcelable {
* This is used internally but is also one of the few pieces of data about the content that is
* exposed and may be needed for disambiguation by the application.
*/
- final Uri uri;
+ private final Uri uri;
/**
* The mime type of the content.
*/
- final String mimeType;
+ private final String mimeType;
/**
* The size of the file in bytes.
*/
- final long size;
+ private final long size;
/**
* The MD5 hash of the file.
*/
- final byte md5Hash[];
-
- /**
- * Gets the parent service for this file.
- */
- public FileServiceInfo getFileServiceInfo() {
- return null;
- }
+ private final byte md5Hash[];
public static final Parcelable.Creator<FileInfo> CREATOR =
new Parcelable.Creator<FileInfo>() {
@@ -68,6 +61,13 @@ public class FileInfo implements Parcelable {
}
};
+ public FileInfo(Uri uri, String mimeType, long size, byte[] md5Hash) {
+ this.uri = uri;
+ this.mimeType = mimeType;
+ this.size = size;
+ this.md5Hash = md5Hash;
+ }
+
private FileInfo(Parcel in) {
uri = in.readParcelable(null);
mimeType = in.readString();
@@ -90,4 +90,20 @@ public class FileInfo implements Parcelable {
public int describeContents() {
return 0;
}
+
+ public Uri getUri() {
+ return uri;
+ }
+
+ public String getMimeType() {
+ return mimeType;
+ }
+
+ public long getSize() {
+ return size;
+ }
+
+ public byte[] getMd5Hash() {
+ return md5Hash;
+ }
}
diff --git a/telephony/java/android/telephony/mbms/FileServiceInfo.java b/telephony/java/android/telephony/mbms/FileServiceInfo.java
index 8e890fd580e3..6646dc8a56df 100644
--- a/telephony/java/android/telephony/mbms/FileServiceInfo.java
+++ b/telephony/java/android/telephony/mbms/FileServiceInfo.java
@@ -30,13 +30,13 @@ import java.util.Map;
* @hide
*/
public class FileServiceInfo extends ServiceInfo implements Parcelable {
- public List<FileInfo> files;
+ private final List<FileInfo> files;
public FileServiceInfo(Map<Locale, String> newNames, String newClassName,
List<Locale> newLocales, String newServiceId, Date start, Date end,
List<FileInfo> newFiles) {
super(newNames, newClassName, newLocales, newServiceId, start, end);
- files = new ArrayList(newFiles);
+ files = new ArrayList<>(newFiles);
}
public static final Parcelable.Creator<FileServiceInfo> CREATOR =
@@ -68,4 +68,9 @@ public class FileServiceInfo extends ServiceInfo implements Parcelable {
public int describeContents() {
return 0;
}
+
+ public List<FileInfo> getFiles() {
+ return files;
+ }
+
}
diff --git a/telephony/java/android/telephony/mbms/IMbmsDownloadManagerCallback.aidl b/telephony/java/android/telephony/mbms/IMbmsDownloadManagerCallback.aidl
index 03227d0d9f5a..ac2f20243896 100755
--- a/telephony/java/android/telephony/mbms/IMbmsDownloadManagerCallback.aidl
+++ b/telephony/java/android/telephony/mbms/IMbmsDownloadManagerCallback.aidl
@@ -24,19 +24,11 @@ import java.util.List;
* The interface the clients top-level file download listener will satisfy.
* @hide
*/
-interface IMbmsDownloadManagerCallback
+oneway interface IMbmsDownloadManagerCallback
{
void error(int errorCode, String message);
- /**
- * Called to indicate published File Services have changed.
- *
- * This will only be called after the application has requested
- * a list of file services and specified a service class list
- * of interest AND the results of a subsequent getFileServices
- * call with the same service class list would
- * return different
- * results.
- */
void fileServicesUpdated(in List<FileServiceInfo> services);
+
+ void middlewareReady();
}
diff --git a/telephony/java/android/telephony/mbms/IMbmsStreamingManagerCallback.aidl b/telephony/java/android/telephony/mbms/IMbmsStreamingManagerCallback.aidl
index cbf0fca461f0..8116a7f0b7c4 100755
--- a/telephony/java/android/telephony/mbms/IMbmsStreamingManagerCallback.aidl
+++ b/telephony/java/android/telephony/mbms/IMbmsStreamingManagerCallback.aidl
@@ -24,30 +24,13 @@ import java.util.List;
* The interface the clients top-level streaming listener will satisfy.
* @hide
*/
-interface IMbmsStreamingManagerCallback
+oneway interface IMbmsStreamingManagerCallback
{
void error(int errorCode, String message);
- /**
- * Called to indicate published Streaming Services have changed.
- *
- * This will only be called after the application has requested
- * a list of streaming services and specified a service class list
- * of interest AND the results of a subsequent getStreamServices
- * call with the same service class list would
- * return different
- * results.
- */
void streamingServicesUpdated(in List<StreamingServiceInfo> services);
- /**
- * Called to indicate the active Streaming Services have changed.
- *
- * This will be caused whenever a new service starts streaming or whenever
- * MbmsStreamServiceManager.getActiveStreamingServices is called.
- *
- * @param services a list of StreamingServiceInfos. May be empty if
- * there are no active StreamingServices
- */
void activeStreamingServicesUpdated(in List<StreamingServiceInfo> services);
+
+ void middlewareReady();
}
diff --git a/telephony/java/android/telephony/mbms/MbmsDownloadManagerCallback.java b/telephony/java/android/telephony/mbms/MbmsDownloadManagerCallback.java
index 16fafe415b0f..5b22199bea1c 100644
--- a/telephony/java/android/telephony/mbms/MbmsDownloadManagerCallback.java
+++ b/telephony/java/android/telephony/mbms/MbmsDownloadManagerCallback.java
@@ -48,4 +48,17 @@ public class MbmsDownloadManagerCallback extends IMbmsDownloadManagerCallback.St
public void fileServicesUpdated(List<FileServiceInfo> services) {
// default implementation empty
}
+
+ /**
+ * Called to indicate that the middleware has been initialized and is ready.
+ *
+ * Before this method is called, calling any method on an instance of
+ * {@link android.telephony.MbmsDownloadManager} will result in an {@link MbmsException}
+ * being thrown with error code {@link MbmsException#ERROR_MIDDLEWARE_NOT_BOUND}
+ * or {@link MbmsException#ERROR_MIDDLEWARE_NOT_YET_READY}
+ */
+ @Override
+ public void middlewareReady() {
+ // default implementation empty
+ }
}
diff --git a/telephony/java/android/telephony/mbms/MbmsDownloadReceiver.java b/telephony/java/android/telephony/mbms/MbmsDownloadReceiver.java
index c01ddaedbd88..b51c367deb36 100644
--- a/telephony/java/android/telephony/mbms/MbmsDownloadReceiver.java
+++ b/telephony/java/android/telephony/mbms/MbmsDownloadReceiver.java
@@ -31,8 +31,8 @@ import java.io.File;
import java.io.IOException;
import java.util.ArrayList;
import java.util.Iterator;
-import java.util.LinkedList;
import java.util.List;
+import java.util.Objects;
import java.util.UUID;
/**
@@ -54,6 +54,11 @@ public class MbmsDownloadReceiver extends BroadcastReceiver {
setResultCode(1 /* TODO: define error constants */);
return;
}
+ if (!Objects.equals(intent.getStringExtra(MbmsDownloadManager.EXTRA_TEMP_FILE_ROOT),
+ MbmsTempFileProvider.getEmbmsTempFileDir(context).getPath())) {
+ setResultCode(1 /* TODO: define error constants */);
+ return;
+ }
if (MbmsDownloadManager.ACTION_DOWNLOAD_RESULT_INTERNAL.equals(intent.getAction())) {
moveDownloadedFile(context, intent);
@@ -74,7 +79,11 @@ public class MbmsDownloadReceiver extends BroadcastReceiver {
Log.w(LOG_TAG, "Download result did not include the associated request. Ignoring.");
return false;
}
- if (!intent.hasExtra(MbmsDownloadManager.EXTRA_INFO)) {
+ if (!intent.hasExtra(MbmsDownloadManager.EXTRA_TEMP_FILE_ROOT)) {
+ Log.w(LOG_TAG, "Download result did not include the temp file root. Ignoring.");
+ return false;
+ }
+ if (!intent.hasExtra(MbmsDownloadManager.EXTRA_FILE_INFO)) {
Log.w(LOG_TAG, "Download result did not include the associated file info. " +
"Ignoring.");
return false;
@@ -90,6 +99,10 @@ public class MbmsDownloadReceiver extends BroadcastReceiver {
Log.w(LOG_TAG, "Temp file request not include the associated request. Ignoring.");
return false;
}
+ if (!intent.hasExtra(MbmsDownloadManager.EXTRA_TEMP_FILE_ROOT)) {
+ Log.w(LOG_TAG, "Download result did not include the temp file root. Ignoring.");
+ return false;
+ }
return true;
}
@@ -121,12 +134,15 @@ public class MbmsDownloadReceiver extends BroadcastReceiver {
}
String relativePath = calculateDestinationFileRelativePath(request,
- (FileInfo) intent.getParcelableExtra(MbmsDownloadManager.EXTRA_INFO));
+ (FileInfo) intent.getParcelableExtra(MbmsDownloadManager.EXTRA_FILE_INFO));
- if (!moveTempFile(finalTempFile, destinationUri, relativePath)) {
+ Uri finalFileLocation = moveTempFile(finalTempFile, destinationUri, relativePath);
+ if (finalFileLocation == null) {
Log.w(LOG_TAG, "Failed to move temp file to final destination");
+ // TODO: how do we notify the app of this?
setResultCode(1);
}
+ intentForApp.putExtra(MbmsDownloadManager.EXTRA_COMPLETED_FILE_URI, finalFileLocation);
context.sendBroadcast(intentForApp);
setResultCode(0);
@@ -226,7 +242,6 @@ public class MbmsDownloadReceiver extends BroadcastReceiver {
return null;
}
-
private ArrayList<UriPathPair> generateUrisForPausedFiles(Context context,
DownloadRequest request, List<Uri> pausedFiles) {
if (pausedFiles == null) {
@@ -258,13 +273,15 @@ public class MbmsDownloadReceiver extends BroadcastReceiver {
private static String calculateDestinationFileRelativePath(DownloadRequest request,
FileInfo info) {
- // TODO: determine whether this is actually the path determination scheme we want to use
- List<String> filePathComponents = info.uri.getPathSegments();
+ List<String> filePathComponents = info.getUri().getPathSegments();
List<String> requestPathComponents = request.getSourceUri().getPathSegments();
Iterator<String> filePathIter = filePathComponents.iterator();
Iterator<String> requestPathIter = requestPathComponents.iterator();
- LinkedList<String> relativePathComponents = new LinkedList<>();
+ StringBuilder pathBuilder = new StringBuilder();
+ // Iterate through the segments of the carrier's URI to the file, along with the segments
+ // of the source URI specified in the download request. The relative path is calculated
+ // as the tail of the file's URI that does not match the path segments in the source URI.
while (filePathIter.hasNext()) {
String currFilePathComponent = filePathIter.next();
if (requestPathIter.hasNext()) {
@@ -273,28 +290,44 @@ public class MbmsDownloadReceiver extends BroadcastReceiver {
continue;
}
}
- relativePathComponents.add(currFilePathComponent);
+ pathBuilder.append(currFilePathComponent);
+ pathBuilder.append('/');
}
- return String.join("/", relativePathComponents);
+ // remove the trailing slash
+ if (pathBuilder.length() > 0) {
+ pathBuilder.deleteCharAt(pathBuilder.length() - 1);
+ }
+ return pathBuilder.toString();
}
- private static boolean moveTempFile(Uri fromPath, Uri toPath, String relativePath) {
+ /*
+ * Moves a tempfile located at fromPath to a new location at toPath. If
+ * toPath is a directory, the destination file will be located at relativePath
+ * underneath toPath.
+ */
+ private static Uri moveTempFile(Uri fromPath, Uri toPath, String relativePath) {
if (!ContentResolver.SCHEME_FILE.equals(fromPath.getScheme())) {
Log.w(LOG_TAG, "Moving source uri " + fromPath+ " does not have a file scheme");
- return false;
+ return null;
}
if (!ContentResolver.SCHEME_FILE.equals(toPath.getScheme())) {
Log.w(LOG_TAG, "Moving destination uri " + toPath + " does not have a file scheme");
- return false;
+ return null;
}
File fromFile = new File(fromPath.getSchemeSpecificPart());
- File toFile = new File(toPath.getSchemeSpecificPart(), relativePath);
+ File toFile = new File(toPath.getSchemeSpecificPart());
+ if (toFile.isDirectory()) {
+ toFile = new File(toFile, relativePath);
+ }
toFile.getParentFile().mkdirs();
- // TODO: This may not work if the two files are on different filesystems. Should we
- // enforce that the temp file storage and the permanent storage are both in the same fs?
- return fromFile.renameTo(toFile);
+ // TODO: This will not work if the two files are on different filesystems. Add manual
+ // copy later.
+ if (fromFile.renameTo(toFile)) {
+ return Uri.fromFile(toFile);
+ }
+ return null;
}
private static boolean verifyTempFilePath(Context context, DownloadRequest request,
@@ -323,8 +356,7 @@ public class MbmsDownloadReceiver extends BroadcastReceiver {
* Returns a File linked to the directory used to store temp files for this request
*/
private static File getEmbmsTempFileDirForRequest(Context context, DownloadRequest request) {
- File embmsTempFileDir = MbmsTempFileProvider.getEmbmsTempFileDir(
- context, getFileProviderAuthority(context));
+ File embmsTempFileDir = MbmsTempFileProvider.getEmbmsTempFileDir(context);
// TODO: better naming scheme for temp file dirs
String tempFileDirName = String.valueOf(request.getFileServiceInfo().getServiceId());
diff --git a/telephony/java/android/telephony/mbms/MbmsException.java b/telephony/java/android/telephony/mbms/MbmsException.java
index 6b905921dba0..8260b728a52f 100644
--- a/telephony/java/android/telephony/mbms/MbmsException.java
+++ b/telephony/java/android/telephony/mbms/MbmsException.java
@@ -22,7 +22,7 @@ public class MbmsException extends Exception {
public static final int ERROR_NO_SERVICE_INSTALLED = 1;
public static final int ERROR_MULTIPLE_SERVICES_INSTALLED = 2;
public static final int ERROR_BIND_TIMEOUT_OR_FAILURE = 3;
- public static final int ERROR_UNABLE_TO_INITIALIZE = 4;
+ public static final int ERROR_MIDDLEWARE_NOT_YET_READY = 4;
public static final int ERROR_ALREADY_INITIALIZED = 5;
public static final int ERROR_CONCURRENT_SERVICE_LIMIT_REACHED = 6;
public static final int ERROR_MIDDLEWARE_NOT_BOUND = 7;
@@ -36,6 +36,7 @@ public class MbmsException extends Exception {
public static final int ERROR_NOT_CONNECTED_TO_HOME_CARRIER_LTE = 15;
public static final int ERROR_UNABLE_TO_READ_SIM = 16;
public static final int ERROR_CARRIER_CHANGE_NOT_ALLOWED = 17;
+ public static final int ERROR_CANNOT_CHANGE_TEMP_FILE_ROOT = 18;
private final int mErrorCode;
diff --git a/telephony/java/android/telephony/mbms/MbmsStreamingManagerCallback.java b/telephony/java/android/telephony/mbms/MbmsStreamingManagerCallback.java
index b3bc8146275e..27d9878a1966 100644
--- a/telephony/java/android/telephony/mbms/MbmsStreamingManagerCallback.java
+++ b/telephony/java/android/telephony/mbms/MbmsStreamingManagerCallback.java
@@ -61,4 +61,17 @@ public class MbmsStreamingManagerCallback extends IMbmsStreamingManagerCallback.
public void activeStreamingServicesUpdated(List<StreamingServiceInfo> services) {
// default implementation empty
}
+
+ /**
+ * Called to indicate that the middleware has been initialized and is ready.
+ *
+ * Before this method is called, calling any method on an instance of
+ * {@link android.telephony.MbmsStreamingManager} will result in an {@link MbmsException}
+ * being thrown with error code {@link MbmsException#ERROR_MIDDLEWARE_NOT_BOUND}
+ * or {@link MbmsException#ERROR_MIDDLEWARE_NOT_YET_READY}
+ */
+ @Override
+ public void middlewareReady() {
+ // default implementation empty
+ }
}
diff --git a/telephony/java/android/telephony/mbms/MbmsTempFileProvider.java b/telephony/java/android/telephony/mbms/MbmsTempFileProvider.java
index 9842581cdc02..c4d033bf2886 100644
--- a/telephony/java/android/telephony/mbms/MbmsTempFileProvider.java
+++ b/telephony/java/android/telephony/mbms/MbmsTempFileProvider.java
@@ -22,6 +22,7 @@ import android.content.ContentProvider;
import android.content.ContentResolver;
import android.content.ContentValues;
import android.content.Context;
+import android.content.SharedPreferences;
import android.content.pm.PackageManager;
import android.content.pm.ProviderInfo;
import android.database.Cursor;
@@ -32,14 +33,15 @@ import android.os.ParcelFileDescriptor;
import java.io.File;
import java.io.FileNotFoundException;
import java.io.IOException;
+import java.util.Objects;
/**
* @hide
*/
public class MbmsTempFileProvider extends ContentProvider {
- public static final String META_DATA_USE_EXTERNAL_STORAGE = "use-external-storage";
- public static final String META_DATA_TEMP_FILE_DIRECTORY = "temp-file-path";
public static final String DEFAULT_TOP_LEVEL_TEMP_DIRECTORY = "androidMbmsTempFileRoot";
+ public static final String TEMP_FILE_ROOT_PREF_FILE_NAME = "MbmsTempFileRootPrefs";
+ public static final String TEMP_FILE_ROOT_PREF_NAME = "mbms_temp_file_root";
private String mAuthority;
private Context mContext;
@@ -114,7 +116,7 @@ public class MbmsTempFileProvider extends ContentProvider {
// Make sure the temp file is contained in the temp file directory as configured in the
// manifest
- File tempFileDir = getEmbmsTempFileDir(context, authority);
+ File tempFileDir = getEmbmsTempFileDir(context);
if (!MbmsUtils.isContainedIn(tempFileDir, file)) {
throw new IllegalArgumentException("File " + file + " is not contained in the temp " +
"file directory, which is " + tempFileDir);
@@ -147,13 +149,17 @@ public class MbmsTempFileProvider extends ContentProvider {
if (!ContentResolver.SCHEME_CONTENT.equals(uri.getScheme())) {
throw new IllegalArgumentException("Uri must have scheme content");
}
+ if (!Objects.equals(authority, uri.getAuthority())) {
+ throw new IllegalArgumentException("Uri does not have a matching authority: " +
+ authority + ", " + uri.getAuthority());
+ }
String relPath = Uri.decode(uri.getEncodedPath());
File file;
File tempFileDir;
try {
- tempFileDir = getEmbmsTempFileDir(context, authority).getCanonicalFile();
+ tempFileDir = getEmbmsTempFileDir(context).getCanonicalFile();
file = new File(tempFileDir, relPath).getCanonicalFile();
} catch (IOException e) {
throw new FileNotFoundException("Could not resolve paths");
@@ -169,25 +175,18 @@ public class MbmsTempFileProvider extends ContentProvider {
/**
* Returns a File for the directory used to store temp files for this app
*/
- public static File getEmbmsTempFileDir(Context context, String authority) {
- Bundle metadata = getMetadata(context, authority);
- File parentDirectory;
- if (metadata.getBoolean(META_DATA_USE_EXTERNAL_STORAGE, false)) {
- parentDirectory = context.getExternalFilesDir(null);
- } else {
- parentDirectory = context.getFilesDir();
- }
-
- String tmpFilePath = metadata.getString(META_DATA_TEMP_FILE_DIRECTORY);
- if (tmpFilePath == null) {
- tmpFilePath = DEFAULT_TOP_LEVEL_TEMP_DIRECTORY;
+ public static File getEmbmsTempFileDir(Context context) {
+ SharedPreferences prefs = context.getSharedPreferences(TEMP_FILE_ROOT_PREF_FILE_NAME, 0);
+ String storedTempFileRoot = prefs.getString(TEMP_FILE_ROOT_PREF_NAME, null);
+ try {
+ if (storedTempFileRoot != null) {
+ return new File(storedTempFileRoot).getCanonicalFile();
+ } else {
+ return new File(context.getFilesDir(), DEFAULT_TOP_LEVEL_TEMP_DIRECTORY)
+ .getCanonicalFile();
+ }
+ } catch (IOException e) {
+ throw new RuntimeException("Unable to canonicalize temp file root path " + e);
}
- return new File(parentDirectory, tmpFilePath);
- }
-
- private static Bundle getMetadata(Context context, String authority) {
- final ProviderInfo info = context.getPackageManager()
- .resolveContentProvider(authority, PackageManager.GET_META_DATA);
- return info.metaData;
}
}
diff --git a/telephony/java/android/telephony/mbms/MbmsUtils.java b/telephony/java/android/telephony/mbms/MbmsUtils.java
index de308053df56..7d4727563eee 100644
--- a/telephony/java/android/telephony/mbms/MbmsUtils.java
+++ b/telephony/java/android/telephony/mbms/MbmsUtils.java
@@ -19,6 +19,7 @@ package android.telephony.mbms;
import android.content.ComponentName;
import android.content.Context;
import android.content.Intent;
+import android.content.ServiceConnection;
import android.content.pm.*;
import android.content.pm.ServiceInfo;
import android.telephony.MbmsDownloadManager;
@@ -46,20 +47,6 @@ public class MbmsUtils {
}
}
- public static void waitOnLatchWithTimeout(CountDownLatch l, long timeoutMs) {
- long endTime = System.currentTimeMillis() + timeoutMs;
- while (System.currentTimeMillis() < endTime) {
- try {
- l.await(timeoutMs, TimeUnit.MILLISECONDS);
- } catch (InterruptedException e) {
- // keep waiting
- }
- if (l.getCount() <= 0) {
- return;
- }
- }
- }
-
public static ComponentName toComponentName(ComponentInfo ci) {
return new ComponentName(ci.packageName, ci.name);
}
@@ -83,4 +70,19 @@ public class MbmsUtils {
}
return downloadServices.get(0).serviceInfo;
}
+
+ public static void startBinding(Context context, String serviceAction,
+ ServiceConnection serviceConnection) throws MbmsException {
+ Intent bindIntent = new Intent();
+ ServiceInfo mbmsServiceInfo =
+ MbmsUtils.getMiddlewareServiceInfo(context, serviceAction);
+
+ if (mbmsServiceInfo == null) {
+ throw new MbmsException(MbmsException.ERROR_NO_SERVICE_INSTALLED);
+ }
+
+ bindIntent.setComponent(MbmsUtils.toComponentName(mbmsServiceInfo));
+
+ context.bindService(bindIntent, serviceConnection, Context.BIND_AUTO_CREATE);
+ }
}
diff --git a/telephony/java/android/telephony/mbms/vendor/IMbmsDownloadService.aidl b/telephony/java/android/telephony/mbms/vendor/IMbmsDownloadService.aidl
index 6c2b8167d519..ff7d233bbf2c 100755
--- a/telephony/java/android/telephony/mbms/vendor/IMbmsDownloadService.aidl
+++ b/telephony/java/android/telephony/mbms/vendor/IMbmsDownloadService.aidl
@@ -47,6 +47,7 @@ interface IMbmsDownloadService
*/
int getFileServices(String appName, int subId, in List<String> serviceClasses);
+ int setTempFileRootDirectory(String appName, int subId, String rootDirectoryPath);
/**
* should move the params into a DownloadRequest parcelable
*/
diff --git a/telephony/java/android/telephony/mbms/vendor/MbmsDownloadServiceBase.java b/telephony/java/android/telephony/mbms/vendor/MbmsDownloadServiceBase.java
index 505aeae15355..9577dd2e3d78 100644
--- a/telephony/java/android/telephony/mbms/vendor/MbmsDownloadServiceBase.java
+++ b/telephony/java/android/telephony/mbms/vendor/MbmsDownloadServiceBase.java
@@ -32,13 +32,19 @@ import java.util.List;
*/
public class MbmsDownloadServiceBase extends IMbmsDownloadService.Stub {
@Override
- public void initialize(String appName, int subId, IMbmsDownloadManagerCallback listener)
+ public void initialize(String appName, int subscriptionId,
+ IMbmsDownloadManagerCallback listener) throws RemoteException {
+ }
+
+ @Override
+ public int getFileServices(String appName, int subscriptionId, List<String> serviceClasses)
throws RemoteException {
+ return 0;
}
@Override
- public int getFileServices(String appName, int subId, List<String> serviceClasses) throws
- RemoteException {
+ public int setTempFileRootDirectory(String appName, int subscriptionId,
+ String rootDirectoryPath) throws RemoteException {
return 0;
}
diff --git a/tests/net/java/com/android/server/connectivity/tethering/OffloadControllerTest.java b/tests/net/java/com/android/server/connectivity/tethering/OffloadControllerTest.java
index 14284d661744..1ddaf66d2274 100644
--- a/tests/net/java/com/android/server/connectivity/tethering/OffloadControllerTest.java
+++ b/tests/net/java/com/android/server/connectivity/tethering/OffloadControllerTest.java
@@ -62,7 +62,8 @@ public class OffloadControllerTest {
@Mock private OffloadHardwareInterface mHardware;
@Mock private ApplicationInfo mApplicationInfo;
@Mock private Context mContext;
- final ArgumentCaptor<ArrayList> mStringArrayCaptor = ArgumentCaptor.forClass(ArrayList.class);
+ private final ArgumentCaptor<ArrayList> mStringArrayCaptor =
+ ArgumentCaptor.forClass(ArrayList.class);
private MockContentResolver mContentResolver;
@Before public void setUp() throws Exception {
diff --git a/tests/net/java/com/android/server/connectivity/tethering/TetherInterfaceStateMachineTest.java b/tests/net/java/com/android/server/connectivity/tethering/TetherInterfaceStateMachineTest.java
index ce419a5070d2..db5373ac34b2 100644
--- a/tests/net/java/com/android/server/connectivity/tethering/TetherInterfaceStateMachineTest.java
+++ b/tests/net/java/com/android/server/connectivity/tethering/TetherInterfaceStateMachineTest.java
@@ -16,6 +16,8 @@
package com.android.server.connectivity.tethering;
+import static org.junit.Assert.assertFalse;
+import static org.junit.Assert.assertTrue;
import static org.mockito.Matchers.any;
import static org.mockito.Matchers.anyString;
import static org.mockito.Matchers.eq;
@@ -40,17 +42,23 @@ import static com.android.server.connectivity.tethering.IControlsTethering.STATE
import android.net.ConnectivityManager;
import android.net.INetworkStatsService;
import android.net.InterfaceConfiguration;
+import android.net.LinkAddress;
import android.net.LinkProperties;
+import android.net.RouteInfo;
import android.net.util.SharedLog;
import android.os.INetworkManagementService;
import android.os.RemoteException;
import android.os.test.TestLooper;
import android.support.test.filters.SmallTest;
import android.support.test.runner.AndroidJUnit4;
+import android.text.TextUtils;
+
+import java.net.Inet4Address;
import org.junit.Before;
import org.junit.Test;
import org.junit.runner.RunWith;
+import org.mockito.ArgumentCaptor;
import org.mockito.InOrder;
import org.mockito.Mock;
import org.mockito.MockitoAnnotations;
@@ -69,6 +77,8 @@ public class TetherInterfaceStateMachineTest {
@Mock private SharedLog mSharedLog;
private final TestLooper mLooper = new TestLooper();
+ private final ArgumentCaptor<LinkProperties> mLinkPropertiesCaptor =
+ ArgumentCaptor.forClass(LinkProperties.class);
private TetherInterfaceStateMachine mTestedSm;
private void initStateMachine(int interfaceType) throws Exception {
@@ -77,7 +87,7 @@ public class TetherInterfaceStateMachineTest {
mNMService, mStatsService, mTetherHelper);
mTestedSm.start();
// Starting the state machine always puts us in a consistent state and notifies
- // the test of the world that we've changed from an unknown to available state.
+ // the rest of the world that we've changed from an unknown to available state.
mLooper.dispatchAll();
reset(mNMService, mStatsService, mTetherHelper);
when(mNMService.getInterfaceConfig(IFACE_NAME)).thenReturn(mInterfaceConfiguration);
@@ -181,7 +191,8 @@ public class TetherInterfaceStateMachineTest {
inOrder.verify(mTetherHelper).updateInterfaceState(
mTestedSm, STATE_TETHERED, TETHER_ERROR_NO_ERROR);
inOrder.verify(mTetherHelper).updateLinkProperties(
- eq(mTestedSm), any(LinkProperties.class));
+ eq(mTestedSm), mLinkPropertiesCaptor.capture());
+ assertIPv4AddressAndDirectlyConnectedRoute(mLinkPropertiesCaptor.getValue());
verifyNoMoreInteractions(mNMService, mStatsService, mTetherHelper);
}
@@ -281,7 +292,8 @@ public class TetherInterfaceStateMachineTest {
usbTeardownOrder.verify(mTetherHelper).updateInterfaceState(
mTestedSm, STATE_UNAVAILABLE, TETHER_ERROR_NO_ERROR);
usbTeardownOrder.verify(mTetherHelper).updateLinkProperties(
- eq(mTestedSm), any(LinkProperties.class));
+ eq(mTestedSm), mLinkPropertiesCaptor.capture());
+ assertNoAddressesNorRoutes(mLinkPropertiesCaptor.getValue());
}
}
@@ -298,7 +310,8 @@ public class TetherInterfaceStateMachineTest {
usbTeardownOrder.verify(mTetherHelper).updateInterfaceState(
mTestedSm, STATE_AVAILABLE, TETHER_ERROR_TETHER_IFACE_ERROR);
usbTeardownOrder.verify(mTetherHelper).updateLinkProperties(
- eq(mTestedSm), any(LinkProperties.class));
+ eq(mTestedSm), mLinkPropertiesCaptor.capture());
+ assertNoAddressesNorRoutes(mLinkPropertiesCaptor.getValue());
}
@Test
@@ -313,7 +326,8 @@ public class TetherInterfaceStateMachineTest {
usbTeardownOrder.verify(mTetherHelper).updateInterfaceState(
mTestedSm, STATE_AVAILABLE, TETHER_ERROR_ENABLE_NAT_ERROR);
usbTeardownOrder.verify(mTetherHelper).updateLinkProperties(
- eq(mTestedSm), any(LinkProperties.class));
+ eq(mTestedSm), mLinkPropertiesCaptor.capture());
+ assertNoAddressesNorRoutes(mLinkPropertiesCaptor.getValue());
}
@Test
@@ -360,4 +374,28 @@ public class TetherInterfaceStateMachineTest {
upstreamIface);
mLooper.dispatchAll();
}
+
+ private void assertIPv4AddressAndDirectlyConnectedRoute(LinkProperties lp) {
+ // Find the first IPv4 LinkAddress.
+ LinkAddress addr4 = null;
+ for (LinkAddress addr : lp.getLinkAddresses()) {
+ if (!(addr.getAddress() instanceof Inet4Address)) continue;
+ addr4 = addr;
+ break;
+ }
+ assertTrue("missing IPv4 address", addr4 != null);
+
+ // Assert the presence of the associated directly connected route.
+ final RouteInfo directlyConnected = new RouteInfo(addr4, null, lp.getInterfaceName());
+ assertTrue("missing directly connected route: '" + directlyConnected.toString() + "'",
+ lp.getRoutes().contains(directlyConnected));
+ }
+
+ private void assertNoAddressesNorRoutes(LinkProperties lp) {
+ assertTrue(lp.getLinkAddresses().isEmpty());
+ assertTrue(lp.getRoutes().isEmpty());
+ // We also check that interface name is non-empty, because we should
+ // never see an empty interface name in any LinkProperties update.
+ assertFalse(TextUtils.isEmpty(lp.getInterfaceName()));
+ }
}