Merge "When a recording session is stopped, no outstanding input video frames exist for the output buffers returned from OMX component to SF."
diff --git a/api/current.txt b/api/current.txt
index 51c9348..d490e29 100644
--- a/api/current.txt
+++ b/api/current.txt
@@ -696,8 +696,10 @@
field public static final int packageNames = 16843651; // 0x1010383
field public static final int padding = 16842965; // 0x10100d5
field public static final int paddingBottom = 16842969; // 0x10100d9
+ field public static final int paddingEnd = 16843673; // 0x1010399
field public static final int paddingLeft = 16842966; // 0x10100d6
field public static final int paddingRight = 16842968; // 0x10100d8
+ field public static final int paddingStart = 16843672; // 0x1010398
field public static final int paddingTop = 16842967; // 0x10100d7
field public static final int panelBackground = 16842846; // 0x101005e
field public static final int panelColorBackground = 16842849; // 0x1010061
@@ -16663,10 +16665,25 @@
method public static android.renderscript.Element F32_3(android.renderscript.RenderScript);
method public static android.renderscript.Element F32_4(android.renderscript.RenderScript);
method public static android.renderscript.Element F64(android.renderscript.RenderScript);
+ method public static android.renderscript.Element F64_2(android.renderscript.RenderScript);
+ method public static android.renderscript.Element F64_3(android.renderscript.RenderScript);
+ method public static android.renderscript.Element F64_4(android.renderscript.RenderScript);
method public static android.renderscript.Element I16(android.renderscript.RenderScript);
+ method public static android.renderscript.Element I16_2(android.renderscript.RenderScript);
+ method public static android.renderscript.Element I16_3(android.renderscript.RenderScript);
+ method public static android.renderscript.Element I16_4(android.renderscript.RenderScript);
method public static android.renderscript.Element I32(android.renderscript.RenderScript);
+ method public static android.renderscript.Element I32_2(android.renderscript.RenderScript);
+ method public static android.renderscript.Element I32_3(android.renderscript.RenderScript);
+ method public static android.renderscript.Element I32_4(android.renderscript.RenderScript);
method public static android.renderscript.Element I64(android.renderscript.RenderScript);
+ method public static android.renderscript.Element I64_2(android.renderscript.RenderScript);
+ method public static android.renderscript.Element I64_3(android.renderscript.RenderScript);
+ method public static android.renderscript.Element I64_4(android.renderscript.RenderScript);
method public static android.renderscript.Element I8(android.renderscript.RenderScript);
+ method public static android.renderscript.Element I8_2(android.renderscript.RenderScript);
+ method public static android.renderscript.Element I8_3(android.renderscript.RenderScript);
+ method public static android.renderscript.Element I8_4(android.renderscript.RenderScript);
method public static android.renderscript.Element MATRIX4X4(android.renderscript.RenderScript);
method public static android.renderscript.Element MATRIX_2X2(android.renderscript.RenderScript);
method public static android.renderscript.Element MATRIX_3X3(android.renderscript.RenderScript);
@@ -16685,9 +16702,20 @@
method public static android.renderscript.Element SCRIPT(android.renderscript.RenderScript);
method public static android.renderscript.Element TYPE(android.renderscript.RenderScript);
method public static android.renderscript.Element U16(android.renderscript.RenderScript);
+ method public static android.renderscript.Element U16_2(android.renderscript.RenderScript);
+ method public static android.renderscript.Element U16_3(android.renderscript.RenderScript);
+ method public static android.renderscript.Element U16_4(android.renderscript.RenderScript);
method public static android.renderscript.Element U32(android.renderscript.RenderScript);
+ method public static android.renderscript.Element U32_2(android.renderscript.RenderScript);
+ method public static android.renderscript.Element U32_3(android.renderscript.RenderScript);
+ method public static android.renderscript.Element U32_4(android.renderscript.RenderScript);
method public static android.renderscript.Element U64(android.renderscript.RenderScript);
+ method public static android.renderscript.Element U64_2(android.renderscript.RenderScript);
+ method public static android.renderscript.Element U64_3(android.renderscript.RenderScript);
+ method public static android.renderscript.Element U64_4(android.renderscript.RenderScript);
method public static android.renderscript.Element U8(android.renderscript.RenderScript);
+ method public static android.renderscript.Element U8_2(android.renderscript.RenderScript);
+ method public static android.renderscript.Element U8_3(android.renderscript.RenderScript);
method public static android.renderscript.Element U8_4(android.renderscript.RenderScript);
method public static android.renderscript.Element createPixel(android.renderscript.RenderScript, android.renderscript.Element.DataType, android.renderscript.Element.DataKind);
method public static android.renderscript.Element createVector(android.renderscript.RenderScript, android.renderscript.Element.DataType, int);
diff --git a/core/java/android/app/DownloadManager.java b/core/java/android/app/DownloadManager.java
index 28559cc..ad8d41f 100644
--- a/core/java/android/app/DownloadManager.java
+++ b/core/java/android/app/DownloadManager.java
@@ -226,6 +226,14 @@
public final static int ERROR_FILE_ALREADY_EXISTS = 1009;
/**
+ * Value of {@link #COLUMN_REASON} when the download has failed because of
+ * {@link NetworkPolicyManager} controls on the requesting application.
+ *
+ * @hide
+ */
+ public final static int ERROR_BLOCKED = 1010;
+
+ /**
* Value of {@link #COLUMN_REASON} when the download is paused because some network error
* occurred and the download manager is waiting before retrying the request.
*/
@@ -249,14 +257,6 @@
public final static int PAUSED_UNKNOWN = 4;
/**
- * Value of {@link #COLUMN_REASON} when the download has been paused because
- * of {@link NetworkPolicyManager} controls on the requesting application.
- *
- * @hide
- */
- public final static int PAUSED_BY_POLICY = 5;
-
- /**
* Broadcast intent action sent by the download manager when a download completes.
*/
public final static String ACTION_DOWNLOAD_COMPLETE = "android.intent.action.DOWNLOAD_COMPLETE";
@@ -804,7 +804,6 @@
parts.add(statusClause("=", Downloads.Impl.STATUS_WAITING_TO_RETRY));
parts.add(statusClause("=", Downloads.Impl.STATUS_WAITING_FOR_NETWORK));
parts.add(statusClause("=", Downloads.Impl.STATUS_QUEUED_FOR_WIFI));
- parts.add(statusClause("=", Downloads.Impl.STATUS_PAUSED_BY_POLICY));
}
if ((mStatusFlags & STATUS_SUCCESSFUL) != 0) {
parts.add(statusClause("=", Downloads.Impl.STATUS_SUCCESS));
@@ -1275,9 +1274,6 @@
case Downloads.Impl.STATUS_QUEUED_FOR_WIFI:
return PAUSED_QUEUED_FOR_WIFI;
- case Downloads.Impl.STATUS_PAUSED_BY_POLICY:
- return PAUSED_BY_POLICY;
-
default:
return PAUSED_UNKNOWN;
}
@@ -1316,6 +1312,9 @@
case Downloads.Impl.STATUS_FILE_ALREADY_EXISTS_ERROR:
return ERROR_FILE_ALREADY_EXISTS;
+ case Downloads.Impl.STATUS_BLOCKED:
+ return ERROR_BLOCKED;
+
default:
return ERROR_UNKNOWN;
}
@@ -1333,7 +1332,6 @@
case Downloads.Impl.STATUS_WAITING_TO_RETRY:
case Downloads.Impl.STATUS_WAITING_FOR_NETWORK:
case Downloads.Impl.STATUS_QUEUED_FOR_WIFI:
- case Downloads.Impl.STATUS_PAUSED_BY_POLICY:
return STATUS_PAUSED;
case Downloads.Impl.STATUS_SUCCESS:
diff --git a/core/java/android/hardware/usb/IUsbManager.aidl b/core/java/android/hardware/usb/IUsbManager.aidl
index 5df2343..2b9c082 100644
--- a/core/java/android/hardware/usb/IUsbManager.aidl
+++ b/core/java/android/hardware/usb/IUsbManager.aidl
@@ -81,4 +81,13 @@
/* Clears default preferences and permissions for the package */
void clearDefaults(String packageName);
+
+ /* Sets the current primary USB function. */
+ void setPrimaryFunction(String functions);
+
+ /* Sets the default primary USB function. */
+ void setDefaultFunction(String functions);
+
+ /* Sets the file path for USB mass storage backing file. */
+ void setMassStorageBackingFile(String path);
}
diff --git a/core/java/android/hardware/usb/UsbManager.java b/core/java/android/hardware/usb/UsbManager.java
index 5994c98..a828a23 100644
--- a/core/java/android/hardware/usb/UsbManager.java
+++ b/core/java/android/hardware/usb/UsbManager.java
@@ -22,12 +22,9 @@
import android.os.Bundle;
import android.os.ParcelFileDescriptor;
import android.os.RemoteException;
+import android.os.SystemProperties;
import android.util.Log;
-import java.io.File;
-import java.io.FileInputStream;
-import java.io.FileOutputStream;
-import java.io.IOException;
import java.util.HashMap;
/**
@@ -50,7 +47,7 @@
* This is a sticky broadcast for clients that includes USB connected/disconnected state,
* <ul>
* <li> {@link #USB_CONNECTED} boolean indicating whether USB is connected or disconnected.
- * <li> {@link #USB_CONFIGURATION} integer containing current USB configuration
+ * <li> {@link #USB_CONFIGURED} boolean indicating whether USB is configured.
* currently zero if not configured, one for configured.
* <li> {@link #USB_FUNCTION_MASS_STORAGE} boolean extra indicating whether the
* mass storage function is enabled
@@ -128,12 +125,12 @@
public static final String USB_CONNECTED = "connected";
/**
- * Integer extra containing currently set USB configuration.
+ * Boolean extra indicating whether USB is configured.
* Used in extras for the {@link #ACTION_USB_STATE} broadcast.
*
* {@hide}
*/
- public static final String USB_CONFIGURATION = "configuration";
+ public static final String USB_CONFIGURED = "configured";
/**
* Name of the USB mass storage USB function.
@@ -388,21 +385,14 @@
}
}
- private static File getFunctionEnableFile(String function) {
- return new File("/sys/class/usb_composite/" + function + "/enable");
- }
-
- /**
- * Returns true if the specified USB function is supported by the kernel.
- * Note that a USB function maybe supported but disabled.
- *
- * @param function name of the USB function
- * @return true if the USB function is supported.
- *
- * {@hide}
- */
- public static boolean isFunctionSupported(String function) {
- return getFunctionEnableFile(function).exists();
+ private static boolean propertyContainsFunction(String property, String function) {
+ String functions = SystemProperties.get(property, "");
+ int index = functions.indexOf(function);
+ if (index < 0) return false;
+ if (index > 0 && functions.charAt(index - 1) != ',') return false;
+ int charAfter = index + function.length();
+ if (charAfter < functions.length() && functions.charAt(charAfter) != ',') return false;
+ return true;
}
/**
@@ -413,30 +403,52 @@
*
* {@hide}
*/
- public static boolean isFunctionEnabled(String function) {
+ public boolean isFunctionEnabled(String function) {
+ return propertyContainsFunction("sys.usb.config", function);
+ }
+
+ /**
+ * Sets the primary USB function.
+ *
+ * @param function name of the USB function
+ *
+ * {@hide}
+ */
+ public void setPrimaryFunction(String function) {
try {
- FileInputStream stream = new FileInputStream(getFunctionEnableFile(function));
- boolean enabled = (stream.read() == '1');
- stream.close();
- return enabled;
- } catch (IOException e) {
- return false;
+ mService.setPrimaryFunction(function);
+ } catch (RemoteException e) {
+ Log.e(TAG, "RemoteException in setPrimaryFunction", e);
}
}
/**
- * Enables or disables a USB function.
+ * Sets the default primary USB function.
+ *
+ * @param function name of the USB function
*
* {@hide}
*/
- public static boolean setFunctionEnabled(String function, boolean enable) {
+ public void setDefaultFunction(String function) {
try {
- FileOutputStream stream = new FileOutputStream(getFunctionEnableFile(function));
- stream.write(enable ? '1' : '0');
- stream.close();
- return true;
- } catch (IOException e) {
- return false;
+ mService.setDefaultFunction(function);
+ } catch (RemoteException e) {
+ Log.e(TAG, "RemoteException in setDefaultFunction", e);
+ }
+ }
+
+ /**
+ * Sets the file path for USB mass storage backing file.
+ *
+ * @param path backing file path
+ *
+ * {@hide}
+ */
+ public void setMassStorageBackingFile(String path) {
+ try {
+ mService.setMassStorageBackingFile(path);
+ } catch (RemoteException e) {
+ Log.e(TAG, "RemoteException in setDefaultFunction", e);
}
}
}
diff --git a/core/java/android/net/INetworkPolicyListener.aidl b/core/java/android/net/INetworkPolicyListener.aidl
index 9230151..a45ec54 100644
--- a/core/java/android/net/INetworkPolicyListener.aidl
+++ b/core/java/android/net/INetworkPolicyListener.aidl
@@ -19,6 +19,7 @@
/** {@hide} */
oneway interface INetworkPolicyListener {
- void onRulesChanged(int uid, int uidRules);
+ void onUidRulesChanged(int uid, int uidRules);
+ void onMeteredIfacesChanged(in String[] meteredIfaces);
}
diff --git a/core/java/android/net/INetworkStatsService.aidl b/core/java/android/net/INetworkStatsService.aidl
index 288112a..ae9aa05 100644
--- a/core/java/android/net/INetworkStatsService.aidl
+++ b/core/java/android/net/INetworkStatsService.aidl
@@ -18,18 +18,19 @@
import android.net.NetworkStats;
import android.net.NetworkStatsHistory;
+import android.net.NetworkTemplate;
/** {@hide} */
interface INetworkStatsService {
/** Return historical stats for traffic that matches template. */
- NetworkStatsHistory getHistoryForNetwork(int networkTemplate);
+ NetworkStatsHistory getHistoryForNetwork(in NetworkTemplate template);
/** Return historical stats for specific UID traffic that matches template. */
- NetworkStatsHistory getHistoryForUid(int uid, int networkTemplate);
+ NetworkStatsHistory getHistoryForUid(in NetworkTemplate template, int uid, int tag);
/** Return usage summary for traffic that matches template. */
- NetworkStats getSummaryForNetwork(long start, long end, int networkTemplate, String subscriberId);
+ NetworkStats getSummaryForNetwork(in NetworkTemplate template, long start, long end);
/** Return usage summary per UID for traffic that matches template. */
- NetworkStats getSummaryForAllUid(long start, long end, int networkTemplate);
+ NetworkStats getSummaryForAllUid(in NetworkTemplate template, long start, long end, boolean includeTags);
}
diff --git a/core/java/android/net/NetworkIdentity.java b/core/java/android/net/NetworkIdentity.java
new file mode 100644
index 0000000..ccef122
--- /dev/null
+++ b/core/java/android/net/NetworkIdentity.java
@@ -0,0 +1,122 @@
+/*
+ * Copyright (C) 2011 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.net;
+
+import static android.net.ConnectivityManager.isNetworkTypeMobile;
+
+import android.content.Context;
+import android.telephony.TelephonyManager;
+
+import com.android.internal.util.Objects;
+
+/**
+ * Network definition that includes strong identity. Analogous to combining
+ * {@link NetworkInfo} and an IMSI.
+ *
+ * @hide
+ */
+public class NetworkIdentity {
+ final int mType;
+ final int mSubType;
+ final String mSubscriberId;
+ final boolean mRoaming;
+
+ public NetworkIdentity(int type, int subType, String subscriberId, boolean roaming) {
+ this.mType = type;
+ this.mSubType = subType;
+ this.mSubscriberId = subscriberId;
+ this.mRoaming = roaming;
+ }
+
+ @Override
+ public int hashCode() {
+ return Objects.hashCode(mType, mSubType, mSubscriberId);
+ }
+
+ @Override
+ public boolean equals(Object obj) {
+ if (obj instanceof NetworkIdentity) {
+ final NetworkIdentity ident = (NetworkIdentity) obj;
+ return mType == ident.mType && mSubType == ident.mSubType
+ && Objects.equal(mSubscriberId, ident.mSubscriberId)
+ && mRoaming == ident.mRoaming;
+ }
+ return false;
+ }
+
+ @Override
+ public String toString() {
+ final String typeName = ConnectivityManager.getNetworkTypeName(mType);
+ final String subTypeName;
+ if (ConnectivityManager.isNetworkTypeMobile(mType)) {
+ subTypeName = TelephonyManager.getNetworkTypeName(mSubType);
+ } else {
+ subTypeName = Integer.toString(mSubType);
+ }
+
+ final String scrubSubscriberId = mSubscriberId != null ? "valid" : "null";
+ final String roaming = mRoaming ? ", ROAMING" : "";
+ return "[type=" + typeName + ", subType=" + subTypeName + ", subscriberId="
+ + scrubSubscriberId + roaming + "]";
+ }
+
+ public int getType() {
+ return mType;
+ }
+
+ public int getSubType() {
+ return mSubType;
+ }
+
+ public String getSubscriberId() {
+ return mSubscriberId;
+ }
+
+ public boolean getRoaming() {
+ return mRoaming;
+ }
+
+ /**
+ * Build a {@link NetworkIdentity} from the given {@link NetworkState},
+ * assuming that any mobile networks are using the current IMSI.
+ */
+ public static NetworkIdentity buildNetworkIdentity(Context context, NetworkState state) {
+ final int type = state.networkInfo.getType();
+ final int subType = state.networkInfo.getSubtype();
+
+ // TODO: consider moving subscriberId over to LinkCapabilities, so it
+ // comes from an authoritative source.
+
+ final String subscriberId;
+ final boolean roaming;
+ if (isNetworkTypeMobile(type)) {
+ final TelephonyManager telephony = (TelephonyManager) context.getSystemService(
+ Context.TELEPHONY_SERVICE);
+ roaming = telephony.isNetworkRoaming();
+ if (state.subscriberId != null) {
+ subscriberId = state.subscriberId;
+ } else {
+ subscriberId = telephony.getSubscriberId();
+ }
+ } else {
+ subscriberId = null;
+ roaming = false;
+ }
+ return new NetworkIdentity(type, subType, subscriberId, roaming);
+ }
+
+}
diff --git a/core/java/android/net/NetworkPolicy.java b/core/java/android/net/NetworkPolicy.java
index 1899281..52cab30 100644
--- a/core/java/android/net/NetworkPolicy.java
+++ b/core/java/android/net/NetworkPolicy.java
@@ -16,37 +16,38 @@
package android.net;
+import static com.android.internal.util.Preconditions.checkNotNull;
+
import android.os.Parcel;
import android.os.Parcelable;
/**
- * Policy for a specific network, including usage cycle and limits to be
- * enforced.
+ * Policy for networks matching a {@link NetworkTemplate}, including usage cycle
+ * and limits to be enforced.
*
* @hide
*/
public class NetworkPolicy implements Parcelable, Comparable<NetworkPolicy> {
- public final int networkTemplate;
- public final String subscriberId;
+ public static final long WARNING_DISABLED = -1;
+ public static final long LIMIT_DISABLED = -1;
+
+ public final NetworkTemplate template;
public int cycleDay;
public long warningBytes;
public long limitBytes;
- public static final long WARNING_DISABLED = -1;
- public static final long LIMIT_DISABLED = -1;
+ // TODO: teach how to snooze limit for current cycle
- public NetworkPolicy(int networkTemplate, String subscriberId, int cycleDay, long warningBytes,
- long limitBytes) {
- this.networkTemplate = networkTemplate;
- this.subscriberId = subscriberId;
+ public NetworkPolicy(
+ NetworkTemplate template, int cycleDay, long warningBytes, long limitBytes) {
+ this.template = checkNotNull(template, "missing NetworkTemplate");
this.cycleDay = cycleDay;
this.warningBytes = warningBytes;
this.limitBytes = limitBytes;
}
public NetworkPolicy(Parcel in) {
- networkTemplate = in.readInt();
- subscriberId = in.readString();
+ template = in.readParcelable(null);
cycleDay = in.readInt();
warningBytes = in.readLong();
limitBytes = in.readLong();
@@ -54,8 +55,7 @@
/** {@inheritDoc} */
public void writeToParcel(Parcel dest, int flags) {
- dest.writeInt(networkTemplate);
- dest.writeString(subscriberId);
+ dest.writeParcelable(template, flags);
dest.writeInt(cycleDay);
dest.writeLong(warningBytes);
dest.writeLong(limitBytes);
@@ -81,8 +81,8 @@
@Override
public String toString() {
- return "NetworkPolicy: networkTemplate=" + networkTemplate + ", cycleDay=" + cycleDay
- + ", warningBytes=" + warningBytes + ", limitBytes=" + limitBytes;
+ return "NetworkPolicy[" + template + "]: cycleDay=" + cycleDay + ", warningBytes="
+ + warningBytes + ", limitBytes=" + limitBytes;
}
public static final Creator<NetworkPolicy> CREATOR = new Creator<NetworkPolicy>() {
diff --git a/core/java/android/net/NetworkPolicyManager.java b/core/java/android/net/NetworkPolicyManager.java
index 0d4d9a9..91af16d 100644
--- a/core/java/android/net/NetworkPolicyManager.java
+++ b/core/java/android/net/NetworkPolicyManager.java
@@ -34,13 +34,13 @@
/** No specific network policy, use system default. */
public static final int POLICY_NONE = 0x0;
- /** Reject network usage on paid networks when application in background. */
- public static final int POLICY_REJECT_PAID_BACKGROUND = 0x1;
+ /** Reject network usage on metered networks when application in background. */
+ public static final int POLICY_REJECT_METERED_BACKGROUND = 0x1;
/** All network traffic should be allowed. */
public static final int RULE_ALLOW_ALL = 0x0;
- /** Reject traffic on paid networks. */
- public static final int RULE_REJECT_PAID = 0x1;
+ /** Reject traffic on metered networks. */
+ public static final int RULE_REJECT_METERED = 0x1;
/**
* {@link Intent} action launched when user selects {@link NetworkPolicy}
@@ -59,7 +59,7 @@
/**
* {@link Intent} extra included in {@link #ACTION_DATA_USAGE_WARNING} and
* {@link #ACTION_DATA_USAGE_LIMIT} to indicate which
- * {@link NetworkPolicy#networkTemplate} it applies to.
+ * {@link NetworkTemplate} rule it applies to.
*/
public static final String EXTRA_NETWORK_TEMPLATE =
"android.intent.extra.NETWORK_TEMPLATE";
@@ -98,7 +98,7 @@
* Set policy flags for specific UID.
*
* @param policy {@link #POLICY_NONE} or combination of flags like
- * {@link #POLICY_REJECT_PAID_BACKGROUND}.
+ * {@link #POLICY_REJECT_METERED_BACKGROUND}.
*/
public void setUidPolicy(int uid, int policy) {
try {
@@ -217,8 +217,8 @@
/** {@hide} */
public static void dumpPolicy(PrintWriter fout, int policy) {
fout.write("[");
- if ((policy & POLICY_REJECT_PAID_BACKGROUND) != 0) {
- fout.write("REJECT_PAID_BACKGROUND");
+ if ((policy & POLICY_REJECT_METERED_BACKGROUND) != 0) {
+ fout.write("REJECT_METERED_BACKGROUND");
}
fout.write("]");
}
@@ -226,8 +226,8 @@
/** {@hide} */
public static void dumpRules(PrintWriter fout, int rules) {
fout.write("[");
- if ((rules & RULE_REJECT_PAID) != 0) {
- fout.write("REJECT_PAID");
+ if ((rules & RULE_REJECT_METERED) != 0) {
+ fout.write("REJECT_METERED");
}
fout.write("]");
}
diff --git a/core/java/android/net/NetworkState.java b/core/java/android/net/NetworkState.java
index 749039a..704111b 100644
--- a/core/java/android/net/NetworkState.java
+++ b/core/java/android/net/NetworkState.java
@@ -29,18 +29,27 @@
public final NetworkInfo networkInfo;
public final LinkProperties linkProperties;
public final LinkCapabilities linkCapabilities;
+ /** Currently only used by testing. */
+ public final String subscriberId;
public NetworkState(NetworkInfo networkInfo, LinkProperties linkProperties,
LinkCapabilities linkCapabilities) {
+ this(networkInfo, linkProperties, linkCapabilities, null);
+ }
+
+ public NetworkState(NetworkInfo networkInfo, LinkProperties linkProperties,
+ LinkCapabilities linkCapabilities, String subscriberId) {
this.networkInfo = networkInfo;
this.linkProperties = linkProperties;
this.linkCapabilities = linkCapabilities;
+ this.subscriberId = subscriberId;
}
public NetworkState(Parcel in) {
networkInfo = in.readParcelable(null);
linkProperties = in.readParcelable(null);
linkCapabilities = in.readParcelable(null);
+ subscriberId = in.readString();
}
/** {@inheritDoc} */
@@ -53,6 +62,7 @@
out.writeParcelable(networkInfo, flags);
out.writeParcelable(linkProperties, flags);
out.writeParcelable(linkCapabilities, flags);
+ out.writeString(subscriberId);
}
public static final Creator<NetworkState> CREATOR = new Creator<NetworkState>() {
diff --git a/core/java/android/net/NetworkStats.java b/core/java/android/net/NetworkStats.java
index 60f740e..9d40c42 100644
--- a/core/java/android/net/NetworkStats.java
+++ b/core/java/android/net/NetworkStats.java
@@ -40,9 +40,8 @@
public static final String IFACE_ALL = null;
/** {@link #uid} value when UID details unavailable. */
public static final int UID_ALL = -1;
-
- // NOTE: data should only be accounted for once in this structure; if data
- // is broken out, the summarized version should not be included.
+ /** {@link #tag} value for without tag. */
+ public static final int TAG_NONE = 0;
/**
* {@link SystemClock#elapsedRealtime()} timestamp when this data was
@@ -52,16 +51,16 @@
public int size;
public String[] iface;
public int[] uid;
+ public int[] tag;
public long[] rx;
public long[] tx;
- // TODO: add fg/bg stats once reported by kernel
-
public NetworkStats(long elapsedRealtime, int initialSize) {
this.elapsedRealtime = elapsedRealtime;
this.size = 0;
this.iface = new String[initialSize];
this.uid = new int[initialSize];
+ this.tag = new int[initialSize];
this.rx = new long[initialSize];
this.tx = new long[initialSize];
}
@@ -71,21 +70,27 @@
size = parcel.readInt();
iface = parcel.createStringArray();
uid = parcel.createIntArray();
+ tag = parcel.createIntArray();
rx = parcel.createLongArray();
tx = parcel.createLongArray();
}
- public NetworkStats addEntry(String iface, int uid, long rx, long tx) {
+ /**
+ * Add new stats entry with given values.
+ */
+ public NetworkStats addEntry(String iface, int uid, int tag, long rx, long tx) {
if (size >= this.iface.length) {
final int newLength = Math.max(this.iface.length, 10) * 3 / 2;
this.iface = Arrays.copyOf(this.iface, newLength);
this.uid = Arrays.copyOf(this.uid, newLength);
+ this.tag = Arrays.copyOf(this.tag, newLength);
this.rx = Arrays.copyOf(this.rx, newLength);
this.tx = Arrays.copyOf(this.tx, newLength);
}
this.iface[size] = iface;
this.uid[size] = uid;
+ this.tag[size] = tag;
this.rx[size] = rx;
this.tx[size] = tx;
size++;
@@ -93,17 +98,29 @@
return this;
}
- @Deprecated
- public int length() {
- return size;
+ /**
+ * Combine given values with an existing row, or create a new row if
+ * {@link #findIndex(String, int, int)} is unable to find match. Can also be
+ * used to subtract values from existing rows.
+ */
+ public NetworkStats combineEntry(String iface, int uid, int tag, long rx, long tx) {
+ final int i = findIndex(iface, uid, tag);
+ if (i == -1) {
+ // only create new entry when positive contribution
+ addEntry(iface, uid, tag, rx, tx);
+ } else {
+ this.rx[i] += rx;
+ this.tx[i] += tx;
+ }
+ return this;
}
/**
* Find first stats index that matches the requested parameters.
*/
- public int findIndex(String iface, int uid) {
+ public int findIndex(String iface, int uid, int tag) {
for (int i = 0; i < size; i++) {
- if (equal(iface, this.iface[i]) && uid == this.uid[i]) {
+ if (equal(iface, this.iface[i]) && uid == this.uid[i] && tag == this.tag[i]) {
return i;
}
}
@@ -186,12 +203,13 @@
for (int i = 0; i < size; i++) {
final String iface = this.iface[i];
final int uid = this.uid[i];
+ final int tag = this.tag[i];
// find remote row that matches, and subtract
- final int j = value.findIndex(iface, uid);
+ final int j = value.findIndex(iface, uid, tag);
if (j == -1) {
// newly appearing row, return entire value
- result.addEntry(iface, uid, this.rx[i], this.tx[i]);
+ result.addEntry(iface, uid, tag, this.rx[i], this.tx[i]);
} else {
// existing row, subtract remote value
long rx = this.rx[i] - value.rx[j];
@@ -203,7 +221,7 @@
rx = Math.max(0, rx);
tx = Math.max(0, tx);
}
- result.addEntry(iface, uid, rx, tx);
+ result.addEntry(iface, uid, tag, rx, tx);
}
}
@@ -221,6 +239,7 @@
pw.print(prefix);
pw.print(" iface="); pw.print(iface[i]);
pw.print(" uid="); pw.print(uid[i]);
+ pw.print(" tag="); pw.print(tag[i]);
pw.print(" rx="); pw.print(rx[i]);
pw.print(" tx="); pw.println(tx[i]);
}
@@ -244,6 +263,7 @@
dest.writeInt(size);
dest.writeStringArray(iface);
dest.writeIntArray(uid);
+ dest.writeIntArray(tag);
dest.writeLongArray(rx);
dest.writeLongArray(tx);
}
diff --git a/core/java/android/net/NetworkStatsHistory.java b/core/java/android/net/NetworkStatsHistory.java
index 5fa8e21..ff6e220 100644
--- a/core/java/android/net/NetworkStatsHistory.java
+++ b/core/java/android/net/NetworkStatsHistory.java
@@ -40,10 +40,9 @@
* @hide
*/
public class NetworkStatsHistory implements Parcelable {
- private static final int VERSION_CURRENT = 1;
+ private static final int VERSION_INIT = 1;
- // TODO: teach about zigzag encoding to use less disk space
- // TODO: teach how to convert between bucket sizes
+ // TODO: teach about varint encoding to use less disk space
public final long bucketDuration;
@@ -83,7 +82,7 @@
public NetworkStatsHistory(DataInputStream in) throws IOException {
final int version = in.readInt();
switch (version) {
- case VERSION_CURRENT: {
+ case VERSION_INIT: {
bucketDuration = in.readLong();
bucketStart = readLongArray(in);
rx = readLongArray(in);
@@ -98,7 +97,7 @@
}
public void writeToStream(DataOutputStream out) throws IOException {
- out.writeInt(VERSION_CURRENT);
+ out.writeInt(VERSION_INIT);
out.writeLong(bucketDuration);
writeLongArray(out, bucketStart, bucketCount);
writeLongArray(out, rx, bucketCount);
@@ -115,6 +114,11 @@
* distribute across internal buckets, creating new buckets as needed.
*/
public void recordData(long start, long end, long rx, long tx) {
+ if (rx < 0 || tx < 0) {
+ throw new IllegalArgumentException(
+ "tried recording negative data: rx=" + rx + ", tx=" + tx);
+ }
+
// create any buckets needed by this range
ensureBuckets(start, end);
diff --git a/core/java/android/net/NetworkTemplate.aidl b/core/java/android/net/NetworkTemplate.aidl
new file mode 100644
index 0000000..3d37488
--- /dev/null
+++ b/core/java/android/net/NetworkTemplate.aidl
@@ -0,0 +1,19 @@
+/**
+ * Copyright (c) 2011, 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.net;
+
+parcelable NetworkTemplate;
diff --git a/core/java/android/net/NetworkTemplate.java b/core/java/android/net/NetworkTemplate.java
new file mode 100644
index 0000000..9381f1d
--- /dev/null
+++ b/core/java/android/net/NetworkTemplate.java
@@ -0,0 +1,217 @@
+/*
+ * Copyright (C) 2011 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.net;
+
+import static android.net.ConnectivityManager.TYPE_WIFI;
+import static android.net.ConnectivityManager.TYPE_WIMAX;
+import static android.net.ConnectivityManager.isNetworkTypeMobile;
+import static android.telephony.TelephonyManager.NETWORK_CLASS_2_G;
+import static android.telephony.TelephonyManager.NETWORK_CLASS_3_G;
+import static android.telephony.TelephonyManager.NETWORK_CLASS_4_G;
+import static android.telephony.TelephonyManager.NETWORK_CLASS_UNKNOWN;
+import static android.telephony.TelephonyManager.getNetworkClass;
+
+import android.os.Parcel;
+import android.os.Parcelable;
+
+import com.android.internal.util.Objects;
+
+/**
+ * Template definition used to generically match {@link NetworkIdentity},
+ * usually when collecting statistics.
+ *
+ * @hide
+ */
+public class NetworkTemplate implements Parcelable {
+
+ /**
+ * Template to combine all {@link ConnectivityManager#TYPE_MOBILE} style
+ * networks together. Only uses statistics for requested IMSI.
+ */
+ public static final int MATCH_MOBILE_ALL = 1;
+
+ /**
+ * Template to combine all {@link ConnectivityManager#TYPE_MOBILE} style
+ * networks together that roughly meet a "3G" definition, or lower. Only
+ * uses statistics for requested IMSI.
+ */
+ public static final int MATCH_MOBILE_3G_LOWER = 2;
+
+ /**
+ * Template to combine all {@link ConnectivityManager#TYPE_MOBILE} style
+ * networks together that meet a "4G" definition. Only uses statistics for
+ * requested IMSI.
+ */
+ public static final int MATCH_MOBILE_4G = 3;
+
+ /**
+ * Template to combine all {@link ConnectivityManager#TYPE_WIFI} style
+ * networks together.
+ */
+ public static final int MATCH_WIFI = 4;
+
+ final int mMatchRule;
+ final String mSubscriberId;
+
+ public NetworkTemplate(int matchRule, String subscriberId) {
+ this.mMatchRule = matchRule;
+ this.mSubscriberId = subscriberId;
+ }
+
+ public NetworkTemplate(Parcel in) {
+ mMatchRule = in.readInt();
+ mSubscriberId = in.readString();
+ }
+
+ /** {@inheritDoc} */
+ public void writeToParcel(Parcel dest, int flags) {
+ dest.writeInt(mMatchRule);
+ dest.writeString(mSubscriberId);
+ }
+
+ /** {@inheritDoc} */
+ public int describeContents() {
+ return 0;
+ }
+
+ @Override
+ public String toString() {
+ final String scrubSubscriberId = mSubscriberId != null ? "valid" : "null";
+ return "NetworkTemplate: matchRule=" + getMatchRuleName(mMatchRule) + ", subscriberId="
+ + scrubSubscriberId;
+ }
+
+ @Override
+ public int hashCode() {
+ return Objects.hashCode(mMatchRule, mSubscriberId);
+ }
+
+ @Override
+ public boolean equals(Object obj) {
+ if (obj instanceof NetworkTemplate) {
+ final NetworkTemplate other = (NetworkTemplate) obj;
+ return mMatchRule == other.mMatchRule
+ && Objects.equal(mSubscriberId, other.mSubscriberId);
+ }
+ return false;
+ }
+
+ public int getMatchRule() {
+ return mMatchRule;
+ }
+
+ public String getSubscriberId() {
+ return mSubscriberId;
+ }
+
+ /**
+ * Test if this network matches the given template and IMEI.
+ */
+ public boolean matches(NetworkIdentity ident) {
+ switch (mMatchRule) {
+ case MATCH_MOBILE_ALL:
+ return matchesMobile(ident);
+ case MATCH_MOBILE_3G_LOWER:
+ return matchesMobile3gLower(ident);
+ case MATCH_MOBILE_4G:
+ return matchesMobile4g(ident);
+ case MATCH_WIFI:
+ return matchesWifi(ident);
+ default:
+ throw new IllegalArgumentException("unknown network template");
+ }
+ }
+
+ /**
+ * Check if mobile network with matching IMEI. Also matches
+ * {@link #TYPE_WIMAX}.
+ */
+ private boolean matchesMobile(NetworkIdentity ident) {
+ if (isNetworkTypeMobile(ident.mType) && Objects.equal(mSubscriberId, ident.mSubscriberId)) {
+ return true;
+ } else if (ident.mType == TYPE_WIMAX) {
+ return true;
+ }
+ return false;
+ }
+
+ /**
+ * Check if mobile network classified 3G or lower with matching IMEI.
+ */
+ private boolean matchesMobile3gLower(NetworkIdentity ident) {
+ if (isNetworkTypeMobile(ident.mType) && Objects.equal(mSubscriberId, ident.mSubscriberId)) {
+ switch (getNetworkClass(ident.mSubType)) {
+ case NETWORK_CLASS_UNKNOWN:
+ case NETWORK_CLASS_2_G:
+ case NETWORK_CLASS_3_G:
+ return true;
+ }
+ }
+ return false;
+ }
+
+ /**
+ * Check if mobile network classified 4G with matching IMEI. Also matches
+ * {@link #TYPE_WIMAX}.
+ */
+ private boolean matchesMobile4g(NetworkIdentity ident) {
+ if (isNetworkTypeMobile(ident.mType) && Objects.equal(mSubscriberId, ident.mSubscriberId)) {
+ switch (getNetworkClass(ident.mSubType)) {
+ case NETWORK_CLASS_4_G:
+ return true;
+ }
+ } else if (ident.mType == TYPE_WIMAX) {
+ return true;
+ }
+ return false;
+ }
+
+ /**
+ * Check if matches Wi-Fi network template.
+ */
+ private boolean matchesWifi(NetworkIdentity ident) {
+ if (ident.mType == TYPE_WIFI) {
+ return true;
+ }
+ return false;
+ }
+
+ public static String getMatchRuleName(int matchRule) {
+ switch (matchRule) {
+ case MATCH_MOBILE_3G_LOWER:
+ return "MOBILE_3G_LOWER";
+ case MATCH_MOBILE_4G:
+ return "MOBILE_4G";
+ case MATCH_MOBILE_ALL:
+ return "MOBILE_ALL";
+ case MATCH_WIFI:
+ return "WIFI";
+ default:
+ return "UNKNOWN";
+ }
+ }
+
+ public static final Creator<NetworkTemplate> CREATOR = new Creator<NetworkTemplate>() {
+ public NetworkTemplate createFromParcel(Parcel in) {
+ return new NetworkTemplate(in);
+ }
+
+ public NetworkTemplate[] newArray(int size) {
+ return new NetworkTemplate[size];
+ }
+ };
+}
diff --git a/core/java/android/net/TrafficStats.java b/core/java/android/net/TrafficStats.java
index 3725fa6..cb47193 100644
--- a/core/java/android/net/TrafficStats.java
+++ b/core/java/android/net/TrafficStats.java
@@ -42,38 +42,12 @@
public final static int UNSUPPORTED = -1;
/**
- * Template to combine all {@link ConnectivityManager#TYPE_MOBILE} style
- * networks together. Only uses statistics for requested IMSI.
+ * Special UID value used when collecting {@link NetworkStatsHistory} for
+ * removed applications.
*
* @hide
*/
- public static final int TEMPLATE_MOBILE_ALL = 1;
-
- /**
- * Template to combine all {@link ConnectivityManager#TYPE_MOBILE} style
- * networks together that roughly meet a "3G" definition, or lower. Only
- * uses statistics for requested IMSI.
- *
- * @hide
- */
- public static final int TEMPLATE_MOBILE_3G_LOWER = 2;
-
- /**
- * Template to combine all {@link ConnectivityManager#TYPE_MOBILE} style
- * networks together that meet a "4G" definition. Only uses statistics for
- * requested IMSI.
- *
- * @hide
- */
- public static final int TEMPLATE_MOBILE_4G = 3;
-
- /**
- * Template to combine all {@link ConnectivityManager#TYPE_WIFI} style
- * networks together.
- *
- * @hide
- */
- public static final int TEMPLATE_WIFI = 4;
+ public static final int UID_REMOVED = -4;
/**
* Snapshot of {@link NetworkStats} when the currently active profiling
@@ -182,17 +156,6 @@
}
}
- /** {@hide} */
- public static boolean isNetworkTemplateMobile(int networkTemplate) {
- switch (networkTemplate) {
- case TEMPLATE_MOBILE_3G_LOWER:
- case TEMPLATE_MOBILE_4G:
- case TEMPLATE_MOBILE_ALL:
- return true;
- }
- return false;
- }
-
/**
* Get the total number of packets transmitted through the mobile interface.
*
diff --git a/core/java/android/nfc/NdefRecord.java b/core/java/android/nfc/NdefRecord.java
index 3fd26dd..5ade9eb 100644
--- a/core/java/android/nfc/NdefRecord.java
+++ b/core/java/android/nfc/NdefRecord.java
@@ -332,6 +332,28 @@
return Uri.parse(new String(fullUri, Charsets.UTF_8));
}
+ /**
+ * Creates an NDEF record of well known type URI.
+ * TODO: Make a public API
+ * @hide
+ */
+ public static NdefRecord createUri(Uri uri) {
+ String uriString = uri.toString();
+ byte prefix = 0x0;
+ for (int i = 1; i < URI_PREFIX_MAP.length; i++) {
+ if (uriString.startsWith(URI_PREFIX_MAP[i])) {
+ prefix = (byte) i;
+ uriString = uriString.substring(URI_PREFIX_MAP[i].length());
+ break;
+ }
+ }
+ byte[] uriBytes = uriString.getBytes(Charsets.UTF_8);
+ byte[] recordBytes = new byte[uriBytes.length + 1];
+ recordBytes[0] = prefix;
+ System.arraycopy(uriBytes, 0, recordBytes, 1, uriBytes.length);
+ return new NdefRecord(TNF_WELL_KNOWN, RTD_URI, new byte[0], recordBytes);
+ }
+
private static byte[] concat(byte[]... arrays) {
int length = 0;
for (byte[] array : arrays) {
diff --git a/core/java/android/provider/Downloads.java b/core/java/android/provider/Downloads.java
index 0a8c3ca..ba4804d 100644
--- a/core/java/android/provider/Downloads.java
+++ b/core/java/android/provider/Downloads.java
@@ -548,14 +548,6 @@
}
/**
- * This download has been paused because requesting application has been
- * blocked by {@link NetworkPolicyManager}.
- *
- * @hide
- */
- public static final int STATUS_PAUSED_BY_POLICY = 189;
-
- /**
* This download hasn't stated yet
*/
public static final int STATUS_PENDING = 190;
@@ -704,6 +696,14 @@
public static final int STATUS_TOO_MANY_REDIRECTS = 497;
/**
+ * This download has failed because requesting application has been
+ * blocked by {@link NetworkPolicyManager}.
+ *
+ * @hide
+ */
+ public static final int STATUS_BLOCKED = 498;
+
+ /**
* This download is visible but only shows in the notifications
* while it's in progress.
*/
diff --git a/core/java/android/provider/Settings.java b/core/java/android/provider/Settings.java
index 01e028e7..603edf0 100644
--- a/core/java/android/provider/Settings.java
+++ b/core/java/android/provider/Settings.java
@@ -3800,6 +3800,8 @@
public static final String NETSTATS_UID_BUCKET_DURATION = "netstats_uid_bucket_duration";
/** {@hide} */
public static final String NETSTATS_UID_MAX_HISTORY = "netstats_uid_max_history";
+ /** {@hide} */
+ public static final String NETSTATS_TAG_MAX_HISTORY = "netstats_tag_max_history";
/**
* @hide
diff --git a/core/java/android/view/TextureView.java b/core/java/android/view/TextureView.java
index 164c657..0421205 100644
--- a/core/java/android/view/TextureView.java
+++ b/core/java/android/view/TextureView.java
@@ -306,6 +306,8 @@
* <p><strong>Do not</strong> invoke this method from a drawing method
* ({@link #onDraw(android.graphics.Canvas)} for instance).</p>
*
+ * <p>If an error occurs during the copy, an empty bitmap will be returned.</p>
+ *
* @return A valid {@link Bitmap.Config#ARGB_8888} bitmap, or null if the surface
* texture is not available or the width <= 0 or the height <= 0
*
@@ -328,6 +330,8 @@
* <p><strong>Do not</strong> invoke this method from a drawing method
* ({@link #onDraw(android.graphics.Canvas)} for instance).</p>
*
+ * <p>If an error occurs during the copy, an empty bitmap will be returned.</p>
+ *
* @param width The width of the bitmap to create
* @param height The height of the bitmap to create
*
@@ -354,6 +358,8 @@
* <p><strong>Do not</strong> invoke this method from a drawing method
* ({@link #onDraw(android.graphics.Canvas)} for instance).</p>
*
+ * <p>If an error occurs, the bitmap is left unchanged.</p>
+ *
* @param bitmap The bitmap to copy the content of the surface texture into,
* cannot be null, all configurations are supported
*
@@ -447,5 +453,6 @@
public void onSurfaceTextureDestroyed(SurfaceTexture surface);
}
- private static native void nSetDefaultBufferSize(SurfaceTexture surfaceTexture, int width, int height);
+ private static native void nSetDefaultBufferSize(SurfaceTexture surfaceTexture,
+ int width, int height);
}
diff --git a/core/java/android/view/View.java b/core/java/android/view/View.java
index 888f0c0..46df88b 100644
--- a/core/java/android/view/View.java
+++ b/core/java/android/view/View.java
@@ -345,7 +345,7 @@
* 2 pixels to the right of the left edge. Padding can be set using the
* {@link #setPadding(int, int, int, int)} method and queried by calling
* {@link #getPaddingLeft()}, {@link #getPaddingTop()},
- * {@link #getPaddingRight()} and {@link #getPaddingBottom()}.
+ * {@link #getPaddingRight()}, {@link #getPaddingBottom()}.
* </p>
*
* <p>
@@ -607,6 +607,8 @@
* @attr ref android.R.styleable#View_paddingLeft
* @attr ref android.R.styleable#View_paddingRight
* @attr ref android.R.styleable#View_paddingTop
+ * @attr ref android.R.styleable#View_paddingStart
+ * @attr ref android.R.styleable#View_paddingEnd
* @attr ref android.R.styleable#View_saveEnabled
* @attr ref android.R.styleable#View_rotation
* @attr ref android.R.styleable#View_rotationX
@@ -1734,11 +1736,20 @@
static final int DRAG_HOVERED = 0x00000002;
/**
- * Indicates whether the view is drawn in right-to-left direction.
+ * Indicates whether the view layout direction has been resolved and drawn to the
+ * right-to-left direction.
*
* @hide
*/
- static final int RESOLVED_LAYOUT_RTL = 0x00000004;
+ static final int LAYOUT_DIRECTION_RESOLVED_RTL = 0x00000004;
+
+ /**
+ * Indicates whether the view layout direction has been resolved.
+ *
+ * @hide
+ */
+ static final int LAYOUT_DIRECTION_RESOLVED = 0x00000008;
+
/* End of masks for mPrivateFlags2 */
@@ -2173,6 +2184,33 @@
int mUserPaddingLeft;
/**
+ * Cache the paddingTop set by the user to append to the scrollbar's size.
+ */
+ @ViewDebug.ExportedProperty(category = "padding")
+ int mUserPaddingTop;
+
+ /**
+ * Cache if the user padding is relative.
+ *
+ */
+ @ViewDebug.ExportedProperty(category = "padding")
+ boolean mUserPaddingRelative;
+
+ /**
+ * Cache the paddingStart set by the user to append to the scrollbar's size.
+ *
+ */
+ @ViewDebug.ExportedProperty(category = "padding")
+ int mUserPaddingStart;
+
+ /**
+ * Cache the paddingEnd set by the user to append to the scrollbar's size.
+ *
+ */
+ @ViewDebug.ExportedProperty(category = "padding")
+ int mUserPaddingEnd;
+
+ /**
* @hide
*/
int mOldWidthMeasureSpec = Integer.MIN_VALUE;
@@ -2523,6 +2561,8 @@
int topPadding = -1;
int rightPadding = -1;
int bottomPadding = -1;
+ int startPadding = -1;
+ int endPadding = -1;
int padding = -1;
@@ -2568,6 +2608,12 @@
case com.android.internal.R.styleable.View_paddingBottom:
bottomPadding = a.getDimensionPixelSize(attr, -1);
break;
+ case com.android.internal.R.styleable.View_paddingStart:
+ startPadding = a.getDimensionPixelSize(attr, -1);
+ break;
+ case com.android.internal.R.styleable.View_paddingEnd:
+ endPadding = a.getDimensionPixelSize(attr, -1);
+ break;
case com.android.internal.R.styleable.View_scrollX:
x = a.getDimensionPixelOffset(attr, 0);
break;
@@ -2822,11 +2868,15 @@
setBackgroundDrawable(background);
}
+ mUserPaddingRelative = (startPadding >= 0 || endPadding >= 0);
+
if (padding >= 0) {
leftPadding = padding;
topPadding = padding;
rightPadding = padding;
bottomPadding = padding;
+ startPadding = padding;
+ endPadding = padding;
}
// If the user specified the padding (either with android:padding or
@@ -2838,6 +2888,15 @@
rightPadding >= 0 ? rightPadding : mPaddingRight,
bottomPadding >= 0 ? bottomPadding : mPaddingBottom);
+ // Cache user padding as we cannot fully resolve padding here (we dont have yet the resolved
+ // layout direction). Those cached values will be used later during padding resolution.
+ mUserPaddingLeft = leftPadding;
+ mUserPaddingRight = rightPadding;
+ mUserPaddingStart = startPadding;
+ mUserPaddingEnd = endPadding;
+ mUserPaddingTop = topPadding;
+ mUserPaddingBottom = bottomPadding;
+
if (viewFlagMasks != 0) {
setFlags(viewFlagValues, viewFlagMasks);
}
@@ -3059,7 +3118,7 @@
}
// Re-apply user/background padding so that scrollbar(s) get added
- recomputePadding();
+ resolvePadding();
}
/**
@@ -3084,7 +3143,7 @@
if (mVerticalScrollbarPosition != position) {
mVerticalScrollbarPosition = position;
computeOpaqueFlags();
- recomputePadding();
+ resolvePadding();
}
}
@@ -4315,8 +4374,8 @@
@ViewDebug.IntToString(from = LAYOUT_DIRECTION_RTL, to = "RESOLVED_DIRECTION_RTL")
})
public int getResolvedLayoutDirection() {
- resolveLayoutDirection();
- return ((mPrivateFlags2 & RESOLVED_LAYOUT_RTL) == RESOLVED_LAYOUT_RTL) ?
+ resolveLayoutDirectionIfNeeded();
+ return ((mPrivateFlags2 & LAYOUT_DIRECTION_RESOLVED_RTL) == LAYOUT_DIRECTION_RESOLVED_RTL) ?
LAYOUT_DIRECTION_RTL : LAYOUT_DIRECTION_LTR;
}
@@ -8265,7 +8324,7 @@
if (isHorizontalScrollBarEnabled() != horizontalScrollBarEnabled) {
mViewFlags ^= SCROLLBARS_HORIZONTAL;
computeOpaqueFlags();
- recomputePadding();
+ resolvePadding();
}
}
@@ -8295,7 +8354,7 @@
if (isVerticalScrollBarEnabled() != verticalScrollBarEnabled) {
mViewFlags ^= SCROLLBARS_VERTICAL;
computeOpaqueFlags();
- recomputePadding();
+ resolvePadding();
}
}
@@ -8354,7 +8413,7 @@
if (style != (mViewFlags & SCROLLBARS_STYLE_MASK)) {
mViewFlags = (mViewFlags & ~SCROLLBARS_STYLE_MASK) | (style & SCROLLBARS_STYLE_MASK);
computeOpaqueFlags();
- recomputePadding();
+ resolvePadding();
}
}
@@ -8739,7 +8798,9 @@
mPrivateFlags &= ~AWAKEN_SCROLL_BARS_ON_ATTACH;
}
jumpDrawablesToCurrentState();
- resolveLayoutDirection();
+ resetLayoutDirectionResolution();
+ resolveLayoutDirectionIfNeeded();
+ resolvePadding();
if (isFocused()) {
InputMethodManager imm = InputMethodManager.peekInstance();
imm.focusIn(this);
@@ -8747,31 +8808,91 @@
}
/**
- * Resolving the layout direction. LTR is set initially.
- * We are supposing here that the parent directionality will be resolved before its children.
+ * Resolve and cache the layout direction. LTR is set initially. This is implicitly supposing
+ * that the parent directionality can and will be resolved before its children.
*/
- private void resolveLayoutDirection() {
- mPrivateFlags2 &= ~RESOLVED_LAYOUT_RTL;
+ private void resolveLayoutDirectionIfNeeded() {
+ // Do not resolve if it is not needed
+ if ((mPrivateFlags2 & LAYOUT_DIRECTION_RESOLVED) == LAYOUT_DIRECTION_RESOLVED) return;
+
+ // Clear any previous layout direction resolution
+ mPrivateFlags2 &= ~LAYOUT_DIRECTION_RESOLVED_RTL;
+
+ // Set resolved depending on layout direction
switch (getLayoutDirection()) {
case LAYOUT_DIRECTION_INHERIT:
// If this is root view, no need to look at parent's layout dir.
if (mParent != null &&
mParent instanceof ViewGroup &&
((ViewGroup) mParent).getResolvedLayoutDirection() == LAYOUT_DIRECTION_RTL) {
- mPrivateFlags2 |= RESOLVED_LAYOUT_RTL;
+ mPrivateFlags2 |= LAYOUT_DIRECTION_RESOLVED_RTL;
}
break;
case LAYOUT_DIRECTION_RTL:
- mPrivateFlags2 |= RESOLVED_LAYOUT_RTL;
+ mPrivateFlags2 |= LAYOUT_DIRECTION_RESOLVED_RTL;
break;
case LAYOUT_DIRECTION_LOCALE:
if(isLayoutDirectionRtl(Locale.getDefault())) {
- mPrivateFlags2 |= RESOLVED_LAYOUT_RTL;
+ mPrivateFlags2 |= LAYOUT_DIRECTION_RESOLVED_RTL;
}
break;
default:
// Nothing to do, LTR by default
}
+
+ // Set to resolved
+ mPrivateFlags2 |= LAYOUT_DIRECTION_RESOLVED;
+ }
+
+ private void resolvePadding() {
+ // If the user specified the absolute padding (either with android:padding or
+ // android:paddingLeft/Top/Right/Bottom), use this padding, otherwise
+ // use the default padding or the padding from the background drawable
+ // (stored at this point in mPadding*)
+ switch (getResolvedLayoutDirection()) {
+ case LAYOUT_DIRECTION_RTL:
+ // Start user padding override Right user padding. Otherwise, if Right user
+ // padding is not defined, use the default Right padding. If Right user padding
+ // is defined, just use it.
+ if (mUserPaddingStart >= 0) {
+ mUserPaddingRight = mUserPaddingStart;
+ } else if (mUserPaddingRight < 0) {
+ mUserPaddingRight = mPaddingRight;
+ }
+ if (mUserPaddingEnd >= 0) {
+ mUserPaddingLeft = mUserPaddingEnd;
+ } else if (mUserPaddingLeft < 0) {
+ mUserPaddingLeft = mPaddingLeft;
+ }
+ break;
+ case LAYOUT_DIRECTION_LTR:
+ default:
+ // Start user padding override Left user padding. Otherwise, if Left user
+ // padding is not defined, use the default left padding. If Left user padding
+ // is defined, just use it.
+ if (mUserPaddingStart >= 0) {
+ mUserPaddingLeft = mUserPaddingStart;
+ } else if (mUserPaddingLeft < 0) {
+ mUserPaddingLeft = mPaddingLeft;
+ }
+ if (mUserPaddingEnd >= 0) {
+ mUserPaddingRight = mUserPaddingEnd;
+ } else if (mUserPaddingRight < 0) {
+ mUserPaddingRight = mPaddingRight;
+ }
+ }
+
+ mPaddingTop = (mUserPaddingTop >= 0) ? mUserPaddingTop : mPaddingTop;
+ mUserPaddingBottom = (mUserPaddingBottom >= 0) ? mUserPaddingBottom : mPaddingBottom;
+
+ recomputePadding();
+ }
+
+ /**
+ * Reset the resolved layout direction by clearing the corresponding flag
+ */
+ private void resetLayoutDirectionResolution() {
+ mPrivateFlags2 &= ~LAYOUT_DIRECTION_RESOLVED;
}
/**
@@ -10745,7 +10866,14 @@
sThreadLocal.set(padding);
}
if (d.getPadding(padding)) {
- setPadding(padding.left, padding.top, padding.right, padding.bottom);
+ switch (d.getResolvedLayoutDirectionSelf()) {
+ case LAYOUT_DIRECTION_RTL:
+ setPadding(padding.right, padding.top, padding.left, padding.bottom);
+ break;
+ case LAYOUT_DIRECTION_LTR:
+ default:
+ setPadding(padding.left, padding.top, padding.right, padding.bottom);
+ }
}
// Compare the minimum sizes of the old Drawable and the new. If there isn't an old or
@@ -10831,6 +10959,8 @@
public void setPadding(int left, int top, int right, int bottom) {
boolean changed = false;
+ mUserPaddingRelative = false;
+
mUserPaddingLeft = left;
mUserPaddingRight = right;
mUserPaddingBottom = bottom;
@@ -10840,11 +10970,16 @@
// Common case is there are no scroll bars.
if ((viewFlags & (SCROLLBARS_VERTICAL|SCROLLBARS_HORIZONTAL)) != 0) {
if ((viewFlags & SCROLLBARS_VERTICAL) != 0) {
- // TODO Determine what to do with SCROLLBAR_POSITION_DEFAULT based on RTL settings.
final int offset = (viewFlags & SCROLLBARS_INSET_MASK) == 0
? 0 : getVerticalScrollbarWidth();
switch (mVerticalScrollbarPosition) {
case SCROLLBAR_POSITION_DEFAULT:
+ if (getResolvedLayoutDirection() == LAYOUT_DIRECTION_RTL) {
+ left += offset;
+ } else {
+ right += offset;
+ }
+ break;
case SCROLLBAR_POSITION_RIGHT:
right += offset;
break;
@@ -10882,6 +11017,37 @@
}
/**
+ * Sets the relative padding. The view may add on the space required to display
+ * the scrollbars, depending on the style and visibility of the scrollbars.
+ * So the values returned from {@link #getPaddingStart}, {@link #getPaddingTop},
+ * {@link #getPaddingEnd} and {@link #getPaddingBottom} may be different
+ * from the values set in this call.
+ *
+ * @attr ref android.R.styleable#View_padding
+ * @attr ref android.R.styleable#View_paddingBottom
+ * @attr ref android.R.styleable#View_paddingStart
+ * @attr ref android.R.styleable#View_paddingEnd
+ * @attr ref android.R.styleable#View_paddingTop
+ * @param start the start padding in pixels
+ * @param top the top padding in pixels
+ * @param end the end padding in pixels
+ * @param bottom the bottom padding in pixels
+ *
+ * @hide
+ */
+ public void setPaddingRelative(int start, int top, int end, int bottom) {
+ mUserPaddingRelative = true;
+ switch(getResolvedLayoutDirection()) {
+ case LAYOUT_DIRECTION_RTL:
+ setPadding(end, top, start, bottom);
+ break;
+ case LAYOUT_DIRECTION_LTR:
+ default:
+ setPadding(start, top, end, bottom);
+ }
+ }
+
+ /**
* Returns the top padding of this view.
*
* @return the top padding in pixels
@@ -10913,6 +11079,20 @@
}
/**
+ * Returns the start padding of this view. If there are inset and enabled
+ * scrollbars, this value may include the space required to display the
+ * scrollbars as well.
+ *
+ * @return the start padding in pixels
+ *
+ * @hide
+ */
+ public int getPaddingStart() {
+ return (getResolvedLayoutDirection() == LAYOUT_DIRECTION_RTL) ?
+ mPaddingRight : mPaddingLeft;
+ }
+
+ /**
* Returns the right padding of this view. If there are inset and enabled
* scrollbars, this value may include the space required to display the
* scrollbars as well.
@@ -10924,6 +11104,34 @@
}
/**
+ * Returns the end padding of this view. If there are inset and enabled
+ * scrollbars, this value may include the space required to display the
+ * scrollbars as well.
+ *
+ * @return the end padding in pixels
+ *
+ * @hide
+ */
+ public int getPaddingEnd() {
+ return (getResolvedLayoutDirection() == LAYOUT_DIRECTION_RTL) ?
+ mPaddingLeft : mPaddingRight;
+ }
+
+ /**
+ * Return if the padding as been set thru relative values
+ * {@link #setPaddingRelative(int, int, int, int)} or thru
+ * @attr ref android.R.styleable#View_paddingStart or
+ * @attr ref android.R.styleable#View_paddingEnd
+ *
+ * @return true if the padding is relative or false if it is not.
+ *
+ * @hide
+ */
+ public boolean isPaddingRelative() {
+ return mUserPaddingRelative;
+ }
+
+ /**
* Changes the selection state of this view. A view can be selected or not.
* Note that selection is not the same as focus. Views are typically
* selected in the context of an AdapterView like ListView or GridView;
diff --git a/core/java/android/view/ViewAncestor.java b/core/java/android/view/ViewAncestor.java
index ba3ae58..afbedaf 100644
--- a/core/java/android/view/ViewAncestor.java
+++ b/core/java/android/view/ViewAncestor.java
@@ -4479,7 +4479,7 @@
ArrayList<View> foundViews = mAttachInfo.mFocusablesTempList;
foundViews.clear();
- View root = null;
+ View root;
if (accessibilityViewId != View.NO_ID) {
root = findViewByAccessibilityId(accessibilityViewId);
} else {
diff --git a/core/java/android/widget/GridLayout.java b/core/java/android/widget/GridLayout.java
index 4a514bf..46e4398 100644
--- a/core/java/android/widget/GridLayout.java
+++ b/core/java/android/widget/GridLayout.java
@@ -765,9 +765,12 @@
int computedWidth = getPaddingLeft() + mHorizontalAxis.getMin() + getPaddingRight();
int computedHeight = getPaddingTop() + mVerticalAxis.getMin() + getPaddingBottom();
+ int measuredWidth = Math.max(computedWidth, getSuggestedMinimumWidth());
+ int measuredHeight = Math.max(computedHeight, getSuggestedMinimumHeight());
+
setMeasuredDimension(
- resolveSizeAndState(computedWidth, widthSpec, 0),
- resolveSizeAndState(computedHeight, heightSpec, 0));
+ resolveSizeAndState(measuredWidth, widthSpec, 0),
+ resolveSizeAndState(measuredHeight, heightSpec, 0));
}
private int protect(int alignment) {
@@ -809,9 +812,9 @@
and sizing to each child view and then placing it in its cell.
*/
@Override
- protected void onLayout(boolean changed, int l, int t, int r, int b) {
- int targetWidth = r - l;
- int targetHeight = b - t;
+ protected void onLayout(boolean changed, int left, int top, int right, int bottom) {
+ int targetWidth = right - left;
+ int targetHeight = bottom - top;
int paddingLeft = getPaddingLeft();
int paddingTop = getPaddingTop();
@@ -2172,9 +2175,8 @@
*
* @param view the view to which this alignment should be applied
* @param viewSize the measured size of the view
- * @param measurementType The type of measurement that should be made. This feature
- * is currently unused as GridLayout only supports one
- * type of measurement: {@link View#measure(int, int)}.
+ * @param measurementType This parameter is currently unused as GridLayout only supports
+ * one type of measurement: {@link View#measure(int, int)}.
*
* @return the alignment value
*/
@@ -2190,9 +2192,8 @@
* @param view the view to which this alignment should be applied
* @param viewSize the measured size of the view
* @param cellSize the size of the cell into which this view will be placed
- * @param measurementType The type of measurement that should be made. This feature
- * is currently unused as GridLayout only supports one
- * type of measurement: {@link View#measure(int, int)}.
+ * @param measurementType This parameter is currently unused as GridLayout only supports
+ * one type of measurement: {@link View#measure(int, int)}.
*
* @return the aligned size
*/
diff --git a/core/java/com/android/internal/widget/ActionBarView.java b/core/java/com/android/internal/widget/ActionBarView.java
index 9d8d361..c475eff 100644
--- a/core/java/com/android/internal/widget/ActionBarView.java
+++ b/core/java/com/android/internal/widget/ActionBarView.java
@@ -96,6 +96,8 @@
private LinearLayout mTitleLayout;
private TextView mTitleView;
private TextView mSubtitleView;
+ private View mTitleUpView;
+
private Spinner mSpinner;
private LinearLayout mListNavLayout;
private ScrollingTabContainerView mTabScrollView;
@@ -152,6 +154,16 @@
}
};
+ private final OnClickListener mUpClickListener = new OnClickListener() {
+ public void onClick(View v) {
+ Context context = getContext();
+ if (context instanceof Activity) {
+ Activity activity = (Activity) context;
+ activity.onMenuItemSelected(Window.FEATURE_OPTIONS_PANEL, mLogoNavItem);
+ }
+ }
+ };
+
public ActionBarView(Context context, AttributeSet attrs) {
super(context, attrs);
@@ -230,15 +242,7 @@
a.recycle();
mLogoNavItem = new ActionMenuItem(context, 0, android.R.id.home, 0, 0, mTitle);
- mHomeLayout.setOnClickListener(new OnClickListener() {
- public void onClick(View v) {
- Context context = getContext();
- if (context instanceof Activity) {
- Activity activity = (Activity) context;
- activity.onMenuItemSelected(Window.FEATURE_OPTIONS_PANEL, mLogoNavItem);
- }
- }
- });
+ mHomeLayout.setOnClickListener(mUpClickListener);
mHomeLayout.setClickable(true);
mHomeLayout.setFocusable(true);
}
@@ -438,7 +442,8 @@
}
if ((flagsChanged & DISPLAY_RELAYOUT_MASK) != 0) {
- final int vis = (options & ActionBar.DISPLAY_SHOW_HOME) != 0 ? VISIBLE : GONE;
+ final boolean showHome = (options & ActionBar.DISPLAY_SHOW_HOME) != 0;
+ final int vis = showHome ? VISIBLE : GONE;
mHomeLayout.setVisibility(vis);
if ((flagsChanged & ActionBar.DISPLAY_HOME_AS_UP) != 0) {
@@ -458,6 +463,14 @@
}
}
+ if (mTitleLayout != null && (flagsChanged &
+ (ActionBar.DISPLAY_HOME_AS_UP | ActionBar.DISPLAY_SHOW_HOME)) != 0) {
+ final boolean homeAsUp = (options & ActionBar.DISPLAY_HOME_AS_UP) != 0;
+ final boolean titleUp = homeAsUp && !showHome;
+ mTitleUpView.setVisibility(titleUp ? VISIBLE : GONE);
+ mTitleLayout.setEnabled(titleUp);
+ }
+
if ((flagsChanged & ActionBar.DISPLAY_SHOW_CUSTOM) != 0 && mCustomNavView != null) {
if ((options & ActionBar.DISPLAY_SHOW_CUSTOM) != 0) {
addView(mCustomNavView);
@@ -637,24 +650,35 @@
}
private void initTitle() {
- LayoutInflater inflater = LayoutInflater.from(getContext());
- mTitleLayout = (LinearLayout) inflater.inflate(R.layout.action_bar_title_item, null);
- mTitleView = (TextView) mTitleLayout.findViewById(R.id.action_bar_title);
- mSubtitleView = (TextView) mTitleLayout.findViewById(R.id.action_bar_subtitle);
+ if (mTitleLayout == null) {
+ LayoutInflater inflater = LayoutInflater.from(getContext());
+ mTitleLayout = (LinearLayout) inflater.inflate(R.layout.action_bar_title_item, null);
+ mTitleView = (TextView) mTitleLayout.findViewById(R.id.action_bar_title);
+ mSubtitleView = (TextView) mTitleLayout.findViewById(R.id.action_bar_subtitle);
+ mTitleUpView = (View) mTitleLayout.findViewById(R.id.up);
- if (mTitleStyleRes != 0) {
- mTitleView.setTextAppearance(mContext, mTitleStyleRes);
- }
- if (mTitle != null) {
- mTitleView.setText(mTitle);
- }
+ mTitleLayout.setOnClickListener(mUpClickListener);
- if (mSubtitleStyleRes != 0) {
- mSubtitleView.setTextAppearance(mContext, mSubtitleStyleRes);
- }
- if (mSubtitle != null) {
- mSubtitleView.setText(mSubtitle);
- mSubtitleView.setVisibility(VISIBLE);
+ if (mTitleStyleRes != 0) {
+ mTitleView.setTextAppearance(mContext, mTitleStyleRes);
+ }
+ if (mTitle != null) {
+ mTitleView.setText(mTitle);
+ }
+
+ if (mSubtitleStyleRes != 0) {
+ mSubtitleView.setTextAppearance(mContext, mSubtitleStyleRes);
+ }
+ if (mSubtitle != null) {
+ mSubtitleView.setText(mSubtitle);
+ mSubtitleView.setVisibility(VISIBLE);
+ }
+
+ final boolean homeAsUp = (mDisplayOptions & ActionBar.DISPLAY_HOME_AS_UP) != 0;
+ final boolean titleUp = homeAsUp &&
+ (mDisplayOptions & ActionBar.DISPLAY_SHOW_HOME) == 0;
+ mTitleUpView.setVisibility(titleUp ? VISIBLE : GONE);
+ mTitleLayout.setEnabled(titleUp);
}
addView(mTitleLayout);
@@ -734,7 +758,7 @@
if (mExpandedActionView == null) {
boolean showTitle = mTitleLayout != null && mTitleLayout.getVisibility() != GONE &&
- (mDisplayOptions & ActionBar.DISPLAY_SHOW_TITLE) != 0;
+ (mDisplayOptions & ActionBar.DISPLAY_SHOW_TITLE) != 0;
if (showTitle) {
availableWidth = measureChildView(mTitleLayout, availableWidth, childSpecHeight, 0);
leftOfCenter = Math.max(0, leftOfCenter - mTitleLayout.getMeasuredWidth());
@@ -872,7 +896,7 @@
if (mExpandedActionView == null) {
final boolean showTitle = mTitleLayout != null && mTitleLayout.getVisibility() != GONE &&
- (mDisplayOptions & ActionBar.DISPLAY_SHOW_TITLE) != 0;
+ (mDisplayOptions & ActionBar.DISPLAY_SHOW_TITLE) != 0;
if (showTitle) {
x += positionChild(mTitleLayout, x, y, contentHeight);
}
@@ -1193,7 +1217,7 @@
addView(mExpandedHomeLayout);
}
mHomeLayout.setVisibility(GONE);
- mTitleLayout.setVisibility(GONE);
+ if (mTitleLayout != null) mTitleLayout.setVisibility(GONE);
if (mTabScrollView != null) mTabScrollView.setVisibility(GONE);
if (mSpinner != null) mSpinner.setVisibility(GONE);
if (mCustomNavView != null) mCustomNavView.setVisibility(GONE);
@@ -1210,7 +1234,11 @@
mHomeLayout.setVisibility(VISIBLE);
}
if ((mDisplayOptions & ActionBar.DISPLAY_SHOW_TITLE) != 0) {
- mTitleLayout.setVisibility(VISIBLE);
+ if (mTitleLayout == null) {
+ initTitle();
+ } else {
+ mTitleLayout.setVisibility(VISIBLE);
+ }
}
if (mTabScrollView != null && mNavigationMode == ActionBar.NAVIGATION_MODE_TABS) {
mTabScrollView.setVisibility(VISIBLE);
diff --git a/core/java/com/android/internal/widget/LockPatternView.java b/core/java/com/android/internal/widget/LockPatternView.java
index fd49ae3..cbb110a 100644
--- a/core/java/com/android/internal/widget/LockPatternView.java
+++ b/core/java/com/android/internal/widget/LockPatternView.java
@@ -618,178 +618,20 @@
}
@Override
- public boolean onTouchEvent(MotionEvent motionEvent) {
+ public boolean onTouchEvent(MotionEvent event) {
if (!mInputEnabled || !isEnabled()) {
return false;
}
- final float x = motionEvent.getX();
- final float y = motionEvent.getY();
- Cell hitCell;
- switch(motionEvent.getAction()) {
+ switch(event.getAction()) {
case MotionEvent.ACTION_DOWN:
- resetPattern();
- hitCell = detectAndAddHit(x, y);
- if (hitCell != null && mOnPatternListener != null) {
- mPatternInProgress = true;
- mPatternDisplayMode = DisplayMode.Correct;
- mOnPatternListener.onPatternStart();
- } else if (mOnPatternListener != null) {
- mPatternInProgress = false;
- mOnPatternListener.onPatternCleared();
- }
- if (hitCell != null) {
- final float startX = getCenterXForColumn(hitCell.column);
- final float startY = getCenterYForRow(hitCell.row);
-
- final float widthOffset = mSquareWidth / 2f;
- final float heightOffset = mSquareHeight / 2f;
-
- invalidate((int) (startX - widthOffset), (int) (startY - heightOffset),
- (int) (startX + widthOffset), (int) (startY + heightOffset));
- }
- mInProgressX = x;
- mInProgressY = y;
- if (PROFILE_DRAWING) {
- if (!mDrawingProfilingStarted) {
- Debug.startMethodTracing("LockPatternDrawing");
- mDrawingProfilingStarted = true;
- }
- }
+ handleActionDown(event);
return true;
case MotionEvent.ACTION_UP:
- // report pattern detected
- if (!mPattern.isEmpty() && mOnPatternListener != null) {
- mPatternInProgress = false;
- mOnPatternListener.onPatternDetected(mPattern);
- invalidate();
- }
- if (PROFILE_DRAWING) {
- if (mDrawingProfilingStarted) {
- Debug.stopMethodTracing();
- mDrawingProfilingStarted = false;
- }
- }
+ handleActionUp(event);
return true;
case MotionEvent.ACTION_MOVE:
- final int patternSizePreHitDetect = mPattern.size();
- hitCell = detectAndAddHit(x, y);
- final int patternSize = mPattern.size();
- if (hitCell != null && (mOnPatternListener != null) && (patternSize == 1)) {
- mPatternInProgress = true;
- mOnPatternListener.onPatternStart();
- }
- // note current x and y for rubber banding of in progress
- // patterns
- final float dx = Math.abs(x - mInProgressX);
- final float dy = Math.abs(y - mInProgressY);
- if (dx + dy > mSquareWidth * 0.01f) {
- float oldX = mInProgressX;
- float oldY = mInProgressY;
-
- mInProgressX = x;
- mInProgressY = y;
-
- if (mPatternInProgress && patternSize > 0) {
- final ArrayList<Cell> pattern = mPattern;
- final float radius = mSquareWidth * mDiameterFactor * 0.5f;
-
- final Cell lastCell = pattern.get(patternSize - 1);
-
- float startX = getCenterXForColumn(lastCell.column);
- float startY = getCenterYForRow(lastCell.row);
-
- float left;
- float top;
- float right;
- float bottom;
-
- final Rect invalidateRect = mInvalidate;
-
- if (startX < x) {
- left = startX;
- right = x;
- } else {
- left = x;
- right = startX;
- }
-
- if (startY < y) {
- top = startY;
- bottom = y;
- } else {
- top = y;
- bottom = startY;
- }
-
- // Invalidate between the pattern's last cell and the current location
- invalidateRect.set((int) (left - radius), (int) (top - radius),
- (int) (right + radius), (int) (bottom + radius));
-
- if (startX < oldX) {
- left = startX;
- right = oldX;
- } else {
- left = oldX;
- right = startX;
- }
-
- if (startY < oldY) {
- top = startY;
- bottom = oldY;
- } else {
- top = oldY;
- bottom = startY;
- }
-
- // Invalidate between the pattern's last cell and the previous location
- invalidateRect.union((int) (left - radius), (int) (top - radius),
- (int) (right + radius), (int) (bottom + radius));
-
- // Invalidate between the pattern's new cell and the pattern's previous cell
- if (hitCell != null) {
- startX = getCenterXForColumn(hitCell.column);
- startY = getCenterYForRow(hitCell.row);
-
- if (patternSize >= 2) {
- // (re-using hitcell for old cell)
- hitCell = pattern.get(patternSize - 1 - (patternSize - patternSizePreHitDetect));
- oldX = getCenterXForColumn(hitCell.column);
- oldY = getCenterYForRow(hitCell.row);
-
- if (startX < oldX) {
- left = startX;
- right = oldX;
- } else {
- left = oldX;
- right = startX;
- }
-
- if (startY < oldY) {
- top = startY;
- bottom = oldY;
- } else {
- top = oldY;
- bottom = startY;
- }
- } else {
- left = right = startX;
- top = bottom = startY;
- }
-
- final float widthOffset = mSquareWidth / 2f;
- final float heightOffset = mSquareHeight / 2f;
-
- invalidateRect.set((int) (left - widthOffset),
- (int) (top - heightOffset), (int) (right + widthOffset),
- (int) (bottom + heightOffset));
- }
-
- invalidate(invalidateRect);
- } else {
- invalidate();
- }
- }
+ handleActionMove(event);
return true;
case MotionEvent.ACTION_CANCEL:
resetPattern();
@@ -808,6 +650,181 @@
return false;
}
+ private void handleActionMove(MotionEvent event) {
+ // Handle all recent motion events so we don't skip any cells even when the device
+ // is busy...
+ final int historySize = event.getHistorySize();
+ for (int i = 0; i < historySize + 1; i++) {
+ final float x = i < historySize ? event.getHistoricalX(i) : event.getX();
+ final float y = i < historySize ? event.getHistoricalY(i) : event.getY();
+ final int patternSizePreHitDetect = mPattern.size();
+ Cell hitCell = detectAndAddHit(x, y);
+ final int patternSize = mPattern.size();
+ if (hitCell != null && (mOnPatternListener != null) && (patternSize == 1)) {
+ mPatternInProgress = true;
+ mOnPatternListener.onPatternStart();
+ }
+ // note current x and y for rubber banding of in progress patterns
+ final float dx = Math.abs(x - mInProgressX);
+ final float dy = Math.abs(y - mInProgressY);
+ if (dx + dy > mSquareWidth * 0.01f) {
+ float oldX = mInProgressX;
+ float oldY = mInProgressY;
+
+ mInProgressX = x;
+ mInProgressY = y;
+
+ if (mPatternInProgress && patternSize > 0) {
+ final ArrayList<Cell> pattern = mPattern;
+ final float radius = mSquareWidth * mDiameterFactor * 0.5f;
+
+ final Cell lastCell = pattern.get(patternSize - 1);
+
+ float startX = getCenterXForColumn(lastCell.column);
+ float startY = getCenterYForRow(lastCell.row);
+
+ float left;
+ float top;
+ float right;
+ float bottom;
+
+ final Rect invalidateRect = mInvalidate;
+
+ if (startX < x) {
+ left = startX;
+ right = x;
+ } else {
+ left = x;
+ right = startX;
+ }
+
+ if (startY < y) {
+ top = startY;
+ bottom = y;
+ } else {
+ top = y;
+ bottom = startY;
+ }
+
+ // Invalidate between the pattern's last cell and the current location
+ invalidateRect.set((int) (left - radius), (int) (top - radius),
+ (int) (right + radius), (int) (bottom + radius));
+
+ if (startX < oldX) {
+ left = startX;
+ right = oldX;
+ } else {
+ left = oldX;
+ right = startX;
+ }
+
+ if (startY < oldY) {
+ top = startY;
+ bottom = oldY;
+ } else {
+ top = oldY;
+ bottom = startY;
+ }
+
+ // Invalidate between the pattern's last cell and the previous location
+ invalidateRect.union((int) (left - radius), (int) (top - radius),
+ (int) (right + radius), (int) (bottom + radius));
+
+ // Invalidate between the pattern's new cell and the pattern's previous cell
+ if (hitCell != null) {
+ startX = getCenterXForColumn(hitCell.column);
+ startY = getCenterYForRow(hitCell.row);
+
+ if (patternSize >= 2) {
+ // (re-using hitcell for old cell)
+ hitCell = pattern.get(patternSize - 1 - (patternSize - patternSizePreHitDetect));
+ oldX = getCenterXForColumn(hitCell.column);
+ oldY = getCenterYForRow(hitCell.row);
+
+ if (startX < oldX) {
+ left = startX;
+ right = oldX;
+ } else {
+ left = oldX;
+ right = startX;
+ }
+
+ if (startY < oldY) {
+ top = startY;
+ bottom = oldY;
+ } else {
+ top = oldY;
+ bottom = startY;
+ }
+ } else {
+ left = right = startX;
+ top = bottom = startY;
+ }
+
+ final float widthOffset = mSquareWidth / 2f;
+ final float heightOffset = mSquareHeight / 2f;
+
+ invalidateRect.set((int) (left - widthOffset),
+ (int) (top - heightOffset), (int) (right + widthOffset),
+ (int) (bottom + heightOffset));
+ }
+
+ invalidate(invalidateRect);
+ } else {
+ invalidate();
+ }
+ }
+ }
+ }
+
+ private void handleActionUp(MotionEvent event) {
+ // report pattern detected
+ if (!mPattern.isEmpty() && mOnPatternListener != null) {
+ mPatternInProgress = false;
+ mOnPatternListener.onPatternDetected(mPattern);
+ invalidate();
+ }
+ if (PROFILE_DRAWING) {
+ if (mDrawingProfilingStarted) {
+ Debug.stopMethodTracing();
+ mDrawingProfilingStarted = false;
+ }
+ }
+ }
+
+ private void handleActionDown(MotionEvent event) {
+ resetPattern();
+ final float x = event.getX();
+ final float y = event.getY();
+ final Cell hitCell = detectAndAddHit(x, y);
+ if (hitCell != null && mOnPatternListener != null) {
+ mPatternInProgress = true;
+ mPatternDisplayMode = DisplayMode.Correct;
+ mOnPatternListener.onPatternStart();
+ } else if (mOnPatternListener != null) {
+ mPatternInProgress = false;
+ mOnPatternListener.onPatternCleared();
+ }
+ if (hitCell != null) {
+ final float startX = getCenterXForColumn(hitCell.column);
+ final float startY = getCenterYForRow(hitCell.row);
+
+ final float widthOffset = mSquareWidth / 2f;
+ final float heightOffset = mSquareHeight / 2f;
+
+ invalidate((int) (startX - widthOffset), (int) (startY - heightOffset),
+ (int) (startX + widthOffset), (int) (startY + heightOffset));
+ }
+ mInProgressX = x;
+ mInProgressY = y;
+ if (PROFILE_DRAWING) {
+ if (!mDrawingProfilingStarted) {
+ Debug.startMethodTracing("LockPatternDrawing");
+ mDrawingProfilingStarted = true;
+ }
+ }
+ }
+
private float getCenterXForColumn(int column) {
return mPaddingLeft + column * mSquareWidth + mSquareWidth / 2f;
}
diff --git a/core/jni/android/graphics/SurfaceTexture.cpp b/core/jni/android/graphics/SurfaceTexture.cpp
index 3f922f6..0d28cb1 100644
--- a/core/jni/android/graphics/SurfaceTexture.cpp
+++ b/core/jni/android/graphics/SurfaceTexture.cpp
@@ -91,6 +91,8 @@
virtual void onFrameAvailable();
private:
+ static JNIEnv* getJNIEnv();
+
jobject mWeakThiz;
jclass mClazz;
};
@@ -101,17 +103,37 @@
mClazz((jclass)env->NewGlobalRef(clazz))
{}
+JNIEnv* JNISurfaceTextureContext::getJNIEnv() {
+ JNIEnv* env;
+ JavaVMAttachArgs args = {JNI_VERSION_1_4, NULL, NULL};
+ JavaVM* vm = AndroidRuntime::getJavaVM();
+ int result = vm->AttachCurrentThread(&env, (void*) &args);
+ if (result != JNI_OK) {
+ LOGE("thread attach failed: %#x", result);
+ return NULL;
+ }
+ return env;
+}
+
JNISurfaceTextureContext::~JNISurfaceTextureContext()
{
- JNIEnv *env = AndroidRuntime::getJNIEnv();
- env->DeleteGlobalRef(mWeakThiz);
- env->DeleteGlobalRef(mClazz);
+ JNIEnv* env = getJNIEnv();
+ if (env != NULL) {
+ env->DeleteGlobalRef(mWeakThiz);
+ env->DeleteGlobalRef(mClazz);
+ } else {
+ LOGW("leaking JNI object references");
+ }
}
void JNISurfaceTextureContext::onFrameAvailable()
{
- JNIEnv *env = AndroidRuntime::getJNIEnv();
- env->CallStaticVoidMethod(mClazz, fields.postEvent, mWeakThiz);
+ JNIEnv *env = getJNIEnv();
+ if (env != NULL) {
+ env->CallStaticVoidMethod(mClazz, fields.postEvent, mWeakThiz);
+ } else {
+ LOGW("onFrameAvailable event will not posted");
+ }
}
// ----------------------------------------------------------------------------
diff --git a/core/res/res/layout/action_bar_title_item.xml b/core/res/res/layout/action_bar_title_item.xml
index d8b729d..e803b26 100644
--- a/core/res/res/layout/action_bar_title_item.xml
+++ b/core/res/res/layout/action_bar_title_item.xml
@@ -17,17 +17,32 @@
<LinearLayout xmlns:android="http://schemas.android.com/apk/res/android"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
- android:orientation="vertical"
- android:paddingRight="32dip" >
- <TextView android:id="@+id/action_bar_title"
- android:layout_width="wrap_content"
- android:layout_height="wrap_content"
- android:singleLine="true"
- android:ellipsize="end" />
- <TextView android:id="@+id/action_bar_subtitle"
- android:layout_width="wrap_content"
- android:layout_height="wrap_content"
- android:singleLine="true"
- android:ellipsize="end"
- android:visibility="gone" />
+ android:orientation="horizontal"
+ android:paddingRight="16dip"
+ android:background="?android:attr/selectableItemBackground"
+ android:enabled="false">
+
+ <ImageView android:id="@android:id/up"
+ android:src="?android:attr/homeAsUpIndicator"
+ android:layout_gravity="center_vertical|left"
+ android:visibility="gone"
+ android:layout_width="wrap_content"
+ android:layout_height="wrap_content" />
+
+ <LinearLayout android:layout_width="wrap_content"
+ android:layout_height="wrap_content"
+ android:layout_gravity="center_vertical|left"
+ android:orientation="vertical">
+ <TextView android:id="@+id/action_bar_title"
+ android:layout_width="wrap_content"
+ android:layout_height="wrap_content"
+ android:singleLine="true"
+ android:ellipsize="end" />
+ <TextView android:id="@+id/action_bar_subtitle"
+ android:layout_width="wrap_content"
+ android:layout_height="wrap_content"
+ android:singleLine="true"
+ android:ellipsize="end"
+ android:visibility="gone" />
+ </LinearLayout>
</LinearLayout>
diff --git a/core/res/res/layout/alert_dialog_holo.xml b/core/res/res/layout/alert_dialog_holo.xml
index 1a3573e..2185467 100644
--- a/core/res/res/layout/alert_dialog_holo.xml
+++ b/core/res/res/layout/alert_dialog_holo.xml
@@ -82,7 +82,9 @@
android:layout_width="match_parent"
android:layout_height="wrap_content"
android:paddingLeft="16dip"
- android:paddingRight="16dip" />
+ android:paddingRight="16dip"
+ android:paddingTop="8dip"
+ android:paddingBottom="8dip"/>
</ScrollView>
</LinearLayout>
diff --git a/core/res/res/layout/keyguard_screen_status_land.xml b/core/res/res/layout/keyguard_screen_status_land.xml
index 259a3af..8a02e1f 100644
--- a/core/res/res/layout/keyguard_screen_status_land.xml
+++ b/core/res/res/layout/keyguard_screen_status_land.xml
@@ -133,16 +133,4 @@
android:textAppearance="?android:attr/textAppearanceMedium"
/>
- <TextView
- android:id="@+id/propertyOf"
- android:lineSpacingExtra="8dip"
- android:layout_width="wrap_content"
- android:layout_height="wrap_content"
- android:textAppearance="?android:attr/textAppearanceMedium"
- android:textSize="17sp"
- android:layout_marginTop="20dip"
- android:singleLine="false"
- android:textColor="@color/lockscreen_owner_info"
- android:visibility="gone"
- />
</LinearLayout>
diff --git a/core/res/res/layout/keyguard_screen_status_port.xml b/core/res/res/layout/keyguard_screen_status_port.xml
index 680c073..1e87fb3 100644
--- a/core/res/res/layout/keyguard_screen_status_port.xml
+++ b/core/res/res/layout/keyguard_screen_status_port.xml
@@ -130,16 +130,4 @@
android:textAppearance="?android:attr/textAppearanceMedium"
/>
- <TextView
- android:id="@+id/propertyOf"
- android:lineSpacingExtra="8dip"
- android:layout_width="wrap_content"
- android:layout_height="wrap_content"
- android:layout_marginTop="20dip"
- android:textAppearance="?android:attr/textAppearanceMedium"
- android:textSize="17sp"
- android:singleLine="false"
- android:visibility="gone"
- android:textColor="@color/lockscreen_owner_info"
- />
</LinearLayout>
diff --git a/core/res/res/layout/keyguard_screen_unlock_portrait.xml b/core/res/res/layout/keyguard_screen_unlock_portrait.xml
index 0132b6c..4ffa3405 100644
--- a/core/res/res/layout/keyguard_screen_unlock_portrait.xml
+++ b/core/res/res/layout/keyguard_screen_unlock_portrait.xml
@@ -63,10 +63,8 @@
<LinearLayout
android:orientation="horizontal"
- android:layout_width="match_parent"
+ android:layout_width="wrap_content"
android:layout_height="wrap_content"
- android:layout_marginTop="0dip"
- android:layout_marginLeft="12dip"
android:layout_gravity="right">
<TextView
@@ -114,8 +112,8 @@
<com.android.internal.widget.LockPatternView
android:id="@+id/lockPattern"
- android:layout_width="match_parent"
- android:layout_height="match_parent"
+ android:layout_width="300dip"
+ android:layout_height="300dip"
android:layout_rowWeight="1"
android:layout_marginTop="8dip"
android:layout_marginRight="8dip"
diff --git a/core/res/res/values-af-rZA/arrays.xml b/core/res/res/values-af-rZA/arrays.xml
new file mode 100644
index 0000000..5bff0f4
--- /dev/null
+++ b/core/res/res/values-af-rZA/arrays.xml
@@ -0,0 +1,32 @@
+<?xml version="1.0" encoding="utf-8"?>
+<!--
+/* //device/apps/common/assets/res/any/colors.xml
+**
+** Copyright 2006, Google Inc.
+**
+** 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.
+*/
+-->
+<resources>
+
+ <!-- Do not translate. -->
+ <integer-array name="maps_starting_lat_lng">
+ <item>-30559482</item>
+ <item>22937506</item>
+ </integer-array>
+ <!-- Do not translate. -->
+ <integer-array name="maps_starting_zoom">
+ <item>4</item>
+ </integer-array>
+
+</resources>
diff --git a/core/res/res/values-am-rET/arrays.xml b/core/res/res/values-am-rET/arrays.xml
new file mode 100644
index 0000000..67e8b08
--- /dev/null
+++ b/core/res/res/values-am-rET/arrays.xml
@@ -0,0 +1,32 @@
+<?xml version="1.0" encoding="utf-8"?>
+<!--
+/* //device/apps/common/assets/res/any/colors.xml
+**
+** Copyright 2006, Google Inc.
+**
+** 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.
+*/
+-->
+<resources>
+
+ <!-- Do not translate. -->
+ <integer-array name="maps_starting_lat_lng">
+ <item>9145000</item>
+ <item>40489673</item>
+ </integer-array>
+ <!-- Do not translate. -->
+ <integer-array name="maps_starting_zoom">
+ <item>4</item>
+ </integer-array>
+
+</resources>
diff --git a/core/res/res/values-fa-rIR/arrays.xml b/core/res/res/values-fa-rIR/arrays.xml
new file mode 100644
index 0000000..de0f368
--- /dev/null
+++ b/core/res/res/values-fa-rIR/arrays.xml
@@ -0,0 +1,32 @@
+<?xml version="1.0" encoding="utf-8"?>
+<!--
+/* //device/apps/common/assets/res/any/colors.xml
+**
+** Copyright 2006, Google Inc.
+**
+** 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.
+*/
+-->
+<resources>
+
+ <!-- Do not translate. -->
+ <integer-array name="maps_starting_lat_lng">
+ <item>32427908</item>
+ <item>53688046</item>
+ </integer-array>
+ <!-- Do not translate. -->
+ <integer-array name="maps_starting_zoom">
+ <item>4</item>
+ </integer-array>
+
+</resources>
diff --git a/core/res/res/values-ms-rMY/arrays.xml b/core/res/res/values-ms-rMY/arrays.xml
new file mode 100644
index 0000000..3a708e6
--- /dev/null
+++ b/core/res/res/values-ms-rMY/arrays.xml
@@ -0,0 +1,32 @@
+<?xml version="1.0" encoding="utf-8"?>
+<!--
+/* //device/apps/common/assets/res/any/colors.xml
+**
+** Copyright 2006, Google Inc.
+**
+** 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.
+*/
+-->
+<resources>
+
+ <!-- Do not translate. -->
+ <integer-array name="maps_starting_lat_lng">
+ <item>4210484</item>
+ <item>101975766</item>
+ </integer-array>
+ <!-- Do not translate. -->
+ <integer-array name="maps_starting_zoom">
+ <item>4</item>
+ </integer-array>
+
+</resources>
diff --git a/core/res/res/values-sw-rTZ/arrays.xml b/core/res/res/values-sw-rTZ/arrays.xml
new file mode 100644
index 0000000..d1018a7
--- /dev/null
+++ b/core/res/res/values-sw-rTZ/arrays.xml
@@ -0,0 +1,32 @@
+<?xml version="1.0" encoding="utf-8"?>
+<!--
+/* //device/apps/common/assets/res/any/colors.xml
+**
+** Copyright 2006, Google Inc.
+**
+** 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.
+*/
+-->
+<resources>
+
+ <!-- Do not translate. -->
+ <integer-array name="maps_starting_lat_lng">
+ <item>-6369028</item>
+ <item>34888822</item>
+ </integer-array>
+ <!-- Do not translate. -->
+ <integer-array name="maps_starting_zoom">
+ <item>4</item>
+ </integer-array>
+
+</resources>
diff --git a/core/res/res/values-zu-rZA/arrays.xml b/core/res/res/values-zu-rZA/arrays.xml
new file mode 100644
index 0000000..5bff0f4
--- /dev/null
+++ b/core/res/res/values-zu-rZA/arrays.xml
@@ -0,0 +1,32 @@
+<?xml version="1.0" encoding="utf-8"?>
+<!--
+/* //device/apps/common/assets/res/any/colors.xml
+**
+** Copyright 2006, Google Inc.
+**
+** 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.
+*/
+-->
+<resources>
+
+ <!-- Do not translate. -->
+ <integer-array name="maps_starting_lat_lng">
+ <item>-30559482</item>
+ <item>22937506</item>
+ </integer-array>
+ <!-- Do not translate. -->
+ <integer-array name="maps_starting_zoom">
+ <item>4</item>
+ </integer-array>
+
+</resources>
diff --git a/core/res/res/values/attrs.xml b/core/res/res/values/attrs.xml
index a59af1a..989adbd 100755
--- a/core/res/res/values/attrs.xml
+++ b/core/res/res/values/attrs.xml
@@ -1661,6 +1661,10 @@
<attr name="paddingRight" format="dimension" />
<!-- Sets the padding, in pixels, of the bottom edge; see {@link android.R.attr#padding}. -->
<attr name="paddingBottom" format="dimension" />
+ <!-- Sets the padding, in pixels, of the start edge; see {@link android.R.attr#padding}. -->
+ <attr name="paddingStart" format="dimension" />
+ <!-- Sets the padding, in pixels, of the end edge; see {@link android.R.attr#padding}. -->
+ <attr name="paddingEnd" format="dimension" />
<!-- Boolean that controls whether a view can take focus. By default the user can not
move focus to a view; by setting this attribute to true the view is
diff --git a/core/res/res/values/colors.xml b/core/res/res/values/colors.xml
index e76c0e5..2a1ebfc 100644
--- a/core/res/res/values/colors.xml
+++ b/core/res/res/values/colors.xml
@@ -18,7 +18,7 @@
*/
-->
<resources>
- <drawable name="screen_background_light">#fff3f3f3</drawable>
+ <drawable name="screen_background_light">#ffffffff</drawable>
<drawable name="screen_background_dark">#ff000000</drawable>
<drawable name="status_bar_closed_default_background">#ff000000</drawable>
<drawable name="status_bar_opened_default_background">#ff000000</drawable>
@@ -37,7 +37,7 @@
<color name="black">#ff000000</color>
<color name="transparent">#00000000</color>
<color name="background_dark">#ff000000</color>
- <color name="background_light">#fff3f3f3</color>
+ <color name="background_light">#ffffffff</color>
<color name="bright_foreground_dark">@android:color/background_light</color>
<color name="bright_foreground_light">@android:color/background_dark</color>
<color name="bright_foreground_dark_disabled">#80ffffff</color>
diff --git a/core/res/res/values/public.xml b/core/res/res/values/public.xml
index e02496c..580c204 100644
--- a/core/res/res/values/public.xml
+++ b/core/res/res/values/public.xml
@@ -1775,4 +1775,7 @@
<public type="integer" name="status_bar_notification_info_maxnum" />
<public type="string" name="status_bar_notification_info_overflow" />
+ <public type="attr" name="paddingStart"/>
+ <public type="attr" name="paddingEnd"/>
+
</resources>
diff --git a/core/res/res/values/strings.xml b/core/res/res/values/strings.xml
index d9e7dac..b5f4084 100755
--- a/core/res/res/values/strings.xml
+++ b/core/res/res/values/strings.xml
@@ -2608,6 +2608,11 @@
<!-- USB_STORAGE_ERROR dialog ok button-->
<string name="dlg_ok">OK</string>
+ <!-- USB_PREFERENCES: When the user connects the phone to a computer via USB, we show a notification asking if he wants to share files across. This is the title -->
+ <string name="usb_preferences_notification_title">USB connected</string>
+ <!-- See USB_PREFERENCES. This is the message. -->
+ <string name="usb_preferece_notification_message">Select to configure USB file transfer.</string>
+
<!-- External media format dialog strings -->
<!-- This is the label for the activity, and should never be visible to the user. -->
<!-- See EXTMEDIA_FORMAT. EXTMEDIA_FORMAT_DIALOG: After the user selects the notification, a dialog is shown asking if he wants to format the SD card. This is the title. [CHAR LIMIT=20] -->
diff --git a/core/tests/coretests/src/android/net/NetworkStatsTest.java b/core/tests/coretests/src/android/net/NetworkStatsTest.java
index 5250a7c..3cb64c7 100644
--- a/core/tests/coretests/src/android/net/NetworkStatsTest.java
+++ b/core/tests/coretests/src/android/net/NetworkStatsTest.java
@@ -16,6 +16,8 @@
package android.net;
+import static android.net.NetworkStats.TAG_NONE;
+
import android.test.suitebuilder.annotation.SmallTest;
import junit.framework.TestCase;
@@ -29,14 +31,14 @@
public void testFindIndex() throws Exception {
final NetworkStats stats = new NetworkStats(TEST_START, 3)
- .addEntry(TEST_IFACE, 100, 1024, 0)
- .addEntry(TEST_IFACE, 101, 0, 1024)
- .addEntry(TEST_IFACE, 102, 1024, 1024);
+ .addEntry(TEST_IFACE, 100, TAG_NONE, 1024L, 0L)
+ .addEntry(TEST_IFACE, 101, TAG_NONE, 0L, 1024L)
+ .addEntry(TEST_IFACE, 102, TAG_NONE, 1024L, 1024L);
- assertEquals(2, stats.findIndex(TEST_IFACE, 102));
- assertEquals(2, stats.findIndex(TEST_IFACE, 102));
- assertEquals(0, stats.findIndex(TEST_IFACE, 100));
- assertEquals(-1, stats.findIndex(TEST_IFACE, 6));
+ assertEquals(2, stats.findIndex(TEST_IFACE, 102, TAG_NONE));
+ assertEquals(2, stats.findIndex(TEST_IFACE, 102, TAG_NONE));
+ assertEquals(0, stats.findIndex(TEST_IFACE, 100, TAG_NONE));
+ assertEquals(-1, stats.findIndex(TEST_IFACE, 6, TAG_NONE));
}
public void testAddEntryGrow() throws Exception {
@@ -45,15 +47,15 @@
assertEquals(0, stats.size);
assertEquals(2, stats.iface.length);
- stats.addEntry(TEST_IFACE, TEST_UID, 1L, 2L);
- stats.addEntry(TEST_IFACE, TEST_UID, 2L, 2L);
+ stats.addEntry(TEST_IFACE, TEST_UID, TAG_NONE, 1L, 2L);
+ stats.addEntry(TEST_IFACE, TEST_UID, TAG_NONE, 2L, 2L);
assertEquals(2, stats.size);
assertEquals(2, stats.iface.length);
- stats.addEntry(TEST_IFACE, TEST_UID, 3L, 4L);
- stats.addEntry(TEST_IFACE, TEST_UID, 4L, 4L);
- stats.addEntry(TEST_IFACE, TEST_UID, 5L, 5L);
+ stats.addEntry(TEST_IFACE, TEST_UID, TAG_NONE, 3L, 4L);
+ stats.addEntry(TEST_IFACE, TEST_UID, TAG_NONE, 4L, 4L);
+ stats.addEntry(TEST_IFACE, TEST_UID, TAG_NONE, 5L, 5L);
assertEquals(5, stats.size);
assertTrue(stats.iface.length >= 5);
@@ -65,14 +67,31 @@
assertEquals(5L, stats.rx[4]);
}
+ public void testCombineExisting() throws Exception {
+ final NetworkStats stats = new NetworkStats(TEST_START, 10);
+
+ stats.addEntry(TEST_IFACE, 1001, TAG_NONE, 512L, 256L);
+ stats.addEntry(TEST_IFACE, 1001, 0xff, 128L, 128L);
+ stats.combineEntry(TEST_IFACE, 1001, TAG_NONE, -128L, -128L);
+
+ assertStatsEntry(stats, 0, TEST_IFACE, 1001, TAG_NONE, 384L, 128L);
+ assertStatsEntry(stats, 1, TEST_IFACE, 1001, 0xff, 128L, 128L);
+
+ // now try combining that should create row
+ stats.combineEntry(TEST_IFACE, 5005, TAG_NONE, 128L, 128L);
+ assertStatsEntry(stats, 2, TEST_IFACE, 5005, TAG_NONE, 128L, 128L);
+ stats.combineEntry(TEST_IFACE, 5005, TAG_NONE, 128L, 128L);
+ assertStatsEntry(stats, 2, TEST_IFACE, 5005, TAG_NONE, 256L, 256L);
+ }
+
public void testSubtractIdenticalData() throws Exception {
final NetworkStats before = new NetworkStats(TEST_START, 2)
- .addEntry(TEST_IFACE, 100, 1024, 0)
- .addEntry(TEST_IFACE, 101, 0, 1024);
+ .addEntry(TEST_IFACE, 100, TAG_NONE, 1024L, 0L)
+ .addEntry(TEST_IFACE, 101, TAG_NONE, 0L, 1024L);
final NetworkStats after = new NetworkStats(TEST_START, 2)
- .addEntry(TEST_IFACE, 100, 1024, 0)
- .addEntry(TEST_IFACE, 101, 0, 1024);
+ .addEntry(TEST_IFACE, 100, TAG_NONE, 1024L, 0L)
+ .addEntry(TEST_IFACE, 101, TAG_NONE, 0L, 1024L);
final NetworkStats result = after.subtract(before);
@@ -85,12 +104,12 @@
public void testSubtractIdenticalRows() throws Exception {
final NetworkStats before = new NetworkStats(TEST_START, 2)
- .addEntry(TEST_IFACE, 100, 1024, 0)
- .addEntry(TEST_IFACE, 101, 0, 1024);
+ .addEntry(TEST_IFACE, 100, TAG_NONE, 1024L, 0L)
+ .addEntry(TEST_IFACE, 101, TAG_NONE, 0L, 1024L);
final NetworkStats after = new NetworkStats(TEST_START, 2)
- .addEntry(TEST_IFACE, 100, 1025, 2)
- .addEntry(TEST_IFACE, 101, 3, 1028);
+ .addEntry(TEST_IFACE, 100, TAG_NONE, 1025L, 2L)
+ .addEntry(TEST_IFACE, 101, TAG_NONE, 3L, 1028L);
final NetworkStats result = after.subtract(before);
@@ -103,13 +122,13 @@
public void testSubtractNewRows() throws Exception {
final NetworkStats before = new NetworkStats(TEST_START, 2)
- .addEntry(TEST_IFACE, 100, 1024, 0)
- .addEntry(TEST_IFACE, 101, 0, 1024);
+ .addEntry(TEST_IFACE, 100, TAG_NONE, 1024L, 0L)
+ .addEntry(TEST_IFACE, 101, TAG_NONE, 0L, 1024L);
final NetworkStats after = new NetworkStats(TEST_START, 3)
- .addEntry(TEST_IFACE, 100, 1024, 0)
- .addEntry(TEST_IFACE, 101, 0, 1024)
- .addEntry(TEST_IFACE, 102, 1024, 1024);
+ .addEntry(TEST_IFACE, 100, TAG_NONE, 1024L, 0L)
+ .addEntry(TEST_IFACE, 101, TAG_NONE, 0L, 1024L)
+ .addEntry(TEST_IFACE, 102, TAG_NONE, 1024L, 1024L);
final NetworkStats result = after.subtract(before);
@@ -122,4 +141,13 @@
assertEquals(1024, result.tx[2]);
}
+ private static void assertStatsEntry(
+ NetworkStats stats, int i, String iface, int uid, int tag, long rx, long tx) {
+ assertEquals(iface, stats.iface[i]);
+ assertEquals(uid, stats.uid[i]);
+ assertEquals(tag, stats.tag[i]);
+ assertEquals(rx, stats.rx[i]);
+ assertEquals(tx, stats.tx[i]);
+ }
+
}
diff --git a/graphics/java/android/renderscript/Allocation.java b/graphics/java/android/renderscript/Allocation.java
index eeab9b4..e900584 100644
--- a/graphics/java/android/renderscript/Allocation.java
+++ b/graphics/java/android/renderscript/Allocation.java
@@ -79,7 +79,7 @@
/**
* GRAPHICS_TEXTURE The allcation will be used as a texture
- * source by one or more graphcics programs.
+ * source by one or more graphics programs.
*
*/
public static final int USAGE_GRAPHICS_TEXTURE = 0x0002;
diff --git a/graphics/java/android/renderscript/Element.java b/graphics/java/android/renderscript/Element.java
index 5a72dbe..f844331 100644
--- a/graphics/java/android/renderscript/Element.java
+++ b/graphics/java/android/renderscript/Element.java
@@ -383,6 +383,41 @@
return rs.mElement_FLOAT_4;
}
+ public static Element F64_2(RenderScript rs) {
+ if(rs.mElement_DOUBLE_2 == null) {
+ rs.mElement_DOUBLE_2 = createVector(rs, DataType.FLOAT_64, 2);
+ }
+ return rs.mElement_DOUBLE_2;
+ }
+
+ public static Element F64_3(RenderScript rs) {
+ if(rs.mElement_DOUBLE_3 == null) {
+ rs.mElement_DOUBLE_3 = createVector(rs, DataType.FLOAT_64, 3);
+ }
+ return rs.mElement_DOUBLE_3;
+ }
+
+ public static Element F64_4(RenderScript rs) {
+ if(rs.mElement_DOUBLE_4 == null) {
+ rs.mElement_DOUBLE_4 = createVector(rs, DataType.FLOAT_64, 4);
+ }
+ return rs.mElement_DOUBLE_4;
+ }
+
+ public static Element U8_2(RenderScript rs) {
+ if(rs.mElement_UCHAR_2 == null) {
+ rs.mElement_UCHAR_2 = createVector(rs, DataType.UNSIGNED_8, 2);
+ }
+ return rs.mElement_UCHAR_2;
+ }
+
+ public static Element U8_3(RenderScript rs) {
+ if(rs.mElement_UCHAR_3 == null) {
+ rs.mElement_UCHAR_3 = createVector(rs, DataType.UNSIGNED_8, 3);
+ }
+ return rs.mElement_UCHAR_3;
+ }
+
public static Element U8_4(RenderScript rs) {
if(rs.mElement_UCHAR_4 == null) {
rs.mElement_UCHAR_4 = createVector(rs, DataType.UNSIGNED_8, 4);
@@ -390,6 +425,153 @@
return rs.mElement_UCHAR_4;
}
+ public static Element I8_2(RenderScript rs) {
+ if(rs.mElement_CHAR_2 == null) {
+ rs.mElement_CHAR_2 = createVector(rs, DataType.SIGNED_8, 2);
+ }
+ return rs.mElement_CHAR_2;
+ }
+
+ public static Element I8_3(RenderScript rs) {
+ if(rs.mElement_CHAR_3 == null) {
+ rs.mElement_CHAR_3 = createVector(rs, DataType.SIGNED_8, 3);
+ }
+ return rs.mElement_CHAR_3;
+ }
+
+ public static Element I8_4(RenderScript rs) {
+ if(rs.mElement_CHAR_4 == null) {
+ rs.mElement_CHAR_4 = createVector(rs, DataType.SIGNED_8, 4);
+ }
+ return rs.mElement_CHAR_4;
+ }
+
+ public static Element U16_2(RenderScript rs) {
+ if(rs.mElement_USHORT_2 == null) {
+ rs.mElement_USHORT_2 = createVector(rs, DataType.UNSIGNED_16, 2);
+ }
+ return rs.mElement_USHORT_2;
+ }
+
+ public static Element U16_3(RenderScript rs) {
+ if(rs.mElement_USHORT_3 == null) {
+ rs.mElement_USHORT_3 = createVector(rs, DataType.UNSIGNED_16, 3);
+ }
+ return rs.mElement_USHORT_3;
+ }
+
+ public static Element U16_4(RenderScript rs) {
+ if(rs.mElement_USHORT_4 == null) {
+ rs.mElement_USHORT_4 = createVector(rs, DataType.UNSIGNED_16, 4);
+ }
+ return rs.mElement_USHORT_4;
+ }
+
+ public static Element I16_2(RenderScript rs) {
+ if(rs.mElement_SHORT_2 == null) {
+ rs.mElement_SHORT_2 = createVector(rs, DataType.SIGNED_16, 2);
+ }
+ return rs.mElement_SHORT_2;
+ }
+
+ public static Element I16_3(RenderScript rs) {
+ if(rs.mElement_SHORT_3 == null) {
+ rs.mElement_SHORT_3 = createVector(rs, DataType.SIGNED_16, 3);
+ }
+ return rs.mElement_SHORT_3;
+ }
+
+ public static Element I16_4(RenderScript rs) {
+ if(rs.mElement_SHORT_4 == null) {
+ rs.mElement_SHORT_4 = createVector(rs, DataType.SIGNED_16, 4);
+ }
+ return rs.mElement_SHORT_4;
+ }
+
+ public static Element U32_2(RenderScript rs) {
+ if(rs.mElement_UINT_2 == null) {
+ rs.mElement_UINT_2 = createVector(rs, DataType.UNSIGNED_32, 2);
+ }
+ return rs.mElement_UINT_2;
+ }
+
+ public static Element U32_3(RenderScript rs) {
+ if(rs.mElement_UINT_3 == null) {
+ rs.mElement_UINT_3 = createVector(rs, DataType.UNSIGNED_32, 3);
+ }
+ return rs.mElement_UINT_3;
+ }
+
+ public static Element U32_4(RenderScript rs) {
+ if(rs.mElement_UINT_4 == null) {
+ rs.mElement_UINT_4 = createVector(rs, DataType.UNSIGNED_32, 4);
+ }
+ return rs.mElement_UINT_4;
+ }
+
+ public static Element I32_2(RenderScript rs) {
+ if(rs.mElement_INT_2 == null) {
+ rs.mElement_INT_2 = createVector(rs, DataType.SIGNED_32, 2);
+ }
+ return rs.mElement_INT_2;
+ }
+
+ public static Element I32_3(RenderScript rs) {
+ if(rs.mElement_INT_3 == null) {
+ rs.mElement_INT_3 = createVector(rs, DataType.SIGNED_32, 3);
+ }
+ return rs.mElement_INT_3;
+ }
+
+ public static Element I32_4(RenderScript rs) {
+ if(rs.mElement_INT_4 == null) {
+ rs.mElement_INT_4 = createVector(rs, DataType.SIGNED_32, 4);
+ }
+ return rs.mElement_INT_4;
+ }
+
+ public static Element U64_2(RenderScript rs) {
+ if(rs.mElement_ULONG_2 == null) {
+ rs.mElement_ULONG_2 = createVector(rs, DataType.UNSIGNED_64, 2);
+ }
+ return rs.mElement_ULONG_2;
+ }
+
+ public static Element U64_3(RenderScript rs) {
+ if(rs.mElement_ULONG_3 == null) {
+ rs.mElement_ULONG_3 = createVector(rs, DataType.UNSIGNED_64, 3);
+ }
+ return rs.mElement_ULONG_3;
+ }
+
+ public static Element U64_4(RenderScript rs) {
+ if(rs.mElement_ULONG_4 == null) {
+ rs.mElement_ULONG_4 = createVector(rs, DataType.UNSIGNED_64, 4);
+ }
+ return rs.mElement_ULONG_4;
+ }
+
+ public static Element I64_2(RenderScript rs) {
+ if(rs.mElement_LONG_2 == null) {
+ rs.mElement_LONG_2 = createVector(rs, DataType.SIGNED_64, 2);
+ }
+ return rs.mElement_LONG_2;
+ }
+
+ public static Element I64_3(RenderScript rs) {
+ if(rs.mElement_LONG_3 == null) {
+ rs.mElement_LONG_3 = createVector(rs, DataType.SIGNED_64, 3);
+ }
+ return rs.mElement_LONG_3;
+ }
+
+ public static Element I64_4(RenderScript rs) {
+ if(rs.mElement_LONG_4 == null) {
+ rs.mElement_LONG_4 = createVector(rs, DataType.SIGNED_64, 4);
+ }
+ return rs.mElement_LONG_4;
+ }
+
public static Element MATRIX_4X4(RenderScript rs) {
if(rs.mElement_MATRIX_4X4 == null) {
rs.mElement_MATRIX_4X4 = createUser(rs, DataType.MATRIX_4X4);
diff --git a/graphics/java/android/renderscript/RenderScript.java b/graphics/java/android/renderscript/RenderScript.java
index 2110e37..9941827 100644
--- a/graphics/java/android/renderscript/RenderScript.java
+++ b/graphics/java/android/renderscript/RenderScript.java
@@ -613,8 +613,43 @@
Element mElement_FLOAT_2;
Element mElement_FLOAT_3;
Element mElement_FLOAT_4;
+
+ Element mElement_DOUBLE_2;
+ Element mElement_DOUBLE_3;
+ Element mElement_DOUBLE_4;
+
+ Element mElement_UCHAR_2;
+ Element mElement_UCHAR_3;
Element mElement_UCHAR_4;
+ Element mElement_CHAR_2;
+ Element mElement_CHAR_3;
+ Element mElement_CHAR_4;
+
+ Element mElement_USHORT_2;
+ Element mElement_USHORT_3;
+ Element mElement_USHORT_4;
+
+ Element mElement_SHORT_2;
+ Element mElement_SHORT_3;
+ Element mElement_SHORT_4;
+
+ Element mElement_UINT_2;
+ Element mElement_UINT_3;
+ Element mElement_UINT_4;
+
+ Element mElement_INT_2;
+ Element mElement_INT_3;
+ Element mElement_INT_4;
+
+ Element mElement_ULONG_2;
+ Element mElement_ULONG_3;
+ Element mElement_ULONG_4;
+
+ Element mElement_LONG_2;
+ Element mElement_LONG_3;
+ Element mElement_LONG_4;
+
Element mElement_MATRIX_4X4;
Element mElement_MATRIX_3X3;
Element mElement_MATRIX_2X2;
diff --git a/libs/hwui/Caches.h b/libs/hwui/Caches.h
index 596781e..e64d8ac 100644
--- a/libs/hwui/Caches.h
+++ b/libs/hwui/Caches.h
@@ -74,7 +74,7 @@
struct CacheLogger {
CacheLogger() {
- LOGD("Creating OpenGL renderer caches");
+ INIT_LOGD("Creating OpenGL renderer caches");
}
}; // struct CacheLogger
diff --git a/libs/hwui/LayerRenderer.cpp b/libs/hwui/LayerRenderer.cpp
index 146e789..77e63d7 100644
--- a/libs/hwui/LayerRenderer.cpp
+++ b/libs/hwui/LayerRenderer.cpp
@@ -326,12 +326,17 @@
return false;
}
+ SkAutoLockPixels alp(*bitmap);
+
GLuint texture;
GLuint previousFbo;
GLenum format;
GLenum type;
+ GLenum error = GL_NO_ERROR;
+ bool status = false;
+
switch (bitmap->config()) {
case SkBitmap::kA8_Config:
format = GL_ALPHA;
@@ -352,10 +357,18 @@
break;
}
+ float alpha = layer->alpha;
+ SkXfermode::Mode mode = layer->mode;
+
+ layer->mode = SkXfermode::kSrc_Mode;
+ layer->alpha = 255;
+ layer->fbo = fbo;
+
glGetIntegerv(GL_FRAMEBUFFER_BINDING, (GLint*) &previousFbo);
glBindFramebuffer(GL_FRAMEBUFFER, fbo);
glGenTextures(1, &texture);
+ if ((error = glGetError()) != GL_NO_ERROR) goto error;
glActiveTexture(GL_TEXTURE0);
glBindTexture(GL_TEXTURE_2D, texture);
@@ -368,39 +381,48 @@
glTexImage2D(GL_TEXTURE_2D, 0, format, bitmap->width(), bitmap->height(),
0, format, type, NULL);
+ if ((error = glGetError()) != GL_NO_ERROR) goto error;
+
glFramebufferTexture2D(GL_FRAMEBUFFER, GL_COLOR_ATTACHMENT0,
GL_TEXTURE_2D, texture, 0);
+ if ((error = glGetError()) != GL_NO_ERROR) goto error;
- glBindTexture(GL_TEXTURE_2D, layer->texture);
+ {
+ LayerRenderer renderer(layer);
+ renderer.setViewport(bitmap->width(), bitmap->height());
+ renderer.OpenGLRenderer::prepareDirty(0.0f, 0.0f,
+ bitmap->width(), bitmap->height(), !layer->blend);
+ if ((error = glGetError()) != GL_NO_ERROR) goto error;
- float alpha = layer->alpha;
- SkXfermode::Mode mode = layer->mode;
+ {
+ Rect bounds;
+ bounds.set(0.0f, 0.0f, bitmap->width(), bitmap->height());
+ renderer.drawTextureLayer(layer, bounds);
- layer->mode = SkXfermode::kSrc_Mode;
- layer->alpha = 255;
- layer->fbo = fbo;
+ glReadPixels(0, 0, bitmap->width(), bitmap->height(), format,
+ type, bitmap->getPixels());
- LayerRenderer renderer(layer);
- renderer.setViewport(bitmap->width(), bitmap->height());
- renderer.OpenGLRenderer::prepareDirty(0.0f, 0.0f,
- bitmap->width(), bitmap->height(), !layer->blend);
+ if ((error = glGetError()) != GL_NO_ERROR) goto error;
+ }
- Rect bounds;
- bounds.set(0.0f, 0.0f, bitmap->width(), bitmap->height());
- renderer.drawTextureLayer(layer, bounds);
+ status = true;
+ }
- SkAutoLockPixels alp(*bitmap);
- glReadPixels(0, 0, bitmap->width(), bitmap->height(), format, type, bitmap->getPixels());
+error:
+#if DEBUG_OPENGL
+ if (error != GL_NO_ERROR) {
+ LOGD("GL error while copying layer into bitmap = 0x%x", error);
+ }
+#endif
glBindFramebuffer(GL_FRAMEBUFFER, previousFbo);
-
layer->mode = mode;
layer->alpha = alpha;
layer->fbo = 0;
glDeleteTextures(1, &texture);
caches.fboCache.put(fbo);
- return true;
+ return status;
}
return false;
}
diff --git a/libs/hwui/OpenGLRenderer.cpp b/libs/hwui/OpenGLRenderer.cpp
index 5343a05..88774c6 100644
--- a/libs/hwui/OpenGLRenderer.cpp
+++ b/libs/hwui/OpenGLRenderer.cpp
@@ -151,7 +151,6 @@
mSaveCount = 1;
glViewport(0, 0, mWidth, mHeight);
-
glDisable(GL_DITHER);
glEnable(GL_SCISSOR_TEST);
diff --git a/media/java/android/mtp/MtpDatabase.java b/media/java/android/mtp/MtpDatabase.java
index c9e0f6f..4e271c7 100644
--- a/media/java/android/mtp/MtpDatabase.java
+++ b/media/java/android/mtp/MtpDatabase.java
@@ -92,13 +92,18 @@
};
private static final String ID_WHERE = Files.FileColumns._ID + "=?";
private static final String PATH_WHERE = Files.FileColumns.DATA + "=?";
- private static final String PARENT_WHERE = Files.FileColumns.PARENT + "=?";
- private static final String PARENT_FORMAT_WHERE = PARENT_WHERE + " AND "
+
+ private static final String STORAGE_WHERE = Files.FileColumns.STORAGE_ID + "=?";
+ private static final String FORMAT_WHERE = Files.FileColumns.PARENT + "=?";
+ private static final String PARENT_WHERE = Files.FileColumns.FORMAT + "=?";
+ private static final String STORAGE_FORMAT_WHERE = STORAGE_WHERE + " AND "
+ Files.FileColumns.FORMAT + "=?";
- private static final String PARENT_STORAGE_WHERE = PARENT_WHERE + " AND "
- + Files.FileColumns.STORAGE_ID + "=?";
- private static final String PARENT_STORAGE_FORMAT_WHERE = PARENT_STORAGE_WHERE + " AND "
- + Files.FileColumns.FORMAT + "=?";
+ private static final String STORAGE_PARENT_WHERE = STORAGE_WHERE + " AND "
+ + Files.FileColumns.PARENT + "=?";
+ private static final String FORMAT_PARENT_WHERE = FORMAT_WHERE + " AND "
+ + Files.FileColumns.PARENT + "=?";
+ private static final String STORAGE_FORMAT_PARENT_WHERE = STORAGE_FORMAT_WHERE + " AND "
+ + Files.FileColumns.PARENT + "=?";
private final MediaScanner mMediaScanner;
@@ -249,26 +254,67 @@
}
private Cursor createObjectQuery(int storageID, int format, int parent) throws RemoteException {
- if (storageID != 0) {
- if (format != 0) {
- return mMediaProvider.query(mObjectsUri, ID_PROJECTION,
- PARENT_STORAGE_FORMAT_WHERE,
- new String[] { Integer.toString(parent), Integer.toString(storageID),
- Integer.toString(format) }, null);
+ if (storageID == 0xFFFFFFFF) {
+ // query all stores
+ if (format == 0) {
+ // query all formats
+ if (parent == 0) {
+ // query all objects
+ return mMediaProvider.query(mObjectsUri, ID_PROJECTION, null, null, null);
+ }
+ if (parent == 0xFFFFFFFF) {
+ // all objects in root of store
+ parent = 0;
+ }
+ return mMediaProvider.query(mObjectsUri, ID_PROJECTION, PARENT_WHERE,
+ new String[] { Integer.toString(parent) }, null);
} else {
- return mMediaProvider.query(mObjectsUri, ID_PROJECTION,
- PARENT_STORAGE_WHERE, new String[]
- { Integer.toString(parent), Integer.toString(storageID) }, null);
+ // query specific format
+ if (parent == 0) {
+ // query all objects
+ return mMediaProvider.query(mObjectsUri, ID_PROJECTION, FORMAT_WHERE,
+ new String[] { Integer.toString(format) }, null);
+ }
+ if (parent == 0xFFFFFFFF) {
+ // all objects in root of store
+ parent = 0;
+ }
+ return mMediaProvider.query(mObjectsUri, ID_PROJECTION, FORMAT_PARENT_WHERE,
+ new String[] { Integer.toString(format), Integer.toString(parent) }, null);
}
} else {
- if (format != 0) {
- return mMediaProvider.query(mObjectsUri, ID_PROJECTION,
- PARENT_FORMAT_WHERE,
- new String[] { Integer.toString(parent), Integer.toString(format) },
- null);
+ // query specific store
+ if (format == 0) {
+ // query all formats
+ if (parent == 0) {
+ // query all objects
+ return mMediaProvider.query(mObjectsUri, ID_PROJECTION, STORAGE_WHERE,
+ new String[] { Integer.toString(storageID) }, null);
+ }
+ if (parent == 0xFFFFFFFF) {
+ // all objects in root of store
+ parent = 0;
+ }
+ return mMediaProvider.query(mObjectsUri, ID_PROJECTION, STORAGE_PARENT_WHERE,
+ new String[] { Integer.toString(storageID), Integer.toString(parent) },
+ null);
} else {
- return mMediaProvider.query(mObjectsUri, ID_PROJECTION,
- PARENT_WHERE, new String[] { Integer.toString(parent) }, null);
+ // query specific format
+ if (parent == 0) {
+ // query all objects
+ return mMediaProvider.query(mObjectsUri, ID_PROJECTION, STORAGE_FORMAT_WHERE,
+ new String[] { Integer.toString(storageID), Integer.toString(format) },
+ null);
+ }
+ if (parent == 0xFFFFFFFF) {
+ // all objects in root of store
+ parent = 0;
+ }
+ return mMediaProvider.query(mObjectsUri, ID_PROJECTION, STORAGE_FORMAT_PARENT_WHERE,
+ new String[] { Integer.toString(storageID),
+ Integer.toString(format),
+ Integer.toString(parent) },
+ null);
}
}
}
diff --git a/media/jni/soundpool/SoundPool.cpp b/media/jni/soundpool/SoundPool.cpp
index 3ea13a6..4ffb2c0 100644
--- a/media/jni/soundpool/SoundPool.cpp
+++ b/media/jni/soundpool/SoundPool.cpp
@@ -23,7 +23,6 @@
// XXX needed for timing latency
#include <utils/Timers.h>
-#include <sys/resource.h>
#include <media/AudioTrack.h>
#include <media/mediaplayer.h>
diff --git a/media/libmedia/ToneGenerator.cpp b/media/libmedia/ToneGenerator.cpp
index 9f1b3d6..7c2200e 100644
--- a/media/libmedia/ToneGenerator.cpp
+++ b/media/libmedia/ToneGenerator.cpp
@@ -21,7 +21,6 @@
#include <stdio.h>
#include <math.h>
#include <utils/Log.h>
-#include <sys/resource.h>
#include <utils/RefBase.h>
#include <utils/Timers.h>
#include <cutils/properties.h>
diff --git a/media/libmediaplayerservice/MetadataRetrieverClient.cpp b/media/libmediaplayerservice/MetadataRetrieverClient.cpp
index 06fb103..d574ea3 100644
--- a/media/libmediaplayerservice/MetadataRetrieverClient.cpp
+++ b/media/libmediaplayerservice/MetadataRetrieverClient.cpp
@@ -21,7 +21,6 @@
#include <sys/types.h>
#include <sys/stat.h>
-#include <sys/resource.h>
#include <dirent.h>
#include <unistd.h>
diff --git a/media/libstagefright/AACWriter.cpp b/media/libstagefright/AACWriter.cpp
index 8413208..d133e91 100644
--- a/media/libstagefright/AACWriter.cpp
+++ b/media/libstagefright/AACWriter.cpp
@@ -27,7 +27,6 @@
#include <media/stagefright/MetaData.h>
#include <media/mediarecorder.h>
#include <sys/prctl.h>
-#include <sys/resource.h>
#include <fcntl.h>
namespace android {
diff --git a/media/libstagefright/AMRWriter.cpp b/media/libstagefright/AMRWriter.cpp
index b10d52c..6436071 100644
--- a/media/libstagefright/AMRWriter.cpp
+++ b/media/libstagefright/AMRWriter.cpp
@@ -23,7 +23,6 @@
#include <media/stagefright/MetaData.h>
#include <media/mediarecorder.h>
#include <sys/prctl.h>
-#include <sys/resource.h>
#include <sys/types.h>
#include <sys/stat.h>
#include <fcntl.h>
diff --git a/media/libstagefright/MPEG4Writer.cpp b/media/libstagefright/MPEG4Writer.cpp
index 58f03a0..58f6699 100644
--- a/media/libstagefright/MPEG4Writer.cpp
+++ b/media/libstagefright/MPEG4Writer.cpp
@@ -22,7 +22,6 @@
#include <pthread.h>
#include <sys/prctl.h>
-#include <sys/resource.h>
#include <media/stagefright/MPEG4Writer.h>
#include <media/stagefright/MediaBuffer.h>
diff --git a/media/libstagefright/TimedEventQueue.cpp b/media/libstagefright/TimedEventQueue.cpp
index a08eb7b..100d8a3 100644
--- a/media/libstagefright/TimedEventQueue.cpp
+++ b/media/libstagefright/TimedEventQueue.cpp
@@ -30,7 +30,6 @@
#include <sys/prctl.h>
#include <sys/time.h>
-#include <sys/resource.h>
#include <media/stagefright/MediaDebug.h>
diff --git a/media/libstagefright/omx/OMX.cpp b/media/libstagefright/omx/OMX.cpp
index 14968e8..d23aa3a 100644
--- a/media/libstagefright/omx/OMX.cpp
+++ b/media/libstagefright/omx/OMX.cpp
@@ -21,7 +21,6 @@
#include <dlfcn.h>
#include <sys/prctl.h>
-#include <sys/resource.h>
#include "../include/OMX.h"
diff --git a/media/mtp/MtpServer.cpp b/media/mtp/MtpServer.cpp
index 4a8fd3e..9ec73c4 100644
--- a/media/mtp/MtpServer.cpp
+++ b/media/mtp/MtpServer.cpp
@@ -533,12 +533,10 @@
MtpStorageID storageID = mRequest.getParameter(1); // 0xFFFFFFFF for all storage
MtpObjectFormat format = mRequest.getParameter(2); // 0 for all formats
MtpObjectHandle parent = mRequest.getParameter(3); // 0xFFFFFFFF for objects with no parent
- // 0x00000000 for all objects?
+ // 0x00000000 for all objects
if (!hasStorage(storageID))
return MTP_RESPONSE_INVALID_STORAGE_ID;
- if (parent == 0xFFFFFFFF)
- parent = 0;
MtpObjectHandleList* handles = mDatabase->getObjectList(storageID, format, parent);
mData.putAUInt32(handles);
@@ -552,11 +550,9 @@
MtpStorageID storageID = mRequest.getParameter(1); // 0xFFFFFFFF for all storage
MtpObjectFormat format = mRequest.getParameter(2); // 0 for all formats
MtpObjectHandle parent = mRequest.getParameter(3); // 0xFFFFFFFF for objects with no parent
- // 0x00000000 for all objects?
+ // 0x00000000 for all objects
if (!hasStorage(storageID))
return MTP_RESPONSE_INVALID_STORAGE_ID;
- if (parent == 0xFFFFFFFF)
- parent = 0;
int count = mDatabase->getNumObjects(storageID, format, parent);
if (count >= 0) {
diff --git a/packages/SystemUI/AndroidManifest.xml b/packages/SystemUI/AndroidManifest.xml
index 55f5280..6d8eab6 100644
--- a/packages/SystemUI/AndroidManifest.xml
+++ b/packages/SystemUI/AndroidManifest.xml
@@ -25,6 +25,10 @@
android:exported="true"
/>
+ <activity android:name=".usb.UsbPreferenceActivity"
+ android:theme="@*android:style/Theme.Holo.Dialog.Alert"
+ android:excludeFromRecents="true">
+ </activity>
<activity android:name=".usb.UsbStorageActivity"
android:excludeFromRecents="true">
</activity>
diff --git a/packages/SystemUI/res/layout/usb_preference_buttons.xml b/packages/SystemUI/res/layout/usb_preference_buttons.xml
new file mode 100644
index 0000000..babe07e
--- /dev/null
+++ b/packages/SystemUI/res/layout/usb_preference_buttons.xml
@@ -0,0 +1,43 @@
+<?xml version="1.0" encoding="utf-8"?>
+<!-- Copyright (C) 2008 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.
+-->
+
+<!-- Check box that is displayed in the activity resolver UI for the user
+ to make their selection the preferred activity. -->
+<LinearLayout
+ xmlns:android="http://schemas.android.com/apk/res/android"
+ android:layout_width="match_parent"
+ android:layout_height="wrap_content"
+ android:paddingLeft="14dip"
+ android:paddingRight="15dip"
+ android:orientation="vertical">
+
+ <Button
+ android:id="@+id/mtp_ptp_button"
+ android:text="@string/use_ptp_button_title"
+ android:layout_width="match_parent"
+ android:layout_height="wrap_content"
+ android:focusable="true"
+ android:clickable="true" />
+
+ <Button
+ android:id="@+id/installer_cd_button"
+ android:text="@string/installer_cd_button_title"
+ android:layout_width="match_parent"
+ android:layout_height="wrap_content"
+ android:focusable="true"
+ android:clickable="true" />
+
+</LinearLayout>
diff --git a/packages/SystemUI/res/values/strings.xml b/packages/SystemUI/res/values/strings.xml
index 8945da5..86e0cd0 100644
--- a/packages/SystemUI/res/values/strings.xml
+++ b/packages/SystemUI/res/values/strings.xml
@@ -156,4 +156,13 @@
<!-- Compatibility mode help screen: body text. [CHAR LIMIT=150] -->
<string name="compat_mode_help_body">When an app was designed for a smaller screen, a zoom control will appear by the clock.</string>
+
+ <!-- Title for the USB function chooser in UsbPreferenceActivity. [CHAR LIMIT=30] -->
+ <string name="usb_preference_title">USB file transfer options</string>
+ <!-- Label for the MTP USB function in UsbPreferenceActivity. [CHAR LIMIT=50] -->
+ <string name="use_mtp_button_title">Mount as a media player (MTP)</string>
+ <!-- Label for the PTP USB function in UsbPreferenceActivity. [CHAR LIMIT=50] -->
+ <string name="use_ptp_button_title">Mount as a camera (PTP)</string>
+ <!-- Label for the installer CD image option in UsbPreferenceActivity. [CHAR LIMIT=50] -->
+ <string name="installer_cd_button_title">Install Android File Transfer application for Mac</string>
</resources>
diff --git a/packages/SystemUI/src/com/android/systemui/usb/UsbPreferenceActivity.java b/packages/SystemUI/src/com/android/systemui/usb/UsbPreferenceActivity.java
new file mode 100644
index 0000000..3ed44e8
--- /dev/null
+++ b/packages/SystemUI/src/com/android/systemui/usb/UsbPreferenceActivity.java
@@ -0,0 +1,91 @@
+/*
+ * Copyright (C) 2011 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 com.android.systemui.usb;
+
+import android.app.Activity;
+import android.app.AlertDialog;
+import android.content.Context;
+import android.content.DialogInterface;
+import android.hardware.usb.UsbManager;
+import android.os.Bundle;
+import android.view.LayoutInflater;
+import android.view.View;
+import android.util.Log;
+import android.widget.Button;
+
+import java.io.File;
+
+import com.android.systemui.R;
+
+public class UsbPreferenceActivity extends Activity implements View.OnClickListener {
+
+ private static final String TAG = "UsbPreferenceActivity";
+
+ private UsbManager mUsbManager;
+ private String mCurrentFunction;
+ private String[] mFunctions;
+ private String mInstallerImagePath;
+ private Button mMtpPtpButton;
+ private Button mInstallerCdButton;
+ private boolean mPtpActive;
+
+ @Override
+ public void onCreate(Bundle icicle) {
+ super.onCreate(icicle);
+
+ mUsbManager = (UsbManager)getSystemService(Context.USB_SERVICE);
+
+ AlertDialog.Builder dialogBuilder = new AlertDialog.Builder(this);
+ dialogBuilder.setTitle(getString(R.string.usb_preference_title));
+
+ LayoutInflater inflater = (LayoutInflater)getSystemService(
+ Context.LAYOUT_INFLATER_SERVICE);
+ View buttonView = inflater.inflate(R.layout.usb_preference_buttons, null);
+ dialogBuilder.setView(buttonView);
+ mMtpPtpButton = (Button)buttonView.findViewById(R.id.mtp_ptp_button);
+ mInstallerCdButton = (Button)buttonView.findViewById(R.id.installer_cd_button);
+ mMtpPtpButton.setOnClickListener(this);
+ mInstallerCdButton.setOnClickListener(this);
+
+ mPtpActive = mUsbManager.isFunctionEnabled(UsbManager.USB_FUNCTION_PTP);
+ if (mPtpActive) {
+ mMtpPtpButton.setText(R.string.use_mtp_button_title);
+ }
+
+ mInstallerImagePath = getString(com.android.internal.R.string.config_isoImagePath);
+ if (!(new File(mInstallerImagePath)).exists()) {
+ mInstallerCdButton.setVisibility(View.GONE);
+ }
+
+ dialogBuilder.show();
+ }
+
+ public void onClick(View v) {
+ if (v.equals(mMtpPtpButton)) {
+ if (mPtpActive) {
+ mUsbManager.setPrimaryFunction(UsbManager.USB_FUNCTION_MTP);
+ } else {
+ mUsbManager.setPrimaryFunction(UsbManager.USB_FUNCTION_PTP);
+ }
+ } else if (v.equals(mInstallerCdButton)) {
+ mUsbManager.setPrimaryFunction(UsbManager.USB_FUNCTION_MASS_STORAGE);
+ mUsbManager.setMassStorageBackingFile(mInstallerImagePath);
+ }
+
+ finish();
+ }
+}
diff --git a/services/java/com/android/server/ConnectivityService.java b/services/java/com/android/server/ConnectivityService.java
index 07855d9..aa3dfa6 100644
--- a/services/java/com/android/server/ConnectivityService.java
+++ b/services/java/com/android/server/ConnectivityService.java
@@ -19,7 +19,7 @@
import static android.Manifest.permission.MANAGE_NETWORK_POLICY;
import static android.net.ConnectivityManager.isNetworkTypeValid;
import static android.net.NetworkPolicyManager.RULE_ALLOW_ALL;
-import static android.net.NetworkPolicyManager.RULE_REJECT_PAID;
+import static android.net.NetworkPolicyManager.RULE_REJECT_METERED;
import android.bluetooth.BluetoothTetheringDataTracker;
import android.content.ContentResolver;
@@ -71,6 +71,7 @@
import com.android.server.connectivity.Vpn;
import com.google.android.collect.Lists;
+import com.google.android.collect.Sets;
import java.io.FileDescriptor;
import java.io.IOException;
@@ -78,8 +79,10 @@
import java.net.InetAddress;
import java.net.UnknownHostException;
import java.util.ArrayList;
+import java.util.Arrays;
import java.util.Collection;
import java.util.GregorianCalendar;
+import java.util.HashSet;
import java.util.List;
import java.util.concurrent.atomic.AtomicBoolean;
@@ -108,8 +111,12 @@
private Vpn mVpn;
+ /** Lock around {@link #mUidRules} and {@link #mMeteredIfaces}. */
+ private Object mRulesLock = new Object();
/** Currently active network rules by UID. */
private SparseIntArray mUidRules = new SparseIntArray();
+ /** Set of ifaces that are costly. */
+ private HashSet<String> mMeteredIfaces = Sets.newHashSet();
/**
* Sometimes we want to refer to the individual network state
@@ -570,31 +577,35 @@
}
/**
- * Check if UID is blocked from using the given {@link NetworkInfo}.
+ * Check if UID should be blocked from using the network represented by the
+ * given {@link NetworkStateTracker}.
*/
- private boolean isNetworkBlocked(NetworkInfo info, int uid) {
- synchronized (mUidRules) {
- // TODO: expand definition of "paid" network to cover tethered or
- // paid hotspot use cases.
- final boolean networkIsPaid = info.getType() != ConnectivityManager.TYPE_WIFI;
- final int uidRules = mUidRules.get(uid, RULE_ALLOW_ALL);
+ private boolean isNetworkBlocked(NetworkStateTracker tracker, int uid) {
+ final String iface = tracker.getLinkProperties().getInterfaceName();
- if (networkIsPaid && (uidRules & RULE_REJECT_PAID) != 0) {
- return true;
- }
-
- // no restrictive rules; network is visible
- return false;
+ final boolean networkCostly;
+ final int uidRules;
+ synchronized (mRulesLock) {
+ networkCostly = mMeteredIfaces.contains(iface);
+ uidRules = mUidRules.get(uid, RULE_ALLOW_ALL);
}
+
+ if (networkCostly && (uidRules & RULE_REJECT_METERED) != 0) {
+ return true;
+ }
+
+ // no restrictive rules; network is visible
+ return false;
}
/**
- * Return a filtered version of the given {@link NetworkInfo}, potentially
- * marked {@link DetailedState#BLOCKED} based on
- * {@link #isNetworkBlocked(NetworkInfo, int)}.
+ * Return a filtered {@link NetworkInfo}, potentially marked
+ * {@link DetailedState#BLOCKED} based on
+ * {@link #isNetworkBlocked(NetworkStateTracker, int)}.
*/
- private NetworkInfo filterNetworkInfo(NetworkInfo info, int uid) {
- if (isNetworkBlocked(info, uid)) {
+ private NetworkInfo getFilteredNetworkInfo(NetworkStateTracker tracker, int uid) {
+ NetworkInfo info = tracker.getNetworkInfo();
+ if (isNetworkBlocked(tracker, uid)) {
// network is blocked; clone and override state
info = new NetworkInfo(info);
info.setDetailedState(DetailedState.BLOCKED, null, null);
@@ -634,7 +645,7 @@
if (isNetworkTypeValid(networkType)) {
final NetworkStateTracker tracker = mNetTrackers[networkType];
if (tracker != null) {
- info = filterNetworkInfo(tracker.getNetworkInfo(), uid);
+ info = getFilteredNetworkInfo(tracker, uid);
}
}
return info;
@@ -645,10 +656,10 @@
enforceAccessPermission();
final int uid = Binder.getCallingUid();
final ArrayList<NetworkInfo> result = Lists.newArrayList();
- synchronized (mUidRules) {
+ synchronized (mRulesLock) {
for (NetworkStateTracker tracker : mNetTrackers) {
if (tracker != null) {
- result.add(filterNetworkInfo(tracker.getNetworkInfo(), uid));
+ result.add(getFilteredNetworkInfo(tracker, uid));
}
}
}
@@ -685,10 +696,10 @@
enforceAccessPermission();
final int uid = Binder.getCallingUid();
final ArrayList<NetworkState> result = Lists.newArrayList();
- synchronized (mUidRules) {
+ synchronized (mRulesLock) {
for (NetworkStateTracker tracker : mNetTrackers) {
if (tracker != null) {
- final NetworkInfo info = filterNetworkInfo(tracker.getNetworkInfo(), uid);
+ final NetworkInfo info = getFilteredNetworkInfo(tracker, uid);
result.add(new NetworkState(
info, tracker.getLinkProperties(), tracker.getLinkCapabilities()));
}
@@ -1139,15 +1150,15 @@
private INetworkPolicyListener mPolicyListener = new INetworkPolicyListener.Stub() {
@Override
- public void onRulesChanged(int uid, int uidRules) {
+ public void onUidRulesChanged(int uid, int uidRules) {
// only someone like NPMS should only be calling us
mContext.enforceCallingOrSelfPermission(MANAGE_NETWORK_POLICY, TAG);
if (LOGD_RULES) {
- Slog.d(TAG, "onRulesChanged(uid=" + uid + ", uidRules=" + uidRules + ")");
+ Slog.d(TAG, "onUidRulesChanged(uid=" + uid + ", uidRules=" + uidRules + ")");
}
- synchronized (mUidRules) {
+ synchronized (mRulesLock) {
// skip update when we've already applied rules
final int oldRules = mUidRules.get(uid, RULE_ALLOW_ALL);
if (oldRules == uidRules) return;
@@ -1158,6 +1169,24 @@
// TODO: dispatch into NMS to push rules towards kernel module
// TODO: notify UID when it has requested targeted updates
}
+
+ @Override
+ public void onMeteredIfacesChanged(String[] meteredIfaces) {
+ // only someone like NPMS should only be calling us
+ mContext.enforceCallingOrSelfPermission(MANAGE_NETWORK_POLICY, TAG);
+
+ if (LOGD_RULES) {
+ Slog.d(TAG,
+ "onMeteredIfacesChanged(ifaces=" + Arrays.toString(meteredIfaces) + ")");
+ }
+
+ synchronized (mRulesLock) {
+ mMeteredIfaces.clear();
+ for (String iface : meteredIfaces) {
+ mMeteredIfaces.add(iface);
+ }
+ }
+ }
};
/**
diff --git a/services/java/com/android/server/MountService.java b/services/java/com/android/server/MountService.java
index c86f962..d3244ec 100644
--- a/services/java/com/android/server/MountService.java
+++ b/services/java/com/android/server/MountService.java
@@ -33,6 +33,7 @@
import android.content.res.Resources;
import android.content.res.TypedArray;
import android.content.res.XmlResourceParser;
+import android.hardware.usb.UsbManager;
import android.net.Uri;
import android.os.Binder;
import android.os.Environment;
@@ -152,7 +153,6 @@
* 600 series - Unsolicited broadcasts.
*/
public static final int VolumeStateChange = 605;
- public static final int ShareAvailabilityChange = 620;
public static final int VolumeDiskInserted = 630;
public static final int VolumeDiskRemoved = 631;
public static final int VolumeBadRemoval = 632;
@@ -167,6 +167,7 @@
private String mExternalStoragePath;
private PackageManagerService mPms;
private boolean mUmsEnabling;
+ private boolean mUmsAvailable = false;
// Used as a lock for methods that register/unregister listeners.
final private ArrayList<MountServiceBinderListener> mListeners =
new ArrayList<MountServiceBinderListener>();
@@ -525,6 +526,10 @@
}
}
}.start();
+ } else if (action.equals(UsbManager.ACTION_USB_STATE)) {
+ boolean available = (intent.getBooleanExtra(UsbManager.USB_CONNECTED, false) &&
+ intent.getBooleanExtra(UsbManager.USB_FUNCTION_MASS_STORAGE, false));
+ notifyShareAvailabilityChange(available);
}
}
};
@@ -654,12 +659,6 @@
updatePublicVolumeState(mExternalStoragePath, Environment.MEDIA_REMOVED);
}
- try {
- boolean avail = doGetShareMethodAvailable("ums");
- notifyShareAvailabilityChange("ums", avail);
- } catch (Exception ex) {
- Slog.w(TAG, "Failed to get share availability");
- }
/*
* Now that we've done our initialization, release
* the hounds!
@@ -694,13 +693,6 @@
notifyVolumeStateChange(
cooked[2], cooked[3], Integer.parseInt(cooked[7]),
Integer.parseInt(cooked[10]));
- } else if (code == VoldResponseCode.ShareAvailabilityChange) {
- // FMT: NNN Share method <method> now <available|unavailable>
- boolean avail = false;
- if (cooked[5].equals("available")) {
- avail = true;
- }
- notifyShareAvailabilityChange(cooked[3], avail);
} else if ((code == VoldResponseCode.VolumeDiskInserted) ||
(code == VoldResponseCode.VolumeDiskRemoved) ||
(code == VoldResponseCode.VolumeBadRemoval)) {
@@ -835,42 +827,6 @@
}
}
- private boolean doGetShareMethodAvailable(String method) {
- ArrayList<String> rsp;
- try {
- rsp = mConnector.doCommand("share status " + method);
- } catch (NativeDaemonConnectorException ex) {
- Slog.e(TAG, "Failed to determine whether share method " + method + " is available.");
- return false;
- }
-
- for (String line : rsp) {
- String[] tok = line.split(" ");
- if (tok.length < 3) {
- Slog.e(TAG, "Malformed response to share status " + method);
- return false;
- }
-
- int code;
- try {
- code = Integer.parseInt(tok[0]);
- } catch (NumberFormatException nfe) {
- Slog.e(TAG, String.format("Error parsing code %s", tok[0]));
- return false;
- }
- if (code == VoldResponseCode.ShareStatusResult) {
- if (tok[2].equals("available"))
- return true;
- return false;
- } else {
- Slog.e(TAG, String.format("Unexpected response code %d", code));
- return false;
- }
- }
- Slog.e(TAG, "Got an empty response");
- return false;
- }
-
private int doMountVolume(String path) {
int rc = StorageResultCode.OperationSucceeded;
@@ -1018,13 +974,9 @@
return false;
}
- private void notifyShareAvailabilityChange(String method, final boolean avail) {
- if (!method.equals("ums")) {
- Slog.w(TAG, "Ignoring unsupported share method {" + method + "}");
- return;
- }
-
+ private void notifyShareAvailabilityChange(final boolean avail) {
synchronized (mListeners) {
+ mUmsAvailable = avail;
for (int i = mListeners.size() -1; i >= 0; i--) {
MountServiceBinderListener bl = mListeners.get(i);
try {
@@ -1189,8 +1141,13 @@
// XXX: This will go away soon in favor of IMountServiceObserver
mPms = (PackageManagerService) ServiceManager.getService("package");
- mContext.registerReceiver(mBroadcastReceiver,
- new IntentFilter(Intent.ACTION_BOOT_COMPLETED), null, null);
+ IntentFilter filter = new IntentFilter();
+ filter.addAction(Intent.ACTION_BOOT_COMPLETED);
+ // don't bother monitoring USB if mass storage is not supported on our primary volume
+ if (mPrimaryVolume != null && mPrimaryVolume.allowMassStorage()) {
+ filter.addAction(UsbManager.ACTION_USB_STATE);
+ }
+ mContext.registerReceiver(mBroadcastReceiver, filter, null, null);
mHandlerThread = new HandlerThread("MountService");
mHandlerThread.start();
@@ -1323,7 +1280,9 @@
if (getUmsEnabling()) {
return true;
}
- return doGetShareMethodAvailable("ums");
+ synchronized (mListeners) {
+ return mUmsAvailable;
+ }
}
public void setUsbMassStorageEnabled(boolean enable) {
@@ -1419,7 +1378,7 @@
return doFormatVolume(path);
}
- public int []getStorageUsers(String path) {
+ public int[] getStorageUsers(String path) {
validatePermission(android.Manifest.permission.MOUNT_UNMOUNT_FILESYSTEMS);
waitForReady();
try {
diff --git a/services/java/com/android/server/NetworkManagementService.java b/services/java/com/android/server/NetworkManagementService.java
index bb0c671..d6704f4 100644
--- a/services/java/com/android/server/NetworkManagementService.java
+++ b/services/java/com/android/server/NetworkManagementService.java
@@ -16,6 +16,10 @@
package com.android.server;
+import static android.net.NetworkStats.IFACE_ALL;
+import static android.net.NetworkStats.TAG_NONE;
+import static android.net.NetworkStats.UID_ALL;
+
import android.content.Context;
import android.content.pm.PackageManager;
import android.net.INetworkManagementEventObserver;
@@ -37,6 +41,7 @@
import java.io.DataInputStream;
import java.io.File;
import java.io.FileInputStream;
+import java.io.FileReader;
import java.io.IOException;
import java.io.InputStreamReader;
import java.net.Inet4Address;
@@ -59,8 +64,9 @@
private static final int ADD = 1;
private static final int REMOVE = 2;
- /** Base path to UID-granularity network statistics. */
- private static final File PATH_PROC_UID_STAT = new File("/proc/uid_stat");
+ @Deprecated
+ private static final File STATS_UIDSTAT = new File("/proc/uid_stat");
+ private static final File STATS_NETFILTER = new File("/proc/net/xt_qtaguid/stats");
class NetdResponseCode {
public static final int InterfaceListResult = 110;
@@ -899,7 +905,7 @@
for (String iface : ifaces) {
final long rx = getInterfaceCounter(iface, true);
final long tx = getInterfaceCounter(iface, false);
- stats.addEntry(iface, NetworkStats.UID_ALL, rx, tx);
+ stats.addEntry(iface, UID_ALL, TAG_NONE, rx, tx);
}
return stats;
@@ -910,16 +916,11 @@
mContext.enforceCallingOrSelfPermission(
android.Manifest.permission.ACCESS_NETWORK_STATE, "NetworkManagementService");
- final String[] knownUids = PATH_PROC_UID_STAT.list();
- final NetworkStats stats = new NetworkStats(
- SystemClock.elapsedRealtime(), knownUids.length);
-
- for (String uid : knownUids) {
- final int uidInt = Integer.parseInt(uid);
- collectNetworkStatsDetail(stats, uidInt);
+ if (STATS_NETFILTER.exists()) {
+ return getNetworkStatsDetailNetfilter(UID_ALL);
+ } else {
+ return getNetworkStatsDetailUidstat(UID_ALL);
}
-
- return stats;
}
@Override
@@ -929,19 +930,84 @@
android.Manifest.permission.ACCESS_NETWORK_STATE, "NetworkManagementService");
}
- final NetworkStats stats = new NetworkStats(SystemClock.elapsedRealtime(), 1);
- collectNetworkStatsDetail(stats, uid);
+ if (STATS_NETFILTER.exists()) {
+ return getNetworkStatsDetailNetfilter(uid);
+ } else {
+ return getNetworkStatsDetailUidstat(uid);
+ }
+ }
+
+ /**
+ * Build {@link NetworkStats} with detailed UID statistics.
+ */
+ private NetworkStats getNetworkStatsDetailNetfilter(int limitUid) {
+ final NetworkStats stats = new NetworkStats(SystemClock.elapsedRealtime(), 24);
+
+ BufferedReader reader = null;
+ try {
+ reader = new BufferedReader(new FileReader(STATS_NETFILTER));
+
+ // assumes format from kernel:
+ // idx iface acct_tag_hex uid_tag_int rx_bytes tx_bytes
+
+ // skip first line, which is legend
+ String line = reader.readLine();
+ while ((line = reader.readLine()) != null) {
+ final StringTokenizer t = new StringTokenizer(line);
+
+ final String idx = t.nextToken();
+ final String iface = t.nextToken();
+
+ try {
+ // TODO: kernel currently emits tag in upper half of long;
+ // eventually switch to directly using int.
+ final int tag = (int) (Long.parseLong(t.nextToken().substring(2), 16) >> 32);
+ final int uid = Integer.parseInt(t.nextToken());
+ final long rx = Long.parseLong(t.nextToken());
+ final long tx = Long.parseLong(t.nextToken());
+
+ if (limitUid == UID_ALL || limitUid == uid) {
+ stats.addEntry(iface, uid, tag, rx, tx);
+ }
+ } catch (NumberFormatException e) {
+ Slog.w(TAG, "problem parsing stats for idx " + idx + ": " + e);
+ }
+ }
+ } catch (IOException e) {
+ Slog.w(TAG, "problem parsing stats: " + e);
+ } finally {
+ IoUtils.closeQuietly(reader);
+ }
+
return stats;
}
- private void collectNetworkStatsDetail(NetworkStats stats, int uid) {
- // TODO: kernel module will provide interface-level stats in future
- // TODO: migrate these stats to come across netd in bulk, instead of all
- // these individual file reads.
- final File uidPath = new File(PATH_PROC_UID_STAT, Integer.toString(uid));
- final long rx = readSingleLongFromFile(new File(uidPath, "tcp_rcv"));
- final long tx = readSingleLongFromFile(new File(uidPath, "tcp_snd"));
- stats.addEntry(NetworkStats.IFACE_ALL, uid, rx, tx);
+ /**
+ * Build {@link NetworkStats} with detailed UID statistics.
+ *
+ * @deprecated since this uses older "uid_stat" data, and doesn't provide
+ * tag-level granularity or additional variables.
+ */
+ @Deprecated
+ private NetworkStats getNetworkStatsDetailUidstat(int limitUid) {
+ final String[] knownUids;
+ if (limitUid == UID_ALL) {
+ knownUids = STATS_UIDSTAT.list();
+ } else {
+ knownUids = new String[] { String.valueOf(limitUid) };
+ }
+
+ final NetworkStats stats = new NetworkStats(
+ SystemClock.elapsedRealtime(), knownUids.length);
+ for (String uid : knownUids) {
+ final int uidInt = Integer.parseInt(uid);
+ final File uidPath = new File(STATS_UIDSTAT, uid);
+ final long rx = readSingleLongFromFile(new File(uidPath, "tcp_rcv"));
+ final long tx = readSingleLongFromFile(new File(uidPath, "tcp_snd"));
+ stats.addEntry(IFACE_ALL, uidInt, TAG_NONE, rx, tx);
+ }
+
+ return stats;
}
public void setInterfaceThrottle(String iface, int rxKbps, int txKbps) {
diff --git a/services/java/com/android/server/SystemServer.java b/services/java/com/android/server/SystemServer.java
index 2769004..a23bacf 100644
--- a/services/java/com/android/server/SystemServer.java
+++ b/services/java/com/android/server/SystemServer.java
@@ -406,8 +406,8 @@
}
try {
- Slog.i(TAG, "USB Observer");
- // Listen for USB changes
+ Slog.i(TAG, "USB Service");
+ // Manage USB host and device support
usb = new UsbService(context);
ServiceManager.addService(Context.USB_SERVICE, usb);
} catch (Throwable e) {
diff --git a/services/java/com/android/server/ThrottleService.java b/services/java/com/android/server/ThrottleService.java
index 510ff62..7266d7d 100644
--- a/services/java/com/android/server/ThrottleService.java
+++ b/services/java/com/android/server/ThrottleService.java
@@ -533,7 +533,8 @@
long incWrite = 0;
try {
final NetworkStats stats = mNMService.getNetworkStatsSummary();
- final int index = stats.findIndex(mIface, NetworkStats.UID_ALL);
+ final int index = stats.findIndex(
+ mIface, NetworkStats.UID_ALL, NetworkStats.TAG_NONE);
if (index != -1) {
incRead = stats.rx[index] - mLastRead;
diff --git a/services/java/com/android/server/net/InterfaceIdentity.java b/services/java/com/android/server/net/InterfaceIdentity.java
deleted file mode 100644
index ff86581..0000000
--- a/services/java/com/android/server/net/InterfaceIdentity.java
+++ /dev/null
@@ -1,73 +0,0 @@
-/*
- * Copyright (C) 2011 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 com.android.server.net;
-
-import java.io.DataInputStream;
-import java.io.DataOutputStream;
-import java.io.IOException;
-import java.net.ProtocolException;
-import java.util.HashSet;
-
-/**
- * Identity of a {@code iface}, defined by the set of {@link NetworkIdentity}
- * active on that interface.
- *
- * @hide
- */
-public class InterfaceIdentity extends HashSet<NetworkIdentity> {
- private static final int VERSION_CURRENT = 1;
-
- public InterfaceIdentity() {
- }
-
- public InterfaceIdentity(DataInputStream in) throws IOException {
- final int version = in.readInt();
- switch (version) {
- case VERSION_CURRENT: {
- final int size = in.readInt();
- for (int i = 0; i < size; i++) {
- add(new NetworkIdentity(in));
- }
- break;
- }
- default: {
- throw new ProtocolException("unexpected version: " + version);
- }
- }
- }
-
- public void writeToStream(DataOutputStream out) throws IOException {
- out.writeInt(VERSION_CURRENT);
- out.writeInt(size());
- for (NetworkIdentity ident : this) {
- ident.writeToStream(out);
- }
- }
-
- /**
- * Test if any {@link NetworkIdentity} on this interface matches the given
- * template and IMEI.
- */
- public boolean matchesTemplate(int networkTemplate, String subscriberId) {
- for (NetworkIdentity ident : this) {
- if (ident.matchesTemplate(networkTemplate, subscriberId)) {
- return true;
- }
- }
- return false;
- }
-}
diff --git a/services/java/com/android/server/net/NetworkIdentity.java b/services/java/com/android/server/net/NetworkIdentity.java
deleted file mode 100644
index 4a207f7..0000000
--- a/services/java/com/android/server/net/NetworkIdentity.java
+++ /dev/null
@@ -1,227 +0,0 @@
-/*
- * Copyright (C) 2011 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 com.android.server.net;
-
-import static android.net.ConnectivityManager.TYPE_WIFI;
-import static android.net.ConnectivityManager.TYPE_WIMAX;
-import static android.net.ConnectivityManager.isNetworkTypeMobile;
-import static android.net.TrafficStats.TEMPLATE_MOBILE_3G_LOWER;
-import static android.net.TrafficStats.TEMPLATE_MOBILE_4G;
-import static android.net.TrafficStats.TEMPLATE_MOBILE_ALL;
-import static android.net.TrafficStats.TEMPLATE_WIFI;
-import static android.telephony.TelephonyManager.NETWORK_CLASS_2_G;
-import static android.telephony.TelephonyManager.NETWORK_CLASS_3_G;
-import static android.telephony.TelephonyManager.NETWORK_CLASS_4_G;
-import static android.telephony.TelephonyManager.NETWORK_CLASS_UNKNOWN;
-import static android.telephony.TelephonyManager.getNetworkClass;
-
-import android.content.Context;
-import android.net.ConnectivityManager;
-import android.net.NetworkInfo;
-import android.net.NetworkState;
-import android.telephony.TelephonyManager;
-
-import com.android.internal.util.Objects;
-
-import java.io.DataInputStream;
-import java.io.DataOutputStream;
-import java.io.IOException;
-import java.net.ProtocolException;
-
-/**
- * Identity of a {@link NetworkInfo}, defined by network type and billing
- * relationship (such as IMSI).
- *
- * @hide
- */
-public class NetworkIdentity {
- private static final int VERSION_CURRENT = 1;
-
- public final int type;
- public final int subType;
- public final String subscriberId;
-
- public NetworkIdentity(int type, int subType, String subscriberId) {
- this.type = type;
- this.subType = subType;
- this.subscriberId = subscriberId;
- }
-
- public NetworkIdentity(DataInputStream in) throws IOException {
- final int version = in.readInt();
- switch (version) {
- case VERSION_CURRENT: {
- type = in.readInt();
- subType = in.readInt();
- subscriberId = readOptionalString(in);
- break;
- }
- default: {
- throw new ProtocolException("unexpected version: " + version);
- }
- }
- }
-
- public void writeToStream(DataOutputStream out) throws IOException {
- out.writeInt(VERSION_CURRENT);
- out.writeInt(type);
- out.writeInt(subType);
- writeOptionalString(out, subscriberId);
- }
-
- @Override
- public int hashCode() {
- return Objects.hashCode(type, subType, subscriberId);
- }
-
- @Override
- public boolean equals(Object obj) {
- if (obj instanceof NetworkIdentity) {
- final NetworkIdentity ident = (NetworkIdentity) obj;
- return type == ident.type && subType == ident.subType
- && Objects.equal(subscriberId, ident.subscriberId);
- }
- return false;
- }
-
- @Override
- public String toString() {
- final String typeName = ConnectivityManager.getNetworkTypeName(type);
- final String subTypeName;
- if (ConnectivityManager.isNetworkTypeMobile(type)) {
- subTypeName = TelephonyManager.getNetworkTypeName(subType);
- } else {
- subTypeName = Integer.toString(subType);
- }
-
- return "[type=" + typeName + ", subType=" + subTypeName + ", subId=" + subscriberId + "]";
- }
-
- /**
- * Test if this network matches the given template and IMEI.
- */
- public boolean matchesTemplate(int networkTemplate, String subscriberId) {
- switch (networkTemplate) {
- case TEMPLATE_MOBILE_ALL:
- return matchesMobile(subscriberId);
- case TEMPLATE_MOBILE_3G_LOWER:
- return matchesMobile3gLower(subscriberId);
- case TEMPLATE_MOBILE_4G:
- return matchesMobile4g(subscriberId);
- case TEMPLATE_WIFI:
- return matchesWifi();
- default:
- throw new IllegalArgumentException("unknown network template");
- }
- }
-
- /**
- * Check if mobile network with matching IMEI. Also matches
- * {@link #TYPE_WIMAX}.
- */
- private boolean matchesMobile(String subscriberId) {
- if (isNetworkTypeMobile(type) && Objects.equal(this.subscriberId, subscriberId)) {
- return true;
- } else if (type == TYPE_WIMAX) {
- return true;
- }
- return false;
- }
-
- /**
- * Check if mobile network classified 3G or lower with matching IMEI.
- */
- private boolean matchesMobile3gLower(String subscriberId) {
- if (isNetworkTypeMobile(type)
- && Objects.equal(this.subscriberId, subscriberId)) {
- switch (getNetworkClass(subType)) {
- case NETWORK_CLASS_UNKNOWN:
- case NETWORK_CLASS_2_G:
- case NETWORK_CLASS_3_G:
- return true;
- }
- }
- return false;
- }
-
- /**
- * Check if mobile network classified 4G with matching IMEI. Also matches
- * {@link #TYPE_WIMAX}.
- */
- private boolean matchesMobile4g(String subscriberId) {
- if (isNetworkTypeMobile(type)
- && Objects.equal(this.subscriberId, subscriberId)) {
- switch (getNetworkClass(subType)) {
- case NETWORK_CLASS_4_G:
- return true;
- }
- } else if (type == TYPE_WIMAX) {
- return true;
- }
- return false;
- }
-
- /**
- * Check if matches Wi-Fi network template.
- */
- private boolean matchesWifi() {
- if (type == TYPE_WIFI) {
- return true;
- }
- return false;
- }
-
- /**
- * Build a {@link NetworkIdentity} from the given {@link NetworkState},
- * assuming that any mobile networks are using the current IMSI.
- */
- public static NetworkIdentity buildNetworkIdentity(Context context, NetworkState state) {
- final int type = state.networkInfo.getType();
- final int subType = state.networkInfo.getSubtype();
-
- // TODO: consider moving subscriberId over to LinkCapabilities, so it
- // comes from an authoritative source.
-
- final String subscriberId;
- if (isNetworkTypeMobile(type)) {
- final TelephonyManager telephony = (TelephonyManager) context.getSystemService(
- Context.TELEPHONY_SERVICE);
- subscriberId = telephony.getSubscriberId();
- } else {
- subscriberId = null;
- }
- return new NetworkIdentity(type, subType, subscriberId);
- }
-
- private static void writeOptionalString(DataOutputStream out, String value) throws IOException {
- if (value != null) {
- out.writeByte(1);
- out.writeUTF(value);
- } else {
- out.writeByte(0);
- }
- }
-
- private static String readOptionalString(DataInputStream in) throws IOException {
- if (in.readByte() != 0) {
- return in.readUTF();
- } else {
- return null;
- }
- }
-
-}
diff --git a/services/java/com/android/server/net/NetworkIdentitySet.java b/services/java/com/android/server/net/NetworkIdentitySet.java
new file mode 100644
index 0000000..af03fb3
--- /dev/null
+++ b/services/java/com/android/server/net/NetworkIdentitySet.java
@@ -0,0 +1,98 @@
+/*
+ * Copyright (C) 2011 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 com.android.server.net;
+
+import android.net.NetworkIdentity;
+
+import java.io.DataInputStream;
+import java.io.DataOutputStream;
+import java.io.IOException;
+import java.net.ProtocolException;
+import java.util.HashSet;
+
+/**
+ * Identity of a {@code iface}, defined by the set of {@link NetworkIdentity}
+ * active on that interface.
+ *
+ * @hide
+ */
+public class NetworkIdentitySet extends HashSet<NetworkIdentity> {
+ private static final int VERSION_INIT = 1;
+ private static final int VERSION_ADD_ROAMING = 2;
+
+ public NetworkIdentitySet() {
+ }
+
+ public NetworkIdentitySet(DataInputStream in) throws IOException {
+ final int version = in.readInt();
+ switch (version) {
+ case VERSION_INIT: {
+ final int size = in.readInt();
+ for (int i = 0; i < size; i++) {
+ final int ignoredVersion = in.readInt();
+ final int type = in.readInt();
+ final int subType = in.readInt();
+ final String subscriberId = readOptionalString(in);
+ add(new NetworkIdentity(type, subType, subscriberId, false));
+ }
+ break;
+ }
+ case VERSION_ADD_ROAMING: {
+ final int size = in.readInt();
+ for (int i = 0; i < size; i++) {
+ final int type = in.readInt();
+ final int subType = in.readInt();
+ final String subscriberId = readOptionalString(in);
+ final boolean roaming = in.readBoolean();
+ add(new NetworkIdentity(type, subType, subscriberId, roaming));
+ }
+ break;
+ }
+ default: {
+ throw new ProtocolException("unexpected version: " + version);
+ }
+ }
+ }
+
+ public void writeToStream(DataOutputStream out) throws IOException {
+ out.writeInt(VERSION_ADD_ROAMING);
+ out.writeInt(size());
+ for (NetworkIdentity ident : this) {
+ out.writeInt(ident.getType());
+ out.writeInt(ident.getSubType());
+ writeOptionalString(out, ident.getSubscriberId());
+ out.writeBoolean(ident.getRoaming());
+ }
+ }
+
+ private static void writeOptionalString(DataOutputStream out, String value) throws IOException {
+ if (value != null) {
+ out.writeByte(1);
+ out.writeUTF(value);
+ } else {
+ out.writeByte(0);
+ }
+ }
+
+ private static String readOptionalString(DataInputStream in) throws IOException {
+ if (in.readByte() != 0) {
+ return in.readUTF();
+ } else {
+ return null;
+ }
+ }
+}
diff --git a/services/java/com/android/server/net/NetworkPolicyManagerService.java b/services/java/com/android/server/net/NetworkPolicyManagerService.java
index 9cbe82d..584cd03 100644
--- a/services/java/com/android/server/net/NetworkPolicyManagerService.java
+++ b/services/java/com/android/server/net/NetworkPolicyManagerService.java
@@ -22,24 +22,26 @@
import static android.Manifest.permission.MANAGE_NETWORK_POLICY;
import static android.Manifest.permission.READ_NETWORK_USAGE_HISTORY;
import static android.Manifest.permission.READ_PHONE_STATE;
+import static android.content.Intent.ACTION_UID_REMOVED;
+import static android.content.Intent.EXTRA_UID;
import static android.net.ConnectivityManager.CONNECTIVITY_ACTION;
+import static android.net.ConnectivityManager.TYPE_MOBILE;
import static android.net.NetworkPolicy.LIMIT_DISABLED;
import static android.net.NetworkPolicy.WARNING_DISABLED;
import static android.net.NetworkPolicyManager.ACTION_DATA_USAGE_LIMIT;
import static android.net.NetworkPolicyManager.ACTION_DATA_USAGE_WARNING;
import static android.net.NetworkPolicyManager.EXTRA_NETWORK_TEMPLATE;
import static android.net.NetworkPolicyManager.POLICY_NONE;
-import static android.net.NetworkPolicyManager.POLICY_REJECT_PAID_BACKGROUND;
+import static android.net.NetworkPolicyManager.POLICY_REJECT_METERED_BACKGROUND;
import static android.net.NetworkPolicyManager.RULE_ALLOW_ALL;
-import static android.net.NetworkPolicyManager.RULE_REJECT_PAID;
+import static android.net.NetworkPolicyManager.RULE_REJECT_METERED;
import static android.net.NetworkPolicyManager.computeLastCycleBoundary;
import static android.net.NetworkPolicyManager.dumpPolicy;
import static android.net.NetworkPolicyManager.dumpRules;
import static android.net.NetworkPolicyManager.isUidValidForPolicy;
-import static android.net.TrafficStats.TEMPLATE_MOBILE_3G_LOWER;
-import static android.net.TrafficStats.TEMPLATE_MOBILE_4G;
-import static android.net.TrafficStats.TEMPLATE_MOBILE_ALL;
-import static android.net.TrafficStats.isNetworkTemplateMobile;
+import static android.net.NetworkTemplate.MATCH_MOBILE_3G_LOWER;
+import static android.net.NetworkTemplate.MATCH_MOBILE_4G;
+import static android.net.NetworkTemplate.MATCH_MOBILE_ALL;
import static android.text.format.DateUtils.DAY_IN_MILLIS;
import static com.android.internal.util.Preconditions.checkNotNull;
import static com.android.server.net.NetworkStatsService.ACTION_NETWORK_STATS_UPDATED;
@@ -61,9 +63,11 @@
import android.net.INetworkPolicyListener;
import android.net.INetworkPolicyManager;
import android.net.INetworkStatsService;
+import android.net.NetworkIdentity;
import android.net.NetworkPolicy;
import android.net.NetworkState;
import android.net.NetworkStats;
+import android.net.NetworkTemplate;
import android.os.Environment;
import android.os.Handler;
import android.os.HandlerThread;
@@ -84,9 +88,9 @@
import com.android.internal.R;
import com.android.internal.os.AtomicFile;
import com.android.internal.util.FastXmlSerializer;
-import com.android.internal.util.Objects;
import com.google.android.collect.Lists;
import com.google.android.collect.Maps;
+import com.google.android.collect.Sets;
import org.xmlpull.v1.XmlPullParser;
import org.xmlpull.v1.XmlPullParserException;
@@ -103,6 +107,7 @@
import java.util.ArrayList;
import java.util.Arrays;
import java.util.HashMap;
+import java.util.HashSet;
import libcore.io.IoUtils;
@@ -164,6 +169,9 @@
/** Current derived network rules for each UID. */
private SparseIntArray mUidRules = new SparseIntArray();
+ /** Set of ifaces that are metered. */
+ private HashSet<String> mMeteredIfaces = Sets.newHashSet();
+
/** Foreground at both UID and PID granularity. */
private SparseBooleanArray mUidForeground = new SparseBooleanArray();
private SparseArray<SparseBooleanArray> mUidPidForeground = new SparseArray<
@@ -241,9 +249,12 @@
mContext.registerReceiver(mScreenReceiver, screenFilter);
// watch for network interfaces to be claimed
- final IntentFilter ifaceFilter = new IntentFilter();
- ifaceFilter.addAction(CONNECTIVITY_ACTION);
- mContext.registerReceiver(mIfaceReceiver, ifaceFilter, CONNECTIVITY_INTERNAL, mHandler);
+ final IntentFilter connFilter = new IntentFilter(CONNECTIVITY_ACTION);
+ mContext.registerReceiver(mConnReceiver, connFilter, CONNECTIVITY_INTERNAL, mHandler);
+
+ // listen for uid removal to clean policy
+ final IntentFilter removedFilter = new IntentFilter(ACTION_UID_REMOVED);
+ mContext.registerReceiver(mRemovedReceiver, removedFilter, null, mHandler);
// listen for warning polling events; currently dispatched by
final IntentFilter statsFilter = new IntentFilter(ACTION_NETWORK_STATS_UPDATED);
@@ -306,6 +317,21 @@
}
};
+ private BroadcastReceiver mRemovedReceiver = new BroadcastReceiver() {
+ @Override
+ public void onReceive(Context context, Intent intent) {
+ // on background handler thread, and UID_REMOVED is protected
+ // broadcast.
+ final int uid = intent.getIntExtra(EXTRA_UID, 0);
+ synchronized (mRulesLock) {
+ // remove any policy and update rules to clean up
+ mUidPolicy.delete(uid);
+ updateRulesForUidLocked(uid);
+ writePolicyLocked();
+ }
+ }
+ };
+
/**
* Receiver that watches for {@link INetworkStatsService} updates, which we
* use to check against {@link NetworkPolicy#warningBytes}.
@@ -348,10 +374,10 @@
final long total;
try {
final NetworkStats stats = mNetworkStats.getSummaryForNetwork(
- start, end, policy.networkTemplate, policy.subscriberId);
+ policy.template, start, end);
total = stats.rx[0] + stats.tx[0];
} catch (RemoteException e) {
- Slog.w(TAG, "problem reading summary for template " + policy.networkTemplate);
+ Slog.w(TAG, "problem reading summary for template " + policy.template);
continue;
}
@@ -375,8 +401,7 @@
* notification of a specific type, like {@link #TYPE_LIMIT}.
*/
private String buildNotificationTag(NetworkPolicy policy, int type) {
- // TODO: consider splicing subscriberId hash into mix
- return TAG + ":" + policy.networkTemplate + ":" + type;
+ return TAG + ":" + policy.template.hashCode() + ":" + type;
}
/**
@@ -403,7 +428,7 @@
final Intent intent = new Intent(ACTION_DATA_USAGE_WARNING);
intent.addCategory(Intent.CATEGORY_DEFAULT);
- intent.putExtra(EXTRA_NETWORK_TEMPLATE, policy.networkTemplate);
+ intent.putExtra(EXTRA_NETWORK_TEMPLATE, policy.template.getMatchRule());
builder.setContentIntent(PendingIntent.getActivity(
mContext, 0, intent, PendingIntent.FLAG_UPDATE_CURRENT));
break;
@@ -411,11 +436,11 @@
case TYPE_LIMIT: {
final String title;
final String body = res.getString(R.string.data_usage_limit_body);
- switch (policy.networkTemplate) {
- case TEMPLATE_MOBILE_3G_LOWER:
+ switch (policy.template.getMatchRule()) {
+ case MATCH_MOBILE_3G_LOWER:
title = res.getString(R.string.data_usage_3g_limit_title);
break;
- case TEMPLATE_MOBILE_4G:
+ case MATCH_MOBILE_4G:
title = res.getString(R.string.data_usage_4g_limit_title);
break;
default:
@@ -430,7 +455,7 @@
final Intent intent = new Intent(ACTION_DATA_USAGE_LIMIT);
intent.addCategory(Intent.CATEGORY_DEFAULT);
- intent.putExtra(EXTRA_NETWORK_TEMPLATE, policy.networkTemplate);
+ intent.putExtra(EXTRA_NETWORK_TEMPLATE, policy.template.getMatchRule());
builder.setContentIntent(PendingIntent.getActivity(
mContext, 0, intent, PendingIntent.FLAG_UPDATE_CURRENT));
break;
@@ -468,7 +493,7 @@
* Receiver that watches for {@link IConnectivityManager} to claim network
* interfaces. Used to apply {@link NetworkPolicy} to matching networks.
*/
- private BroadcastReceiver mIfaceReceiver = new BroadcastReceiver() {
+ private BroadcastReceiver mConnReceiver = new BroadcastReceiver() {
@Override
public void onReceive(Context context, Intent intent) {
// on background handler thread, and verified CONNECTIVITY_INTERNAL
@@ -516,7 +541,7 @@
// collect all active ifaces that match this template
ifaceList.clear();
for (NetworkIdentity ident : networks.keySet()) {
- if (ident.matchesTemplate(policy.networkTemplate, policy.subscriberId)) {
+ if (policy.template.matches(ident)) {
final String iface = networks.get(ident);
ifaceList.add(iface);
}
@@ -536,6 +561,8 @@
final long currentTime = mTime.hasCache() ? mTime.currentTimeMillis()
: System.currentTimeMillis();
+ mMeteredIfaces.clear();
+
// apply each policy that we found ifaces for; compute remaining data
// based on current cycle and historical stats, and push to kernel.
for (NetworkPolicy policy : rules.keySet()) {
@@ -547,11 +574,10 @@
final NetworkStats stats;
final long total;
try {
- stats = mNetworkStats.getSummaryForNetwork(
- start, end, policy.networkTemplate, policy.subscriberId);
+ stats = mNetworkStats.getSummaryForNetwork(policy.template, start, end);
total = stats.rx[0] + stats.tx[0];
} catch (RemoteException e) {
- Slog.w(TAG, "problem reading summary for template " + policy.networkTemplate);
+ Slog.w(TAG, "problem reading summary for template " + policy.template);
continue;
}
@@ -566,8 +592,27 @@
// remaining "quota" is based on usage in current cycle
final long quota = Math.max(0, policy.limitBytes - total);
//kernelSetIfacesQuota(ifaces, quota);
+
+ for (String iface : ifaces) {
+ mMeteredIfaces.add(iface);
+ }
}
}
+
+ // dispatch changed rule to existing listeners
+ // TODO: dispatch outside of holding lock
+ final String[] meteredIfaces = mMeteredIfaces.toArray(new String[mMeteredIfaces.size()]);
+ final int length = mListeners.beginBroadcast();
+ for (int i = 0; i < length; i++) {
+ final INetworkPolicyListener listener = mListeners.getBroadcastItem(i);
+ if (listener != null) {
+ try {
+ listener.onMeteredIfacesChanged(meteredIfaces);
+ } catch (RemoteException e) {
+ }
+ }
+ }
+ mListeners.finishBroadcast();
}
/**
@@ -577,12 +622,13 @@
private void ensureActiveMobilePolicyLocked() {
if (LOGV) Slog.v(TAG, "ensureActiveMobilePolicyLocked()");
final String subscriberId = getActiveSubscriberId();
+ final NetworkIdentity probeIdent = new NetworkIdentity(
+ TYPE_MOBILE, TelephonyManager.NETWORK_TYPE_UNKNOWN, subscriberId, false);
// examine to see if any policy is defined for active mobile
boolean mobileDefined = false;
for (NetworkPolicy policy : mNetworkPolicy) {
- if (isNetworkTemplateMobile(policy.networkTemplate)
- && Objects.equal(subscriberId, policy.subscriberId)) {
+ if (policy.template.matches(probeIdent)) {
mobileDefined = true;
}
}
@@ -598,8 +644,9 @@
time.setToNow();
final int cycleDay = time.monthDay;
- mNetworkPolicy.add(new NetworkPolicy(
- TEMPLATE_MOBILE_ALL, subscriberId, cycleDay, 4 * GB_IN_BYTES, LIMIT_DISABLED));
+ final NetworkTemplate template = new NetworkTemplate(MATCH_MOBILE_ALL, subscriberId);
+ mNetworkPolicy.add(
+ new NetworkPolicy(template, cycleDay, 4 * GB_IN_BYTES, LIMIT_DISABLED));
writePolicyLocked();
}
}
@@ -632,8 +679,10 @@
final long warningBytes = readLongAttribute(in, ATTR_WARNING_BYTES);
final long limitBytes = readLongAttribute(in, ATTR_LIMIT_BYTES);
- mNetworkPolicy.add(new NetworkPolicy(
- networkTemplate, subscriberId, cycleDay, warningBytes, limitBytes));
+ final NetworkTemplate template = new NetworkTemplate(
+ networkTemplate, subscriberId);
+ mNetworkPolicy.add(
+ new NetworkPolicy(template, cycleDay, warningBytes, limitBytes));
} else if (TAG_UID_POLICY.equals(tag)) {
final int uid = readIntAttribute(in, ATTR_UID);
@@ -675,10 +724,13 @@
// write all known network policies
for (NetworkPolicy policy : mNetworkPolicy) {
+ final NetworkTemplate template = policy.template;
+
out.startTag(null, TAG_NETWORK_POLICY);
- writeIntAttribute(out, ATTR_NETWORK_TEMPLATE, policy.networkTemplate);
- if (policy.subscriberId != null) {
- out.attribute(null, ATTR_SUBSCRIBER_ID, policy.subscriberId);
+ writeIntAttribute(out, ATTR_NETWORK_TEMPLATE, template.getMatchRule());
+ final String subscriberId = template.getSubscriberId();
+ if (subscriberId != null) {
+ out.attribute(null, ATTR_SUBSCRIBER_ID, subscriberId);
}
writeIntAttribute(out, ATTR_CYCLE_DAY, policy.cycleDay);
writeLongAttribute(out, ATTR_WARNING_BYTES, policy.warningBytes);
@@ -754,17 +806,29 @@
synchronized (mRulesLock) {
// dispatch any existing rules to new listeners
+ // TODO: dispatch outside of holding lock
final int size = mUidRules.size();
for (int i = 0; i < size; i++) {
final int uid = mUidRules.keyAt(i);
final int uidRules = mUidRules.valueAt(i);
if (uidRules != RULE_ALLOW_ALL) {
try {
- listener.onRulesChanged(uid, uidRules);
+ listener.onUidRulesChanged(uid, uidRules);
} catch (RemoteException e) {
}
}
}
+
+ // dispatch any metered ifaces to new listeners
+ // TODO: dispatch outside of holding lock
+ if (mMeteredIfaces.size() > 0) {
+ final String[] meteredIfaces = mMeteredIfaces.toArray(
+ new String[mMeteredIfaces.size()]);
+ try {
+ listener.onMeteredIfacesChanged(meteredIfaces);
+ } catch (RemoteException e) {
+ }
+ }
}
}
@@ -921,9 +985,9 @@
// derive active rules based on policy and active state
int uidRules = RULE_ALLOW_ALL;
- if (!uidForeground && (uidPolicy & POLICY_REJECT_PAID_BACKGROUND) != 0) {
- // uid in background, and policy says to block paid data
- uidRules = RULE_REJECT_PAID;
+ if (!uidForeground && (uidPolicy & POLICY_REJECT_METERED_BACKGROUND) != 0) {
+ // uid in background, and policy says to block metered data
+ uidRules = RULE_REJECT_METERED;
}
// TODO: only dispatch when rules actually change
@@ -931,16 +995,17 @@
// record rule locally to dispatch to new listeners
mUidRules.put(uid, uidRules);
- final boolean rejectPaid = (uidRules & RULE_REJECT_PAID) != 0;
+ final boolean rejectMetered = (uidRules & RULE_REJECT_METERED) != 0;
//kernelSetUidRejectPaid(uid, rejectPaid);
// dispatch changed rule to existing listeners
+ // TODO: dispatch outside of holding lock
final int length = mListeners.beginBroadcast();
for (int i = 0; i < length; i++) {
final INetworkPolicyListener listener = mListeners.getBroadcastItem(i);
if (listener != null) {
try {
- listener.onRulesChanged(uid, uidRules);
+ listener.onUidRulesChanged(uid, uidRules);
} catch (RemoteException e) {
}
}
diff --git a/services/java/com/android/server/net/NetworkStatsService.java b/services/java/com/android/server/net/NetworkStatsService.java
index 0a84bc7..043a581 100644
--- a/services/java/com/android/server/net/NetworkStatsService.java
+++ b/services/java/com/android/server/net/NetworkStatsService.java
@@ -19,14 +19,19 @@
import static android.Manifest.permission.CONNECTIVITY_INTERNAL;
import static android.Manifest.permission.DUMP;
import static android.Manifest.permission.READ_NETWORK_USAGE_HISTORY;
-import static android.Manifest.permission.SHUTDOWN;
+import static android.content.Intent.ACTION_SHUTDOWN;
+import static android.content.Intent.ACTION_UID_REMOVED;
+import static android.content.Intent.EXTRA_UID;
import static android.net.ConnectivityManager.CONNECTIVITY_ACTION;
import static android.net.NetworkStats.IFACE_ALL;
+import static android.net.NetworkStats.TAG_NONE;
import static android.net.NetworkStats.UID_ALL;
+import static android.net.TrafficStats.UID_REMOVED;
import static android.provider.Settings.Secure.NETSTATS_NETWORK_BUCKET_DURATION;
import static android.provider.Settings.Secure.NETSTATS_NETWORK_MAX_HISTORY;
import static android.provider.Settings.Secure.NETSTATS_PERSIST_THRESHOLD;
import static android.provider.Settings.Secure.NETSTATS_POLL_INTERVAL;
+import static android.provider.Settings.Secure.NETSTATS_TAG_MAX_HISTORY;
import static android.provider.Settings.Secure.NETSTATS_UID_BUCKET_DURATION;
import static android.provider.Settings.Secure.NETSTATS_UID_MAX_HISTORY;
import static android.text.format.DateUtils.DAY_IN_MILLIS;
@@ -45,10 +50,12 @@
import android.content.pm.ApplicationInfo;
import android.net.IConnectivityManager;
import android.net.INetworkStatsService;
+import android.net.NetworkIdentity;
import android.net.NetworkInfo;
import android.net.NetworkState;
import android.net.NetworkStats;
import android.net.NetworkStatsHistory;
+import android.net.NetworkTemplate;
import android.os.Environment;
import android.os.Handler;
import android.os.HandlerThread;
@@ -57,14 +64,14 @@
import android.os.SystemClock;
import android.provider.Settings;
import android.telephony.TelephonyManager;
+import android.util.LongSparseArray;
import android.util.NtpTrustedTime;
import android.util.Slog;
-import android.util.SparseArray;
import android.util.TrustedTime;
import com.android.internal.os.AtomicFile;
-import com.google.android.collect.Lists;
import com.google.android.collect.Maps;
+import com.google.android.collect.Sets;
import java.io.DataInputStream;
import java.io.DataOutputStream;
@@ -76,9 +83,9 @@
import java.io.IOException;
import java.io.PrintWriter;
import java.net.ProtocolException;
-import java.util.ArrayList;
import java.util.HashMap;
import java.util.HashSet;
+import java.util.List;
import libcore.io.IoUtils;
@@ -93,7 +100,10 @@
/** File header magic number: "ANET" */
private static final int FILE_MAGIC = 0x414E4554;
- private static final int VERSION_CURRENT = 1;
+ private static final int VERSION_NETWORK_INIT = 1;
+ private static final int VERSION_UID_INIT = 1;
+ private static final int VERSION_UID_WITH_IDENT = 2;
+ private static final int VERSION_UID_WITH_TAG = 3;
private final Context mContext;
private final INetworkManagementService mNetworkManager;
@@ -112,6 +122,9 @@
private PendingIntent mPollIntent;
// TODO: listen for kernel push events through netd instead of polling
+ // TODO: watch for UID uninstall, and transfer stats into single bucket
+
+ // TODO: trim empty history objects entirely
private static final long KB_IN_BYTES = 1024;
private static final long MB_IN_BYTES = 1024 * KB_IN_BYTES;
@@ -127,26 +140,27 @@
public long getNetworkMaxHistory();
public long getUidBucketDuration();
public long getUidMaxHistory();
+ public long getTagMaxHistory();
public long getTimeCacheMaxAge();
}
private final Object mStatsLock = new Object();
- /** Set of active ifaces during this boot. */
- private HashMap<String, InterfaceIdentity> mActiveIface = Maps.newHashMap();
-
- /** Set of historical stats for known ifaces. */
- private HashMap<InterfaceIdentity, NetworkStatsHistory> mNetworkStats = Maps.newHashMap();
+ /** Set of currently active ifaces. */
+ private HashMap<String, NetworkIdentitySet> mActiveIfaces = Maps.newHashMap();
+ /** Set of historical stats for known networks. */
+ private HashMap<NetworkIdentitySet, NetworkStatsHistory> mNetworkStats = Maps.newHashMap();
/** Set of historical stats for known UIDs. */
- private SparseArray<NetworkStatsHistory> mUidStats = new SparseArray<NetworkStatsHistory>();
+ private HashMap<NetworkIdentitySet, LongSparseArray<NetworkStatsHistory>> mUidStats =
+ Maps.newHashMap();
/** Flag if {@link #mUidStats} have been loaded from disk. */
private boolean mUidStatsLoaded = false;
- private NetworkStats mLastNetworkPoll;
- private NetworkStats mLastNetworkPersist;
+ private NetworkStats mLastNetworkSnapshot;
+ private NetworkStats mLastPersistNetworkSnapshot;
- private NetworkStats mLastUidPoll;
+ private NetworkStats mLastUidSnapshot;
private final HandlerThread mHandlerThread;
private final Handler mHandler;
@@ -200,17 +214,20 @@
}
// watch for network interfaces to be claimed
- final IntentFilter ifaceFilter = new IntentFilter();
- ifaceFilter.addAction(CONNECTIVITY_ACTION);
- mContext.registerReceiver(mIfaceReceiver, ifaceFilter, CONNECTIVITY_INTERNAL, mHandler);
+ final IntentFilter connFilter = new IntentFilter(CONNECTIVITY_ACTION);
+ mContext.registerReceiver(mConnReceiver, connFilter, CONNECTIVITY_INTERNAL, mHandler);
// listen for periodic polling events
final IntentFilter pollFilter = new IntentFilter(ACTION_NETWORK_STATS_POLL);
mContext.registerReceiver(mPollReceiver, pollFilter, READ_NETWORK_USAGE_HISTORY, mHandler);
+ // listen for uid removal to clean stats
+ final IntentFilter removedFilter = new IntentFilter(ACTION_UID_REMOVED);
+ mContext.registerReceiver(mRemovedReceiver, removedFilter, null, mHandler);
+
// persist stats during clean shutdown
- final IntentFilter shutdownFilter = new IntentFilter(Intent.ACTION_SHUTDOWN);
- mContext.registerReceiver(mShutdownReceiver, shutdownFilter, SHUTDOWN, null);
+ final IntentFilter shutdownFilter = new IntentFilter(ACTION_SHUTDOWN);
+ mContext.registerReceiver(mShutdownReceiver, shutdownFilter);
try {
registerPollAlarmLocked();
@@ -220,8 +237,9 @@
}
private void shutdownLocked() {
- mContext.unregisterReceiver(mIfaceReceiver);
+ mContext.unregisterReceiver(mConnReceiver);
mContext.unregisterReceiver(mPollReceiver);
+ mContext.unregisterReceiver(mRemovedReceiver);
mContext.unregisterReceiver(mShutdownReceiver);
writeNetworkStatsLocked();
@@ -251,18 +269,19 @@
}
@Override
- public NetworkStatsHistory getHistoryForNetwork(int networkTemplate) {
+ public NetworkStatsHistory getHistoryForNetwork(NetworkTemplate template) {
mContext.enforceCallingOrSelfPermission(READ_NETWORK_USAGE_HISTORY, TAG);
synchronized (mStatsLock) {
// combine all interfaces that match template
- final String subscriberId = getActiveSubscriberId();
final NetworkStatsHistory combined = new NetworkStatsHistory(
mSettings.getNetworkBucketDuration(), estimateNetworkBuckets());
- for (InterfaceIdentity ident : mNetworkStats.keySet()) {
- final NetworkStatsHistory history = mNetworkStats.get(ident);
- if (ident.matchesTemplate(networkTemplate, subscriberId)) {
- combined.recordEntireHistory(history);
+ for (NetworkIdentitySet ident : mNetworkStats.keySet()) {
+ if (templateMatches(template, ident)) {
+ final NetworkStatsHistory history = mNetworkStats.get(ident);
+ if (history != null) {
+ combined.recordEntireHistory(history);
+ }
}
}
return combined;
@@ -270,19 +289,30 @@
}
@Override
- public NetworkStatsHistory getHistoryForUid(int uid, int networkTemplate) {
+ public NetworkStatsHistory getHistoryForUid(NetworkTemplate template, int uid, int tag) {
mContext.enforceCallingOrSelfPermission(READ_NETWORK_USAGE_HISTORY, TAG);
synchronized (mStatsLock) {
- // TODO: combine based on template, if we store that granularity
ensureUidStatsLoadedLocked();
- return mUidStats.get(uid);
+ final long packed = packUidAndTag(uid, tag);
+
+ // combine all interfaces that match template
+ final NetworkStatsHistory combined = new NetworkStatsHistory(
+ mSettings.getUidBucketDuration(), estimateUidBuckets());
+ for (NetworkIdentitySet ident : mUidStats.keySet()) {
+ if (templateMatches(template, ident)) {
+ final NetworkStatsHistory history = mUidStats.get(ident).get(packed);
+ if (history != null) {
+ combined.recordEntireHistory(history);
+ }
+ }
+ }
+ return combined;
}
}
@Override
- public NetworkStats getSummaryForNetwork(
- long start, long end, int networkTemplate, String subscriberId) {
+ public NetworkStats getSummaryForNetwork(NetworkTemplate template, long start, long end) {
mContext.enforceCallingOrSelfPermission(READ_NETWORK_USAGE_HISTORY, TAG);
synchronized (mStatsLock) {
@@ -291,9 +321,9 @@
long[] networkTotal = new long[2];
// combine total from all interfaces that match template
- for (InterfaceIdentity ident : mNetworkStats.keySet()) {
- final NetworkStatsHistory history = mNetworkStats.get(ident);
- if (ident.matchesTemplate(networkTemplate, subscriberId)) {
+ for (NetworkIdentitySet ident : mNetworkStats.keySet()) {
+ if (templateMatches(template, ident)) {
+ final NetworkStatsHistory history = mNetworkStats.get(ident);
networkTotal = history.getTotalData(start, end, networkTotal);
rx += networkTotal[0];
tx += networkTotal[1];
@@ -301,30 +331,45 @@
}
final NetworkStats stats = new NetworkStats(end - start, 1);
- stats.addEntry(IFACE_ALL, UID_ALL, rx, tx);
+ stats.addEntry(IFACE_ALL, UID_ALL, TAG_NONE, rx, tx);
return stats;
}
}
@Override
- public NetworkStats getSummaryForAllUid(long start, long end, int networkTemplate) {
+ public NetworkStats getSummaryForAllUid(
+ NetworkTemplate template, long start, long end, boolean includeTags) {
mContext.enforceCallingOrSelfPermission(READ_NETWORK_USAGE_HISTORY, TAG);
- // TODO: apply networktemplate once granular uid stats are stored.
-
synchronized (mStatsLock) {
ensureUidStatsLoadedLocked();
- final int size = mUidStats.size();
- final NetworkStats stats = new NetworkStats(end - start, size);
-
+ final NetworkStats stats = new NetworkStats(end - start, 24);
long[] total = new long[2];
- for (int i = 0; i < size; i++) {
- final int uid = mUidStats.keyAt(i);
- final NetworkStatsHistory history = mUidStats.valueAt(i);
- total = history.getTotalData(start, end, total);
- stats.addEntry(IFACE_ALL, uid, total[0], total[1]);
+
+ for (NetworkIdentitySet ident : mUidStats.keySet()) {
+ if (templateMatches(template, ident)) {
+ final LongSparseArray<NetworkStatsHistory> uidStats = mUidStats.get(ident);
+ for (int i = 0; i < uidStats.size(); i++) {
+ final long packed = uidStats.keyAt(i);
+ final int uid = unpackUid(packed);
+ final int tag = unpackTag(packed);
+
+ // always include summary under TAG_NONE, and include
+ // other tags when requested.
+ if (tag == TAG_NONE || includeTags) {
+ final NetworkStatsHistory history = uidStats.valueAt(i);
+ total = history.getTotalData(start, end, total);
+ final long rx = total[0];
+ final long tx = total[1];
+ if (rx > 0 || tx > 0) {
+ stats.combineEntry(IFACE_ALL, uid, tag, rx, tx);
+ }
+ }
+ }
+ }
}
+
return stats;
}
}
@@ -334,7 +379,7 @@
* interfaces. Used to associate {@link TelephonyManager#getSubscriberId()}
* with mobile interfaces.
*/
- private BroadcastReceiver mIfaceReceiver = new BroadcastReceiver() {
+ private BroadcastReceiver mConnReceiver = new BroadcastReceiver() {
@Override
public void onReceive(Context context, Intent intent) {
// on background handler thread, and verified CONNECTIVITY_INTERNAL
@@ -352,7 +397,19 @@
// permission above.
synchronized (mStatsLock) {
// TODO: acquire wakelock while performing poll
- performPollLocked(true);
+ performPollLocked(true, false);
+ }
+ }
+ };
+
+ private BroadcastReceiver mRemovedReceiver = new BroadcastReceiver() {
+ @Override
+ public void onReceive(Context context, Intent intent) {
+ // on background handler thread, and UID_REMOVED is protected
+ // broadcast.
+ final int uid = intent.getIntExtra(EXTRA_UID, 0);
+ synchronized (mStatsLock) {
+ removeUidLocked(uid);
}
}
};
@@ -360,7 +417,7 @@
private BroadcastReceiver mShutdownReceiver = new BroadcastReceiver() {
@Override
public void onReceive(Context context, Intent intent) {
- // verified SHUTDOWN permission above.
+ // SHUTDOWN is protected broadcast.
synchronized (mStatsLock) {
shutdownLocked();
}
@@ -371,7 +428,7 @@
* Inspect all current {@link NetworkState} to derive mapping from {@code
* iface} to {@link NetworkStatsHistory}. When multiple {@link NetworkInfo}
* are active on a single {@code iface}, they are combined under a single
- * {@link InterfaceIdentity}.
+ * {@link NetworkIdentitySet}.
*/
private void updateIfacesLocked() {
if (LOGV) Slog.v(TAG, "updateIfacesLocked()");
@@ -379,7 +436,7 @@
// take one last stats snapshot before updating iface mapping. this
// isn't perfect, since the kernel may already be counting traffic from
// the updated network.
- performPollLocked(false);
+ performPollLocked(false, false);
final NetworkState[] states;
try {
@@ -390,13 +447,19 @@
}
// rebuild active interfaces based on connected networks
- mActiveIface.clear();
+ mActiveIfaces.clear();
for (NetworkState state : states) {
if (state.networkInfo.isConnected()) {
// collect networks under their parent interfaces
final String iface = state.linkProperties.getInterfaceName();
- final InterfaceIdentity ident = findOrCreateInterfaceLocked(iface);
+
+ NetworkIdentitySet ident = mActiveIfaces.get(iface);
+ if (ident == null) {
+ ident = new NetworkIdentitySet();
+ mActiveIfaces.put(iface, ident);
+ }
+
ident.add(NetworkIdentity.buildNetworkIdentity(mContext, state));
}
}
@@ -409,7 +472,7 @@
* @param detailedPoll Indicate if detailed UID stats should be collected
* during this poll operation.
*/
- private void performPollLocked(boolean detailedPoll) {
+ private void performPollLocked(boolean detailedPoll, boolean forcePersist) {
if (LOGV) Slog.v(TAG, "performPollLocked()");
// try refreshing time source when stale
@@ -421,33 +484,34 @@
final long currentTime = mTime.hasCache() ? mTime.currentTimeMillis()
: System.currentTimeMillis();
- final NetworkStats networkStats;
- final NetworkStats uidStats;
+ final NetworkStats networkSnapshot;
+ final NetworkStats uidSnapshot;
try {
- networkStats = mNetworkManager.getNetworkStatsSummary();
- uidStats = detailedPoll ? mNetworkManager.getNetworkStatsDetail() : null;
+ networkSnapshot = mNetworkManager.getNetworkStatsSummary();
+ uidSnapshot = detailedPoll ? mNetworkManager.getNetworkStatsDetail() : null;
} catch (RemoteException e) {
Slog.w(TAG, "problem reading network stats");
return;
}
- performNetworkPollLocked(networkStats, currentTime);
+ performNetworkPollLocked(networkSnapshot, currentTime);
if (detailedPoll) {
- performUidPollLocked(uidStats, currentTime);
+ performUidPollLocked(uidSnapshot, currentTime);
}
// decide if enough has changed to trigger persist
- final NetworkStats persistDelta = computeStatsDelta(mLastNetworkPersist, networkStats);
+ final NetworkStats persistDelta = computeStatsDelta(
+ mLastPersistNetworkSnapshot, networkSnapshot);
final long persistThreshold = mSettings.getPersistThreshold();
for (String iface : persistDelta.getUniqueIfaces()) {
- final int index = persistDelta.findIndex(iface, UID_ALL);
- if (persistDelta.rx[index] > persistThreshold
+ final int index = persistDelta.findIndex(iface, UID_ALL, TAG_NONE);
+ if (forcePersist || persistDelta.rx[index] > persistThreshold
|| persistDelta.tx[index] > persistThreshold) {
writeNetworkStatsLocked();
if (mUidStatsLoaded) {
writeUidStatsLocked();
}
- mLastNetworkPersist = networkStats;
+ mLastPersistNetworkSnapshot = networkSnapshot;
break;
}
}
@@ -461,28 +525,33 @@
/**
* Update {@link #mNetworkStats} historical usage.
*/
- private void performNetworkPollLocked(NetworkStats networkStats, long currentTime) {
- final ArrayList<String> unknownIface = Lists.newArrayList();
+ private void performNetworkPollLocked(NetworkStats networkSnapshot, long currentTime) {
+ final HashSet<String> unknownIface = Sets.newHashSet();
- final NetworkStats delta = computeStatsDelta(mLastNetworkPoll, networkStats);
+ final NetworkStats delta = computeStatsDelta(mLastNetworkSnapshot, networkSnapshot);
final long timeStart = currentTime - delta.elapsedRealtime;
- final long maxHistory = mSettings.getNetworkMaxHistory();
- for (String iface : delta.getUniqueIfaces()) {
- final InterfaceIdentity ident = mActiveIface.get(iface);
+ for (int i = 0; i < delta.size; i++) {
+ final String iface = delta.iface[i];
+ final NetworkIdentitySet ident = mActiveIfaces.get(iface);
if (ident == null) {
unknownIface.add(iface);
continue;
}
- final int index = delta.findIndex(iface, UID_ALL);
- final long rx = delta.rx[index];
- final long tx = delta.tx[index];
+ final long rx = delta.rx[i];
+ final long tx = delta.tx[i];
- final NetworkStatsHistory history = findOrCreateNetworkLocked(ident);
+ final NetworkStatsHistory history = findOrCreateNetworkStatsLocked(ident);
history.recordData(timeStart, currentTime, rx, tx);
+ }
+
+ // trim any history beyond max
+ final long maxHistory = mSettings.getNetworkMaxHistory();
+ for (NetworkStatsHistory history : mNetworkStats.values()) {
history.removeBucketsBefore(currentTime - maxHistory);
}
- mLastNetworkPoll = networkStats;
+
+ mLastNetworkSnapshot = networkSnapshot;
if (LOGD && unknownIface.size() > 0) {
Slog.w(TAG, "unknown interfaces " + unknownIface.toString() + ", ignoring those stats");
@@ -492,32 +561,84 @@
/**
* Update {@link #mUidStats} historical usage.
*/
- private void performUidPollLocked(NetworkStats uidStats, long currentTime) {
+ private void performUidPollLocked(NetworkStats uidSnapshot, long currentTime) {
ensureUidStatsLoadedLocked();
- final NetworkStats delta = computeStatsDelta(mLastUidPoll, uidStats);
+ final NetworkStats delta = computeStatsDelta(mLastUidSnapshot, uidSnapshot);
final long timeStart = currentTime - delta.elapsedRealtime;
- final long maxHistory = mSettings.getUidMaxHistory();
- for (int uid : delta.getUniqueUids()) {
- // TODO: traverse all ifaces once surfaced in stats
- final int index = delta.findIndex(IFACE_ALL, uid);
- if (index != -1) {
- final long rx = delta.rx[index];
- final long tx = delta.tx[index];
- final NetworkStatsHistory history = findOrCreateUidLocked(uid);
- history.recordData(timeStart, currentTime, rx, tx);
- history.removeBucketsBefore(currentTime - maxHistory);
+ for (int i = 0; i < delta.size; i++) {
+ final String iface = delta.iface[i];
+ final NetworkIdentitySet ident = mActiveIfaces.get(iface);
+ if (ident == null) {
+ continue;
+ }
+
+ final int uid = delta.uid[i];
+ final int tag = delta.tag[i];
+ final long rx = delta.rx[i];
+ final long tx = delta.tx[i];
+
+ final NetworkStatsHistory history = findOrCreateUidStatsLocked(ident, uid, tag);
+ history.recordData(timeStart, currentTime, rx, tx);
+ }
+
+ // trim any history beyond max
+ final long maxUidHistory = mSettings.getUidMaxHistory();
+ final long maxTagHistory = mSettings.getTagMaxHistory();
+ for (LongSparseArray<NetworkStatsHistory> uidStats : mUidStats.values()) {
+ for (int i = 0; i < uidStats.size(); i++) {
+ final long packed = uidStats.keyAt(i);
+ final NetworkStatsHistory history = uidStats.valueAt(i);
+
+ // detailed tags are trimmed sooner than summary in TAG_NONE
+ if (unpackTag(packed) == TAG_NONE) {
+ history.removeBucketsBefore(currentTime - maxUidHistory);
+ } else {
+ history.removeBucketsBefore(currentTime - maxTagHistory);
+ }
}
}
- mLastUidPoll = uidStats;
+
+ mLastUidSnapshot = uidSnapshot;
}
- private NetworkStatsHistory findOrCreateNetworkLocked(InterfaceIdentity ident) {
- final long bucketDuration = mSettings.getNetworkBucketDuration();
+ /**
+ * Clean up {@link #mUidStats} after UID is removed.
+ */
+ private void removeUidLocked(int uid) {
+ ensureUidStatsLoadedLocked();
+
+ // migrate all UID stats into special "removed" bucket
+ for (NetworkIdentitySet ident : mUidStats.keySet()) {
+ final LongSparseArray<NetworkStatsHistory> uidStats = mUidStats.get(ident);
+ for (int i = 0; i < uidStats.size(); i++) {
+ final long packed = uidStats.keyAt(i);
+ if (unpackUid(packed) == uid) {
+ // only migrate combined TAG_NONE history
+ if (unpackTag(packed) == TAG_NONE) {
+ final NetworkStatsHistory uidHistory = uidStats.valueAt(i);
+ final NetworkStatsHistory removedHistory = findOrCreateUidStatsLocked(
+ ident, UID_REMOVED, TAG_NONE);
+ removedHistory.recordEntireHistory(uidHistory);
+ }
+ uidStats.remove(packed);
+ }
+ }
+ }
+
+ // TODO: push kernel event to wipe stats for UID, otherwise we risk
+ // picking them up again during next poll.
+
+ // since this was radical rewrite, push to disk
+ writeUidStatsLocked();
+ }
+
+ private NetworkStatsHistory findOrCreateNetworkStatsLocked(NetworkIdentitySet ident) {
final NetworkStatsHistory existing = mNetworkStats.get(ident);
// update when no existing, or when bucket duration changed
+ final long bucketDuration = mSettings.getNetworkBucketDuration();
NetworkStatsHistory updated = null;
if (existing == null) {
updated = new NetworkStatsHistory(bucketDuration, 10);
@@ -535,11 +656,21 @@
}
}
- private NetworkStatsHistory findOrCreateUidLocked(int uid) {
- final long bucketDuration = mSettings.getUidBucketDuration();
- final NetworkStatsHistory existing = mUidStats.get(uid);
+ private NetworkStatsHistory findOrCreateUidStatsLocked(
+ NetworkIdentitySet ident, int uid, int tag) {
+ ensureUidStatsLoadedLocked();
+
+ LongSparseArray<NetworkStatsHistory> uidStats = mUidStats.get(ident);
+ if (uidStats == null) {
+ uidStats = new LongSparseArray<NetworkStatsHistory>();
+ mUidStats.put(ident, uidStats);
+ }
+
+ final long packed = packUidAndTag(uid, tag);
+ final NetworkStatsHistory existing = uidStats.get(packed);
// update when no existing, or when bucket duration changed
+ final long bucketDuration = mSettings.getUidBucketDuration();
NetworkStatsHistory updated = null;
if (existing == null) {
updated = new NetworkStatsHistory(bucketDuration, 10);
@@ -550,22 +681,13 @@
}
if (updated != null) {
- mUidStats.put(uid, updated);
+ uidStats.put(packed, updated);
return updated;
} else {
return existing;
}
}
- private InterfaceIdentity findOrCreateInterfaceLocked(String iface) {
- InterfaceIdentity ident = mActiveIface.get(iface);
- if (ident == null) {
- ident = new InterfaceIdentity();
- mActiveIface.put(iface, ident);
- }
- return ident;
- }
-
private void readNetworkStatsLocked() {
if (LOGV) Slog.v(TAG, "readNetworkStatsLocked()");
@@ -585,15 +707,12 @@
final int version = in.readInt();
switch (version) {
- case VERSION_CURRENT: {
- // file format is pairs of interfaces and stats:
- // network := size *(InterfaceIdentity NetworkStatsHistory)
-
+ case VERSION_NETWORK_INIT: {
+ // network := size *(NetworkIdentitySet NetworkStatsHistory)
final int size = in.readInt();
for (int i = 0; i < size; i++) {
- final InterfaceIdentity ident = new InterfaceIdentity(in);
+ final NetworkIdentitySet ident = new NetworkIdentitySet(in);
final NetworkStatsHistory history = new NetworkStatsHistory(in);
-
mNetworkStats.put(ident, history);
}
break;
@@ -637,16 +756,39 @@
final int version = in.readInt();
switch (version) {
- case VERSION_CURRENT: {
- // file format is pairs of UIDs and stats:
+ case VERSION_UID_INIT: {
// uid := size *(UID NetworkStatsHistory)
- final int size = in.readInt();
- for (int i = 0; i < size; i++) {
- final int uid = in.readInt();
- final NetworkStatsHistory history = new NetworkStatsHistory(in);
+ // drop this data version, since we don't have a good
+ // mapping into NetworkIdentitySet.
+ break;
+ }
+ case VERSION_UID_WITH_IDENT: {
+ // uid := size *(NetworkIdentitySet size *(UID NetworkStatsHistory))
- mUidStats.put(uid, history);
+ // drop this data version, since this version only existed
+ // for a short time.
+ break;
+ }
+ case VERSION_UID_WITH_TAG: {
+ // uid := size *(NetworkIdentitySet size *(UID tag NetworkStatsHistory))
+ final int ifaceSize = in.readInt();
+ for (int i = 0; i < ifaceSize; i++) {
+ final NetworkIdentitySet ident = new NetworkIdentitySet(in);
+
+ final int childSize = in.readInt();
+ final LongSparseArray<NetworkStatsHistory> uidStats = new LongSparseArray<
+ NetworkStatsHistory>(childSize);
+ for (int j = 0; j < childSize; j++) {
+ final int uid = in.readInt();
+ final int tag = in.readInt();
+ final long packed = packUidAndTag(uid, tag);
+
+ final NetworkStatsHistory history = new NetworkStatsHistory(in);
+ uidStats.put(packed, history);
+ }
+
+ mUidStats.put(ident, uidStats);
}
break;
}
@@ -674,10 +816,10 @@
final DataOutputStream out = new DataOutputStream(fos);
out.writeInt(FILE_MAGIC);
- out.writeInt(VERSION_CURRENT);
+ out.writeInt(VERSION_NETWORK_INIT);
out.writeInt(mNetworkStats.size());
- for (InterfaceIdentity ident : mNetworkStats.keySet()) {
+ for (NetworkIdentitySet ident : mNetworkStats.keySet()) {
final NetworkStatsHistory history = mNetworkStats.get(ident);
ident.writeToStream(out);
history.writeToStream(out);
@@ -694,6 +836,11 @@
private void writeUidStatsLocked() {
if (LOGV) Slog.v(TAG, "writeUidStatsLocked()");
+ if (!mUidStatsLoaded) {
+ Slog.w(TAG, "asked to write UID stats when not loaded; skipping");
+ return;
+ }
+
// TODO: consider duplicating stats and releasing lock while writing
FileOutputStream fos = null;
@@ -702,16 +849,25 @@
final DataOutputStream out = new DataOutputStream(fos);
out.writeInt(FILE_MAGIC);
- out.writeInt(VERSION_CURRENT);
+ out.writeInt(VERSION_UID_WITH_TAG);
final int size = mUidStats.size();
-
out.writeInt(size);
- for (int i = 0; i < size; i++) {
- final int uid = mUidStats.keyAt(i);
- final NetworkStatsHistory history = mUidStats.valueAt(i);
- out.writeInt(uid);
- history.writeToStream(out);
+ for (NetworkIdentitySet ident : mUidStats.keySet()) {
+ final LongSparseArray<NetworkStatsHistory> uidStats = mUidStats.get(ident);
+ ident.writeToStream(out);
+
+ final int childSize = uidStats.size();
+ out.writeInt(childSize);
+ for (int i = 0; i < childSize; i++) {
+ final long packed = uidStats.keyAt(i);
+ final int uid = unpackUid(packed);
+ final int tag = unpackTag(packed);
+ final NetworkStatsHistory history = uidStats.valueAt(i);
+ out.writeInt(uid);
+ out.writeInt(tag);
+ history.writeToStream(out);
+ }
}
mUidFile.finishWrite(fos);
@@ -740,35 +896,44 @@
}
if (argSet.contains("poll")) {
- performPollLocked(true);
+ performPollLocked(true, true);
pw.println("Forced poll");
return;
}
pw.println("Active interfaces:");
- for (String iface : mActiveIface.keySet()) {
- final InterfaceIdentity ident = mActiveIface.get(iface);
+ for (String iface : mActiveIfaces.keySet()) {
+ final NetworkIdentitySet ident = mActiveIfaces.get(iface);
pw.print(" iface="); pw.print(iface);
pw.print(" ident="); pw.println(ident.toString());
}
pw.println("Known historical stats:");
- for (InterfaceIdentity ident : mNetworkStats.keySet()) {
- final NetworkStatsHistory stats = mNetworkStats.get(ident);
+ for (NetworkIdentitySet ident : mNetworkStats.keySet()) {
+ final NetworkStatsHistory history = mNetworkStats.get(ident);
pw.print(" ident="); pw.println(ident.toString());
- stats.dump(" ", pw);
+ history.dump(" ", pw);
}
if (argSet.contains("detail")) {
// since explicitly requested with argument, we're okay to load
// from disk if not already in memory.
ensureUidStatsLoadedLocked();
- pw.println("Known UID stats:");
- for (int i = 0; i < mUidStats.size(); i++) {
- final int uid = mUidStats.keyAt(i);
- final NetworkStatsHistory stats = mUidStats.valueAt(i);
- pw.print(" UID="); pw.println(uid);
- stats.dump(" ", pw);
+
+ pw.println("Detailed UID stats:");
+ for (NetworkIdentitySet ident : mUidStats.keySet()) {
+ pw.print(" ident="); pw.println(ident.toString());
+
+ final LongSparseArray<NetworkStatsHistory> uidStats = mUidStats.get(ident);
+ for (int i = 0; i < uidStats.size(); i++) {
+ final long packed = uidStats.keyAt(i);
+ final int uid = unpackUid(packed);
+ final int tag = unpackTag(packed);
+ final NetworkStatsHistory history = uidStats.valueAt(i);
+ pw.print(" UID="); pw.print(uid);
+ pw.print(" tag="); pw.println(tag);
+ history.dump(" ", pw);
+ }
}
}
}
@@ -779,27 +944,30 @@
*/
@Deprecated
private void generateRandomLocked() {
- long end = System.currentTimeMillis();
- long start = end - mSettings.getNetworkMaxHistory();
- long rx = 3 * GB_IN_BYTES;
- long tx = 2 * GB_IN_BYTES;
+ long networkEnd = System.currentTimeMillis();
+ long networkStart = networkEnd - mSettings.getNetworkMaxHistory();
+ long networkRx = 3 * GB_IN_BYTES;
+ long networkTx = 2 * GB_IN_BYTES;
+
+ long uidEnd = System.currentTimeMillis();
+ long uidStart = uidEnd - mSettings.getUidMaxHistory();
+ long uidRx = 500 * MB_IN_BYTES;
+ long uidTx = 100 * MB_IN_BYTES;
+
+ final List<ApplicationInfo> installedApps = mContext
+ .getPackageManager().getInstalledApplications(0);
mNetworkStats.clear();
- for (InterfaceIdentity ident : mActiveIface.values()) {
- final NetworkStatsHistory stats = findOrCreateNetworkLocked(ident);
- stats.generateRandom(start, end, rx, tx);
- }
-
- end = System.currentTimeMillis();
- start = end - mSettings.getUidMaxHistory();
- rx = 500 * MB_IN_BYTES;
- tx = 100 * MB_IN_BYTES;
-
mUidStats.clear();
- for (ApplicationInfo info : mContext.getPackageManager().getInstalledApplications(0)) {
- final int uid = info.uid;
- final NetworkStatsHistory stats = findOrCreateUidLocked(uid);
- stats.generateRandom(start, end, rx, tx);
+ for (NetworkIdentitySet ident : mActiveIfaces.values()) {
+ findOrCreateNetworkStatsLocked(ident).generateRandom(
+ networkStart, networkEnd, networkRx, networkTx);
+
+ for (ApplicationInfo info : installedApps) {
+ final int uid = info.uid;
+ findOrCreateUidStatsLocked(ident, uid, TAG_NONE).generateRandom(
+ uidStart, uidEnd, uidRx, uidTx);
+ }
}
}
@@ -815,12 +983,6 @@
}
}
- private String getActiveSubscriberId() {
- final TelephonyManager telephony = (TelephonyManager) mContext.getSystemService(
- Context.TELEPHONY_SERVICE);
- return telephony.getSubscriberId();
- }
-
private int estimateNetworkBuckets() {
return (int) (mSettings.getNetworkMaxHistory() / mSettings.getNetworkBucketDuration());
}
@@ -833,6 +995,36 @@
return (int) (existing.bucketCount * existing.bucketDuration / newBucketDuration);
}
+ // @VisibleForTesting
+ public static long packUidAndTag(int uid, int tag) {
+ final long uidLong = uid;
+ final long tagLong = tag;
+ return (uidLong << 32) | (tagLong & 0xFFFFFFFFL);
+ }
+
+ // @VisibleForTesting
+ public static int unpackUid(long packed) {
+ return (int) (packed >> 32);
+ }
+
+ // @VisibleForTesting
+ public static int unpackTag(long packed) {
+ return (int) (packed & 0xFFFFFFFFL);
+ }
+
+ /**
+ * Test if given {@link NetworkTemplate} matches any {@link NetworkIdentity}
+ * in the given {@link NetworkIdentitySet}.
+ */
+ private static boolean templateMatches(NetworkTemplate template, NetworkIdentitySet identSet) {
+ for (NetworkIdentity ident : identSet) {
+ if (template.matches(ident)) {
+ return true;
+ }
+ }
+ return false;
+ }
+
/**
* Default external settings that read from {@link Settings.Secure}.
*/
@@ -866,6 +1058,9 @@
public long getUidMaxHistory() {
return getSecureLong(NETSTATS_UID_MAX_HISTORY, 90 * DAY_IN_MILLIS);
}
+ public long getTagMaxHistory() {
+ return getSecureLong(NETSTATS_TAG_MAX_HISTORY, 30 * DAY_IN_MILLIS);
+ }
public long getTimeCacheMaxAge() {
return DAY_IN_MILLIS;
}
diff --git a/services/java/com/android/server/usb/UsbDeviceManager.java b/services/java/com/android/server/usb/UsbDeviceManager.java
index ca8a184..b7f9d5c 100644
--- a/services/java/com/android/server/usb/UsbDeviceManager.java
+++ b/services/java/com/android/server/usb/UsbDeviceManager.java
@@ -19,7 +19,6 @@
import android.app.PendingIntent;
import android.app.Notification;
import android.app.NotificationManager;
-import android.content.BroadcastReceiver;
import android.content.ComponentName;
import android.content.ContentResolver;
import android.content.Context;
@@ -33,10 +32,16 @@
import android.net.Uri;
import android.os.Binder;
import android.os.Bundle;
+import android.os.FileUtils;
import android.os.Handler;
+import android.os.HandlerThread;
+import android.os.Looper;
import android.os.Message;
import android.os.Parcelable;
import android.os.ParcelFileDescriptor;
+import android.os.Process;
+import android.os.storage.StorageManager;
+import android.os.storage.StorageVolume;
import android.os.SystemProperties;
import android.os.UEventObserver;
import android.provider.Settings;
@@ -46,7 +51,7 @@
import java.io.File;
import java.io.FileDescriptor;
import java.io.FileNotFoundException;
-import java.io.FileReader;
+import java.io.IOException;
import java.io.PrintWriter;
import java.util.ArrayList;
import java.util.List;
@@ -55,77 +60,110 @@
* UsbDeviceManager manages USB state in device mode.
*/
public class UsbDeviceManager {
+
private static final String TAG = UsbDeviceManager.class.getSimpleName();
private static final boolean LOG = false;
- private static final String USB_CONNECTED_MATCH =
- "DEVPATH=/devices/virtual/switch/usb_connected";
- private static final String USB_CONFIGURATION_MATCH =
- "DEVPATH=/devices/virtual/switch/usb_configuration";
- private static final String USB_FUNCTIONS_MATCH =
- "DEVPATH=/devices/virtual/usb_composite/";
- private static final String USB_CONNECTED_PATH =
- "/sys/class/switch/usb_connected/state";
- private static final String USB_CONFIGURATION_PATH =
- "/sys/class/switch/usb_configuration/state";
- private static final String USB_COMPOSITE_CLASS_PATH =
- "/sys/class/usb_composite";
+ private static final String USB_STATE_MATCH =
+ "DEVPATH=/devices/virtual/android_usb/android0";
+ private static final String ACCESSORY_START_MATCH =
+ "DEVPATH=/devices/virtual/misc/usb_accessory";
+ private static final String FUNCTIONS_PATH =
+ "/sys/class/android_usb/android0/functions";
+ private static final String STATE_PATH =
+ "/sys/class/android_usb/android0/state";
+ private static final String MASS_STORAGE_FILE_PATH =
+ "/sys/class/android_usb/f_mass_storage/lun/file";
private static final int MSG_UPDATE_STATE = 0;
- private static final int MSG_FUNCTION_ENABLED = 1;
- private static final int MSG_FUNCTION_DISABLED = 2;
+ private static final int MSG_ENABLE_ADB = 1;
+ private static final int MSG_SET_PRIMARY_FUNCTION = 2;
+ private static final int MSG_SET_DEFAULT_FUNCTION = 3;
+ private static final int MSG_SYSTEM_READY = 4;
// Delay for debouncing USB disconnects.
// We often get rapid connect/disconnect events when enabling USB functions,
// which need debouncing.
private static final int UPDATE_DELAY = 1000;
- // current connected and configuration state
- private int mConnected;
- private int mConfiguration;
-
- // last broadcasted connected and configuration state
- private int mLastConnected = -1;
- private int mLastConfiguration = -1;
-
- // lists of enabled and disabled USB functions
- private final ArrayList<String> mEnabledFunctions = new ArrayList<String>();
-
+ private UsbHandler mHandler;
private boolean mSystemReady;
- private UsbAccessory mCurrentAccessory;
- // USB functions that are enabled by default, to restore after exiting accessory mode
- private final ArrayList<String> mDefaultFunctions = new ArrayList<String>();
-
private final Context mContext;
- ContentResolver mContentResolver;
- private final Object mLock = new Object();
+ private final ContentResolver mContentResolver;
private final UsbSettingsManager mSettingsManager;
private NotificationManager mNotificationManager;
private final boolean mHasUsbAccessory;
- // for adb connected notifications
- private boolean mAdbNotificationShown = false;
+ // for USB connected notification
+ private boolean mUsbNotificationShown;
+ private boolean mUseUsbNotification;
+ private Notification mUsbNotification;
+
+ // for adb connected notification
+ private boolean mAdbNotificationShown;
private Notification mAdbNotification;
private boolean mAdbEnabled;
+
private class AdbSettingsObserver extends ContentObserver {
public AdbSettingsObserver() {
super(null);
}
@Override
public void onChange(boolean selfChange) {
- mAdbEnabled = (Settings.Secure.getInt(mContentResolver,
- Settings.Secure.ADB_ENABLED, 0) > 0);
- // setting this secure property will start or stop adbd
- SystemProperties.set("persist.service.adb.enable", mAdbEnabled ? "1" : "0");
- updateAdbNotification();
+ boolean enable = (Settings.Secure.getInt(mContentResolver,
+ Settings.Secure.ADB_ENABLED, 0) > 0);
+ mHandler.sendMessage(MSG_ENABLE_ADB, enable);
}
}
- private void updateAdbNotification() {
+ private void updateUsbNotification(boolean connected) {
+ if (mNotificationManager == null || !mUseUsbNotification) return;
+ if (connected) {
+ if (!mUsbNotificationShown) {
+ Resources r = mContext.getResources();
+ CharSequence title = r.getText(
+ com.android.internal.R.string.usb_preferences_notification_title);
+ CharSequence message = r.getText(
+ com.android.internal.R.string.usb_preferece_notification_message);
+
+ if (mUsbNotification == null) {
+ mUsbNotification = new Notification();
+ mUsbNotification.icon = com.android.internal.R.drawable.stat_sys_data_usb;
+ mUsbNotification.when = 0;
+ mUsbNotification.flags = Notification.FLAG_ONGOING_EVENT;
+ mUsbNotification.tickerText = title;
+ mUsbNotification.defaults = 0; // please be quiet
+ mUsbNotification.sound = null;
+ mUsbNotification.vibrate = null;
+ }
+
+ Intent intent = new Intent();
+ intent.setFlags(Intent.FLAG_ACTIVITY_NEW_TASK |
+ Intent.FLAG_ACTIVITY_RESET_TASK_IF_NEEDED);
+ intent.setClassName("com.android.systemui",
+ "com.android.systemui.usb.UsbPreferenceActivity");
+ PendingIntent pi = PendingIntent.getActivity(mContext, 0,
+ intent, 0);
+
+ mUsbNotification.setLatestEventInfo(mContext, title, message, pi);
+
+ mUsbNotificationShown = true;
+ mNotificationManager.notify(
+ com.android.internal.R.string.usb_preferences_notification_title,
+ mUsbNotification);
+ }
+
+ } else if (mUsbNotificationShown) {
+ mUsbNotificationShown = false;
+ mNotificationManager.cancel(
+ com.android.internal.R.string.usb_preferences_notification_title);
+ }
+ }
+
+ private void updateAdbNotification(boolean adbEnabled) {
if (mNotificationManager == null) return;
- boolean adbEnabled = mAdbEnabled && (mConnected == 1);
if (adbEnabled) {
if ("0".equals(SystemProperties.get("persist.adb.notify"))) return;
@@ -173,38 +211,6 @@
}
}
- private final void readCurrentAccessoryLocked() {
- if (mHasUsbAccessory) {
- String[] strings = nativeGetAccessoryStrings();
- if (strings != null) {
- mCurrentAccessory = new UsbAccessory(strings);
- Log.d(TAG, "entering USB accessory mode: " + mCurrentAccessory);
- if (mSystemReady) {
- mSettingsManager.accessoryAttached(mCurrentAccessory);
- }
- } else {
- Log.e(TAG, "nativeGetAccessoryStrings failed");
- }
- }
- }
-
- /*
- * Handles USB function enable/disable events
- */
- private final void functionEnabledLocked(String function, boolean enabled) {
- if (enabled) {
- if (!mEnabledFunctions.contains(function)) {
- mEnabledFunctions.add(function);
- }
-
- if (UsbManager.USB_FUNCTION_ACCESSORY.equals(function)) {
- readCurrentAccessoryLocked();
- }
- } else {
- mEnabledFunctions.remove(function);
- }
- }
-
/*
* Listens for uevent messages from the kernel to monitor the USB state
*/
@@ -215,53 +221,13 @@
Slog.v(TAG, "USB UEVENT: " + event.toString());
}
- synchronized (mLock) {
- String name = event.get("SWITCH_NAME");
- String state = event.get("SWITCH_STATE");
- if (name != null && state != null) {
- try {
- int intState = Integer.parseInt(state);
- if ("usb_connected".equals(name)) {
- mConnected = intState;
- // trigger an Intent broadcast
- if (mSystemReady) {
- // debounce disconnects to avoid problems bringing up USB tethering
- update(mConnected == 0);
- }
- } else if ("usb_configuration".equals(name)) {
- mConfiguration = intState;
- // trigger an Intent broadcast
- if (mSystemReady) {
- update(mConnected == 0);
- }
- }
- } catch (NumberFormatException e) {
- Slog.e(TAG, "Could not parse switch state from event " + event);
- }
- } else {
- String function = event.get("FUNCTION");
- String enabledStr = event.get("ENABLED");
- if (function != null && enabledStr != null) {
- // Note: we do not broadcast a change when a function is enabled or disabled.
- // We just record the state change for the next broadcast.
- int what = ("1".equals(enabledStr) ?
- MSG_FUNCTION_ENABLED : MSG_FUNCTION_DISABLED);
- Message msg = Message.obtain(mHandler, what);
- msg.obj = function;
- mHandler.sendMessage(msg);
- }
- }
- }
- }
- };
-
- private final BroadcastReceiver mBootCompletedReceiver = new BroadcastReceiver() {
- public void onReceive(Context context, Intent intent) {
- // handle accessories attached at boot time
- synchronized (mLock) {
- if (mCurrentAccessory != null) {
- mSettingsManager.accessoryAttached(mCurrentAccessory);
- }
+ String state = event.get("USB_STATE");
+ String accessory = event.get("ACCESSORY");
+ if (state != null) {
+ mHandler.updateState(state);
+ } else if ("START".equals(accessory)) {
+ Slog.d(TAG, "got accessory start");
+ setPrimaryFunction(UsbManager.USB_FUNCTION_ACCESSORY);
}
}
};
@@ -273,229 +239,378 @@
PackageManager pm = mContext.getPackageManager();
mHasUsbAccessory = pm.hasSystemFeature(PackageManager.FEATURE_USB_ACCESSORY);
- synchronized (mLock) {
- init(); // set initial status
-
- // make sure the ADB_ENABLED setting value matches the secure property value
- mAdbEnabled = "1".equals(SystemProperties.get("persist.service.adb.enable"));
- Settings.Secure.putInt(mContentResolver, Settings.Secure.ADB_ENABLED,
- mAdbEnabled ? 1 : 0);
-
- // register observer to listen for settings changes
- mContentResolver.registerContentObserver(
- Settings.Secure.getUriFor(Settings.Secure.ADB_ENABLED),
- false, new AdbSettingsObserver());
-
- // Watch for USB configuration changes
- if (mConfiguration >= 0) {
- mUEventObserver.startObserving(USB_CONNECTED_MATCH);
- mUEventObserver.startObserving(USB_CONFIGURATION_MATCH);
- mUEventObserver.startObserving(USB_FUNCTIONS_MATCH);
+ // create a thread for our Handler
+ HandlerThread thread = new HandlerThread("UsbDeviceManager",
+ Process.THREAD_PRIORITY_BACKGROUND) {
+ protected void onLooperPrepared() {
+ mHandler = new UsbHandler();
}
- }
- }
-
- private final void init() {
- char[] buffer = new char[1024];
- boolean inAccessoryMode = false;
-
- // Read initial USB state
- mConfiguration = -1;
- try {
- FileReader file = new FileReader(USB_CONNECTED_PATH);
- int len = file.read(buffer, 0, 1024);
- file.close();
- mConnected = Integer.valueOf((new String(buffer, 0, len)).trim());
-
- file = new FileReader(USB_CONFIGURATION_PATH);
- len = file.read(buffer, 0, 1024);
- file.close();
- mConfiguration = Integer.valueOf((new String(buffer, 0, len)).trim());
-
- } catch (FileNotFoundException e) {
- Slog.i(TAG, "This kernel does not have USB configuration switch support");
- } catch (Exception e) {
- Slog.e(TAG, "" , e);
- }
- if (mConfiguration < 0) {
- // This may happen in the emulator or devices without USB device mode support
- return;
- }
-
- // Read initial list of enabled and disabled functions (device mode)
- try {
- File[] files = new File(USB_COMPOSITE_CLASS_PATH).listFiles();
- for (int i = 0; i < files.length; i++) {
- File file = new File(files[i], "enable");
- FileReader reader = new FileReader(file);
- int len = reader.read(buffer, 0, 1024);
- reader.close();
- int value = Integer.valueOf((new String(buffer, 0, len)).trim());
- String functionName = files[i].getName();
- if (value == 1) {
- mEnabledFunctions.add(functionName);
- if (UsbManager.USB_FUNCTION_ACCESSORY.equals(functionName)) {
- // The USB accessory driver is on by default, but it might have been
- // enabled before the USB service has initialized.
- inAccessoryMode = true;
- } else if (!UsbManager.USB_FUNCTION_ADB.equals(functionName)) {
- // adb is enabled/disabled automatically by the adbd daemon,
- // so don't treat it as a default function.
- mDefaultFunctions.add(functionName);
- }
- }
- }
- } catch (FileNotFoundException e) {
- Slog.w(TAG, "This kernel does not have USB composite class support");
- } catch (Exception e) {
- Slog.e(TAG, "" , e);
- }
-
- // handle the case where an accessory switched the driver to accessory mode
- // before the framework finished booting
- if (inAccessoryMode) {
- readCurrentAccessoryLocked();
-
- // FIXME - if we booted in accessory mode, then we have no way to figure out
- // which functions are enabled by default.
- // For now, assume that MTP or mass storage are the only possibilities
- if (!mEnabledFunctions.contains(UsbManager.USB_FUNCTION_MTP)) {
- mDefaultFunctions.add(UsbManager.USB_FUNCTION_MTP);
- } else if (!mEnabledFunctions.contains(UsbManager.USB_FUNCTION_MASS_STORAGE)) {
- mDefaultFunctions.add(UsbManager.USB_FUNCTION_MASS_STORAGE);
- }
- }
+ };
+ thread.start();
}
public void systemReady() {
- synchronized (mLock) {
- mNotificationManager = (NotificationManager)
- mContext.getSystemService(Context.NOTIFICATION_SERVICE);
+ mSystemReady = true;
- update(false);
- if (mCurrentAccessory != null) {
- Log.d(TAG, "accessoryAttached at systemReady");
- // its still too early to handle accessories, so add a BOOT_COMPLETED receiver
- // to handle this later.
- mContext.registerReceiver(mBootCompletedReceiver,
- new IntentFilter(Intent.ACTION_BOOT_COMPLETED));
- }
- mSystemReady = true;
+ mNotificationManager = (NotificationManager)
+ mContext.getSystemService(Context.NOTIFICATION_SERVICE);
+
+ // We do not show the USB notification if the primary volume supports mass storage.
+ // The legacy mass storage UI will be used instead.
+ boolean massStorageSupported = false;
+ StorageManager storageManager = (StorageManager)
+ mContext.getSystemService(Context.STORAGE_SERVICE);
+ StorageVolume[] volumes = storageManager.getVolumeList();
+ if (volumes.length > 0) {
+ massStorageSupported = volumes[0].allowMassStorage();
}
+ mUseUsbNotification = !massStorageSupported;
+
+ // make sure the ADB_ENABLED setting value matches the current state
+ Settings.Secure.putInt(mContentResolver, Settings.Secure.ADB_ENABLED, mAdbEnabled ? 1 : 0);
+
+ mHandler.sendEmptyMessage(MSG_SYSTEM_READY);
}
- /*
- * Sends a message to update the USB connected and configured state (device mode).
- * If delayed is true, then we add a small delay in sending the message to debounce
- * the USB connection when enabling USB tethering.
- */
- private final void update(boolean delayed) {
- mHandler.removeMessages(MSG_UPDATE_STATE);
- mHandler.sendEmptyMessageDelayed(MSG_UPDATE_STATE, delayed ? UPDATE_DELAY : 0);
- }
-
- /* returns the currently attached USB accessory (device mode) */
- public UsbAccessory getCurrentAccessory() {
- return mCurrentAccessory;
- }
-
- /* opens the currently attached USB accessory (device mode) */
- public ParcelFileDescriptor openAccessory(UsbAccessory accessory) {
- synchronized (mLock) {
- if (mCurrentAccessory == null) {
- throw new IllegalArgumentException("no accessory attached");
+ private static String addFunction(String functions, String function) {
+ if (!containsFunction(functions, function)) {
+ if (functions.length() > 0) {
+ functions += ",";
}
- if (!mCurrentAccessory.equals(accessory)) {
- Log.e(TAG, accessory.toString() + " does not match current accessory "
- + mCurrentAccessory);
- throw new IllegalArgumentException("accessory not attached");
- }
- mSettingsManager.checkPermission(mCurrentAccessory);
- return nativeOpenAccessory();
+ functions += function;
}
+ return functions;
}
- /*
- * This handler is for deferred handling of events related to device mode and accessories.
- */
- private final Handler mHandler = new Handler() {
+ private static String removeFunction(String functions, String function) {
+ String[] split = functions.split(",");
+ for (int i = 0; i < split.length; i++) {
+ if (function.equals(split[i])) {
+ split[i] = null;
+ }
+ }
+ StringBuilder builder = new StringBuilder();
+ for (int i = 0; i < split.length; i++) {
+ String s = split[i];
+ if (s != null) {
+ if (builder.length() > 0) {
+ builder.append(",");
+ }
+ builder.append(s);
+ }
+ }
+ return builder.toString();
+ }
- @Override
- public void handleMessage(Message msg) {
- synchronized (mLock) {
- switch (msg.what) {
- case MSG_UPDATE_STATE:
- if (mConnected != mLastConnected || mConfiguration != mLastConfiguration) {
- updateAdbNotification();
- if (mConnected == 0) {
- if (UsbManager.isFunctionEnabled(
- UsbManager.USB_FUNCTION_ACCESSORY)) {
- // make sure accessory mode is off, and restore default functions
- Log.d(TAG, "exited USB accessory mode");
- if (!UsbManager.setFunctionEnabled
- (UsbManager.USB_FUNCTION_ACCESSORY, false)) {
- Log.e(TAG, "could not disable accessory function");
- }
- int count = mDefaultFunctions.size();
- for (int i = 0; i < count; i++) {
- String function = mDefaultFunctions.get(i);
- if (!UsbManager.setFunctionEnabled(function, true)) {
- Log.e(TAG, "could not reenable function " + function);
- }
- }
+ private static boolean containsFunction(String functions, String function) {
+ int index = functions.indexOf(function);
+ if (index < 0) return false;
+ if (index > 0 && functions.charAt(index - 1) != ',') return false;
+ int charAfter = index + function.length();
+ if (charAfter < functions.length() && functions.charAt(charAfter) != ',') return false;
+ return true;
+ }
- if (mCurrentAccessory != null) {
- mSettingsManager.accessoryDetached(mCurrentAccessory);
- mCurrentAccessory = null;
- }
- }
- }
+ private final class UsbHandler extends Handler {
- final ContentResolver cr = mContext.getContentResolver();
- if (Settings.Secure.getInt(cr,
- Settings.Secure.DEVICE_PROVISIONED, 0) == 0) {
- Slog.i(TAG, "Device not provisioned, skipping USB broadcast");
- return;
- }
+ // current USB state
+ private boolean mConnected;
+ private boolean mConfigured;
+ private String mCurrentFunctions;
+ private String mDefaultFunctions;
+ private UsbAccessory mCurrentAccessory;
+ private boolean mDeferAccessoryAttached;
- mLastConnected = mConnected;
- mLastConfiguration = mConfiguration;
+ public UsbHandler() {
+ // Read initial USB state
+ try {
+ mCurrentFunctions = FileUtils.readTextFile(
+ new File(FUNCTIONS_PATH), 0, null).trim();
+ mDefaultFunctions = mCurrentFunctions;
+ String state = FileUtils.readTextFile(new File(STATE_PATH), 0, null).trim();
+ updateState(state);
- // send a sticky broadcast containing current USB state
- Intent intent = new Intent(UsbManager.ACTION_USB_STATE);
- intent.addFlags(Intent.FLAG_RECEIVER_REPLACE_PENDING);
- intent.putExtra(UsbManager.USB_CONNECTED, mConnected != 0);
- intent.putExtra(UsbManager.USB_CONFIGURATION, mConfiguration);
- for (int i = 0; i < mEnabledFunctions.size(); i++) {
- intent.putExtra(mEnabledFunctions.get(i), true);
- }
- mContext.sendStickyBroadcast(intent);
- }
- break;
- case MSG_FUNCTION_ENABLED:
- case MSG_FUNCTION_DISABLED:
- functionEnabledLocked((String)msg.obj, msg.what == MSG_FUNCTION_ENABLED);
- break;
+ mAdbEnabled = containsFunction(mCurrentFunctions, UsbManager.USB_FUNCTION_ADB);
+
+ // Upgrade step for previous versions that used persist.service.adb.enable
+ String value = SystemProperties.get("persist.service.adb.enable", "");
+ if (value.length() > 0) {
+ char enable = value.charAt(0);
+ if (enable == '1') {
+ setAdbEnabled(true);
+ } else if (enable == '0') {
+ setAdbEnabled(false);
+ }
+ SystemProperties.set("persist.service.adb.enable", "");
+ }
+
+ // register observer to listen for settings changes
+ mContentResolver.registerContentObserver(
+ Settings.Secure.getUriFor(Settings.Secure.ADB_ENABLED),
+ false, new AdbSettingsObserver());
+
+ // Watch for USB configuration changes
+ mUEventObserver.startObserving(USB_STATE_MATCH);
+ mUEventObserver.startObserving(ACCESSORY_START_MATCH);
+ } catch (Exception e) {
+ Slog.e(TAG, "Error initializing UsbHandler", e);
+ }
+ }
+
+ public void sendMessage(int what, boolean arg) {
+ removeMessages(what);
+ Message m = Message.obtain(this, what);
+ m.arg1 = (arg ? 1 : 0);
+ sendMessage(m);
+ }
+
+ public void sendMessage(int what, Object arg) {
+ removeMessages(what);
+ Message m = Message.obtain(this, what);
+ m.obj = arg;
+ sendMessage(m);
+ }
+
+ public void updateState(String state) {
+ int connected, configured;
+
+ if ("DISCONNECTED".equals(state)) {
+ connected = 0;
+ configured = 0;
+ } else if ("CONNECTED".equals(state)) {
+ connected = 1;
+ configured = 0;
+ } else if ("CONFIGURED".equals(state)) {
+ connected = 1;
+ configured = 1;
+ } else {
+ Slog.e(TAG, "unknown state " + state);
+ return;
+ }
+ removeMessages(MSG_UPDATE_STATE);
+ Message msg = Message.obtain(this, MSG_UPDATE_STATE);
+ msg.arg1 = connected;
+ msg.arg2 = configured;
+ // debounce disconnects to avoid problems bringing up USB tethering
+ sendMessageDelayed(msg, (connected == 0) ? UPDATE_DELAY : 0);
+ }
+
+ private boolean setUsbConfig(String config) {
+ // set the new configuration
+ SystemProperties.set("sys.usb.config", config);
+ // wait for the transition to complete.
+ // give up after 1 second.
+ for (int i = 0; i < 20; i++) {
+ // State transition is done when sys.usb.conf.done is set to the new configuration
+ if (config.equals(SystemProperties.get("sys.usb.state"))) return true;
+ try {
+ // try again in 50ms
+ Thread.sleep(50);
+ } catch (InterruptedException e) {
+ }
+ }
+ return false;
+ }
+
+ private void setCurrentFunctions(String functions) {
+ if (!mCurrentFunctions.equals(functions)) {
+ if (!setUsbConfig("none") || !setUsbConfig(functions)) {
+ Log.e(TAG, "Failed to switch USB configuration to " + functions);
+ // revert to previous configuration if we fail
+ setUsbConfig(mCurrentFunctions);
+ } else {
+ mCurrentFunctions = functions;
}
}
}
- };
+
+ private void setAdbEnabled(boolean enable) {
+ if (enable != mAdbEnabled) {
+ mAdbEnabled = enable;
+ String functions;
+ if (enable) {
+ functions = addFunction(mCurrentFunctions, UsbManager.USB_FUNCTION_ADB);
+ mDefaultFunctions = addFunction(mDefaultFunctions,
+ UsbManager.USB_FUNCTION_ADB);
+ } else {
+ functions = removeFunction(mCurrentFunctions, UsbManager.USB_FUNCTION_ADB);
+ mDefaultFunctions = removeFunction(mDefaultFunctions,
+ UsbManager.USB_FUNCTION_ADB);
+ }
+ SystemProperties.set("persist.sys.usb.config", mDefaultFunctions);
+ setCurrentFunctions(functions);
+ updateAdbNotification(mAdbEnabled && mConnected);
+ }
+ }
+
+ private void setEnabledFunctions(String functionList) {
+ if (mAdbEnabled) {
+ functionList = addFunction(functionList, UsbManager.USB_FUNCTION_ADB);
+ } else {
+ functionList = removeFunction(functionList, UsbManager.USB_FUNCTION_ADB);
+ }
+ setCurrentFunctions(functionList);
+ }
+
+ private void updateCurrentAccessory() {
+ if (!mHasUsbAccessory) return;
+
+ if (mConfigured) {
+ String[] strings = nativeGetAccessoryStrings();
+ if (strings != null) {
+ mCurrentAccessory = new UsbAccessory(strings);
+ Log.d(TAG, "entering USB accessory mode: " + mCurrentAccessory);
+ // defer accessoryAttached if system is not ready
+ if (mSystemReady) {
+ mSettingsManager.accessoryAttached(mCurrentAccessory);
+ } else {
+ mDeferAccessoryAttached = true;
+ }
+ } else {
+ Log.e(TAG, "nativeGetAccessoryStrings failed");
+ }
+ } else if (!mConnected) {
+ // make sure accessory mode is off
+ // and restore default functions
+ Log.d(TAG, "exited USB accessory mode");
+ setEnabledFunctions(mDefaultFunctions);
+
+ if (mCurrentAccessory != null) {
+ if (mSystemReady) {
+ mSettingsManager.accessoryDetached(mCurrentAccessory);
+ }
+ mCurrentAccessory = null;
+ }
+ }
+ }
+
+ private void updateUsbState() {
+ // send a sticky broadcast containing current USB state
+ Intent intent = new Intent(UsbManager.ACTION_USB_STATE);
+ intent.addFlags(Intent.FLAG_RECEIVER_REPLACE_PENDING);
+ intent.putExtra(UsbManager.USB_CONNECTED, mConnected);
+ intent.putExtra(UsbManager.USB_CONFIGURED, mConfigured);
+
+ if (mCurrentFunctions != null) {
+ String[] functions = mCurrentFunctions.split(",");
+ for (int i = 0; i < functions.length; i++) {
+ intent.putExtra(functions[i], true);
+ }
+ }
+
+ mContext.sendStickyBroadcast(intent);
+ }
+
+ @Override
+ public void handleMessage(Message msg) {
+ String function;
+
+ switch (msg.what) {
+ case MSG_UPDATE_STATE:
+ mConnected = (msg.arg1 == 1);
+ mConfigured = (msg.arg2 == 1);
+ updateUsbNotification(mConnected);
+ updateAdbNotification(mAdbEnabled && mConnected);
+ if (containsFunction(mCurrentFunctions,
+ UsbManager.USB_FUNCTION_ACCESSORY)) {
+ updateCurrentAccessory();
+ }
+
+ if (!mConnected) {
+ // restore defaults when USB is disconnected
+ setCurrentFunctions(mDefaultFunctions);
+ }
+ if (mSystemReady) {
+ updateUsbState();
+ }
+ break;
+ case MSG_ENABLE_ADB:
+ setAdbEnabled(msg.arg1 == 1);
+ break;
+ case MSG_SET_PRIMARY_FUNCTION:
+ function = (String)msg.obj;
+ if (function == null) {
+ function = mDefaultFunctions;
+ }
+ setEnabledFunctions(function);
+ break;
+ case MSG_SET_DEFAULT_FUNCTION:
+ function = (String)msg.obj;
+ if (mAdbEnabled) {
+ function = addFunction(function, UsbManager.USB_FUNCTION_ADB);
+ }
+ SystemProperties.set("persist.sys.usb.config", function);
+ mDefaultFunctions = function;
+ break;
+ case MSG_SYSTEM_READY:
+ updateUsbNotification(mConnected);
+ updateAdbNotification(mAdbEnabled && mConnected);
+ updateUsbState();
+ if (mCurrentAccessory != null && mDeferAccessoryAttached) {
+ mSettingsManager.accessoryAttached(mCurrentAccessory);
+ }
+ break;
+ }
+ }
+
+ public UsbAccessory getCurrentAccessory() {
+ return mCurrentAccessory;
+ }
+
+ public void dump(FileDescriptor fd, PrintWriter pw) {
+ pw.println(" USB Device State:");
+ pw.println(" Current Functions: " + mCurrentFunctions);
+ pw.println(" Default Functions: " + mDefaultFunctions);
+ pw.println(" mConnected: " + mConnected);
+ pw.println(" mConfigured: " + mConfigured);
+ pw.println(" mCurrentAccessory: " + mCurrentAccessory);
+ }
+ }
+
+ /* returns the currently attached USB accessory */
+ public UsbAccessory getCurrentAccessory() {
+ return mHandler.getCurrentAccessory();
+ }
+
+ /* opens the currently attached USB accessory */
+ public ParcelFileDescriptor openAccessory(UsbAccessory accessory) {
+ UsbAccessory currentAccessory = mHandler.getCurrentAccessory();
+ if (currentAccessory == null) {
+ throw new IllegalArgumentException("no accessory attached");
+ }
+ if (!currentAccessory.equals(accessory)) {
+ String error = accessory.toString()
+ + " does not match current accessory "
+ + currentAccessory;
+ throw new IllegalArgumentException(error);
+ }
+ mSettingsManager.checkPermission(accessory);
+ return nativeOpenAccessory();
+ }
+
+ public void setPrimaryFunction(String function) {
+ mHandler.sendMessage(MSG_SET_PRIMARY_FUNCTION, function);
+ }
+
+ public void setDefaultFunction(String function) {
+ if (function == null) {
+ throw new NullPointerException();
+ }
+ mHandler.sendMessage(MSG_SET_DEFAULT_FUNCTION, function);
+ }
+
+ public void setMassStorageBackingFile(String path) {
+ if (path == null) path = "";
+ try {
+ FileUtils.stringToFile(MASS_STORAGE_FILE_PATH, path);
+ } catch (IOException e) {
+ Slog.e(TAG, "failed to write to " + MASS_STORAGE_FILE_PATH);
+ }
+ }
public void dump(FileDescriptor fd, PrintWriter pw) {
- synchronized (mLock) {
- pw.println(" USB Device State:");
- pw.print(" Enabled Functions: ");
- for (int i = 0; i < mEnabledFunctions.size(); i++) {
- pw.print(mEnabledFunctions.get(i) + " ");
- }
- pw.println("");
- pw.print(" Default Functions: ");
- for (int i = 0; i < mDefaultFunctions.size(); i++) {
- pw.print(mDefaultFunctions.get(i) + " ");
- }
- pw.println("");
- pw.println(" mConnected: " + mConnected + ", mConfiguration: " + mConfiguration);
- pw.println(" mCurrentAccessory: " + mCurrentAccessory);
+ if (mHandler != null) {
+ mHandler.dump(fd, pw);
}
}
diff --git a/services/java/com/android/server/usb/UsbService.java b/services/java/com/android/server/usb/UsbService.java
index 21e5997c..193638f 100644
--- a/services/java/com/android/server/usb/UsbService.java
+++ b/services/java/com/android/server/usb/UsbService.java
@@ -50,7 +50,7 @@
if (pm.hasSystemFeature(PackageManager.FEATURE_USB_HOST)) {
mHostManager = new UsbHostManager(context, mSettingsManager);
}
- if (new File("/sys/class/usb_composite").exists()) {
+ if (new File("/sys/class/android_usb").exists()) {
mDeviceManager = new UsbDeviceManager(context, mSettingsManager);
}
}
@@ -92,7 +92,7 @@
/* opens the currently attached USB accessory (device mode) */
public ParcelFileDescriptor openAccessory(UsbAccessory accessory) {
if (mDeviceManager != null) {
- return openAccessory(accessory);
+ return mDeviceManager.openAccessory(accessory);
} else {
return null;
}
@@ -146,6 +146,33 @@
mSettingsManager.clearDefaults(packageName);
}
+ public void setPrimaryFunction(String function) {
+ mContext.enforceCallingOrSelfPermission(android.Manifest.permission.MANAGE_USB, null);
+ if (mDeviceManager != null) {
+ mDeviceManager.setPrimaryFunction(function);
+ } else {
+ throw new IllegalStateException("USB device mode not supported");
+ }
+ }
+
+ public void setDefaultFunction(String function) {
+ mContext.enforceCallingOrSelfPermission(android.Manifest.permission.MANAGE_USB, null);
+ if (mDeviceManager != null) {
+ mDeviceManager.setDefaultFunction(function);
+ } else {
+ throw new IllegalStateException("USB device mode not supported");
+ }
+ }
+
+ public void setMassStorageBackingFile(String path) {
+ mContext.enforceCallingOrSelfPermission(android.Manifest.permission.MANAGE_USB, null);
+ if (mDeviceManager != null) {
+ mDeviceManager.setMassStorageBackingFile(path);
+ } else {
+ throw new IllegalStateException("USB device mode not supported");
+ }
+ }
+
@Override
public void dump(FileDescriptor fd, PrintWriter pw, String[] args) {
if (mContext.checkCallingOrSelfPermission(android.Manifest.permission.DUMP)
diff --git a/services/tests/servicestests/src/com/android/server/NetworkPolicyManagerServiceTest.java b/services/tests/servicestests/src/com/android/server/NetworkPolicyManagerServiceTest.java
index edccf6c..07e5425 100644
--- a/services/tests/servicestests/src/com/android/server/NetworkPolicyManagerServiceTest.java
+++ b/services/tests/servicestests/src/com/android/server/NetworkPolicyManagerServiceTest.java
@@ -16,16 +16,20 @@
package com.android.server;
+import static android.content.Intent.ACTION_UID_REMOVED;
+import static android.content.Intent.EXTRA_UID;
import static android.net.ConnectivityManager.CONNECTIVITY_ACTION;
import static android.net.ConnectivityManager.TYPE_WIFI;
import static android.net.NetworkPolicyManager.POLICY_NONE;
-import static android.net.NetworkPolicyManager.POLICY_REJECT_PAID_BACKGROUND;
+import static android.net.NetworkPolicyManager.POLICY_REJECT_METERED_BACKGROUND;
import static android.net.NetworkPolicyManager.RULE_ALLOW_ALL;
-import static android.net.NetworkPolicyManager.RULE_REJECT_PAID;
+import static android.net.NetworkPolicyManager.RULE_REJECT_METERED;
import static android.net.NetworkPolicyManager.computeLastCycleBoundary;
+import static android.net.NetworkStats.TAG_NONE;
import static android.net.NetworkStats.UID_ALL;
-import static android.net.TrafficStats.TEMPLATE_WIFI;
+import static android.net.NetworkTemplate.MATCH_WIFI;
import static org.easymock.EasyMock.anyInt;
+import static org.easymock.EasyMock.aryEq;
import static org.easymock.EasyMock.capture;
import static org.easymock.EasyMock.createMock;
import static org.easymock.EasyMock.eq;
@@ -48,6 +52,7 @@
import android.net.NetworkPolicy;
import android.net.NetworkState;
import android.net.NetworkStats;
+import android.net.NetworkTemplate;
import android.os.Binder;
import android.os.IPowerManager;
import android.test.AndroidTestCase;
@@ -75,6 +80,8 @@
private static final long TEST_START = 1194220800000L;
private static final String TEST_IFACE = "test0";
+ private static NetworkTemplate sTemplateWifi = new NetworkTemplate(MATCH_WIFI, null);
+
private BroadcastInterceptingContext mServiceContext;
private File mPolicyDir;
@@ -182,7 +189,7 @@
final Future<Intent> backgroundChanged = mServiceContext.nextBroadcastIntent(
ConnectivityManager.ACTION_BACKGROUND_DATA_SETTING_CHANGED);
- mService.setUidPolicy(UID_A, POLICY_REJECT_PAID_BACKGROUND);
+ mService.setUidPolicy(UID_A, POLICY_REJECT_METERED_BACKGROUND);
backgroundChanged.get();
}
@@ -225,12 +232,12 @@
expectRulesChanged(UID_A, RULE_ALLOW_ALL);
replay();
mProcessObserver.onForegroundActivitiesChanged(PID_1, UID_A, true);
- mService.setUidPolicy(UID_A, POLICY_REJECT_PAID_BACKGROUND);
+ mService.setUidPolicy(UID_A, POLICY_REJECT_METERED_BACKGROUND);
verifyAndReset();
// now turn screen off and verify REJECT rule
expect(mPowerManager.isScreenOn()).andReturn(false).atLeastOnce();
- expectRulesChanged(UID_A, RULE_REJECT_PAID);
+ expectRulesChanged(UID_A, RULE_REJECT_METERED);
replay();
mServiceContext.sendBroadcast(new Intent(Intent.ACTION_SCREEN_OFF));
verifyAndReset();
@@ -260,9 +267,9 @@
public void testPolicyReject() throws Exception {
// POLICY_REJECT should RULE_ALLOW in background
- expectRulesChanged(UID_A, RULE_REJECT_PAID);
+ expectRulesChanged(UID_A, RULE_REJECT_METERED);
replay();
- mService.setUidPolicy(UID_A, POLICY_REJECT_PAID_BACKGROUND);
+ mService.setUidPolicy(UID_A, POLICY_REJECT_METERED_BACKGROUND);
verifyAndReset();
// POLICY_REJECT should RULE_ALLOW in foreground
@@ -272,7 +279,7 @@
verifyAndReset();
// POLICY_REJECT should RULE_REJECT in background
- expectRulesChanged(UID_A, RULE_REJECT_PAID);
+ expectRulesChanged(UID_A, RULE_REJECT_METERED);
replay();
mProcessObserver.onForegroundActivitiesChanged(PID_1, UID_A, false);
verifyAndReset();
@@ -287,9 +294,9 @@
verifyAndReset();
// adding POLICY_REJECT should cause RULE_REJECT
- expectRulesChanged(UID_A, RULE_REJECT_PAID);
+ expectRulesChanged(UID_A, RULE_REJECT_METERED);
replay();
- mService.setUidPolicy(UID_A, POLICY_REJECT_PAID_BACKGROUND);
+ mService.setUidPolicy(UID_A, POLICY_REJECT_METERED_BACKGROUND);
verifyAndReset();
// removing POLICY_REJECT should return us to RULE_ALLOW
@@ -304,7 +311,7 @@
final long currentTime = parseTime("2007-11-14T00:00:00.000Z");
final long expectedCycle = parseTime("2007-11-05T00:00:00.000Z");
- final NetworkPolicy policy = new NetworkPolicy(TEMPLATE_WIFI, null, 5, 1024L, 1024L);
+ final NetworkPolicy policy = new NetworkPolicy(sTemplateWifi, 5, 1024L, 1024L);
final long actualCycle = computeLastCycleBoundary(currentTime, policy);
assertEquals(expectedCycle, actualCycle);
}
@@ -314,7 +321,7 @@
final long currentTime = parseTime("2007-11-14T00:00:00.000Z");
final long expectedCycle = parseTime("2007-10-20T00:00:00.000Z");
- final NetworkPolicy policy = new NetworkPolicy(TEMPLATE_WIFI, null, 20, 1024L, 1024L);
+ final NetworkPolicy policy = new NetworkPolicy(sTemplateWifi, 20, 1024L, 1024L);
final long actualCycle = computeLastCycleBoundary(currentTime, policy);
assertEquals(expectedCycle, actualCycle);
}
@@ -324,7 +331,7 @@
final long currentTime = parseTime("2007-02-14T00:00:00.000Z");
final long expectedCycle = parseTime("2007-01-30T00:00:00.000Z");
- final NetworkPolicy policy = new NetworkPolicy(TEMPLATE_WIFI, null, 30, 1024L, 1024L);
+ final NetworkPolicy policy = new NetworkPolicy(sTemplateWifi, 30, 1024L, 1024L);
final long actualCycle = computeLastCycleBoundary(currentTime, policy);
assertEquals(expectedCycle, actualCycle);
}
@@ -334,7 +341,7 @@
final long currentTime = parseTime("2007-03-14T00:00:00.000Z");
final long expectedCycle = parseTime("2007-03-01T00:00:00.000Z");
- final NetworkPolicy policy = new NetworkPolicy(TEMPLATE_WIFI, null, 30, 1024L, 1024L);
+ final NetworkPolicy policy = new NetworkPolicy(sTemplateWifi, 30, 1024L, 1024L);
final long actualCycle = computeLastCycleBoundary(currentTime, policy);
assertEquals(expectedCycle, actualCycle);
}
@@ -353,6 +360,7 @@
state = new NetworkState[] { buildWifi() };
expect(mConnManager.getAllNetworkState()).andReturn(state).atLeastOnce();
expectTime(TIME_MAR_10 + elapsedRealtime);
+ expectMeteredIfacesChanged();
replay();
mServiceContext.sendBroadcast(new Intent(CONNECTIVITY_ACTION));
@@ -365,17 +373,34 @@
// pretend that 512 bytes total have happened
stats = new NetworkStats(elapsedRealtime, 1)
- .addEntry(TEST_IFACE, UID_ALL, 256L, 256L);
- expect(mStatsService.getSummaryForNetwork(TIME_FEB_15, TIME_MAR_10, TEMPLATE_WIFI, null))
+ .addEntry(TEST_IFACE, UID_ALL, TAG_NONE, 256L, 256L);
+ expect(mStatsService.getSummaryForNetwork(sTemplateWifi, TIME_FEB_15, TIME_MAR_10))
.andReturn(stats).atLeastOnce();
// expect that quota remaining should be 1536 bytes
// TODO: write up NetworkManagementService mock
expectClearNotifications();
+ expectMeteredIfacesChanged(TEST_IFACE);
replay();
- setNetworkPolicies(new NetworkPolicy(TEMPLATE_WIFI, null, CYCLE_DAY, 1024L, 2048L));
+ setNetworkPolicies(new NetworkPolicy(sTemplateWifi, CYCLE_DAY, 1024L, 2048L));
+ verifyAndReset();
+ }
+
+ public void testUidRemovedPolicyCleared() throws Exception {
+ // POLICY_REJECT should RULE_REJECT in background
+ expectRulesChanged(UID_A, RULE_REJECT_METERED);
+ replay();
+ mService.setUidPolicy(UID_A, POLICY_REJECT_METERED_BACKGROUND);
+ verifyAndReset();
+
+ // uninstall should clear RULE_REJECT
+ expectRulesChanged(UID_A, RULE_ALLOW_ALL);
+ replay();
+ final Intent intent = new Intent(ACTION_UID_REMOVED);
+ intent.putExtra(EXTRA_UID, UID_A);
+ mServiceContext.sendBroadcast(intent);
verifyAndReset();
}
@@ -411,7 +436,12 @@
}
private void expectRulesChanged(int uid, int policy) throws Exception {
- mPolicyListener.onRulesChanged(eq(uid), eq(policy));
+ mPolicyListener.onUidRulesChanged(eq(uid), eq(policy));
+ expectLastCall().atLeastOnce();
+ }
+
+ private void expectMeteredIfacesChanged(String... ifaces) throws Exception {
+ mPolicyListener.onMeteredIfacesChanged(aryEq(ifaces));
expectLastCall().atLeastOnce();
}
diff --git a/services/tests/servicestests/src/com/android/server/NetworkStatsServiceTest.java b/services/tests/servicestests/src/com/android/server/NetworkStatsServiceTest.java
index 2457ff3..636d059 100644
--- a/services/tests/servicestests/src/com/android/server/NetworkStatsServiceTest.java
+++ b/services/tests/servicestests/src/com/android/server/NetworkStatsServiceTest.java
@@ -16,16 +16,26 @@
package com.android.server;
+import static android.content.Intent.ACTION_UID_REMOVED;
+import static android.content.Intent.EXTRA_UID;
import static android.net.ConnectivityManager.CONNECTIVITY_ACTION;
+import static android.net.ConnectivityManager.TYPE_MOBILE;
import static android.net.ConnectivityManager.TYPE_WIFI;
+import static android.net.ConnectivityManager.TYPE_WIMAX;
import static android.net.NetworkStats.IFACE_ALL;
+import static android.net.NetworkStats.TAG_NONE;
import static android.net.NetworkStats.UID_ALL;
-import static android.net.TrafficStats.TEMPLATE_WIFI;
+import static android.net.NetworkTemplate.MATCH_MOBILE_ALL;
+import static android.net.NetworkTemplate.MATCH_WIFI;
+import static android.net.TrafficStats.UID_REMOVED;
import static android.text.format.DateUtils.DAY_IN_MILLIS;
import static android.text.format.DateUtils.HOUR_IN_MILLIS;
import static android.text.format.DateUtils.MINUTE_IN_MILLIS;
import static android.text.format.DateUtils.WEEK_IN_MILLIS;
import static com.android.server.net.NetworkStatsService.ACTION_NETWORK_STATS_POLL;
+import static com.android.server.net.NetworkStatsService.packUidAndTag;
+import static com.android.server.net.NetworkStatsService.unpackTag;
+import static com.android.server.net.NetworkStatsService.unpackUid;
import static org.easymock.EasyMock.anyLong;
import static org.easymock.EasyMock.createMock;
import static org.easymock.EasyMock.eq;
@@ -44,9 +54,12 @@
import android.net.NetworkState;
import android.net.NetworkStats;
import android.net.NetworkStatsHistory;
+import android.net.NetworkTemplate;
import android.os.INetworkManagementService;
+import android.telephony.TelephonyManager;
import android.test.AndroidTestCase;
import android.test.suitebuilder.annotation.LargeTest;
+import android.util.Log;
import android.util.TrustedTime;
import com.android.server.net.NetworkStatsService;
@@ -66,8 +79,16 @@
private static final String TEST_IFACE = "test0";
private static final long TEST_START = 1194220800000L;
- private static final int TEST_UID_1 = 1001;
- private static final int TEST_UID_2 = 1002;
+ private static final String IMSI_1 = "310004";
+ private static final String IMSI_2 = "310260";
+
+ private static NetworkTemplate sTemplateWifi = new NetworkTemplate(MATCH_WIFI, null);
+ private static NetworkTemplate sTemplateImsi1 = new NetworkTemplate(MATCH_MOBILE_ALL, IMSI_1);
+ private static NetworkTemplate sTemplateImsi2 = new NetworkTemplate(MATCH_MOBILE_ALL, IMSI_2);
+
+ private static final int UID_RED = 1001;
+ private static final int UID_BLUE = 1002;
+ private static final int UID_GREEN = 1003;
private BroadcastInterceptingContext mServiceContext;
private File mStatsDir;
@@ -118,13 +139,15 @@
mNetManager = null;
mAlarmManager = null;
mTime = null;
+ mSettings = null;
+ mConnManager = null;
mService = null;
super.tearDown();
}
- public void testSummaryStatsWifi() throws Exception {
+ public void testNetworkStatsWifi() throws Exception {
long elapsedRealtime = 0;
// pretend that wifi network comes online; service should ask about full
@@ -138,7 +161,7 @@
mServiceContext.sendBroadcast(new Intent(CONNECTIVITY_ACTION));
// verify service has empty history for wifi
- assertNetworkTotal(TEMPLATE_WIFI, 0L, 0L);
+ assertNetworkTotal(sTemplateWifi, 0L, 0L);
verifyAndReset();
// modify some number on wifi, and trigger poll event
@@ -146,14 +169,14 @@
expectTime(TEST_START + elapsedRealtime);
expectDefaultSettings();
expectNetworkStatsSummary(new NetworkStats(elapsedRealtime, 1)
- .addEntry(TEST_IFACE, UID_ALL, 1024L, 2048L));
+ .addEntry(TEST_IFACE, UID_ALL, TAG_NONE, 1024L, 2048L));
expectNetworkStatsDetail(buildEmptyStats(elapsedRealtime));
replay();
mServiceContext.sendBroadcast(new Intent(ACTION_NETWORK_STATS_POLL));
// verify service recorded history
- assertNetworkTotal(TEMPLATE_WIFI, 1024L, 2048L);
+ assertNetworkTotal(sTemplateWifi, 1024L, 2048L);
verifyAndReset();
// and bump forward again, with counters going higher. this is
@@ -162,14 +185,14 @@
expectTime(TEST_START + elapsedRealtime);
expectDefaultSettings();
expectNetworkStatsSummary(new NetworkStats(elapsedRealtime, 1)
- .addEntry(TEST_IFACE, UID_ALL, 4096L, 8192L));
+ .addEntry(TEST_IFACE, UID_ALL, TAG_NONE, 4096L, 8192L));
expectNetworkStatsDetail(buildEmptyStats(elapsedRealtime));
replay();
mServiceContext.sendBroadcast(new Intent(ACTION_NETWORK_STATS_POLL));
// verify service recorded history
- assertNetworkTotal(TEMPLATE_WIFI, 4096L, 8192L);
+ assertNetworkTotal(sTemplateWifi, 4096L, 8192L);
verifyAndReset();
}
@@ -189,7 +212,7 @@
mServiceContext.sendBroadcast(new Intent(CONNECTIVITY_ACTION));
// verify service has empty history for wifi
- assertNetworkTotal(TEMPLATE_WIFI, 0L, 0L);
+ assertNetworkTotal(sTemplateWifi, 0L, 0L);
verifyAndReset();
// modify some number on wifi, and trigger poll event
@@ -197,19 +220,18 @@
expectTime(TEST_START + elapsedRealtime);
expectDefaultSettings();
expectNetworkStatsSummary(new NetworkStats(elapsedRealtime, 1)
- .addEntry(TEST_IFACE, UID_ALL, 1024L, 2048L));
- // TODO: switch these stats to specific iface
+ .addEntry(TEST_IFACE, UID_ALL, TAG_NONE, 1024L, 2048L));
expectNetworkStatsDetail(new NetworkStats(elapsedRealtime, 2)
- .addEntry(IFACE_ALL, TEST_UID_1, 512L, 256L)
- .addEntry(IFACE_ALL, TEST_UID_2, 128L, 128L));
+ .addEntry(TEST_IFACE, UID_RED, TAG_NONE, 512L, 256L)
+ .addEntry(TEST_IFACE, UID_BLUE, TAG_NONE, 128L, 128L));
replay();
mServiceContext.sendBroadcast(new Intent(ACTION_NETWORK_STATS_POLL));
// verify service recorded history
- assertNetworkTotal(TEMPLATE_WIFI, 1024L, 2048L);
- assertUidTotal(TEST_UID_1, TEMPLATE_WIFI, 512L, 256L);
- assertUidTotal(TEST_UID_2, TEMPLATE_WIFI, 128L, 128L);
+ assertNetworkTotal(sTemplateWifi, 1024L, 2048L);
+ assertUidTotal(sTemplateWifi, UID_RED, 512L, 256L);
+ assertUidTotal(sTemplateWifi, UID_BLUE, 128L, 128L);
verifyAndReset();
// graceful shutdown system, which should trigger persist of stats, and
@@ -220,7 +242,7 @@
// we persisted them to file.
expectDefaultSettings();
replay();
- assertNetworkTotal(TEMPLATE_WIFI, 0L, 0L);
+ assertNetworkTotal(sTemplateWifi, 0L, 0L);
verifyAndReset();
assertStatsFilesExist(true);
@@ -233,9 +255,9 @@
mService.systemReady();
// after systemReady(), we should have historical stats loaded again
- assertNetworkTotal(TEMPLATE_WIFI, 1024L, 2048L);
- assertUidTotal(TEST_UID_1, TEMPLATE_WIFI, 512L, 256L);
- assertUidTotal(TEST_UID_2, TEMPLATE_WIFI, 128L, 128L);
+ assertNetworkTotal(sTemplateWifi, 1024L, 2048L);
+ assertUidTotal(sTemplateWifi, UID_RED, 512L, 256L);
+ assertUidTotal(sTemplateWifi, UID_BLUE, 128L, 128L);
verifyAndReset();
}
@@ -263,14 +285,14 @@
expectTime(TEST_START + elapsedRealtime);
expectSettings(0L, HOUR_IN_MILLIS, WEEK_IN_MILLIS);
expectNetworkStatsSummary(new NetworkStats(elapsedRealtime, 1)
- .addEntry(TEST_IFACE, UID_ALL, 512L, 512L));
+ .addEntry(TEST_IFACE, UID_ALL, TAG_NONE, 512L, 512L));
expectNetworkStatsDetail(buildEmptyStats(elapsedRealtime));
replay();
mServiceContext.sendBroadcast(new Intent(ACTION_NETWORK_STATS_POLL));
// verify service recorded history
- history = mService.getHistoryForNetwork(TEMPLATE_WIFI);
+ history = mService.getHistoryForNetwork(new NetworkTemplate(MATCH_WIFI, null));
total = history.getTotalData(Long.MIN_VALUE, Long.MAX_VALUE, null);
assertEquals(512L, total[0]);
assertEquals(512L, total[1]);
@@ -289,7 +311,7 @@
mServiceContext.sendBroadcast(new Intent(ACTION_NETWORK_STATS_POLL));
// verify identical stats, but spread across 4 buckets now
- history = mService.getHistoryForNetwork(TEMPLATE_WIFI);
+ history = mService.getHistoryForNetwork(new NetworkTemplate(MATCH_WIFI, null));
total = history.getTotalData(Long.MIN_VALUE, Long.MAX_VALUE, null);
assertEquals(512L, total[0]);
assertEquals(512L, total[1]);
@@ -299,15 +321,284 @@
}
- private void assertNetworkTotal(int template, long rx, long tx) {
+ public void testUidStatsAcrossNetworks() throws Exception {
+ long elapsedRealtime = 0;
+
+ // pretend first mobile network comes online
+ expectTime(TEST_START + elapsedRealtime);
+ expectDefaultSettings();
+ expectNetworkState(buildMobile3gState(IMSI_1));
+ expectNetworkStatsSummary(buildEmptyStats(elapsedRealtime));
+
+ replay();
+ mServiceContext.sendBroadcast(new Intent(CONNECTIVITY_ACTION));
+ verifyAndReset();
+
+ // create some traffic on first network
+ elapsedRealtime += HOUR_IN_MILLIS;
+ expectTime(TEST_START + elapsedRealtime);
+ expectDefaultSettings();
+ expectNetworkStatsSummary(new NetworkStats(elapsedRealtime, 1)
+ .addEntry(TEST_IFACE, UID_ALL, TAG_NONE, 2048L, 512L));
+ expectNetworkStatsDetail(new NetworkStats(elapsedRealtime, 3)
+ .addEntry(TEST_IFACE, UID_RED, TAG_NONE, 1536L, 512L)
+ .addEntry(TEST_IFACE, UID_RED, 0xF00D, 512L, 512L)
+ .addEntry(TEST_IFACE, UID_BLUE, TAG_NONE, 512L, 0L));
+
+ replay();
+ mServiceContext.sendBroadcast(new Intent(ACTION_NETWORK_STATS_POLL));
+
+ // verify service recorded history
+ assertNetworkTotal(sTemplateImsi1, 2048L, 512L);
+ assertNetworkTotal(sTemplateWifi, 0L, 0L);
+ assertUidTotal(sTemplateImsi1, UID_RED, 1536L, 512L);
+ assertUidTotal(sTemplateImsi1, UID_BLUE, 512L, 0L);
+ verifyAndReset();
+
+ // now switch networks; this also tests that we're okay with interfaces
+ // disappearing, to verify we don't count backwards.
+ elapsedRealtime += HOUR_IN_MILLIS;
+ expectTime(TEST_START + elapsedRealtime);
+ expectDefaultSettings();
+ expectNetworkState(buildMobile3gState(IMSI_2));
+ expectNetworkStatsSummary(buildEmptyStats(elapsedRealtime));
+ expectNetworkStatsDetail(buildEmptyStats(elapsedRealtime));
+
+ replay();
+ mServiceContext.sendBroadcast(new Intent(CONNECTIVITY_ACTION));
+ mServiceContext.sendBroadcast(new Intent(ACTION_NETWORK_STATS_POLL));
+ verifyAndReset();
+
+ // create traffic on second network
+ elapsedRealtime += HOUR_IN_MILLIS;
+ expectTime(TEST_START + elapsedRealtime);
+ expectDefaultSettings();
+ expectNetworkStatsSummary(new NetworkStats(elapsedRealtime, 1)
+ .addEntry(TEST_IFACE, UID_ALL, TAG_NONE, 128L, 1024L));
+ expectNetworkStatsDetail(new NetworkStats(elapsedRealtime, 1)
+ .addEntry(TEST_IFACE, UID_BLUE, TAG_NONE, 128L, 1024L));
+
+ replay();
+ mServiceContext.sendBroadcast(new Intent(ACTION_NETWORK_STATS_POLL));
+
+ // verify original history still intact
+ assertNetworkTotal(sTemplateImsi1, 2048L, 512L);
+ assertUidTotal(sTemplateImsi1, UID_RED, 1536L, 512L);
+ assertUidTotal(sTemplateImsi1, UID_BLUE, 512L, 0L);
+
+ // and verify new history also recorded under different template, which
+ // verifies that we didn't cross the streams.
+ assertNetworkTotal(sTemplateImsi2, 128L, 1024L);
+ assertNetworkTotal(sTemplateWifi, 0L, 0L);
+ assertUidTotal(sTemplateImsi2, UID_BLUE, 128L, 1024L);
+ verifyAndReset();
+
+ }
+
+ public void testUidRemovedIsMoved() throws Exception {
+ long elapsedRealtime = 0;
+
+ // pretend that network comes online
+ expectTime(TEST_START + elapsedRealtime);
+ expectDefaultSettings();
+ expectNetworkState(buildWifiState());
+ expectNetworkStatsSummary(buildEmptyStats(elapsedRealtime));
+
+ replay();
+ mServiceContext.sendBroadcast(new Intent(CONNECTIVITY_ACTION));
+ verifyAndReset();
+
+ // create some traffic
+ elapsedRealtime += HOUR_IN_MILLIS;
+ expectTime(TEST_START + elapsedRealtime);
+ expectDefaultSettings();
+ expectNetworkStatsSummary(new NetworkStats(elapsedRealtime, 1)
+ .addEntry(TEST_IFACE, UID_ALL, TAG_NONE, 4128L, 544L));
+ expectNetworkStatsDetail(new NetworkStats(elapsedRealtime, 1)
+ .addEntry(TEST_IFACE, UID_RED, TAG_NONE, 16L, 16L)
+ .addEntry(TEST_IFACE, UID_BLUE, TAG_NONE, 4096L, 512L)
+ .addEntry(TEST_IFACE, UID_GREEN, TAG_NONE, 16L, 16L));
+
+ replay();
+ mServiceContext.sendBroadcast(new Intent(ACTION_NETWORK_STATS_POLL));
+
+ // verify service recorded history
+ assertNetworkTotal(sTemplateWifi, 4128L, 544L);
+ assertUidTotal(sTemplateWifi, UID_RED, 16L, 16L);
+ assertUidTotal(sTemplateWifi, UID_BLUE, 4096L, 512L);
+ assertUidTotal(sTemplateWifi, UID_GREEN, 16L, 16L);
+ verifyAndReset();
+
+ // now pretend two UIDs are uninstalled, which should migrate stats to
+ // special "removed" bucket.
+ expectDefaultSettings();
+ replay();
+ final Intent intent = new Intent(ACTION_UID_REMOVED);
+ intent.putExtra(EXTRA_UID, UID_BLUE);
+ mServiceContext.sendBroadcast(intent);
+ intent.putExtra(EXTRA_UID, UID_RED);
+ mServiceContext.sendBroadcast(intent);
+
+ // existing uid and total should remain unchanged; but removed UID
+ // should be gone completely.
+ assertNetworkTotal(sTemplateWifi, 4128L, 544L);
+ assertUidTotal(sTemplateWifi, UID_RED, 0L, 0L);
+ assertUidTotal(sTemplateWifi, UID_BLUE, 0L, 0L);
+ assertUidTotal(sTemplateWifi, UID_GREEN, 16L, 16L);
+ assertUidTotal(sTemplateWifi, UID_REMOVED, 4112L, 528L);
+ verifyAndReset();
+
+ }
+
+ public void testUid3g4gCombinedByTemplate() throws Exception {
+ long elapsedRealtime = 0;
+
+ // pretend that network comes online
+ expectTime(TEST_START + elapsedRealtime);
+ expectDefaultSettings();
+ expectNetworkState(buildMobile3gState(IMSI_1));
+ expectNetworkStatsSummary(buildEmptyStats(elapsedRealtime));
+
+ replay();
+ mServiceContext.sendBroadcast(new Intent(CONNECTIVITY_ACTION));
+ verifyAndReset();
+
+ // create some traffic
+ elapsedRealtime += HOUR_IN_MILLIS;
+ expectTime(TEST_START + elapsedRealtime);
+ expectDefaultSettings();
+ expectNetworkStatsSummary(buildEmptyStats(elapsedRealtime));
+ expectNetworkStatsDetail(new NetworkStats(elapsedRealtime, 1)
+ .addEntry(TEST_IFACE, UID_RED, TAG_NONE, 1024L, 1024L)
+ .addEntry(TEST_IFACE, UID_RED, 0xF00D, 512L, 512L));
+
+ replay();
+ mServiceContext.sendBroadcast(new Intent(ACTION_NETWORK_STATS_POLL));
+
+ // verify service recorded history
+ assertUidTotal(sTemplateImsi1, UID_RED, 1024L, 1024L);
+ verifyAndReset();
+
+ // now switch over to 4g network
+ elapsedRealtime += HOUR_IN_MILLIS;
+ expectTime(TEST_START + elapsedRealtime);
+ expectDefaultSettings();
+ expectNetworkState(buildMobile4gState());
+ expectNetworkStatsSummary(buildEmptyStats(elapsedRealtime));
+ expectNetworkStatsDetail(buildEmptyStats(elapsedRealtime));
+
+ replay();
+ mServiceContext.sendBroadcast(new Intent(CONNECTIVITY_ACTION));
+ mServiceContext.sendBroadcast(new Intent(ACTION_NETWORK_STATS_POLL));
+ verifyAndReset();
+
+ // create traffic on second network
+ elapsedRealtime += HOUR_IN_MILLIS;
+ expectTime(TEST_START + elapsedRealtime);
+ expectDefaultSettings();
+ expectNetworkStatsSummary(buildEmptyStats(elapsedRealtime));
+ expectNetworkStatsDetail(new NetworkStats(elapsedRealtime, 1)
+ .addEntry(TEST_IFACE, UID_RED, TAG_NONE, 512L, 256L));
+
+ replay();
+ mServiceContext.sendBroadcast(new Intent(ACTION_NETWORK_STATS_POLL));
+
+ // verify that ALL_MOBILE template combines both
+ assertUidTotal(sTemplateImsi1, UID_RED, 1536L, 1280L);
+
+ verifyAndReset();
+
+ }
+
+ public void testPackedUidAndTag() throws Exception {
+ assertEquals(0x0000000000000000L, packUidAndTag(0, 0x0));
+ assertEquals(0x000003E900000000L, packUidAndTag(1001, 0x0));
+ assertEquals(0x000003E90000F00DL, packUidAndTag(1001, 0xF00D));
+
+ long packed;
+ packed = packUidAndTag(Integer.MAX_VALUE, Integer.MIN_VALUE);
+ assertEquals(Integer.MAX_VALUE, unpackUid(packed));
+ assertEquals(Integer.MIN_VALUE, unpackTag(packed));
+
+ packed = packUidAndTag(Integer.MIN_VALUE, Integer.MAX_VALUE);
+ assertEquals(Integer.MIN_VALUE, unpackUid(packed));
+ assertEquals(Integer.MAX_VALUE, unpackTag(packed));
+
+ packed = packUidAndTag(10005, 0xFFFFFFFF);
+ assertEquals(10005, unpackUid(packed));
+ assertEquals(0xFFFFFFFF, unpackTag(packed));
+
+ }
+
+ public void testSummaryForAllUid() throws Exception {
+ long elapsedRealtime = 0;
+
+ // pretend that network comes online
+ expectTime(TEST_START + elapsedRealtime);
+ expectDefaultSettings();
+ expectNetworkState(buildWifiState());
+ expectNetworkStatsSummary(buildEmptyStats(elapsedRealtime));
+
+ replay();
+ mServiceContext.sendBroadcast(new Intent(CONNECTIVITY_ACTION));
+ verifyAndReset();
+
+ // create some traffic for two apps
+ elapsedRealtime += HOUR_IN_MILLIS;
+ expectTime(TEST_START + elapsedRealtime);
+ expectDefaultSettings();
+ expectNetworkStatsSummary(buildEmptyStats(elapsedRealtime));
+ expectNetworkStatsDetail(new NetworkStats(elapsedRealtime, 1)
+ .addEntry(TEST_IFACE, UID_RED, TAG_NONE, 50L, 50L)
+ .addEntry(TEST_IFACE, UID_RED, 0xF00D, 10L, 10L)
+ .addEntry(TEST_IFACE, UID_BLUE, TAG_NONE, 1024L, 512L));
+
+ replay();
+ mServiceContext.sendBroadcast(new Intent(ACTION_NETWORK_STATS_POLL));
+
+ // verify service recorded history
+ assertUidTotal(sTemplateWifi, UID_RED, 50L, 50L);
+ assertUidTotal(sTemplateWifi, UID_BLUE, 1024L, 512L);
+ verifyAndReset();
+
+ // now create more traffic in next hour, but only for one app
+ elapsedRealtime += HOUR_IN_MILLIS;
+ expectTime(TEST_START + elapsedRealtime);
+ expectDefaultSettings();
+ expectNetworkStatsSummary(buildEmptyStats(elapsedRealtime));
+ expectNetworkStatsDetail(new NetworkStats(elapsedRealtime, 1)
+ .addEntry(TEST_IFACE, UID_BLUE, TAG_NONE, 2048L, 1024L));
+
+ replay();
+ mServiceContext.sendBroadcast(new Intent(ACTION_NETWORK_STATS_POLL));
+
+ // first verify entire history present
+ NetworkStats stats = mService.getSummaryForAllUid(
+ sTemplateWifi, Long.MIN_VALUE, Long.MAX_VALUE, true);
+ assertEquals(3, stats.size);
+ assertStatsEntry(stats, 0, IFACE_ALL, UID_RED, TAG_NONE, 50L, 50L);
+ assertStatsEntry(stats, 1, IFACE_ALL, UID_RED, 0xF00D, 10L, 10L);
+ assertStatsEntry(stats, 2, IFACE_ALL, UID_BLUE, TAG_NONE, 2048L, 1024L);
+
+ // now verify that recent history only contains one uid
+ final long currentTime = TEST_START + elapsedRealtime;
+ stats = mService.getSummaryForAllUid(
+ sTemplateWifi, currentTime - HOUR_IN_MILLIS, currentTime, true);
+ assertEquals(1, stats.size);
+ assertStatsEntry(stats, 0, IFACE_ALL, UID_BLUE, TAG_NONE, 1024L, 512L);
+
+ verifyAndReset();
+ }
+
+ private void assertNetworkTotal(NetworkTemplate template, long rx, long tx) {
final NetworkStatsHistory history = mService.getHistoryForNetwork(template);
final long[] total = history.getTotalData(Long.MIN_VALUE, Long.MAX_VALUE, null);
assertEquals(rx, total[0]);
assertEquals(tx, total[1]);
}
- private void assertUidTotal(int uid, int template, long rx, long tx) {
- final NetworkStatsHistory history = mService.getHistoryForUid(uid, template);
+ private void assertUidTotal(NetworkTemplate template, int uid, long rx, long tx) {
+ final NetworkStatsHistory history = mService.getHistoryForUid(template, uid, TAG_NONE);
final long[] total = history.getTotalData(Long.MIN_VALUE, Long.MAX_VALUE, null);
assertEquals(rx, total[0]);
assertEquals(tx, total[1]);
@@ -346,6 +637,7 @@
expect(mSettings.getNetworkMaxHistory()).andReturn(maxHistory).anyTimes();
expect(mSettings.getUidBucketDuration()).andReturn(bucketDuration).anyTimes();
expect(mSettings.getUidMaxHistory()).andReturn(maxHistory).anyTimes();
+ expect(mSettings.getTagMaxHistory()).andReturn(maxHistory).anyTimes();
expect(mSettings.getTimeCacheMaxAge()).andReturn(DAY_IN_MILLIS).anyTimes();
}
@@ -358,17 +650,26 @@
}
private void assertStatsFilesExist(boolean exist) {
- final File summaryFile = new File(mStatsDir, "netstats.bin");
- final File detailFile = new File(mStatsDir, "netstats_uid.bin");
+ final File networkFile = new File(mStatsDir, "netstats.bin");
+ final File uidFile = new File(mStatsDir, "netstats_uid.bin");
if (exist) {
- assertTrue(summaryFile.exists());
- assertTrue(detailFile.exists());
+ assertTrue(networkFile.exists());
+ assertTrue(uidFile.exists());
} else {
- assertFalse(summaryFile.exists());
- assertFalse(detailFile.exists());
+ assertFalse(networkFile.exists());
+ assertFalse(uidFile.exists());
}
}
+ private static void assertStatsEntry(
+ NetworkStats stats, int i, String iface, int uid, int tag, long rx, long tx) {
+ assertEquals(iface, stats.iface[i]);
+ assertEquals(uid, stats.uid[i]);
+ assertEquals(tag, stats.tag[i]);
+ assertEquals(rx, stats.rx[i]);
+ assertEquals(tx, stats.tx[i]);
+ }
+
private static NetworkState buildWifiState() {
final NetworkInfo info = new NetworkInfo(TYPE_WIFI, 0, null, null);
info.setDetailedState(DetailedState.CONNECTED, null, null);
@@ -377,6 +678,23 @@
return new NetworkState(info, prop, null);
}
+ private static NetworkState buildMobile3gState(String subscriberId) {
+ final NetworkInfo info = new NetworkInfo(
+ TYPE_MOBILE, TelephonyManager.NETWORK_TYPE_UMTS, null, null);
+ info.setDetailedState(DetailedState.CONNECTED, null, null);
+ final LinkProperties prop = new LinkProperties();
+ prop.setInterfaceName(TEST_IFACE);
+ return new NetworkState(info, prop, null, subscriberId);
+ }
+
+ private static NetworkState buildMobile4gState() {
+ final NetworkInfo info = new NetworkInfo(TYPE_WIMAX, 0, null, null);
+ info.setDetailedState(DetailedState.CONNECTED, null, null);
+ final LinkProperties prop = new LinkProperties();
+ prop.setInterfaceName(TEST_IFACE);
+ return new NetworkState(info, prop, null);
+ }
+
private static NetworkStats buildEmptyStats(long elapsedRealtime) {
return new NetworkStats(elapsedRealtime, 0);
}
diff --git a/services/tests/servicestests/src/com/android/server/ThrottleServiceTest.java b/services/tests/servicestests/src/com/android/server/ThrottleServiceTest.java
index 30afdd8..2f275c3 100644
--- a/services/tests/servicestests/src/com/android/server/ThrottleServiceTest.java
+++ b/services/tests/servicestests/src/com/android/server/ThrottleServiceTest.java
@@ -289,7 +289,7 @@
public void expectGetInterfaceCounter(long rx, long tx) throws Exception {
// TODO: provide elapsedRealtime mock to match TimeAuthority
final NetworkStats stats = new NetworkStats(SystemClock.elapsedRealtime(), 1);
- stats.addEntry(TEST_IFACE, NetworkStats.UID_ALL, rx, tx);
+ stats.addEntry(TEST_IFACE, NetworkStats.UID_ALL, NetworkStats.TAG_NONE, rx, tx);
expect(mMockNMService.getNetworkStatsSummary()).andReturn(stats).atLeastOnce();
}
diff --git a/tests/BiDiTests/res/layout/view_padding_mixed.xml b/tests/BiDiTests/res/layout/view_padding_mixed.xml
new file mode 100644
index 0000000..092f55b
--- /dev/null
+++ b/tests/BiDiTests/res/layout/view_padding_mixed.xml
@@ -0,0 +1,135 @@
+<?xml version="1.0" encoding="utf-8"?>
+<!-- Copyright (C) 2011 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.
+-->
+
+<FrameLayout xmlns:android="http://schemas.android.com/apk/res/android"
+ android:id="@+id/view_padding_mixed"
+ android:layout_width="fill_parent"
+ android:layout_height="fill_parent">
+
+ <FrameLayout android:layout_width="match_parent"
+ android:layout_height="match_parent">
+
+ <FrameLayout
+ android:layout_width="300dp"
+ android:layout_height="300dp"
+ android:layout_gravity="top|left"
+ android:background="#FF888888"
+ android:paddingTop="20dip"
+ android:paddingLeft="40dip"
+ android:paddingStart="5dip"
+ android:paddingBottom="30dip"
+ android:paddingRight="50dip"
+ android:layoutDirection="ltr">
+
+ <FrameLayout
+ android:layout_width="100dp"
+ android:layout_height="100dp"
+ android:layout_gravity="top|left"
+ android:background="#FF0000FF">
+ </FrameLayout>
+
+ <FrameLayout
+ android:layout_width="100dp"
+ android:layout_height="100dp"
+ android:layout_gravity="bottom|right"
+ android:background="#FF00FF00">
+ </FrameLayout>
+ </FrameLayout>
+
+ <FrameLayout
+ android:layout_width="300dp"
+ android:layout_height="300dp"
+ android:layout_gravity="top|center_horizontal"
+ android:background="#FF888888"
+ android:paddingTop="20dip"
+ android:paddingLeft="40dip"
+ android:paddingEnd="5dip"
+ android:paddingBottom="30dip"
+ android:paddingRight="50dip"
+ android:layoutDirection="ltr">
+
+ <FrameLayout
+ android:layout_width="100dp"
+ android:layout_height="100dp"
+ android:layout_gravity="top|left"
+ android:background="#FF0000FF">
+ </FrameLayout>
+
+ <FrameLayout
+ android:layout_width="100dp"
+ android:layout_height="100dp"
+ android:layout_gravity="bottom|right"
+ android:background="#FF00FF00">
+ </FrameLayout>
+ </FrameLayout>
+
+ <FrameLayout
+ android:layout_width="300dp"
+ android:layout_height="300dp"
+ android:layout_gravity="bottom|left"
+ android:background="#FF888888"
+ android:paddingTop="20dip"
+ android:paddingLeft="40dip"
+ android:paddingStart="5dip"
+ android:paddingBottom="30dip"
+ android:paddingRight="50dip"
+ android:layoutDirection="rtl">
+
+ <FrameLayout
+ android:layout_width="100dp"
+ android:layout_height="100dp"
+ android:layout_gravity="top|left"
+ android:background="#FF0000FF">
+ </FrameLayout>
+
+ <FrameLayout
+ android:layout_width="100dp"
+ android:layout_height="100dp"
+ android:layout_gravity="bottom|right"
+ android:background="#FF00FF00">
+ </FrameLayout>
+ </FrameLayout>
+
+ <FrameLayout
+ android:layout_width="300dp"
+ android:layout_height="300dp"
+ android:layout_gravity="bottom|center_horizontal"
+ android:background="#FF888888"
+ android:paddingTop="20dip"
+ android:paddingLeft="40dip"
+ android:paddingEnd="5dip"
+ android:paddingBottom="30dip"
+ android:paddingRight="50dip"
+ android:layoutDirection="rtl">
+
+ <FrameLayout
+ android:layout_width="100dp"
+ android:layout_height="100dp"
+ android:layout_gravity="top|left"
+ android:background="#FF0000FF">
+ </FrameLayout>
+
+ <FrameLayout
+ android:layout_width="100dp"
+ android:layout_height="100dp"
+ android:layout_gravity="bottom|right"
+ android:background="#FF00FF00">
+ </FrameLayout>
+ </FrameLayout>
+
+ </FrameLayout>
+
+</FrameLayout>
diff --git a/tests/BiDiTests/src/com/android/bidi/BiDiTestActivity.java b/tests/BiDiTests/src/com/android/bidi/BiDiTestActivity.java
index 0bed7ce..c5e2273 100644
--- a/tests/BiDiTests/src/com/android/bidi/BiDiTestActivity.java
+++ b/tests/BiDiTests/src/com/android/bidi/BiDiTestActivity.java
@@ -120,6 +120,7 @@
addItem(result, "Table LOC", BiDiTestTableLayoutLocale.class, R.id.table_layout_locale);
addItem(result, "ViewPadding", BiDiTestViewPadding.class, R.id.view_padding);
+ addItem(result, "ViewPadding MIXED", BiDiTestViewPaddingMixed.class, R.id.view_padding_mixed);
return result;
}
diff --git a/tests/BiDiTests/src/com/android/bidi/BiDiTestViewPaddingMixed.java b/tests/BiDiTests/src/com/android/bidi/BiDiTestViewPaddingMixed.java
new file mode 100644
index 0000000..7ca2707
--- /dev/null
+++ b/tests/BiDiTests/src/com/android/bidi/BiDiTestViewPaddingMixed.java
@@ -0,0 +1,17 @@
+// Copyright 2011 Google Inc. All Rights Reserved.
+package com.android.bidi;
+
+import android.app.Fragment;
+import android.os.Bundle;
+import android.view.LayoutInflater;
+import android.view.View;
+import android.view.ViewGroup;
+
+public class BiDiTestViewPaddingMixed extends Fragment {
+
+ @Override
+ public View onCreateView(LayoutInflater inflater, ViewGroup container,
+ Bundle savedInstanceState) {
+ return inflater.inflate(R.layout.view_padding_mixed, container, false);
+ }
+}
diff --git a/tests/HwAccelerationTest/AndroidManifest.xml b/tests/HwAccelerationTest/AndroidManifest.xml
index d5dcd4e..c650021 100644
--- a/tests/HwAccelerationTest/AndroidManifest.xml
+++ b/tests/HwAccelerationTest/AndroidManifest.xml
@@ -31,6 +31,15 @@
android:hardwareAccelerated="true">
<activity
+ android:name="GetBitmapActivity"
+ android:label="_GetBitmap">
+ <intent-filter>
+ <action android:name="android.intent.action.MAIN" />
+ <category android:name="android.intent.category.LAUNCHER" />
+ </intent-filter>
+ </activity>
+
+ <activity
android:name="SmallCircleActivity"
android:label="_SmallCircle">
<intent-filter>
diff --git a/tests/HwAccelerationTest/src/com/android/test/hwui/GetBitmapActivity.java b/tests/HwAccelerationTest/src/com/android/test/hwui/GetBitmapActivity.java
new file mode 100644
index 0000000..2e23aaa
--- /dev/null
+++ b/tests/HwAccelerationTest/src/com/android/test/hwui/GetBitmapActivity.java
@@ -0,0 +1,103 @@
+/*
+ * Copyright (C) 2011 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 com.android.test.hwui;
+
+import android.app.Activity;
+import android.graphics.Bitmap;
+import android.graphics.SurfaceTexture;
+import android.hardware.Camera;
+import android.os.Bundle;
+import android.os.Environment;
+import android.view.Gravity;
+import android.view.TextureView;
+import android.view.View;
+import android.widget.Button;
+import android.widget.FrameLayout;
+
+import java.io.FileNotFoundException;
+import java.io.FileOutputStream;
+import java.io.IOException;
+
+@SuppressWarnings({"UnusedDeclaration"})
+public class GetBitmapActivity extends Activity implements TextureView.SurfaceTextureListener {
+ private Camera mCamera;
+ private TextureView mTextureView;
+
+ @Override
+ protected void onCreate(Bundle savedInstanceState) {
+ super.onCreate(savedInstanceState);
+
+ FrameLayout content = new FrameLayout(this);
+
+ mTextureView = new TextureView(this);
+ mTextureView.setSurfaceTextureListener(this);
+
+ Button button = new Button(this);
+ button.setText("Copy bitmap to /sdcard/textureview.png");
+ button.setOnClickListener(new View.OnClickListener() {
+ @Override
+ public void onClick(View v) {
+ Bitmap b = mTextureView.getBitmap();
+ try {
+ FileOutputStream out = new FileOutputStream(
+ Environment.getExternalStorageDirectory() + "/textureview.png");
+ try {
+ b.compress(Bitmap.CompressFormat.PNG, 100, out);
+ } finally {
+ try {
+ out.close();
+ } catch (IOException e) {
+ // Ignore
+ }
+ }
+ } catch (FileNotFoundException e) {
+ // Ignore
+ }
+ }
+ });
+
+ content.addView(mTextureView, new FrameLayout.LayoutParams(500, 400, Gravity.CENTER));
+ content.addView(button, new FrameLayout.LayoutParams(
+ FrameLayout.LayoutParams.WRAP_CONTENT, FrameLayout.LayoutParams.WRAP_CONTENT,
+ Gravity.CENTER_HORIZONTAL | Gravity.BOTTOM));
+ setContentView(content);
+ }
+
+ @Override
+ public void onSurfaceTextureAvailable(SurfaceTexture surface, int width, int height) {
+ mCamera = Camera.open();
+
+ try {
+ mCamera.setPreviewTexture(surface);
+ } catch (IOException t) {
+ android.util.Log.e("TextureView", "Cannot set preview texture target!", t);
+ }
+
+ mCamera.startPreview();
+ }
+
+ @Override
+ public void onSurfaceTextureSizeChanged(SurfaceTexture surface, int width, int height) {
+ // Ignored, the Camera does all the work for us
+ }
+
+ @Override
+ public void onSurfaceTextureDestroyed(SurfaceTexture surface) {
+ mCamera.stopPreview();
+ mCamera.release();
+ }
+}