Merge "Remove dead code 1." into lmp-dev
diff --git a/core/java/android/hardware/soundtrigger/IRecognitionStatusCallback.aidl b/core/java/android/hardware/soundtrigger/IRecognitionStatusCallback.aidl
index 038d7ef..0bf4f25 100644
--- a/core/java/android/hardware/soundtrigger/IRecognitionStatusCallback.aidl
+++ b/core/java/android/hardware/soundtrigger/IRecognitionStatusCallback.aidl
@@ -16,6 +16,8 @@
package android.hardware.soundtrigger;
+import android.hardware.soundtrigger.SoundTrigger;
+
/**
* @hide
*/
@@ -27,7 +29,7 @@
* TODO: See if the data being passed in works well, if not use shared memory.
* This *MUST* not exceed 100K.
*/
- void onDetected(in byte[] data);
+ void onDetected(in SoundTrigger.RecognitionEvent recognitionEvent);
/**
* Called when the detection for the associated keyphrase stops.
*/
diff --git a/core/java/android/hardware/soundtrigger/SoundTrigger.aidl b/core/java/android/hardware/soundtrigger/SoundTrigger.aidl
index 837691a..9adc6bc 100644
--- a/core/java/android/hardware/soundtrigger/SoundTrigger.aidl
+++ b/core/java/android/hardware/soundtrigger/SoundTrigger.aidl
@@ -21,4 +21,5 @@
parcelable SoundTrigger.KeyphraseRecognitionExtra;
parcelable SoundTrigger.KeyphraseSoundModel;
parcelable SoundTrigger.ModuleProperties;
-parcelable SoundTrigger.RecognitionConfig;
\ No newline at end of file
+parcelable SoundTrigger.RecognitionConfig;
+parcelable SoundTrigger.RecognitionEvent;
\ No newline at end of file
diff --git a/core/java/android/hardware/soundtrigger/SoundTrigger.java b/core/java/android/hardware/soundtrigger/SoundTrigger.java
index 9a5cd9b..3e84368 100644
--- a/core/java/android/hardware/soundtrigger/SoundTrigger.java
+++ b/core/java/android/hardware/soundtrigger/SoundTrigger.java
@@ -347,12 +347,7 @@
private static KeyphraseSoundModel fromParcel(Parcel in) {
UUID uuid = UUID.fromString(in.readString());
- byte[] data = null;
- int dataLength = in.readInt();
- if (dataLength >= 0) {
- data = new byte[dataLength];
- in.readByteArray(data);
- }
+ byte[] data = in.readBlob();
Keyphrase[] keyphrases = in.createTypedArray(Keyphrase.CREATOR);
return new KeyphraseSoundModel(uuid, data, keyphrases);
}
@@ -365,12 +360,7 @@
@Override
public void writeToParcel(Parcel dest, int flags) {
dest.writeString(uuid.toString());
- if (data != null) {
- dest.writeInt(data.length);
- dest.writeByteArray(data);
- } else {
- dest.writeInt(-1);
- }
+ dest.writeBlob(data);
dest.writeTypedArray(keyphrases, 0);
}
@@ -406,7 +396,7 @@
* {@link StatusListener#onRecognition(RecognitionEvent)}
* callback upon recognition success or failure.
*/
- public static class RecognitionEvent {
+ public static class RecognitionEvent implements Parcelable {
/** Recognition status e.g {@link #RECOGNITION_STATUS_SUCCESS} */
public final int status;
/** Sound Model corresponding to this event callback */
@@ -425,7 +415,7 @@
* typically during enrollment. */
public final byte[] data;
- RecognitionEvent(int status, int soundModelHandle, boolean captureAvailable,
+ public RecognitionEvent(int status, int soundModelHandle, boolean captureAvailable,
int captureSession, int captureDelayMs, int capturePreambleMs, byte[] data) {
this.status = status;
this.soundModelHandle = soundModelHandle;
@@ -435,6 +425,85 @@
this.capturePreambleMs = capturePreambleMs;
this.data = data;
}
+
+ public static final Parcelable.Creator<RecognitionEvent> CREATOR
+ = new Parcelable.Creator<RecognitionEvent>() {
+ public RecognitionEvent createFromParcel(Parcel in) {
+ return RecognitionEvent.fromParcel(in);
+ }
+
+ public RecognitionEvent[] newArray(int size) {
+ return new RecognitionEvent[size];
+ }
+ };
+
+ private static RecognitionEvent fromParcel(Parcel in) {
+ int status = in.readInt();
+ int soundModelHandle = in.readInt();
+ boolean captureAvailable = in.readByte() == 1;
+ int captureSession = in.readInt();
+ int captureDelayMs = in.readInt();
+ int capturePreambleMs = in.readInt();
+ byte[] data = in.readBlob();
+ return new RecognitionEvent(status, soundModelHandle, captureAvailable, captureSession,
+ captureDelayMs, capturePreambleMs, data);
+ }
+
+ @Override
+ public int describeContents() {
+ return 0;
+ }
+
+ @Override
+ public void writeToParcel(Parcel dest, int flags) {
+ dest.writeInt(status);
+ dest.writeInt(soundModelHandle);
+ dest.writeByte((byte) (captureAvailable ? 1 : 0));
+ dest.writeInt(captureSession);
+ dest.writeInt(captureDelayMs);
+ dest.writeInt(capturePreambleMs);
+ dest.writeBlob(data);
+ }
+
+ @Override
+ public int hashCode() {
+ final int prime = 31;
+ int result = 1;
+ result = prime * result + (captureAvailable ? 1231 : 1237);
+ result = prime * result + captureDelayMs;
+ result = prime * result + capturePreambleMs;
+ result = prime * result + captureSession;
+ result = prime * result + Arrays.hashCode(data);
+ result = prime * result + soundModelHandle;
+ result = prime * result + status;
+ return result;
+ }
+
+ @Override
+ public boolean equals(Object obj) {
+ if (this == obj)
+ return true;
+ if (obj == null)
+ return false;
+ if (getClass() != obj.getClass())
+ return false;
+ RecognitionEvent other = (RecognitionEvent) obj;
+ if (captureAvailable != other.captureAvailable)
+ return false;
+ if (captureDelayMs != other.captureDelayMs)
+ return false;
+ if (capturePreambleMs != other.capturePreambleMs)
+ return false;
+ if (captureSession != other.captureSession)
+ return false;
+ if (!Arrays.equals(data, other.data))
+ return false;
+ if (soundModelHandle != other.soundModelHandle)
+ return false;
+ if (status != other.status)
+ return false;
+ return true;
+ }
}
/**
@@ -475,12 +544,7 @@
boolean captureRequested = in.readByte() == 1;
KeyphraseRecognitionExtra[] keyphrases =
in.createTypedArray(KeyphraseRecognitionExtra.CREATOR);
- byte[] data = null;
- int dataLength = in.readInt();
- if (dataLength >= 0) {
- data = new byte[dataLength];
- in.readByteArray(data);
- }
+ byte[] data = in.readBlob();
return new RecognitionConfig(captureRequested, keyphrases, data);
}
@@ -488,12 +552,7 @@
public void writeToParcel(Parcel dest, int flags) {
dest.writeByte((byte) (captureRequested ? 1 : 0));
dest.writeTypedArray(keyphrases, 0);
- if (data != null) {
- dest.writeInt(data.length);
- dest.writeByteArray(data);
- } else {
- dest.writeInt(-1);
- }
+ dest.writeBlob(data);
}
@Override
diff --git a/core/java/android/net/NetworkAgent.java b/core/java/android/net/NetworkAgent.java
index a365af0..22da90e 100644
--- a/core/java/android/net/NetworkAgent.java
+++ b/core/java/android/net/NetworkAgent.java
@@ -106,6 +106,20 @@
*/
public static final int EVENT_UID_RANGES_REMOVED = BASE + 6;
+ /**
+ * Sent by the NetworkAgent to ConnectivityService to block all routes for a certain address
+ * family (AF_INET or AF_INET6) on this Network. For VPNs only.
+ * obj = Integer representing the family (AF_INET or AF_INET6)
+ */
+ public static final int EVENT_BLOCK_ADDRESS_FAMILY = BASE + 7;
+
+ /**
+ * Sent by the NetworkAgent to ConnectivityService to unblock routes for a certain address
+ * family (AF_INET or AF_INET6) on this Network. For VPNs only.
+ * obj = Integer representing the family (AF_INET or AF_INET6)
+ */
+ public static final int EVENT_UNBLOCK_ADDRESS_FAMILY = BASE + 8;
+
public NetworkAgent(Looper looper, Context context, String logTag, NetworkInfo ni,
NetworkCapabilities nc, LinkProperties lp, int score) {
this(looper, context, logTag, ni, nc, lp, score, null);
@@ -229,6 +243,21 @@
}
/**
+ * Called by the VPN code when it wants to block an address family from being routed, typically
+ * because the VPN network doesn't support that family.
+ */
+ public void blockAddressFamily(int family) {
+ queueOrSendMessage(EVENT_BLOCK_ADDRESS_FAMILY, family);
+ }
+
+ /**
+ * Called by the VPN code when it wants to unblock an address family from being routed.
+ */
+ public void unblockAddressFamily(int family) {
+ queueOrSendMessage(EVENT_UNBLOCK_ADDRESS_FAMILY, family);
+ }
+
+ /**
* Called when ConnectivityService has indicated they no longer want this network.
* The parent factory should (previously) have received indication of the change
* as well, either canceling NetworkRequests or altering their score such that this
diff --git a/core/java/android/net/VpnService.java b/core/java/android/net/VpnService.java
index 4b07e3f..9b66997 100644
--- a/core/java/android/net/VpnService.java
+++ b/core/java/android/net/VpnService.java
@@ -373,6 +373,7 @@
throw new IllegalArgumentException("Bad address");
}
mAddresses.add(new LinkAddress(address, prefixLength));
+ mConfig.updateAllowedFamilies(address);
return this;
}
@@ -413,6 +414,7 @@
}
}
mRoutes.add(new RouteInfo(new LinkAddress(address, prefixLength), null));
+ mConfig.updateAllowedFamilies(address);
return this;
}
@@ -497,7 +499,14 @@
* @return this {@link Builder} object to facilitate chaining of method calls.
*/
public Builder allowFamily(int family) {
- // TODO
+ if (family == AF_INET) {
+ mConfig.allowIPv4 = true;
+ } else if (family == AF_INET6) {
+ mConfig.allowIPv6 = true;
+ } else {
+ throw new IllegalArgumentException(family + " is neither " + AF_INET + " nor " +
+ AF_INET6);
+ }
return this;
}
diff --git a/core/java/android/os/INetworkManagementService.aidl b/core/java/android/os/INetworkManagementService.aidl
index c467d4a..d6ee8e0 100644
--- a/core/java/android/os/INetworkManagementService.aidl
+++ b/core/java/android/os/INetworkManagementService.aidl
@@ -404,4 +404,7 @@
void addInterfaceToLocalNetwork(String iface, in List<RouteInfo> routes);
void removeInterfaceFromLocalNetwork(String iface);
+
+ void blockAddressFamily(int family, int netId, String iface);
+ void unblockAddressFamily(int family, int netId, String iface);
}
diff --git a/core/java/android/os/Parcel.java b/core/java/android/os/Parcel.java
index 59cd97c..a1c2aa1 100644
--- a/core/java/android/os/Parcel.java
+++ b/core/java/android/os/Parcel.java
@@ -485,6 +485,7 @@
* growing {@link #dataCapacity} if needed.
* @param b Bytes to place into the parcel.
* {@hide}
+ * {@SystemApi}
*/
public final void writeBlob(byte[] b) {
nativeWriteBlob(mNativePtr, b, 0, (b != null) ? b.length : 0);
@@ -1714,6 +1715,7 @@
/**
* Read a blob of data from the parcel and return it as a byte array.
* {@hide}
+ * {@SystemApi}
*/
public final byte[] readBlob() {
return nativeReadBlob(mNativePtr);
diff --git a/core/java/android/service/voice/AlwaysOnHotwordDetector.java b/core/java/android/service/voice/AlwaysOnHotwordDetector.java
index d077a17..a8c08d55 100644
--- a/core/java/android/service/voice/AlwaysOnHotwordDetector.java
+++ b/core/java/android/service/voice/AlwaysOnHotwordDetector.java
@@ -27,6 +27,7 @@
import android.hardware.soundtrigger.SoundTrigger.KeyphraseSoundModel;
import android.hardware.soundtrigger.SoundTrigger.ModuleProperties;
import android.hardware.soundtrigger.SoundTrigger.RecognitionConfig;
+import android.hardware.soundtrigger.SoundTrigger.RecognitionEvent;
import android.os.AsyncTask;
import android.os.Handler;
import android.os.Message;
@@ -380,10 +381,10 @@
}
@Override
- public void onDetected(byte[] data) {
+ public void onDetected(RecognitionEvent recognitionEvent) {
Slog.i(TAG, "onDetected");
Message message = Message.obtain(mHandler, MSG_HOTWORD_DETECTED);
- message.obj = data;
+ message.obj = recognitionEvent.data;
message.sendToTarget();
}
diff --git a/core/java/com/android/internal/net/VpnConfig.java b/core/java/com/android/internal/net/VpnConfig.java
index dac59f9..0099269 100644
--- a/core/java/com/android/internal/net/VpnConfig.java
+++ b/core/java/com/android/internal/net/VpnConfig.java
@@ -27,6 +27,7 @@
import android.net.RouteInfo;
import android.net.LinkAddress;
+import java.net.Inet4Address;
import java.net.InetAddress;
import java.util.List;
import java.util.ArrayList;
@@ -75,6 +76,16 @@
public boolean legacy;
public boolean blocking;
public boolean allowBypass;
+ public boolean allowIPv4;
+ public boolean allowIPv6;
+
+ public void updateAllowedFamilies(InetAddress address) {
+ if (address instanceof Inet4Address) {
+ allowIPv4 = true;
+ } else {
+ allowIPv6 = true;
+ }
+ }
public void addLegacyRoutes(String routesStr) {
if (routesStr.trim().equals("")) {
@@ -87,6 +98,7 @@
RouteInfo info = new RouteInfo(new LinkAddress
(InetAddress.parseNumericAddress(split[0]), Integer.parseInt(split[1])), null);
this.routes.add(info);
+ updateAllowedFamilies(info.getDestination().getAddress());
}
}
@@ -101,6 +113,7 @@
LinkAddress addr = new LinkAddress(InetAddress.parseNumericAddress(split[0]),
Integer.parseInt(split[1]));
this.addresses.add(addr);
+ updateAllowedFamilies(addr.getAddress());
}
}
@@ -124,6 +137,8 @@
out.writeInt(legacy ? 1 : 0);
out.writeInt(blocking ? 1 : 0);
out.writeInt(allowBypass ? 1 : 0);
+ out.writeInt(allowIPv4 ? 1 : 0);
+ out.writeInt(allowIPv6 ? 1 : 0);
}
public static final Parcelable.Creator<VpnConfig> CREATOR =
@@ -144,6 +159,8 @@
config.legacy = in.readInt() != 0;
config.blocking = in.readInt() != 0;
config.allowBypass = in.readInt() != 0;
+ config.allowIPv4 = in.readInt() != 0;
+ config.allowIPv6 = in.readInt() != 0;
return config;
}
diff --git a/core/jni/android_os_Parcel.cpp b/core/jni/android_os_Parcel.cpp
index 3ba481e..44863cc 100644
--- a/core/jni/android_os_Parcel.cpp
+++ b/core/jni/android_os_Parcel.cpp
@@ -194,6 +194,14 @@
return;
}
+ if (data == NULL) {
+ const status_t err = parcel->writeInt32(-1);
+ if (err != NO_ERROR) {
+ signalExceptionForError(env, clazz, err);
+ }
+ return;
+ }
+
const status_t err = parcel->writeInt32(length);
if (err != NO_ERROR) {
signalExceptionForError(env, clazz, err);
diff --git a/graphics/java/android/graphics/ImageFormat.java b/graphics/java/android/graphics/ImageFormat.java
index ab4258d..3efb9c0 100644
--- a/graphics/java/android/graphics/ImageFormat.java
+++ b/graphics/java/android/graphics/ImageFormat.java
@@ -236,17 +236,17 @@
* Android 10-bit raw format
* </p>
* <p>
- * This is a single-plane, 10-bit per pixel, densely packed, unprocessed
- * format, usually representing raw Bayer-pattern images coming from an image
- * sensor.
+ * This is a single-plane, 10-bit per pixel, densely packed (in each row),
+ * unprocessed format, usually representing raw Bayer-pattern images coming
+ * from an image sensor.
* </p>
* <p>
- * In an image buffer with this format, starting from the first pixel, each
- * 4 consecutive pixels are packed into 5 bytes (40 bits). Each one of the
- * first 4 bytes contains the top 8 bits of each pixel, The fifth byte
- * contains the 2 least significant bits of the 4 pixels, the exact layout
- * data for each 4 consecutive pixels is illustrated below (Pi[j] stands for
- * the jth bit of the ith pixel):
+ * In an image buffer with this format, starting from the first pixel of
+ * each row, each 4 consecutive pixels are packed into 5 bytes (40 bits).
+ * Each one of the first 4 bytes contains the top 8 bits of each pixel, The
+ * fifth byte contains the 2 least significant bits of the 4 pixels, the
+ * exact layout data for each 4 consecutive pixels is illustrated below
+ * ({@code Pi[j]} stands for the jth bit of the ith pixel):
* </p>
* <table>
* <thead>
@@ -327,23 +327,26 @@
* </ul>
* </p>
*
- * <pre>
- * size = width * height * 10 / 8
- * </pre>
- * <p>
- * Since this is a densely packed format, the pixel and row stride are always
- * 0. The application must use the pixel data layout defined in above table
- * to access data.
- * </p>
+ * <pre>size = row stride * height</pre> where the row stride is in <em>bytes</em>,
+ * not pixels.
*
* <p>
+ * Since this is a densely packed format, the pixel stride is always 0. The
+ * application must use the pixel data layout defined in above table to
+ * access each row data. When row stride is equal to {@code width * (10 / 8)}, there
+ * will be no padding bytes at the end of each row, the entire image data is
+ * densely packed. When stride is larger than {@code width * (10 / 8)}, padding
+ * bytes will be present at the end of each row.
+ * </p>
+ * <p>
* For example, the {@link android.media.Image} object can provide data in
- * this format from a {@link android.hardware.camera2.CameraDevice} (if supported)
- * through a {@link android.media.ImageReader} object. The
+ * this format from a {@link android.hardware.camera2.CameraDevice} (if
+ * supported) through a {@link android.media.ImageReader} object. The
* {@link android.media.Image#getPlanes() Image#getPlanes()} will return a
- * single plane containing the pixel data. The pixel stride and row stride
- * are always 0 in {@link android.media.Image.Plane#getPixelStride()} and
- * {@link android.media.Image.Plane#getRowStride()} respectively.
+ * single plane containing the pixel data. The pixel stride is always 0 in
+ * {@link android.media.Image.Plane#getPixelStride()}, and the
+ * {@link android.media.Image.Plane#getRowStride()} describes the vertical
+ * neighboring pixel distance (in bytes) between adjacent rows.
* </p>
*
* @see android.media.Image
diff --git a/media/jni/android_media_ImageReader.cpp b/media/jni/android_media_ImageReader.cpp
index 35317e1..fa4439d 100644
--- a/media/jni/android_media_ImageReader.cpp
+++ b/media/jni/android_media_ImageReader.cpp
@@ -420,8 +420,11 @@
"Width is not multiple of 4 %d", buffer->width);
LOG_ALWAYS_FATAL_IF(buffer->height % 2,
"Height is not even %d", buffer->height);
+ LOG_ALWAYS_FATAL_IF(buffer->stride < (buffer->width * 10 / 8),
+ "stride (%d) should be at least %d",
+ buffer->stride, buffer->width * 10 / 8);
pData = buffer->data;
- dataSize = buffer->width * buffer->height * 10 / 8;
+ dataSize = buffer->stride * buffer->height;
break;
case HAL_PIXEL_FORMAT_RGBA_8888:
case HAL_PIXEL_FORMAT_RGBX_8888:
@@ -535,12 +538,15 @@
rowStride = (idx == 0) ? buffer->stride : ALIGN(buffer->stride / 2, 16);
break;
case HAL_PIXEL_FORMAT_BLOB:
- case HAL_PIXEL_FORMAT_RAW10:
// Blob is used for JPEG data, RAW10 is used for 10-bit raw data, they are
// single plane, row and pixel strides are 0.
ALOG_ASSERT(idx == 0, "Wrong index: %d", idx);
rowStride = 0;
break;
+ case HAL_PIXEL_FORMAT_RAW10:
+ ALOG_ASSERT(idx == 0, "Wrong index: %d", idx);
+ rowStride = buffer->stride;
+ break;
case HAL_PIXEL_FORMAT_Y8:
ALOG_ASSERT(idx == 0, "Wrong index: %d", idx);
LOG_ALWAYS_FATAL_IF(buffer->stride % 16,
diff --git a/services/core/java/com/android/server/ConnectivityService.java b/services/core/java/com/android/server/ConnectivityService.java
index 3d9307d..7349ebf 100644
--- a/services/core/java/com/android/server/ConnectivityService.java
+++ b/services/core/java/com/android/server/ConnectivityService.java
@@ -2580,7 +2580,9 @@
}
try {
mNetd.addVpnUidRanges(nai.network.netId, (UidRange[])msg.obj);
- } catch (RemoteException e) {
+ } catch (Exception e) {
+ // Never crash!
+ loge("Exception in addVpnUidRanges: " + e);
}
break;
}
@@ -2592,7 +2594,39 @@
}
try {
mNetd.removeVpnUidRanges(nai.network.netId, (UidRange[])msg.obj);
- } catch (RemoteException e) {
+ } catch (Exception e) {
+ // Never crash!
+ loge("Exception in removeVpnUidRanges: " + e);
+ }
+ break;
+ }
+ case NetworkAgent.EVENT_BLOCK_ADDRESS_FAMILY: {
+ NetworkAgentInfo nai = mNetworkAgentInfos.get(msg.replyTo);
+ if (nai == null) {
+ loge("EVENT_BLOCK_ADDRESS_FAMILY from unknown NetworkAgent");
+ break;
+ }
+ try {
+ mNetd.blockAddressFamily((Integer) msg.obj, nai.network.netId,
+ nai.linkProperties.getInterfaceName());
+ } catch (Exception e) {
+ // Never crash!
+ loge("Exception in blockAddressFamily: " + e);
+ }
+ break;
+ }
+ case NetworkAgent.EVENT_UNBLOCK_ADDRESS_FAMILY: {
+ NetworkAgentInfo nai = mNetworkAgentInfos.get(msg.replyTo);
+ if (nai == null) {
+ loge("EVENT_UNBLOCK_ADDRESS_FAMILY from unknown NetworkAgent");
+ break;
+ }
+ try {
+ mNetd.unblockAddressFamily((Integer) msg.obj, nai.network.netId,
+ nai.linkProperties.getInterfaceName());
+ } catch (Exception e) {
+ // Never crash!
+ loge("Exception in blockAddressFamily: " + e);
}
break;
}
diff --git a/services/core/java/com/android/server/NetworkManagementService.java b/services/core/java/com/android/server/NetworkManagementService.java
index 75a7878..362a745 100644
--- a/services/core/java/com/android/server/NetworkManagementService.java
+++ b/services/core/java/com/android/server/NetworkManagementService.java
@@ -25,6 +25,8 @@
import static android.net.NetworkStats.TAG_NONE;
import static android.net.NetworkStats.UID_ALL;
import static android.net.TrafficStats.UID_TETHERING;
+import static android.system.OsConstants.AF_INET;
+import static android.system.OsConstants.AF_INET6;
import static com.android.server.NetworkManagementService.NetdResponseCode.ClatdStatusResult;
import static com.android.server.NetworkManagementService.NetdResponseCode.InterfaceGetCfgResult;
import static com.android.server.NetworkManagementService.NetdResponseCode.InterfaceListResult;
@@ -85,6 +87,7 @@
import java.io.InputStreamReader;
import java.io.PrintWriter;
import java.net.Inet4Address;
+import java.net.Inet6Address;
import java.net.InetAddress;
import java.net.InterfaceAddress;
import java.net.NetworkInterface;
@@ -2102,4 +2105,36 @@
public void removeInterfaceFromLocalNetwork(String iface) {
modifyInterfaceInNetwork("remove", "local", iface);
}
+
+ @Override
+ public void blockAddressFamily(int family, int netId, String iface) {
+ modifyAddressFamily("add", family, netId, iface);
+ }
+
+ @Override
+ public void unblockAddressFamily(int family, int netId, String iface) {
+ modifyAddressFamily("remove", family, netId, iface);
+ }
+
+ private void modifyAddressFamily(String action, int family, int netId, String iface) {
+ mContext.enforceCallingOrSelfPermission(CONNECTIVITY_INTERNAL, TAG);
+
+ final Command cmd = new Command("network", "route", action, netId, iface);
+
+ if (family == AF_INET) {
+ cmd.appendArg(Inet4Address.ANY.getHostAddress() + "/0");
+ } else if (family == AF_INET6) {
+ cmd.appendArg(Inet6Address.ANY.getHostAddress() + "/0");
+ } else {
+ throw new IllegalStateException(family + " is neither " + AF_INET + " nor " + AF_INET6);
+ }
+
+ cmd.appendArg("unreachable");
+
+ try {
+ mConnector.execute(cmd);
+ } catch (NativeDaemonConnectorException e) {
+ throw e.rethrowAsParcelableException();
+ }
+ }
}
diff --git a/services/core/java/com/android/server/connectivity/Vpn.java b/services/core/java/com/android/server/connectivity/Vpn.java
index 0305424..0a8ca6a 100644
--- a/services/core/java/com/android/server/connectivity/Vpn.java
+++ b/services/core/java/com/android/server/connectivity/Vpn.java
@@ -17,6 +17,8 @@
package com.android.server.connectivity;
import static android.Manifest.permission.BIND_VPN_SERVICE;
+import static android.system.OsConstants.AF_INET;
+import static android.system.OsConstants.AF_INET6;
import android.app.AppGlobals;
import android.app.Notification;
@@ -107,6 +109,8 @@
private String mPackage;
private int mOwnerUID;
private String mInterface;
+ private boolean mAllowIPv4;
+ private boolean mAllowIPv6;
private Connection mConnection;
private LegacyVpnRunner mLegacyVpnRunner;
private PendingIntent mStatusIntent;
@@ -307,6 +311,7 @@
private void agentConnect() {
LinkProperties lp = new LinkProperties();
lp.setInterfaceName(mInterface);
+
boolean hasDefaultRoute = false;
for (RouteInfo route : mConfig.routes) {
lp.addRoute(route);
@@ -317,11 +322,19 @@
} else {
mNetworkCapabilities.removeCapability(NetworkCapabilities.NET_CAPABILITY_INTERNET);
}
+
if (mConfig.dnsServers != null) {
for (String dnsServer : mConfig.dnsServers) {
- lp.addDnsServer(InetAddress.parseNumericAddress(dnsServer));
+ InetAddress address = InetAddress.parseNumericAddress(dnsServer);
+ lp.addDnsServer(address);
+ if (address instanceof Inet4Address) {
+ mAllowIPv4 = true;
+ } else {
+ mAllowIPv6 = true;
+ }
}
}
+
// Concatenate search domains into a string.
StringBuilder buffer = new StringBuilder();
if (mConfig.searchDomains != null) {
@@ -330,12 +343,13 @@
}
}
lp.setDomains(buffer.toString().trim());
+
mNetworkInfo.setIsAvailable(true);
mNetworkInfo.setDetailedState(DetailedState.CONNECTED, null, null);
+
NetworkMisc networkMisc = new NetworkMisc();
- if (mConfig.allowBypass) {
- networkMisc.allowBypass = true;
- }
+ networkMisc.allowBypass = mConfig.allowBypass;
+
long token = Binder.clearCallingIdentity();
try {
mNetworkAgent = new NetworkAgent(mLooper, mContext, NETWORKTYPE,
@@ -347,6 +361,14 @@
} finally {
Binder.restoreCallingIdentity(token);
}
+
+ if (!mAllowIPv4) {
+ mNetworkAgent.blockAddressFamily(AF_INET);
+ }
+ if (!mAllowIPv6) {
+ mNetworkAgent.blockAddressFamily(AF_INET6);
+ }
+
addVpnUserLocked(mUserId);
// If we are owner assign all Restricted Users to this VPN
if (mUserId == UserHandle.USER_OWNER) {
@@ -432,6 +454,8 @@
NetworkAgent oldNetworkAgent = mNetworkAgent;
mNetworkAgent = null;
List<UidRange> oldUsers = mVpnUsers;
+ boolean oldAllowIPv4 = mAllowIPv4;
+ boolean oldAllowIPv6 = mAllowIPv6;
// Configure the interface. Abort if any of these steps fails.
ParcelFileDescriptor tun = ParcelFileDescriptor.adoptFd(jniCreate(config.mtu));
@@ -464,6 +488,9 @@
// Set up forwarding and DNS rules.
mVpnUsers = new ArrayList<UidRange>();
+ mAllowIPv4 = mConfig.allowIPv4;
+ mAllowIPv6 = mConfig.allowIPv6;
+
agentConnect();
if (oldConnection != null) {
@@ -492,6 +519,8 @@
mVpnUsers = oldUsers;
mNetworkAgent = oldNetworkAgent;
mInterface = oldInterface;
+ mAllowIPv4 = oldAllowIPv4;
+ mAllowIPv6 = oldAllowIPv6;
throw e;
}
Log.i(TAG, "Established by " + config.user + " on " + mInterface);
@@ -1175,6 +1204,8 @@
// Now INetworkManagementEventObserver is watching our back.
mInterface = mConfig.interfaze;
mVpnUsers = new ArrayList<UidRange>();
+ mAllowIPv4 = mConfig.allowIPv4;
+ mAllowIPv6 = mConfig.allowIPv6;
agentConnect();
diff --git a/services/voiceinteraction/java/com/android/server/voiceinteraction/SoundTriggerHelper.java b/services/voiceinteraction/java/com/android/server/voiceinteraction/SoundTriggerHelper.java
index 86dca79..1e4a518 100644
--- a/services/voiceinteraction/java/com/android/server/voiceinteraction/SoundTriggerHelper.java
+++ b/services/voiceinteraction/java/com/android/server/voiceinteraction/SoundTriggerHelper.java
@@ -238,20 +238,13 @@
switch (event.status) {
case SoundTrigger.RECOGNITION_STATUS_SUCCESS:
- // TODO: Pass the captured audio back.
try {
- listener.onDetected(null);
+ listener.onDetected(event);
} catch (RemoteException e) {
Slog.w(TAG, "RemoteException in onDetected");
}
break;
- case SoundTrigger.RECOGNITION_STATUS_ABORT:
- try {
- listener.onDetectionStopped();
- } catch (RemoteException e) {
- Slog.w(TAG, "RemoteException in onDetectionStopped");
- }
- break;
+ case SoundTrigger.RECOGNITION_STATUS_ABORT: // fall-through
case SoundTrigger.RECOGNITION_STATUS_FAILURE:
try {
listener.onDetectionStopped();
diff --git a/tests/SoundTriggerTests/Android.mk b/tests/SoundTriggerTests/Android.mk
new file mode 100644
index 0000000..407a9d7
--- /dev/null
+++ b/tests/SoundTriggerTests/Android.mk
@@ -0,0 +1,27 @@
+#
+# Copyright (C) 2014 The Android Open Source Project
+#
+# Licensed under the Apache License, Version 2.0 (the "License");
+# you may not use this file except in compliance with the License.
+# You may obtain a copy of the License at
+#
+# http://www.apache.org/licenses/LICENSE-2.0
+#
+# Unless required by applicable law or agreed to in writing, software
+# distributed under the License is distributed on an "AS IS" BASIS,
+# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+# See the License for the specific language governing permissions and
+# limitations under the License.
+#
+LOCAL_PATH:= $(call my-dir)
+include $(CLEAR_VARS)
+
+LOCAL_MODULE_TAGS := tests
+
+LOCAL_SRC_FILES := $(call all-subdir-java-files)
+
+LOCAL_JAVA_LIBRARIES := android.test.runner
+
+LOCAL_PACKAGE_NAME := SoundTriggerTests
+
+include $(BUILD_PACKAGE)
diff --git a/tests/SoundTriggerTests/AndroidManifest.xml b/tests/SoundTriggerTests/AndroidManifest.xml
new file mode 100644
index 0000000..5e5a108
--- /dev/null
+++ b/tests/SoundTriggerTests/AndroidManifest.xml
@@ -0,0 +1,25 @@
+<?xml version="1.0" encoding="utf-8"?>
+<!-- Copyright (C) 2014 The Android Open Source Project
+
+ Licensed under the Apache License, Version 2.0 (the "License");
+ you may not use this file except in compliance with the License.
+ You may obtain a copy of the License at
+
+ http://www.apache.org/licenses/LICENSE-2.0
+
+ Unless required by applicable law or agreed to in writing, software
+ distributed under the License is distributed on an "AS IS" BASIS,
+ WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ See the License for the specific language governing permissions and
+ limitations under the License.
+-->
+
+<manifest xmlns:android="http://schemas.android.com/apk/res/android" package="android.hardware.soundtrigger">
+ <application>
+ <uses-library android:name="android.test.runner" />
+ </application>
+
+ <instrumentation android:name="android.test.InstrumentationTestRunner"
+ android:targetPackage="android.hardware.soundtrigger"
+ android:label="Tests for android.hardware.soundtrigger" />
+</manifest>
diff --git a/tests/SoundTriggerTests/src/android/hardware/soundtrigger/SoundTriggerTest.java b/tests/SoundTriggerTests/src/android/hardware/soundtrigger/SoundTriggerTest.java
new file mode 100644
index 0000000..5d32c66
--- /dev/null
+++ b/tests/SoundTriggerTests/src/android/hardware/soundtrigger/SoundTriggerTest.java
@@ -0,0 +1,256 @@
+/*
+ * Copyright (C) 2014 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package android.hardware.soundtrigger;
+
+import android.hardware.soundtrigger.SoundTrigger;
+import android.hardware.soundtrigger.SoundTrigger.Keyphrase;
+import android.hardware.soundtrigger.SoundTrigger.KeyphraseSoundModel;
+import android.hardware.soundtrigger.SoundTrigger.RecognitionEvent;
+import android.os.Parcel;
+import android.test.InstrumentationTestCase;
+import android.test.suitebuilder.annotation.LargeTest;
+import android.test.suitebuilder.annotation.SmallTest;
+
+import java.util.Arrays;
+import java.util.Random;
+import java.util.UUID;
+
+public class SoundTriggerTest extends InstrumentationTestCase {
+ private Random mRandom = new Random();
+
+ @SmallTest
+ public void testKeyphraseParcelUnparcel_noUsers() throws Exception {
+ Keyphrase keyphrase = new Keyphrase(1, 0, "en-US", "hello", null);
+
+ // Write to a parcel
+ Parcel parcel = Parcel.obtain();
+ keyphrase.writeToParcel(parcel, 0);
+
+ // Read from it
+ parcel.setDataPosition(0);
+ Keyphrase unparceled = Keyphrase.CREATOR.createFromParcel(parcel);
+
+ // Verify that they are the same
+ assertEquals(keyphrase.id, unparceled.id);
+ assertNull(unparceled.users);
+ assertEquals(keyphrase.locale, unparceled.locale);
+ assertEquals(keyphrase.text, unparceled.text);
+ }
+
+ @SmallTest
+ public void testKeyphraseParcelUnparcel_zeroUsers() throws Exception {
+ Keyphrase keyphrase = new Keyphrase(1, 0, "en-US", "hello", new int[0]);
+
+ // Write to a parcel
+ Parcel parcel = Parcel.obtain();
+ keyphrase.writeToParcel(parcel, 0);
+
+ // Read from it
+ parcel.setDataPosition(0);
+ Keyphrase unparceled = Keyphrase.CREATOR.createFromParcel(parcel);
+
+ // Verify that they are the same
+ assertEquals(keyphrase.id, unparceled.id);
+ assertTrue(Arrays.equals(keyphrase.users, unparceled.users));
+ assertEquals(keyphrase.locale, unparceled.locale);
+ assertEquals(keyphrase.text, unparceled.text);
+ }
+
+ @SmallTest
+ public void testKeyphraseParcelUnparcel_pos() throws Exception {
+ Keyphrase keyphrase = new Keyphrase(1, 0, "en-US", "hello", new int[] {1, 2, 3, 4, 5});
+
+ // Write to a parcel
+ Parcel parcel = Parcel.obtain();
+ keyphrase.writeToParcel(parcel, 0);
+
+ // Read from it
+ parcel.setDataPosition(0);
+ Keyphrase unparceled = Keyphrase.CREATOR.createFromParcel(parcel);
+
+ // Verify that they are the same
+ assertEquals(keyphrase.id, unparceled.id);
+ assertTrue(Arrays.equals(keyphrase.users, unparceled.users));
+ assertEquals(keyphrase.locale, unparceled.locale);
+ assertEquals(keyphrase.text, unparceled.text);
+ }
+
+ @SmallTest
+ public void testKeyphraseSoundModelParcelUnparcel_noData() throws Exception {
+ Keyphrase[] keyphrases = new Keyphrase[2];
+ keyphrases[0] = new Keyphrase(1, 0, "en-US", "hello", new int[] {0});
+ keyphrases[1] = new Keyphrase(2, 0, "fr-FR", "there", new int[] {1, 2});
+ KeyphraseSoundModel ksm = new KeyphraseSoundModel(UUID.randomUUID(), null, keyphrases);
+
+ // Write to a parcel
+ Parcel parcel = Parcel.obtain();
+ ksm.writeToParcel(parcel, 0);
+
+ // Read from it
+ parcel.setDataPosition(0);
+ KeyphraseSoundModel unparceled = KeyphraseSoundModel.CREATOR.createFromParcel(parcel);
+
+ // Verify that they are the same
+ assertEquals(ksm.uuid, unparceled.uuid);
+ assertNull(unparceled.data);
+ assertEquals(ksm.type, unparceled.type);
+ assertTrue(Arrays.equals(keyphrases, unparceled.keyphrases));
+ }
+
+ @SmallTest
+ public void testKeyphraseSoundModelParcelUnparcel_zeroData() throws Exception {
+ Keyphrase[] keyphrases = new Keyphrase[2];
+ keyphrases[0] = new Keyphrase(1, 0, "en-US", "hello", new int[] {0});
+ keyphrases[1] = new Keyphrase(2, 0, "fr-FR", "there", new int[] {1, 2});
+ KeyphraseSoundModel ksm = new KeyphraseSoundModel(UUID.randomUUID(), new byte[0],
+ keyphrases);
+
+ // Write to a parcel
+ Parcel parcel = Parcel.obtain();
+ ksm.writeToParcel(parcel, 0);
+
+ // Read from it
+ parcel.setDataPosition(0);
+ KeyphraseSoundModel unparceled = KeyphraseSoundModel.CREATOR.createFromParcel(parcel);
+
+ // Verify that they are the same
+ assertEquals(ksm.uuid, unparceled.uuid);
+ assertEquals(ksm.type, unparceled.type);
+ assertTrue(Arrays.equals(ksm.keyphrases, unparceled.keyphrases));
+ assertTrue(Arrays.equals(ksm.data, unparceled.data));
+ }
+
+ @SmallTest
+ public void testKeyphraseSoundModelParcelUnparcel_noKeyphrases() throws Exception {
+ byte[] data = new byte[10];
+ mRandom.nextBytes(data);
+ KeyphraseSoundModel ksm = new KeyphraseSoundModel(UUID.randomUUID(), data, null);
+
+ // Write to a parcel
+ Parcel parcel = Parcel.obtain();
+ ksm.writeToParcel(parcel, 0);
+
+ // Read from it
+ parcel.setDataPosition(0);
+ KeyphraseSoundModel unparceled = KeyphraseSoundModel.CREATOR.createFromParcel(parcel);
+
+ // Verify that they are the same
+ assertEquals(ksm.uuid, unparceled.uuid);
+ assertEquals(ksm.type, unparceled.type);
+ assertNull(unparceled.keyphrases);
+ assertTrue(Arrays.equals(ksm.data, unparceled.data));
+ }
+
+ @SmallTest
+ public void testKeyphraseSoundModelParcelUnparcel_zeroKeyphrases() throws Exception {
+ byte[] data = new byte[10];
+ mRandom.nextBytes(data);
+ KeyphraseSoundModel ksm = new KeyphraseSoundModel(UUID.randomUUID(), data,
+ new Keyphrase[0]);
+
+ // Write to a parcel
+ Parcel parcel = Parcel.obtain();
+ ksm.writeToParcel(parcel, 0);
+
+ // Read from it
+ parcel.setDataPosition(0);
+ KeyphraseSoundModel unparceled = KeyphraseSoundModel.CREATOR.createFromParcel(parcel);
+
+ // Verify that they are the same
+ assertEquals(ksm.uuid, unparceled.uuid);
+ assertEquals(ksm.type, unparceled.type);
+ assertTrue(Arrays.equals(ksm.keyphrases, unparceled.keyphrases));
+ assertTrue(Arrays.equals(ksm.data, unparceled.data));
+ }
+
+ @LargeTest
+ public void testKeyphraseSoundModelParcelUnparcel_largeData() throws Exception {
+ Keyphrase[] keyphrases = new Keyphrase[2];
+ keyphrases[0] = new Keyphrase(1, 0, "en-US", "hello", new int[] {0});
+ keyphrases[1] = new Keyphrase(2, 0, "fr-FR", "there", new int[] {1, 2});
+ byte[] data = new byte[200 * 1024];
+ mRandom.nextBytes(data);
+ KeyphraseSoundModel ksm = new KeyphraseSoundModel(UUID.randomUUID(), data, keyphrases);
+
+ // Write to a parcel
+ Parcel parcel = Parcel.obtain();
+ ksm.writeToParcel(parcel, 0);
+
+ // Read from it
+ parcel.setDataPosition(0);
+ KeyphraseSoundModel unparceled = KeyphraseSoundModel.CREATOR.createFromParcel(parcel);
+
+ // Verify that they are the same
+ assertEquals(ksm.uuid, unparceled.uuid);
+ assertEquals(ksm.type, unparceled.type);
+ assertTrue(Arrays.equals(ksm.data, unparceled.data));
+ assertTrue(Arrays.equals(ksm.keyphrases, unparceled.keyphrases));
+ }
+
+ @SmallTest
+ public void testRecognitionEventParcelUnparcel_noData() throws Exception {
+ RecognitionEvent re = new RecognitionEvent(SoundTrigger.RECOGNITION_STATUS_SUCCESS, 1,
+ true, 2, 3, 4, null);
+
+ // Write to a parcel
+ Parcel parcel = Parcel.obtain();
+ re.writeToParcel(parcel, 0);
+
+ // Read from it
+ parcel.setDataPosition(0);
+ RecognitionEvent unparceled = RecognitionEvent.CREATOR.createFromParcel(parcel);
+
+ // Verify that they are the same
+ assertEquals(re, unparceled);
+ }
+
+ @SmallTest
+ public void testRecognitionEventParcelUnparcel_zeroData() throws Exception {
+ RecognitionEvent re = new RecognitionEvent(SoundTrigger.RECOGNITION_STATUS_FAILURE, 1,
+ true, 2, 3, 4, new byte[1]);
+
+ // Write to a parcel
+ Parcel parcel = Parcel.obtain();
+ re.writeToParcel(parcel, 0);
+
+ // Read from it
+ parcel.setDataPosition(0);
+ RecognitionEvent unparceled = RecognitionEvent.CREATOR.createFromParcel(parcel);
+
+ // Verify that they are the same
+ assertEquals(re, unparceled);
+ }
+
+ @SmallTest
+ public void testRecognitionEventParcelUnparcel_largeData() throws Exception {
+ byte[] data = new byte[200 * 1024];
+ mRandom.nextBytes(data);
+ RecognitionEvent re = new RecognitionEvent(SoundTrigger.RECOGNITION_STATUS_ABORT, 1,
+ false, 2, 3, 4, data);
+
+ // Write to a parcel
+ Parcel parcel = Parcel.obtain();
+ re.writeToParcel(parcel, 0);
+
+ // Read from it
+ parcel.setDataPosition(0);
+ RecognitionEvent unparceled = RecognitionEvent.CREATOR.createFromParcel(parcel);
+
+ // Verify that they are the same
+ assertEquals(re, unparceled);
+ }
+}