diff options
116 files changed, 3423 insertions, 1837 deletions
diff --git a/api/current.txt b/api/current.txt index 852a53366d1f..80d0687a8873 100644 --- a/api/current.txt +++ b/api/current.txt @@ -486,7 +486,6 @@ package android { field public static final int hint = 16843088; // 0x1010150 field public static final int homeAsUpIndicator = 16843531; // 0x101030b field public static final int homeLayout = 16843549; // 0x101031d - field public static final int horizontalDirection = 16843631; // 0x101036f field public static final int horizontalDivider = 16843053; // 0x101012d field public static final int horizontalGap = 16843327; // 0x101023f field public static final int horizontalScrollViewStyle = 16843603; // 0x1010353 @@ -571,6 +570,7 @@ package android { field public static final int layerType = 16843604; // 0x1010354 field public static final int layout = 16842994; // 0x10100f2 field public static final int layoutAnimation = 16842988; // 0x10100ec + field public static final int layoutDirection = 16843631; // 0x101036f field public static final int layout_above = 16843140; // 0x1010184 field public static final int layout_alignBaseline = 16843142; // 0x1010186 field public static final int layout_alignBottom = 16843146; // 0x101018a @@ -1516,12 +1516,13 @@ package android { field public static final int Theme_Holo_Light_Dialog_NoActionBar = 16973941; // 0x1030075 field public static final int Theme_Holo_Light_Dialog_NoActionBar_MinWidth = 16973942; // 0x1030076 field public static final int Theme_Holo_Light_NoActionBar = 16974064; // 0x10300f0 + field public static final int Theme_Holo_Light_NoActionBar_Fullscreen = 16974065; // 0x10300f1 field public static final int Theme_Holo_Light_Panel = 16973948; // 0x103007c - field public static final int Theme_Holo_Light_SplitActionBarWhenNarrow = 16974067; // 0x10300f3 + field public static final int Theme_Holo_Light_SplitActionBarWhenNarrow = 16974068; // 0x10300f4 field public static final int Theme_Holo_NoActionBar = 16973932; // 0x103006c field public static final int Theme_Holo_NoActionBar_Fullscreen = 16973933; // 0x103006d field public static final int Theme_Holo_Panel = 16973947; // 0x103007b - field public static final int Theme_Holo_SplitActionBarWhenNarrow = 16974066; // 0x10300f2 + field public static final int Theme_Holo_SplitActionBarWhenNarrow = 16974067; // 0x10300f3 field public static final int Theme_Holo_Wallpaper = 16973949; // 0x103007d field public static final int Theme_Holo_Wallpaper_NoTitleBar = 16973950; // 0x103007e field public static final int Theme_InputMethod = 16973908; // 0x1030054 @@ -4967,6 +4968,7 @@ package android.content { field public static final java.lang.String ACTION_INSERT_OR_EDIT = "android.intent.action.INSERT_OR_EDIT"; field public static final java.lang.String ACTION_LOCALE_CHANGED = "android.intent.action.LOCALE_CHANGED"; field public static final java.lang.String ACTION_MAIN = "android.intent.action.MAIN"; + field public static final java.lang.String ACTION_MANAGE_NETWORK_USAGE = "android.intent.action.MANAGE_NETWORK_USAGE"; field public static final java.lang.String ACTION_MANAGE_PACKAGE_STORAGE = "android.intent.action.MANAGE_PACKAGE_STORAGE"; field public static final java.lang.String ACTION_MEDIA_BAD_REMOVAL = "android.intent.action.MEDIA_BAD_REMOVAL"; field public static final java.lang.String ACTION_MEDIA_BUTTON = "android.intent.action.MEDIA_BUTTON"; @@ -5015,8 +5017,8 @@ package android.content { field public static final java.lang.String ACTION_TIME_CHANGED = "android.intent.action.TIME_SET"; field public static final java.lang.String ACTION_TIME_TICK = "android.intent.action.TIME_TICK"; field public static final java.lang.String ACTION_UID_REMOVED = "android.intent.action.UID_REMOVED"; - field public static final java.lang.String ACTION_UMS_CONNECTED = "android.intent.action.UMS_CONNECTED"; - field public static final java.lang.String ACTION_UMS_DISCONNECTED = "android.intent.action.UMS_DISCONNECTED"; + field public static final deprecated java.lang.String ACTION_UMS_CONNECTED = "android.intent.action.UMS_CONNECTED"; + field public static final deprecated java.lang.String ACTION_UMS_DISCONNECTED = "android.intent.action.UMS_DISCONNECTED"; field public static final java.lang.String ACTION_USER_PRESENT = "android.intent.action.USER_PRESENT"; field public static final java.lang.String ACTION_VIEW = "android.intent.action.VIEW"; field public static final java.lang.String ACTION_VOICE_COMMAND = "android.intent.action.VOICE_COMMAND"; @@ -20458,8 +20460,8 @@ package android.view { field public static final int HORIZONTAL_GRAVITY_MASK = 7; // 0x7 field public static final int LEFT = 3; // 0x3 field public static final int NO_GRAVITY = 0; // 0x0 - field public static final int RELATIVE_HORIZONTAL_DIRECTION = 8388608; // 0x800000 field public static final int RELATIVE_HORIZONTAL_GRAVITY_MASK = 8388615; // 0x800007 + field public static final int RELATIVE_LAYOUT_DIRECTION = 8388608; // 0x800000 field public static final int RIGHT = 5; // 0x5 field public static final int START = 8388611; // 0x800003 field public static final int TOP = 48; // 0x30 @@ -23276,6 +23278,8 @@ package android.view.inputmethod { } public final class InputMethodSubtype implements android.os.Parcelable { + ctor public InputMethodSubtype(int, int, java.lang.String, java.lang.String, java.lang.String); + ctor public InputMethodSubtype(int, int, java.lang.String, java.lang.String, java.lang.String, boolean); method public boolean containsExtraValueKey(java.lang.String); method public int describeContents(); method public java.lang.CharSequence getDisplayName(android.content.Context, java.lang.String, android.content.pm.ApplicationInfo); diff --git a/core/java/android/app/backup/BackupAgent.java b/core/java/android/app/backup/BackupAgent.java index 17f8adbb0ccf..63f325870f91 100644 --- a/core/java/android/app/backup/BackupAgent.java +++ b/core/java/android/app/backup/BackupAgent.java @@ -278,7 +278,6 @@ public abstract class BackupAgent extends ContextWrapper { int token, IBackupManager callbackBinder) throws RemoteException { long ident = Binder.clearCallingIdentity(); try { -Log.d(TAG, "doRestoreFile() => onRestoreFile()"); BackupAgent.this.onRestoreFile(data, size, type, domain, path, mode, mtime); } catch (IOException e) { throw new RuntimeException(e); diff --git a/core/java/android/app/backup/FullBackup.java b/core/java/android/app/backup/FullBackup.java index dfb0dd7f5f04..3b70e19ad244 100644 --- a/core/java/android/app/backup/FullBackup.java +++ b/core/java/android/app/backup/FullBackup.java @@ -46,7 +46,7 @@ public class FullBackup { public static final String SHARED_STORAGE_TOKEN = "shared"; public static final String APPS_PREFIX = "apps/"; - public static final String SHARED_PREFIX = "shared/"; + public static final String SHARED_PREFIX = SHARED_STORAGE_TOKEN + "/"; public static final String FULL_BACKUP_INTENT_ACTION = "fullback"; public static final String FULL_RESTORE_INTENT_ACTION = "fullrest"; @@ -61,7 +61,8 @@ public class FullBackup { String linkdomain, String rootpath, String path, BackupDataOutput output); static public void restoreToFile(ParcelFileDescriptor data, - long size, int type, long mode, long mtime, File outFile) throws IOException { + long size, int type, long mode, long mtime, File outFile, + boolean doChmod) throws IOException { if (type == FullBackup.TYPE_DIRECTORY) { // Canonically a directory has no associated content, so we don't need to read // anything from the pipe in this case. Just create the directory here and @@ -116,7 +117,7 @@ public class FullBackup { } // Now twiddle the state to match the backup, assuming all went well - if (outFile != null) { + if (doChmod && outFile != null) { try { Libcore.os.chmod(outFile.getPath(), (int)mode); } catch (ErrnoException e) { diff --git a/core/java/android/app/backup/FullBackupAgent.java b/core/java/android/app/backup/FullBackupAgent.java index 4dca5936dbc3..df1c3639bc58 100644 --- a/core/java/android/app/backup/FullBackupAgent.java +++ b/core/java/android/app/backup/FullBackupAgent.java @@ -28,8 +28,6 @@ import libcore.io.OsConstants; import libcore.io.StructStat; import java.io.File; -import java.io.FileInputStream; -import java.io.FileOutputStream; import java.io.IOException; import java.util.HashSet; import java.util.LinkedList; @@ -84,9 +82,10 @@ public class FullBackupAgent extends BackupAgent { @Override public void onBackup(ParcelFileDescriptor oldState, BackupDataOutput data, - ParcelFileDescriptor newState) { + ParcelFileDescriptor newState) throws IOException { // Filters, the scan queue, and the set of resulting entities HashSet<String> filterSet = new HashSet<String>(); + String packageName = getPackageName(); // Okay, start with the app's root tree, but exclude all of the canonical subdirs if (mLibDir != null) { @@ -96,25 +95,28 @@ public class FullBackupAgent extends BackupAgent { filterSet.add(mDatabaseDir); filterSet.add(mSharedPrefsDir); filterSet.add(mFilesDir); - processTree(FullBackup.ROOT_TREE_TOKEN, mMainDir, filterSet, data); + processTree(packageName, FullBackup.ROOT_TREE_TOKEN, mMainDir, filterSet, data); // Now do the same for the files dir, db dir, and shared prefs dir filterSet.add(mMainDir); filterSet.remove(mFilesDir); - processTree(FullBackup.DATA_TREE_TOKEN, mFilesDir, filterSet, data); + processTree(packageName, FullBackup.DATA_TREE_TOKEN, mFilesDir, filterSet, data); filterSet.add(mFilesDir); filterSet.remove(mDatabaseDir); - processTree(FullBackup.DATABASE_TREE_TOKEN, mDatabaseDir, filterSet, data); + processTree(packageName, FullBackup.DATABASE_TREE_TOKEN, mDatabaseDir, filterSet, data); filterSet.add(mDatabaseDir); filterSet.remove(mSharedPrefsDir); - processTree(FullBackup.SHAREDPREFS_TREE_TOKEN, mSharedPrefsDir, filterSet, data); + processTree(packageName, FullBackup.SHAREDPREFS_TREE_TOKEN, mSharedPrefsDir, filterSet, data); } - private void processTree(String domain, String rootPath, + // Scan the dir tree (if it actually exists) and process each entry we find. If the + // 'excludes' parameter is non-null, it is consulted each time a new file system entity + // is visited to see whether that entity (and its subtree, if appropriate) should be + // omitted from the backup process. + protected void processTree(String packageName, String domain, String rootPath, HashSet<String> excludes, BackupDataOutput data) { - // Scan the dir tree (if it actually exists) and process each entry we find File rootFile = new File(rootPath); if (rootFile.exists()) { LinkedList<File> scanQueue = new LinkedList<File>(); @@ -125,7 +127,7 @@ public class FullBackupAgent extends BackupAgent { String filePath = file.getAbsolutePath(); // prune this subtree? - if (excludes.contains(filePath)) { + if (excludes != null && excludes.contains(filePath)) { continue; } @@ -149,7 +151,7 @@ public class FullBackupAgent extends BackupAgent { } // Finally, back this file up before proceeding - FullBackup.backupToTar(getPackageName(), domain, null, rootPath, filePath, data); + FullBackup.backupToTar(packageName, domain, null, rootPath, filePath, data); } } } @@ -218,6 +220,6 @@ public class FullBackupAgent extends BackupAgent { if (DEBUG) Log.i(TAG, "[" + domain + " : " + relpath + "] mapped to " + outFile.getPath()); // Now that we've figured out where the data goes, send it on its way - FullBackup.restoreToFile(data, size, type, mode, mtime, outFile); + FullBackup.restoreToFile(data, size, type, mode, mtime, outFile, true); } } diff --git a/core/java/android/content/Intent.java b/core/java/android/content/Intent.java index 3d637e9e5dd8..2f9627a4403d 100644 --- a/core/java/android/content/Intent.java +++ b/core/java/android/content/Intent.java @@ -1160,6 +1160,15 @@ public class Intent implements Parcelable, Cloneable { public static final String ACTION_UPGRADE_SETUP = "android.intent.action.UPGRADE_SETUP"; /** + * Activity Action: Show settings for managing network data usage of a + * specific application. Applications should define an activity that offers + * options to control data usage. + */ + @SdkConstant(SdkConstantType.ACTIVITY_INTENT_ACTION) + public static final String ACTION_MANAGE_NETWORK_USAGE = + "android.intent.action.MANAGE_NETWORK_USAGE"; + + /** * A string associated with a {@link #ACTION_UPGRADE_SETUP} activity * describing the last run version of the platform that was setup. * @hide @@ -1654,8 +1663,9 @@ public class Intent implements Parcelable, Cloneable { * This is used mainly for the USB Settings panel. * Apps should listen for ACTION_MEDIA_MOUNTED and ACTION_MEDIA_UNMOUNTED broadcasts to be notified * when the SD card file system is mounted or unmounted + * @deprecated replaced by android.os.storage.StorageEventListener */ - @SdkConstant(SdkConstantType.BROADCAST_INTENT_ACTION) + @Deprecated public static final String ACTION_UMS_CONNECTED = "android.intent.action.UMS_CONNECTED"; /** @@ -1663,8 +1673,9 @@ public class Intent implements Parcelable, Cloneable { * This is used mainly for the USB Settings panel. * Apps should listen for ACTION_MEDIA_MOUNTED and ACTION_MEDIA_UNMOUNTED broadcasts to be notified * when the SD card file system is mounted or unmounted + * @deprecated replaced by android.os.storage.StorageEventListener */ - @SdkConstant(SdkConstantType.BROADCAST_INTENT_ACTION) + @Deprecated public static final String ACTION_UMS_DISCONNECTED = "android.intent.action.UMS_DISCONNECTED"; /** diff --git a/core/java/android/content/pm/PackageParser.java b/core/java/android/content/pm/PackageParser.java index 09fede0d616b..31ad6e95dad6 100644 --- a/core/java/android/content/pm/PackageParser.java +++ b/core/java/android/content/pm/PackageParser.java @@ -1517,11 +1517,12 @@ public class PackageParser { } } + // fullBackupAgent is explicitly handled even if allowBackup is false name = sa.getNonConfigurationString( com.android.internal.R.styleable.AndroidManifestApplication_fullBackupAgent, 0); if (name != null) { ai.fullBackupAgentName = buildClassName(pkgName, name, outError); - if (true) { + if (false) { Log.v(TAG, "android:fullBackupAgent=" + ai.fullBackupAgentName + " from " + pkgName + "+" + name); } diff --git a/core/java/android/net/ConnectivityManager.java b/core/java/android/net/ConnectivityManager.java index c72c4b0fad5e..21cce44a158a 100644 --- a/core/java/android/net/ConnectivityManager.java +++ b/core/java/android/net/ConnectivityManager.java @@ -256,10 +256,61 @@ public class ConnectivityManager { private final IConnectivityManager mService; - static public boolean isNetworkTypeValid(int networkType) { + public static boolean isNetworkTypeValid(int networkType) { return networkType >= 0 && networkType <= MAX_NETWORK_TYPE; } + /** {@hide} */ + public static String getNetworkTypeName(int type) { + switch (type) { + case TYPE_MOBILE: + return "MOBILE"; + case TYPE_WIFI: + return "WIFI"; + case TYPE_MOBILE_MMS: + return "MOBILE_MMS"; + case TYPE_MOBILE_SUPL: + return "MOBILE_SUPL"; + case TYPE_MOBILE_DUN: + return "MOBILE_DUN"; + case TYPE_MOBILE_HIPRI: + return "MOBILE_HIPRI"; + case TYPE_WIMAX: + return "WIMAX"; + case TYPE_BLUETOOTH: + return "BLUETOOTH"; + case TYPE_DUMMY: + return "DUMMY"; + case TYPE_ETHERNET: + return "ETHERNET"; + case TYPE_MOBILE_FOTA: + return "MOBILE_FOTA"; + case TYPE_MOBILE_IMS: + return "MOBILE_IMS"; + case TYPE_MOBILE_CBS: + return "MOBILE_CBS"; + default: + return Integer.toString(type); + } + } + + /** {@hide} */ + public static boolean isNetworkTypeMobile(int networkType) { + switch (networkType) { + case TYPE_MOBILE: + case TYPE_MOBILE_MMS: + case TYPE_MOBILE_SUPL: + case TYPE_MOBILE_DUN: + case TYPE_MOBILE_HIPRI: + case TYPE_MOBILE_FOTA: + case TYPE_MOBILE_IMS: + case TYPE_MOBILE_CBS: + return true; + default: + return false; + } + } + public void setNetworkPreference(int preference) { try { mService.setNetworkPreference(preference); diff --git a/core/java/android/net/IConnectivityManager.aidl b/core/java/android/net/IConnectivityManager.aidl index 647a60a97d73..07f6cecea5d3 100644 --- a/core/java/android/net/IConnectivityManager.aidl +++ b/core/java/android/net/IConnectivityManager.aidl @@ -18,6 +18,7 @@ package android.net; import android.net.LinkProperties; import android.net.NetworkInfo; +import android.net.NetworkState; import android.net.ProxyProperties; import android.os.IBinder; @@ -40,6 +41,8 @@ interface IConnectivityManager LinkProperties getActiveLinkProperties(); LinkProperties getLinkProperties(int networkType); + NetworkState[] getAllNetworkState(); + boolean setRadios(boolean onOff); boolean setRadio(int networkType, boolean turnOn); diff --git a/core/java/android/net/INetworkStatsService.aidl b/core/java/android/net/INetworkStatsService.aidl index 6d57036fbde7..d38d16c4e759 100644 --- a/core/java/android/net/INetworkStatsService.aidl +++ b/core/java/android/net/INetworkStatsService.aidl @@ -16,12 +16,18 @@ package android.net; +import android.net.NetworkStats; import android.net.NetworkStatsHistory; /** {@hide} */ interface INetworkStatsService { - NetworkStatsHistory[] getNetworkStatsSummary(int networkType); - NetworkStatsHistory getNetworkStatsUid(int uid); + /** Return historical stats for traffic that matches template. */ + NetworkStatsHistory getHistoryForNetwork(int networkTemplate); + /** Return historical stats for specific UID traffic that matches template. */ + NetworkStatsHistory getHistoryForUid(int uid, int networkTemplate); + + /** Return usage summary per UID for traffic that matches template. */ + NetworkStats getSummaryPerUid(long start, long end, int networkTemplate); } diff --git a/core/java/android/net/NetworkState.aidl b/core/java/android/net/NetworkState.aidl new file mode 100644 index 000000000000..c0b6cdc88e4f --- /dev/null +++ b/core/java/android/net/NetworkState.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 NetworkState; diff --git a/core/java/android/net/NetworkState.java b/core/java/android/net/NetworkState.java new file mode 100644 index 000000000000..749039a1afe3 --- /dev/null +++ b/core/java/android/net/NetworkState.java @@ -0,0 +1,68 @@ +/* + * 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 android.os.Parcel; +import android.os.Parcelable; + +/** + * Snapshot of network state. + * + * @hide + */ +public class NetworkState implements Parcelable { + + public final NetworkInfo networkInfo; + public final LinkProperties linkProperties; + public final LinkCapabilities linkCapabilities; + + public NetworkState(NetworkInfo networkInfo, LinkProperties linkProperties, + LinkCapabilities linkCapabilities) { + this.networkInfo = networkInfo; + this.linkProperties = linkProperties; + this.linkCapabilities = linkCapabilities; + } + + public NetworkState(Parcel in) { + networkInfo = in.readParcelable(null); + linkProperties = in.readParcelable(null); + linkCapabilities = in.readParcelable(null); + } + + /** {@inheritDoc} */ + public int describeContents() { + return 0; + } + + /** {@inheritDoc} */ + public void writeToParcel(Parcel out, int flags) { + out.writeParcelable(networkInfo, flags); + out.writeParcelable(linkProperties, flags); + out.writeParcelable(linkCapabilities, flags); + } + + public static final Creator<NetworkState> CREATOR = new Creator<NetworkState>() { + public NetworkState createFromParcel(Parcel in) { + return new NetworkState(in); + } + + public NetworkState[] newArray(int size) { + return new NetworkState[size]; + } + }; + +} diff --git a/core/java/android/net/NetworkStats.java b/core/java/android/net/NetworkStats.java index 588bf64b4a3e..ee415fa6bc65 100644 --- a/core/java/android/net/NetworkStats.java +++ b/core/java/android/net/NetworkStats.java @@ -19,6 +19,7 @@ package android.net; import android.os.Parcel; import android.os.Parcelable; import android.os.SystemClock; +import android.util.SparseBooleanArray; import java.io.CharArrayWriter; import java.io.PrintWriter; @@ -125,7 +126,7 @@ public class NetworkStats implements Parcelable { /** * Return list of unique interfaces known by this data structure. */ - public String[] getKnownIfaces() { + public String[] getUniqueIfaces() { final HashSet<String> ifaces = new HashSet<String>(); for (String iface : this.iface) { if (iface != IFACE_ALL) { @@ -136,6 +137,23 @@ public class NetworkStats implements Parcelable { } /** + * Return list of unique UIDs known by this data structure. + */ + public int[] getUniqueUids() { + final SparseBooleanArray uids = new SparseBooleanArray(); + for (int uid : this.uid) { + uids.put(uid, true); + } + + final int size = uids.size(); + final int[] result = new int[size]; + for (int i = 0; i < size; i++) { + result[i] = uids.keyAt(i); + } + return result; + } + + /** * Subtract the given {@link NetworkStats}, effectively leaving the delta * between two snapshots in time. Assumes that statistics rows collect over * time, and that none of them have disappeared. diff --git a/core/java/android/net/NetworkStatsHistory.java b/core/java/android/net/NetworkStatsHistory.java index b16101fae6d5..5edbf5830dfe 100644 --- a/core/java/android/net/NetworkStatsHistory.java +++ b/core/java/android/net/NetworkStatsHistory.java @@ -24,7 +24,9 @@ import java.io.DataInputStream; import java.io.DataOutputStream; import java.io.IOException; import java.io.PrintWriter; +import java.net.ProtocolException; import java.util.Arrays; +import java.util.Random; /** * Collection of historical network statistics, recorded into equally-sized @@ -38,28 +40,19 @@ import java.util.Arrays; * @hide */ public class NetworkStatsHistory implements Parcelable { - private static final int VERSION = 1; - - /** {@link #uid} value when UID details unavailable. */ - public static final int UID_ALL = -1; + private static final int VERSION_CURRENT = 1; // TODO: teach about zigzag encoding to use less disk space // TODO: teach how to convert between bucket sizes - public final int networkType; - public final String identity; - public final int uid; public final long bucketDuration; - int bucketCount; - long[] bucketStart; - long[] rx; - long[] tx; + public int bucketCount; + public long[] bucketStart; + public long[] rx; + public long[] tx; - public NetworkStatsHistory(int networkType, String identity, int uid, long bucketDuration) { - this.networkType = networkType; - this.identity = identity; - this.uid = uid; + public NetworkStatsHistory(long bucketDuration) { this.bucketDuration = bucketDuration; bucketStart = new long[0]; rx = new long[0]; @@ -68,9 +61,6 @@ public class NetworkStatsHistory implements Parcelable { } public NetworkStatsHistory(Parcel in) { - networkType = in.readInt(); - identity = in.readString(); - uid = in.readInt(); bucketDuration = in.readLong(); bucketStart = readLongArray(in); rx = in.createLongArray(); @@ -80,9 +70,6 @@ public class NetworkStatsHistory implements Parcelable { /** {@inheritDoc} */ public void writeToParcel(Parcel out, int flags) { - out.writeInt(networkType); - out.writeString(identity); - out.writeInt(uid); out.writeLong(bucketDuration); writeLongArray(out, bucketStart, bucketCount); writeLongArray(out, rx, bucketCount); @@ -91,21 +78,23 @@ public class NetworkStatsHistory implements Parcelable { public NetworkStatsHistory(DataInputStream in) throws IOException { final int version = in.readInt(); - networkType = in.readInt(); - identity = in.readUTF(); - uid = in.readInt(); - bucketDuration = in.readLong(); - bucketStart = readLongArray(in); - rx = readLongArray(in); - tx = readLongArray(in); - bucketCount = bucketStart.length; + switch (version) { + case VERSION_CURRENT: { + bucketDuration = in.readLong(); + bucketStart = readLongArray(in); + rx = readLongArray(in); + tx = readLongArray(in); + bucketCount = bucketStart.length; + break; + } + default: { + throw new ProtocolException("unexpected version: " + version); + } + } } public void writeToStream(DataOutputStream out) throws IOException { - out.writeInt(VERSION); - out.writeInt(networkType); - out.writeUTF(identity); - out.writeInt(uid); + out.writeInt(VERSION_CURRENT); out.writeLong(bucketDuration); writeLongArray(out, bucketStart, bucketCount); writeLongArray(out, rx, bucketCount); @@ -145,6 +134,18 @@ public class NetworkStatsHistory implements Parcelable { } /** + * Record an entire {@link NetworkStatsHistory} into this history. Usually + * for combining together stats for external reporting. + */ + public void recordEntireHistory(NetworkStatsHistory input) { + for (int i = 0; i < input.bucketCount; i++) { + final long start = input.bucketStart[i]; + final long end = start + input.bucketDuration; + recordData(start, end, input.rx[i], input.tx[i]); + } + } + + /** * Ensure that buckets exist for given time range, creating as needed. */ private void ensureBuckets(long start, long end) { @@ -213,15 +214,65 @@ public class NetworkStatsHistory implements Parcelable { } } + /** + * Return interpolated data usage across the requested range. Interpolates + * across buckets, so values may be rounded slightly. + */ + public void getTotalData(long start, long end, long[] outTotal) { + long rx = 0; + long tx = 0; + + for (int i = bucketCount - 1; i >= 0; i--) { + final long curStart = bucketStart[i]; + final long curEnd = curStart + bucketDuration; + + // bucket is older than record; we're finished + if (curEnd < start) break; + // bucket is newer than record; keep looking + if (curStart > end) continue; + + final long overlap = Math.min(curEnd, end) - Math.max(curStart, start); + if (overlap > 0) { + rx += this.rx[i] * overlap / bucketDuration; + tx += this.tx[i] * overlap / bucketDuration; + } + } + + outTotal[0] = rx; + outTotal[1] = tx; + } + + /** + * @deprecated only for temporary testing + */ + @Deprecated + public void generateRandom(long start, long end, long rx, long tx) { + ensureBuckets(start, end); + + final Random r = new Random(); + while (rx > 1024 && tx > 1024) { + final long curStart = randomLong(r, start, end); + final long curEnd = randomLong(r, curStart, end); + final long curRx = randomLong(r, 0, rx); + final long curTx = randomLong(r, 0, tx); + + recordData(curStart, curEnd, curRx, curTx); + + rx -= curRx; + tx -= curTx; + } + } + + private static long randomLong(Random r, long start, long end) { + return (long) (start + (r.nextFloat() * (end - start))); + } + public void dump(String prefix, PrintWriter pw) { - // TODO: consider stripping identity when dumping pw.print(prefix); - pw.print("NetworkStatsHistory: networkType="); pw.print(networkType); - pw.print(" identity="); pw.print(identity); - pw.print(" uid="); pw.println(uid); + pw.print("NetworkStatsHistory: bucketDuration="); pw.println(bucketDuration); for (int i = 0; i < bucketCount; i++) { pw.print(prefix); - pw.print(" timestamp="); pw.print(bucketStart[i]); + pw.print(" bucketStart="); pw.print(bucketStart[i]); pw.print(" rx="); pw.print(rx[i]); pw.print(" tx="); pw.println(tx[i]); } diff --git a/core/java/android/net/TrafficStats.java b/core/java/android/net/TrafficStats.java index 8ab64fabd245..a0738c1da2d6 100644 --- a/core/java/android/net/TrafficStats.java +++ b/core/java/android/net/TrafficStats.java @@ -41,6 +41,42 @@ public class TrafficStats { */ public final static int UNSUPPORTED = -1; + // TODO: find better home for these template constants + + /** + * Template to combine all {@link ConnectivityManager#TYPE_MOBILE} style + * networks together. Only uses statistics for currently active IMSI. + * + * @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 currently active 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 + * currently active 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; + /** * Snapshot of {@link NetworkStats} when the currently active profiling * session started, or {@code null} if no session active. diff --git a/core/java/android/os/storage/StorageVolume.java b/core/java/android/os/storage/StorageVolume.java index bc4208a045f3..792e4c1f5003 100644 --- a/core/java/android/os/storage/StorageVolume.java +++ b/core/java/android/os/storage/StorageVolume.java @@ -32,6 +32,7 @@ public class StorageVolume implements Parcelable { private final boolean mRemovable; private final boolean mEmulated; private final int mMtpReserveSpace; + private final boolean mAllowMassStorage; private int mStorageId; // StorageVolume extra for ACTION_MEDIA_REMOVED, ACTION_MEDIA_UNMOUNTED, ACTION_MEDIA_CHECKING, @@ -39,23 +40,25 @@ public class StorageVolume implements Parcelable { // ACTION_MEDIA_BAD_REMOVAL, ACTION_MEDIA_UNMOUNTABLE and ACTION_MEDIA_EJECT broadcasts. public static final String EXTRA_STORAGE_VOLUME = "storage_volume"; - public StorageVolume(String path, String description, - boolean removable, boolean emulated, int mtpReserveSpace) { + public StorageVolume(String path, String description, boolean removable, + boolean emulated, int mtpReserveSpace, boolean allowMassStorage) { mPath = path; mDescription = description; mRemovable = removable; mEmulated = emulated; mMtpReserveSpace = mtpReserveSpace; + mAllowMassStorage = allowMassStorage; } // for parcelling only - private StorageVolume(String path, String description, - boolean removable, boolean emulated, int mtpReserveSpace, int storageId) { + private StorageVolume(String path, String description, boolean removable, + boolean emulated, int mtpReserveSpace, int storageId, boolean allowMassStorage) { mPath = path; mDescription = description; mRemovable = removable; mEmulated = emulated; mMtpReserveSpace = mtpReserveSpace; + mAllowMassStorage = allowMassStorage; mStorageId = storageId; } @@ -130,6 +133,15 @@ public class StorageVolume implements Parcelable { return mMtpReserveSpace; } + /** + * Returns true if this volume can be shared via USB mass storage. + * + * @return whether mass storage is allowed + */ + public boolean allowMassStorage() { + return mAllowMassStorage; + } + @Override public boolean equals(Object obj) { if (obj instanceof StorageVolume && mPath != null) { @@ -158,9 +170,10 @@ public class StorageVolume implements Parcelable { int emulated = in.readInt(); int storageId = in.readInt(); int mtpReserveSpace = in.readInt(); + int allowMassStorage = in.readInt(); return new StorageVolume(path, description, removable == 1, emulated == 1, - mtpReserveSpace, storageId); + mtpReserveSpace, storageId, allowMassStorage == 1); } public StorageVolume[] newArray(int size) { @@ -179,5 +192,6 @@ public class StorageVolume implements Parcelable { parcel.writeInt(mEmulated ? 1 : 0); parcel.writeInt(mStorageId); parcel.writeInt(mMtpReserveSpace); + parcel.writeInt(mAllowMassStorage ? 1 : 0); } } diff --git a/core/java/android/provider/Calendar.java b/core/java/android/provider/Calendar.java index 2df2688b15bf..a06d0f6af732 100644 --- a/core/java/android/provider/Calendar.java +++ b/core/java/android/provider/Calendar.java @@ -183,6 +183,15 @@ public final class Calendar { */ public static final String DIRTY = "dirty"; + /** + * If set to 1 this causes events on this calendar to be duplicated with + * {@link EventsColumns#LAST_SYNCED} set to 1 whenever the event transitions from non-dirty + * to dirty. The duplicated event will not be expanded in the instances table and will only + * show up in sync adapter queries of the events table. It will also be deleted when the + * originating event has its dirty flag cleared by the sync adapter. + * <P>Type: INTEGER (boolean)</P> + */ + public static final String CAN_PARTIALLY_UPDATE = "canPartiallyUpdate"; } /** @@ -317,7 +326,8 @@ public final class Calendar { DatabaseUtils.cursorStringToContentValuesIfPresent(cursor, cv, Calendars.NAME); DatabaseUtils.cursorStringToContentValuesIfPresent(cursor, cv, Calendars.DISPLAY_NAME); - DatabaseUtils.cursorIntToContentValuesIfPresent(cursor, cv, Calendars.CALENDAR_COLOR); + DatabaseUtils.cursorIntToContentValuesIfPresent(cursor, cv, + Calendars.CALENDAR_COLOR); DatabaseUtils.cursorIntToContentValuesIfPresent(cursor, cv, ACCESS_LEVEL); DatabaseUtils.cursorIntToContentValuesIfPresent(cursor, cv, VISIBLE); DatabaseUtils.cursorIntToContentValuesIfPresent(cursor, cv, SYNC_EVENTS); @@ -332,6 +342,8 @@ public final class Calendar { Calendars.CAN_MODIFY_TIME_ZONE); DatabaseUtils.cursorIntToContentValuesIfPresent(cursor, cv, Calendars.MAX_REMINDERS); + DatabaseUtils.cursorIntToContentValuesIfPresent(cursor, cv, + Calendars.CAN_PARTIALLY_UPDATE); DatabaseUtils.cursorIntToContentValuesIfPresent(cursor, cv, DELETED); @@ -512,6 +524,7 @@ public final class Calendar { MAX_REMINDERS, CAN_MODIFY_TIME_ZONE, CAN_ORGANIZER_RESPOND, + CAN_PARTIALLY_UPDATE, CALENDAR_LOCATION, CALENDAR_TIMEZONE, ACCESS_LEVEL, @@ -686,6 +699,23 @@ public final class Calendar { public static final String SYNC_DATA1 = "sync_data1"; /** + * This column is available for use by sync adapters + * <P>Type: TEXT</P> + */ + public static final String SYNC_DATA7 = "sync_data7"; + + /** + * Used to indicate that a row is not a real event but an original copy of a locally + * modified event. A copy is made when an event changes from non-dirty to dirty and the + * event is on a calendar with {@link Calendars#CAN_PARTIALLY_UPDATE} set to 1. This copy + * does not get expanded in the instances table and is only visible in queries made by a + * sync adapter. The copy gets removed when the event is changed back to non-dirty by a + * sync adapter. + * <P>Type: INTEGER (boolean)</P> + */ + public static final String LAST_SYNCED = "lastSynced"; + + /** * The comments feed uri. Column name. * TODO change to sync_data6 * <P>Type: TEXT</P> @@ -1030,7 +1060,9 @@ public final class Calendar { DatabaseUtils.cursorStringToContentValuesIfPresent(cursor, cv, ORGANIZER); DatabaseUtils.cursorStringToContentValuesIfPresent(cursor, cv, _SYNC_ID); DatabaseUtils.cursorStringToContentValuesIfPresent(cursor, cv, _SYNC_DATA); + DatabaseUtils.cursorLongToContentValuesIfPresent(cursor, cv, SYNC_DATA7); DatabaseUtils.cursorLongToContentValuesIfPresent(cursor, cv, DIRTY); + DatabaseUtils.cursorLongToContentValuesIfPresent(cursor, cv, LAST_SYNCED); DatabaseUtils.cursorStringToContentValuesIfPresent(cursor, cv, _SYNC_VERSION); DatabaseUtils.cursorIntToContentValuesIfPresent(cursor, cv, DELETED); DatabaseUtils.cursorStringToContentValuesIfPresent(cursor, cv, CAL_SYNC1); @@ -1191,7 +1223,8 @@ public final class Calendar { CAL_SYNC3, CAL_SYNC4, CAL_SYNC5, - CAL_SYNC6 + CAL_SYNC6, + CAN_PARTIALLY_UPDATE, }; /** @@ -1706,9 +1739,9 @@ public final class Calendar { public static final String NOTIFY_TIME = "notifyTime"; /** - * The state of this alert. It starts out as {@link SCHEDULED}, then - * when the alarm goes off, it changes to {@link FIRED}, and then when - * the user dismisses the alarm it changes to {@link DISMISSED}. Column + * The state of this alert. It starts out as {@link #SCHEDULED}, then + * when the alarm goes off, it changes to {@link #FIRED}, and then when + * the user dismisses the alarm it changes to {@link #DISMISSED}. Column * name. * <P>Type: INTEGER</P> */ diff --git a/core/java/android/provider/Settings.java b/core/java/android/provider/Settings.java index 893947d4ad07..6ab7738896cc 100644 --- a/core/java/android/provider/Settings.java +++ b/core/java/android/provider/Settings.java @@ -2924,6 +2924,30 @@ public final class Settings { public static final String WIFI_WATCHDOG_PING_TIMEOUT_MS = "wifi_watchdog_ping_timeout_ms"; /** + * Setting to turn off walled garden test on Wi-Fi. Feature is enabled by default and + * the setting needs to be set to 0 to disable it. + * @hide + */ + public static final String WIFI_WATCHDOG_WALLED_GARDEN_TEST_ENABLED = + "wifi_watchdog_walled_garden_test_enabled"; + + /** + * The URL used for walled garden check upon a new conection. WifiWatchdogService + * fetches the URL and checks to see if {@link #WIFI_WATCHDOG_WALLED_GARDEN_PATTERN} + * is not part of the title string to notify the user on the presence of a walled garden. + * @hide + */ + public static final String WIFI_WATCHDOG_WALLED_GARDEN_URL = + "wifi_watchdog_walled_garden_url"; + + /** + * The pattern string in the fetched URL used to detect a walled garden + * @hide + */ + public static final String WIFI_WATCHDOG_WALLED_GARDEN_PATTERN = + "wifi_watchdog_walled_garden_pattern"; + + /** * The maximum number of times we will retry a connection to an access * point for which we have failed in acquiring an IP address from DHCP. * A value of N means that we will make N+1 connection attempts in all. @@ -3771,6 +3795,19 @@ public final class Settings { public static final String DREAM_TIMEOUT = "dream_timeout"; + /** {@hide} */ + public static final String NETSTATS_POLL_INTERVAL = "netstats_poll_interval"; + /** {@hide} */ + public static final String NETSTATS_PERSIST_THRESHOLD = "netstats_persist_threshold"; + /** {@hide} */ + public static final String NETSTATS_SUMMARY_BUCKET_DURATION = "netstats_summary_bucket_duration"; + /** {@hide} */ + public static final String NETSTATS_SUMMARY_MAX_HISTORY = "netstats_summary_max_history"; + /** {@hide} */ + public static final String NETSTATS_DETAIL_BUCKET_DURATION = "netstats_detail_bucket_duration"; + /** {@hide} */ + public static final String NETSTATS_DETAIL_MAX_HISTORY = "netstats_detail_max_history"; + /** * @hide */ diff --git a/core/java/android/speech/tts/AudioMessageParams.java b/core/java/android/speech/tts/AudioMessageParams.java new file mode 100644 index 000000000000..db4d6226df15 --- /dev/null +++ b/core/java/android/speech/tts/AudioMessageParams.java @@ -0,0 +1,37 @@ +/* + * 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.speech.tts; + +import android.speech.tts.TextToSpeechService.UtteranceCompletedDispatcher; + +class AudioMessageParams extends MessageParams { + private final BlockingMediaPlayer mPlayer; + + AudioMessageParams(UtteranceCompletedDispatcher dispatcher, BlockingMediaPlayer player) { + super(dispatcher); + mPlayer = player; + } + + BlockingMediaPlayer getPlayer() { + return mPlayer; + } + + @Override + int getType() { + return TYPE_AUDIO; + } + +} diff --git a/core/java/android/speech/tts/AudioPlaybackHandler.java b/core/java/android/speech/tts/AudioPlaybackHandler.java new file mode 100644 index 000000000000..924bbbc05325 --- /dev/null +++ b/core/java/android/speech/tts/AudioPlaybackHandler.java @@ -0,0 +1,358 @@ +/* + * 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.speech.tts; + +import android.media.AudioFormat; +import android.media.AudioTrack; +import android.os.Handler; +import android.os.Looper; +import android.os.Message; +import android.speech.tts.SynthesisMessageParams.ListEntry; +import android.util.Log; + +class AudioPlaybackHandler extends Handler { + private static final String TAG = "TTS.AudioPlaybackHandler"; + private static final boolean DBG = false; + + private static final int MIN_AUDIO_BUFFER_SIZE = 8192; + + private static final int SYNTHESIS_START = 1; + private static final int SYNTHESIS_DATA_AVAILABLE = 2; + private static final int SYNTHESIS_COMPLETE_DATA_AVAILABLE = 3; + private static final int SYNTHESIS_DONE = 4; + + private static final int PLAY_AUDIO = 5; + private static final int PLAY_SILENCE = 6; + + // Accessed by multiple threads, synchronized by "this". + private MessageParams mCurrentParams; + // Used only for book keeping and error detection. + private SynthesisMessageParams mLastSynthesisRequest; + + AudioPlaybackHandler(Looper looper) { + super(looper); + } + + @Override + public synchronized void handleMessage(Message msg) { + if (msg.what == SYNTHESIS_START) { + mCurrentParams = (SynthesisMessageParams) msg.obj; + handleSynthesisStart(msg); + } else if (msg.what == SYNTHESIS_DATA_AVAILABLE) { + handleSynthesisDataAvailable(msg); + } else if (msg.what == SYNTHESIS_DONE) { + handleSynthesisDone(msg); + } else if (msg.what == SYNTHESIS_COMPLETE_DATA_AVAILABLE) { + handleSynthesisCompleteDataAvailable(msg); + } else if (msg.what == PLAY_AUDIO) { + handleAudio(msg); + } else if (msg.what == PLAY_SILENCE) { + handleSilence(msg); + } + + mCurrentParams = null; + } + + /** + * Stops all synthesis for a given {@code token}. If the current token + * is currently being processed, an effort will be made to stop it but + * that is not guaranteed. + */ + synchronized public void stop(MessageParams token) { + removeCallbacksAndMessages(token); + + if (token.getType() == MessageParams.TYPE_SYNTHESIS) { + sendMessageAtFrontOfQueue(obtainMessage(SYNTHESIS_DONE, token)); + } else if (token == mCurrentParams) { + if (token.getType() == MessageParams.TYPE_AUDIO) { + ((AudioMessageParams) mCurrentParams).getPlayer().stop(); + } else if (token.getType() == MessageParams.TYPE_SILENCE) { + ((SilenceMessageParams) mCurrentParams).getConditionVariable().open(); + } + } + } + + /** + * Shut down the audio playback thread. + */ + synchronized public void quit() { + if (mCurrentParams != null) { + stop(mCurrentParams); + } + getLooper().quit(); + } + + void enqueueSynthesisStart(SynthesisMessageParams token) { + sendMessage(obtainMessage(SYNTHESIS_START, token)); + } + + void enqueueSynthesisDataAvailable(SynthesisMessageParams token) { + sendMessage(obtainMessage(SYNTHESIS_DATA_AVAILABLE, token)); + } + + void enqueueSynthesisCompleteDataAvailable(SynthesisMessageParams token) { + sendMessage(obtainMessage(SYNTHESIS_COMPLETE_DATA_AVAILABLE, token)); + } + + void enqueueSynthesisDone(SynthesisMessageParams token) { + sendMessage(obtainMessage(SYNTHESIS_DONE, token)); + } + + void enqueueAudio(AudioMessageParams token) { + sendMessage(obtainMessage(PLAY_AUDIO, token)); + } + + void enqueueSilence(SilenceMessageParams token) { + sendMessage(obtainMessage(PLAY_SILENCE, token)); + } + + // ----------------------------------------- + // End of public API methods. + // ----------------------------------------- + + // Currently implemented as blocking the audio playback thread for the + // specified duration. If a call to stop() is made, the thread + // unblocks. + private void handleSilence(Message msg) { + if (DBG) Log.d(TAG, "handleSilence()"); + SilenceMessageParams params = (SilenceMessageParams) msg.obj; + if (params.getSilenceDurationMs() > 0) { + params.getConditionVariable().block(params.getSilenceDurationMs()); + } + params.getDispatcher().dispatchUtteranceCompleted(); + if (DBG) Log.d(TAG, "handleSilence() done."); + } + + // Plays back audio from a given URI. No TTS engine involvement here. + private void handleAudio(Message msg) { + if (DBG) Log.d(TAG, "handleAudio()"); + AudioMessageParams params = (AudioMessageParams) msg.obj; + // Note that the BlockingMediaPlayer spawns a separate thread. + // + // TODO: This can be avoided. + params.getPlayer().startAndWait(); + params.getDispatcher().dispatchUtteranceCompleted(); + if (DBG) Log.d(TAG, "handleAudio() done."); + } + + // Denotes the start of a new synthesis request. We create a new + // audio track, and prepare it for incoming data. + // + // Note that since all TTS synthesis happens on a single thread, we + // should ALWAYS see the following order : + // + // handleSynthesisStart -> handleSynthesisDataAvailable(*) -> handleSynthesisDone + // OR + // handleSynthesisCompleteDataAvailable. + private void handleSynthesisStart(Message msg) { + if (DBG) Log.d(TAG, "handleSynthesisStart()"); + final SynthesisMessageParams param = (SynthesisMessageParams) msg.obj; + + // Oops, looks like the engine forgot to call done(). We go through + // extra trouble to clean the data to prevent the AudioTrack resources + // from being leaked. + if (mLastSynthesisRequest != null) { + Log.w(TAG, "Error : Missing call to done() for request : " + + mLastSynthesisRequest); + handleSynthesisDone(mLastSynthesisRequest); + } + + mLastSynthesisRequest = param; + + // Create the audio track. + final AudioTrack audioTrack = createStreamingAudioTrack( + param.mStreamType, param.mSampleRateInHz, param.mAudioFormat, + param.mChannelCount, param.mVolume, param.mPan); + + param.setAudioTrack(audioTrack); + } + + // More data available to be flushed to the audio track. + private void handleSynthesisDataAvailable(Message msg) { + final SynthesisMessageParams param = (SynthesisMessageParams) msg.obj; + if (param.getAudioTrack() == null) { + Log.w(TAG, "Error : null audio track in handleDataAvailable."); + return; + } + + if (param != mLastSynthesisRequest) { + Log.e(TAG, "Call to dataAvailable without done() / start()"); + return; + } + + final AudioTrack audioTrack = param.getAudioTrack(); + final ListEntry bufferCopy = param.getNextBuffer(); + + if (bufferCopy == null) { + Log.e(TAG, "No buffers available to play."); + return; + } + + int playState = audioTrack.getPlayState(); + if (playState == AudioTrack.PLAYSTATE_STOPPED) { + if (DBG) Log.d(TAG, "AudioTrack stopped, restarting : " + audioTrack.hashCode()); + audioTrack.play(); + } + int count = 0; + while (count < bufferCopy.mLength) { + // Note that we don't take bufferCopy.mOffset into account because + // it is guaranteed to be 0. + int written = audioTrack.write(bufferCopy.mBytes, count, bufferCopy.mLength); + if (written <= 0) { + break; + } + count += written; + } + } + + private void handleSynthesisDone(Message msg) { + final SynthesisMessageParams params = (SynthesisMessageParams) msg.obj; + handleSynthesisDone(params); + } + + // Flush all remaining data to the audio track, stop it and release + // all it's resources. + private void handleSynthesisDone(SynthesisMessageParams params) { + if (DBG) Log.d(TAG, "handleSynthesisDone()"); + final AudioTrack audioTrack = params.getAudioTrack(); + + try { + if (audioTrack != null) { + audioTrack.flush(); + audioTrack.stop(); + audioTrack.release(); + } + } finally { + params.setAudioTrack(null); + params.getDispatcher().dispatchUtteranceCompleted(); + mLastSynthesisRequest = null; + } + } + + private void handleSynthesisCompleteDataAvailable(Message msg) { + final SynthesisMessageParams params = (SynthesisMessageParams) msg.obj; + if (DBG) Log.d(TAG, "completeAudioAvailable(" + params + ")"); + + // Channel config and bytes per frame are checked before + // this message is sent. + int channelConfig = AudioPlaybackHandler.getChannelConfig(params.mChannelCount); + int bytesPerFrame = AudioPlaybackHandler.getBytesPerFrame(params.mAudioFormat); + + ListEntry entry = params.getNextBuffer(); + + if (entry == null) { + Log.w(TAG, "completeDataAvailable : No buffers available to play."); + return; + } + + final AudioTrack audioTrack = new AudioTrack(params.mStreamType, params.mSampleRateInHz, + channelConfig, params.mAudioFormat, entry.mLength, AudioTrack.MODE_STATIC); + + // So that handleDone can access this correctly. + params.mAudioTrack = audioTrack; + + try { + audioTrack.write(entry.mBytes, entry.mOffset, entry.mLength); + setupVolume(audioTrack, params.mVolume, params.mPan); + audioTrack.play(); + blockUntilDone(audioTrack, bytesPerFrame, entry.mLength); + if (DBG) Log.d(TAG, "Wrote data to audio track successfully : " + entry.mLength); + } catch (IllegalStateException ex) { + Log.e(TAG, "Playback error", ex); + } finally { + handleSynthesisDone(msg); + } + } + + + private static void blockUntilDone(AudioTrack audioTrack, int bytesPerFrame, int length) { + int lengthInFrames = length / bytesPerFrame; + int currentPosition = 0; + while ((currentPosition = audioTrack.getPlaybackHeadPosition()) < lengthInFrames) { + long estimatedTimeMs = ((lengthInFrames - currentPosition) * 1000) / + audioTrack.getSampleRate(); + audioTrack.getPlayState(); + if (DBG) Log.d(TAG, "About to sleep for : " + estimatedTimeMs + " ms," + + " Playback position : " + currentPosition); + try { + Thread.sleep(estimatedTimeMs); + } catch (InterruptedException ie) { + break; + } + } + } + + private static AudioTrack createStreamingAudioTrack(int streamType, int sampleRateInHz, + int audioFormat, int channelCount, float volume, float pan) { + int channelConfig = getChannelConfig(channelCount); + + int minBufferSizeInBytes + = AudioTrack.getMinBufferSize(sampleRateInHz, channelConfig, audioFormat); + int bufferSizeInBytes = Math.max(MIN_AUDIO_BUFFER_SIZE, minBufferSizeInBytes); + + AudioTrack audioTrack = new AudioTrack(streamType, sampleRateInHz, channelConfig, + audioFormat, bufferSizeInBytes, AudioTrack.MODE_STREAM); + if (audioTrack.getState() != AudioTrack.STATE_INITIALIZED) { + Log.w(TAG, "Unable to create audio track."); + audioTrack.release(); + return null; + } + + setupVolume(audioTrack, volume, pan); + return audioTrack; + } + + static int getChannelConfig(int channelCount) { + if (channelCount == 1) { + return AudioFormat.CHANNEL_OUT_MONO; + } else if (channelCount == 2){ + return AudioFormat.CHANNEL_OUT_STEREO; + } + + return 0; + } + + static int getBytesPerFrame(int audioFormat) { + if (audioFormat == AudioFormat.ENCODING_PCM_8BIT) { + return 1; + } else if (audioFormat == AudioFormat.ENCODING_PCM_16BIT) { + return 2; + } + + return -1; + } + + private static void setupVolume(AudioTrack audioTrack, float volume, float pan) { + float vol = clip(volume, 0.0f, 1.0f); + float panning = clip(pan, -1.0f, 1.0f); + float volLeft = vol; + float volRight = vol; + if (panning > 0.0f) { + volLeft *= (1.0f - panning); + } else if (panning < 0.0f) { + volRight *= (1.0f + panning); + } + if (DBG) Log.d(TAG, "volLeft=" + volLeft + ",volRight=" + volRight); + if (audioTrack.setStereoVolume(volLeft, volRight) != AudioTrack.SUCCESS) { + Log.e(TAG, "Failed to set volume"); + } + } + + private static float clip(float value, float min, float max) { + return value > max ? max : (value < min ? min : value); + } + +} diff --git a/core/java/android/speech/tts/MessageParams.java b/core/java/android/speech/tts/MessageParams.java new file mode 100644 index 000000000000..2d96df43b957 --- /dev/null +++ b/core/java/android/speech/tts/MessageParams.java @@ -0,0 +1,36 @@ +/* + * 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.speech.tts; + +import android.speech.tts.TextToSpeechService.UtteranceCompletedDispatcher; + +abstract class MessageParams { + private final UtteranceCompletedDispatcher mDispatcher; + + static final int TYPE_SYNTHESIS = 1; + static final int TYPE_AUDIO = 2; + static final int TYPE_SILENCE = 3; + + MessageParams(UtteranceCompletedDispatcher dispatcher) { + mDispatcher = dispatcher; + } + + UtteranceCompletedDispatcher getDispatcher() { + return mDispatcher; + } + + abstract int getType(); +} diff --git a/core/java/android/speech/tts/PlaybackSynthesisRequest.java b/core/java/android/speech/tts/PlaybackSynthesisRequest.java index d698b5428002..34b263cb74ef 100644 --- a/core/java/android/speech/tts/PlaybackSynthesisRequest.java +++ b/core/java/android/speech/tts/PlaybackSynthesisRequest.java @@ -15,10 +15,8 @@ */ package android.speech.tts; -import android.media.AudioFormat; -import android.media.AudioTrack; import android.os.Bundle; -import android.os.Handler; +import android.speech.tts.TextToSpeechService.UtteranceCompletedDispatcher; import android.util.Log; /** @@ -49,54 +47,48 @@ class PlaybackSynthesisRequest extends SynthesisRequest { */ private final float mPan; + /** + * Guards {@link #mAudioTrackHandler}, {@link #mToken} and {@link #mStopped}. + */ private final Object mStateLock = new Object(); - private final Handler mAudioTrackHandler; - private volatile AudioTrack mAudioTrack = null; + + // Handler associated with a thread that plays back audio requests. + private final AudioPlaybackHandler mAudioTrackHandler; + // A request "token", which will be non null after start() or + // completeAudioAvailable() have been called. + private SynthesisMessageParams mToken = null; + // Whether this request has been stopped. This is useful for keeping + // track whether stop() has been called before start(). In all other cases, + // a non-null value of mToken will provide the same information. private boolean mStopped = false; - private boolean mDone = false; - private volatile boolean mWriteErrorOccured; - PlaybackSynthesisRequest(String text, Bundle params, - int streamType, float volume, float pan, Handler audioTrackHandler) { + private volatile boolean mDone = false; + + private final UtteranceCompletedDispatcher mDispatcher; + + PlaybackSynthesisRequest(String text, Bundle params, int streamType, float volume, float pan, + AudioPlaybackHandler audioTrackHandler, UtteranceCompletedDispatcher dispatcher) { super(text, params); mStreamType = streamType; mVolume = volume; mPan = pan; mAudioTrackHandler = audioTrackHandler; - mWriteErrorOccured = false; + mDispatcher = dispatcher; } @Override void stop() { if (DBG) Log.d(TAG, "stop()"); + synchronized (mStateLock) { + if (mToken == null || mStopped) { + Log.w(TAG, "stop() called twice, before start(), or after done()"); + return; + } + mAudioTrackHandler.stop(mToken); + mToken = null; mStopped = true; - cleanUp(); - } - } - - // Always guarded by mStateLock. - private void cleanUp() { - if (DBG) Log.d(TAG, "cleanUp()"); - if (mAudioTrack == null) { - return; } - - final AudioTrack audioTrack = mAudioTrack; - mAudioTrack = null; - - // Clean up on the audiotrack handler thread. - // - // NOTE: It isn't very clear whether AudioTrack is thread safe. - // If it is we can clean up on the current (synthesis) thread. - mAudioTrackHandler.post(new Runnable() { - @Override - public void run() { - audioTrack.flush(); - audioTrack.stop(); - audioTrack.release(); - } - }); } @Override @@ -111,7 +103,6 @@ class PlaybackSynthesisRequest extends SynthesisRequest { return mDone; } - // TODO: add a thread that writes to the AudioTrack? @Override public int start(int sampleRateInHz, int audioFormat, int channelCount) { if (DBG) { @@ -119,45 +110,28 @@ class PlaybackSynthesisRequest extends SynthesisRequest { + "," + channelCount + ")"); } + int channelConfig = AudioPlaybackHandler.getChannelConfig(channelCount); + if (channelConfig == 0) { + Log.e(TAG, "Unsupported number of channels :" + channelCount); + return TextToSpeech.ERROR; + } + synchronized (mStateLock) { if (mStopped) { - if (DBG) Log.d(TAG, "Request has been aborted."); - return TextToSpeech.ERROR; - } - if (mAudioTrack != null) { - Log.e(TAG, "start() called twice"); - cleanUp(); + if (DBG) Log.d(TAG, "stop() called before start(), returning."); return TextToSpeech.ERROR; } + SynthesisMessageParams params = new SynthesisMessageParams( + mStreamType, sampleRateInHz, audioFormat, channelCount, mVolume, mPan, + mDispatcher); + mAudioTrackHandler.enqueueSynthesisStart(params); - mAudioTrack = createStreamingAudioTrack(sampleRateInHz, audioFormat, channelCount); - if (mAudioTrack == null) { - return TextToSpeech.ERROR; - } + mToken = params; } return TextToSpeech.SUCCESS; } - private void setupVolume(AudioTrack audioTrack, float volume, float pan) { - float vol = clip(volume, 0.0f, 1.0f); - float panning = clip(pan, -1.0f, 1.0f); - float volLeft = vol; - float volRight = vol; - if (panning > 0.0f) { - volLeft *= (1.0f - panning); - } else if (panning < 0.0f) { - volRight *= (1.0f + panning); - } - if (DBG) Log.d(TAG, "volLeft=" + volLeft + ",volRight=" + volRight); - if (audioTrack.setStereoVolume(volLeft, volRight) != AudioTrack.SUCCESS) { - Log.e(TAG, "Failed to set volume"); - } - } - - private float clip(float value, float min, float max) { - return value > max ? max : (value < min ? min : value); - } @Override public int audioAvailable(byte[] buffer, int offset, int length) { @@ -169,195 +143,78 @@ class PlaybackSynthesisRequest extends SynthesisRequest { throw new IllegalArgumentException("buffer is too large or of zero length (" + + length + " bytes)"); } + synchronized (mStateLock) { - if (mWriteErrorOccured) { - if (DBG) Log.d(TAG, "Error writing to audio track, count < 0"); - return TextToSpeech.ERROR; - } - if (mStopped) { - if (DBG) Log.d(TAG, "Request has been aborted."); + if (mToken == null) { return TextToSpeech.ERROR; } - if (mAudioTrack == null) { - Log.e(TAG, "audioAvailable(): Not started"); - return TextToSpeech.ERROR; - } - final AudioTrack audioTrack = mAudioTrack; + // Sigh, another copy. final byte[] bufferCopy = new byte[length]; System.arraycopy(buffer, offset, bufferCopy, 0, length); - - mAudioTrackHandler.post(new Runnable() { - @Override - public void run() { - int playState = audioTrack.getPlayState(); - if (playState == AudioTrack.PLAYSTATE_STOPPED) { - if (DBG) Log.d(TAG, "AudioTrack stopped, restarting"); - audioTrack.play(); - } - // TODO: loop until all data is written? - if (DBG) Log.d(TAG, "AudioTrack.write()"); - int count = audioTrack.write(bufferCopy, 0, bufferCopy.length); - // The semantics of this change very slightly. Earlier, we would - // report an error immediately, Now we will return an error on - // the next API call, usually done( ) or another audioAvailable( ) - // call. - if (count < 0) { - mWriteErrorOccured = true; - } - } - }); - - return TextToSpeech.SUCCESS; + mToken.addBuffer(bufferCopy); + mAudioTrackHandler.enqueueSynthesisDataAvailable(mToken); } + + return TextToSpeech.SUCCESS; } @Override public int done() { if (DBG) Log.d(TAG, "done()"); + synchronized (mStateLock) { - if (mWriteErrorOccured) { - if (DBG) Log.d(TAG, "Error writing to audio track, count < 0"); - return TextToSpeech.ERROR; - } - if (mStopped) { - if (DBG) Log.d(TAG, "Request has been aborted."); + if (mDone) { + Log.w(TAG, "Duplicate call to done()"); return TextToSpeech.ERROR; } - if (mAudioTrack == null) { - Log.e(TAG, "done(): Not started"); + + mDone = true; + + if (mToken == null) { return TextToSpeech.ERROR; } - mDone = true; - cleanUp(); + + mAudioTrackHandler.enqueueSynthesisDone(mToken); } return TextToSpeech.SUCCESS; } @Override public void error() { - if (DBG) Log.d(TAG, "error()"); - synchronized (mStateLock) { - cleanUp(); - } + if (DBG) Log.d(TAG, "error() [will call stop]"); + stop(); } @Override public int completeAudioAvailable(int sampleRateInHz, int audioFormat, int channelCount, byte[] buffer, int offset, int length) { - if (DBG) { - Log.d(TAG, "completeAudioAvailable(" + sampleRateInHz + "," + audioFormat - + "," + channelCount + "byte[" + buffer.length + "]," - + offset + "," + length + ")"); + int channelConfig = AudioPlaybackHandler.getChannelConfig(channelCount); + if (channelConfig == 0) { + Log.e(TAG, "Unsupported number of channels :" + channelCount); + return TextToSpeech.ERROR; + } + + int bytesPerFrame = AudioPlaybackHandler.getBytesPerFrame(audioFormat); + if (bytesPerFrame < 0) { + Log.e(TAG, "Unsupported audio format :" + audioFormat); + return TextToSpeech.ERROR; } synchronized (mStateLock) { if (mStopped) { - if (DBG) Log.d(TAG, "Request has been aborted."); - return TextToSpeech.ERROR; - } - if (mAudioTrack != null) { - Log.e(TAG, "start() called before completeAudioAvailable()"); - cleanUp(); return TextToSpeech.ERROR; } + SynthesisMessageParams params = new SynthesisMessageParams( + mStreamType, sampleRateInHz, audioFormat, channelCount, mVolume, mPan, + mDispatcher); + params.addBuffer(buffer, offset, length); - int channelConfig = getChannelConfig(channelCount); - if (channelConfig < 0) { - Log.e(TAG, "Unsupported number of channels :" + channelCount); - cleanUp(); - return TextToSpeech.ERROR; - } - int bytesPerFrame = getBytesPerFrame(audioFormat); - if (bytesPerFrame < 0) { - Log.e(TAG, "Unsupported audio format :" + audioFormat); - cleanUp(); - return TextToSpeech.ERROR; - } - - mAudioTrack = new AudioTrack(mStreamType, sampleRateInHz, channelConfig, - audioFormat, buffer.length, AudioTrack.MODE_STATIC); - if (mAudioTrack == null) { - return TextToSpeech.ERROR; - } - - try { - mAudioTrack.write(buffer, offset, length); - setupVolume(mAudioTrack, mVolume, mPan); - mAudioTrack.play(); - blockUntilDone(mAudioTrack, bytesPerFrame, length); - mDone = true; - if (DBG) Log.d(TAG, "Wrote data to audio track succesfully : " + length); - } catch (IllegalStateException ex) { - Log.e(TAG, "Playback error", ex); - return TextToSpeech.ERROR; - } finally { - cleanUp(); - } + mAudioTrackHandler.enqueueSynthesisCompleteDataAvailable(params); + mToken = params; } return TextToSpeech.SUCCESS; } - private void blockUntilDone(AudioTrack audioTrack, int bytesPerFrame, int length) { - int lengthInFrames = length / bytesPerFrame; - int currentPosition = 0; - while ((currentPosition = audioTrack.getPlaybackHeadPosition()) < lengthInFrames) { - long estimatedTimeMs = ((lengthInFrames - currentPosition) * 1000) / - audioTrack.getSampleRate(); - if (DBG) Log.d(TAG, "About to sleep for : " + estimatedTimeMs + " ms," + - " Playback position : " + currentPosition); - try { - Thread.sleep(estimatedTimeMs); - } catch (InterruptedException ie) { - break; - } - } - } - - private int getBytesPerFrame(int audioFormat) { - if (audioFormat == AudioFormat.ENCODING_PCM_8BIT) { - return 1; - } else if (audioFormat == AudioFormat.ENCODING_PCM_16BIT) { - return 2; - } - - return -1; - } - - private int getChannelConfig(int channelCount) { - if (channelCount == 1) { - return AudioFormat.CHANNEL_OUT_MONO; - } else if (channelCount == 2){ - return AudioFormat.CHANNEL_OUT_STEREO; - } - - return -1; - } - - private AudioTrack createStreamingAudioTrack(int sampleRateInHz, int audioFormat, - int channelCount) { - int channelConfig = getChannelConfig(channelCount); - - if (channelConfig < 0) { - Log.e(TAG, "Unsupported number of channels : " + channelCount); - return null; - } - - int minBufferSizeInBytes - = AudioTrack.getMinBufferSize(sampleRateInHz, channelConfig, audioFormat); - int bufferSizeInBytes = Math.max(MIN_AUDIO_BUFFER_SIZE, minBufferSizeInBytes); - AudioTrack audioTrack = new AudioTrack(mStreamType, sampleRateInHz, channelConfig, - audioFormat, bufferSizeInBytes, AudioTrack.MODE_STREAM); - if (audioTrack == null) { - return null; - } - - if (audioTrack.getState() != AudioTrack.STATE_INITIALIZED) { - audioTrack.release(); - return null; - } - setupVolume(audioTrack, mVolume, mPan); - return audioTrack; - } } diff --git a/core/java/android/speech/tts/SilenceMessageParams.java b/core/java/android/speech/tts/SilenceMessageParams.java new file mode 100644 index 000000000000..eee8b6816438 --- /dev/null +++ b/core/java/android/speech/tts/SilenceMessageParams.java @@ -0,0 +1,43 @@ +/* + * 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.speech.tts; + +import android.os.ConditionVariable; +import android.speech.tts.TextToSpeechService.UtteranceCompletedDispatcher; + +class SilenceMessageParams extends MessageParams { + private final ConditionVariable mCondVar = new ConditionVariable(); + private final long mSilenceDurationMs; + + SilenceMessageParams(UtteranceCompletedDispatcher dispatcher, long silenceDurationMs) { + super(dispatcher); + mSilenceDurationMs = silenceDurationMs; + } + + long getSilenceDurationMs() { + return mSilenceDurationMs; + } + + @Override + int getType() { + return TYPE_SILENCE; + } + + ConditionVariable getConditionVariable() { + return mCondVar; + } + +} diff --git a/core/java/android/speech/tts/SynthesisMessageParams.java b/core/java/android/speech/tts/SynthesisMessageParams.java new file mode 100644 index 000000000000..aabaa5ae5efc --- /dev/null +++ b/core/java/android/speech/tts/SynthesisMessageParams.java @@ -0,0 +1,92 @@ +/* + * 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.speech.tts; + +import android.media.AudioTrack; +import android.speech.tts.TextToSpeechService.UtteranceCompletedDispatcher; + +import java.util.LinkedList; + +/** + * Params required to play back a synthesis request. + */ +final class SynthesisMessageParams extends MessageParams { + final int mStreamType; + final int mSampleRateInHz; + final int mAudioFormat; + final int mChannelCount; + final float mVolume; + final float mPan; + + public volatile AudioTrack mAudioTrack; + + private final LinkedList<ListEntry> mDataBufferList = new LinkedList<ListEntry>(); + + SynthesisMessageParams(int streamType, int sampleRate, + int audioFormat, int channelCount, + float volume, float pan, UtteranceCompletedDispatcher dispatcher) { + super(dispatcher); + + mStreamType = streamType; + mSampleRateInHz = sampleRate; + mAudioFormat = audioFormat; + mChannelCount = channelCount; + mVolume = volume; + mPan = pan; + + // initially null. + mAudioTrack = null; + } + + @Override + int getType() { + return TYPE_SYNTHESIS; + } + + synchronized void addBuffer(byte[] buffer, int offset, int length) { + mDataBufferList.add(new ListEntry(buffer, offset, length)); + } + + synchronized void addBuffer(byte[] buffer) { + mDataBufferList.add(new ListEntry(buffer, 0, buffer.length)); + } + + synchronized ListEntry getNextBuffer() { + return mDataBufferList.poll(); + } + + + void setAudioTrack(AudioTrack audioTrack) { + mAudioTrack = audioTrack; + } + + AudioTrack getAudioTrack() { + return mAudioTrack; + } + + static final class ListEntry { + final byte[] mBytes; + final int mOffset; + final int mLength; + + ListEntry(byte[] bytes, int offset, int length) { + mBytes = bytes; + mOffset = offset; + mLength = length; + } + } +} + diff --git a/core/java/android/speech/tts/TextToSpeechService.java b/core/java/android/speech/tts/TextToSpeechService.java index ddd32521891d..e553f7735b35 100644 --- a/core/java/android/speech/tts/TextToSpeechService.java +++ b/core/java/android/speech/tts/TextToSpeechService.java @@ -51,7 +51,10 @@ public abstract class TextToSpeechService extends Service { private static final String SYNTH_THREAD_NAME = "SynthThread"; private SynthHandler mSynthHandler; - private Handler mAudioTrackHandler; + // A thread and it's associated handler for playing back any audio + // associated with this TTS engine. Will handle all requests except synthesis + // to file requests, which occur on the synthesis thread. + private AudioPlaybackHandler mAudioPlaybackHandler; private CallbackMap mCallbacks; @@ -68,7 +71,7 @@ public abstract class TextToSpeechService extends Service { HandlerThread audioTrackThread = new HandlerThread("TTS.audioTrackThread"); audioTrackThread.start(); - mAudioTrackHandler = new Handler(audioTrackThread.getLooper()); + mAudioPlaybackHandler = new AudioPlaybackHandler(audioTrackThread.getLooper()); mCallbacks = new CallbackMap(); @@ -83,8 +86,8 @@ public abstract class TextToSpeechService extends Service { // Tell the synthesizer to stop mSynthHandler.quit(); - mAudioTrackHandler.getLooper().quit(); - + // Tell the audio playback thread to stop. + mAudioPlaybackHandler.quit(); // Unregister all callbacks. mCallbacks.kill(); @@ -236,13 +239,6 @@ public abstract class TextToSpeechService extends Service { super(looper); } - private void dispatchUtteranceCompleted(SpeechItem item) { - String utteranceId = item.getUtteranceId(); - if (!TextUtils.isEmpty(utteranceId)) { - mCallbacks.dispatchUtteranceCompleted(item.getCallingApp(), utteranceId); - } - } - private synchronized SpeechItem getCurrentSpeechItem() { return mCurrentSpeechItem; } @@ -286,9 +282,7 @@ public abstract class TextToSpeechService extends Service { @Override public void run() { setCurrentSpeechItem(speechItem); - if (speechItem.play() == TextToSpeech.SUCCESS) { - dispatchUtteranceCompleted(speechItem); - } + speechItem.play(); setCurrentSpeechItem(null); } }; @@ -318,14 +312,19 @@ public abstract class TextToSpeechService extends Service { if (current != null && TextUtils.equals(callingApp, current.getCallingApp())) { current.stop(); } + return TextToSpeech.SUCCESS; } } + interface UtteranceCompletedDispatcher { + public void dispatchUtteranceCompleted(); + } + /** * An item in the synth thread queue. */ - private static abstract class SpeechItem { + private abstract class SpeechItem implements UtteranceCompletedDispatcher { private final String mCallingApp; protected final Bundle mParams; private boolean mStarted = false; @@ -380,6 +379,13 @@ public abstract class TextToSpeechService extends Service { stopImpl(); } + public void dispatchUtteranceCompleted() { + final String utteranceId = getUtteranceId(); + if (!TextUtils.isEmpty(utteranceId)) { + mCallbacks.dispatchUtteranceCompleted(getCallingApp(), utteranceId); + } + } + protected abstract int playImpl(); protected abstract void stopImpl(); @@ -413,7 +419,7 @@ public abstract class TextToSpeechService extends Service { } } - private class SynthesisSpeechItem extends SpeechItem { + class SynthesisSpeechItem extends SpeechItem { private final String mText; private SynthesisRequest mSynthesisRequest; @@ -453,7 +459,8 @@ public abstract class TextToSpeechService extends Service { protected SynthesisRequest createSynthesisRequest() { return new PlaybackSynthesisRequest(mText, mParams, - getStreamType(), getVolume(), getPan(), mAudioTrackHandler); + getStreamType(), getVolume(), getPan(), mAudioPlaybackHandler, + this); } private void setRequestParams(SynthesisRequest request) { @@ -526,6 +533,15 @@ public abstract class TextToSpeechService extends Service { return new FileSynthesisRequest(getText(), mParams, mFile); } + @Override + protected int playImpl() { + int status = super.playImpl(); + if (status == TextToSpeech.SUCCESS) { + dispatchUtteranceCompleted(); + } + return status; + } + /** * Checks that the given file can be used for synthesis output. */ @@ -557,6 +573,7 @@ public abstract class TextToSpeechService extends Service { private class AudioSpeechItem extends SpeechItem { private final BlockingMediaPlayer mPlayer; + private AudioMessageParams mToken; public AudioSpeechItem(String callingApp, Bundle params, Uri uri) { super(callingApp, params); @@ -570,23 +587,26 @@ public abstract class TextToSpeechService extends Service { @Override protected int playImpl() { - return mPlayer.startAndWait() ? TextToSpeech.SUCCESS : TextToSpeech.ERROR; + mToken = new AudioMessageParams(this, mPlayer); + mAudioPlaybackHandler.enqueueAudio(mToken); + return TextToSpeech.SUCCESS; } @Override protected void stopImpl() { - mPlayer.stop(); + if (mToken != null) { + mAudioPlaybackHandler.stop(mToken); + } } } private class SilenceSpeechItem extends SpeechItem { private final long mDuration; - private final ConditionVariable mDone; + private SilenceMessageParams mToken; public SilenceSpeechItem(String callingApp, Bundle params, long duration) { super(callingApp, params); mDuration = duration; - mDone = new ConditionVariable(); } @Override @@ -596,13 +616,16 @@ public abstract class TextToSpeechService extends Service { @Override protected int playImpl() { - boolean aborted = mDone.block(mDuration); - return aborted ? TextToSpeech.ERROR : TextToSpeech.SUCCESS; + mToken = new SilenceMessageParams(this, mDuration); + mAudioPlaybackHandler.enqueueSilence(mToken); + return TextToSpeech.SUCCESS; } @Override protected void stopImpl() { - mDone.open(); + if (mToken != null) { + mAudioPlaybackHandler.stop(mToken); + } } } diff --git a/core/java/android/view/Gravity.java b/core/java/android/view/Gravity.java index ba067956f6d5..69e64898e343 100644 --- a/core/java/android/view/Gravity.java +++ b/core/java/android/view/Gravity.java @@ -81,8 +81,10 @@ public class Gravity * horizontal axis. */ public static final int CLIP_HORIZONTAL = AXIS_CLIP<<AXIS_X_SHIFT; - /** Raw bit controlling whether the horizontal direction is relative (before/after) or not. */ - public static final int RELATIVE_HORIZONTAL_DIRECTION = 0x00800000; + /** Raw bit controlling whether the layout direction is relative or not (START/END instead of + * absolute LEFT/RIGHT). + */ + public static final int RELATIVE_LAYOUT_DIRECTION = 0x00800000; /** * Binary mask to get the absolute horizontal gravity of a gravity. @@ -110,10 +112,10 @@ public class Gravity public static final int DISPLAY_CLIP_HORIZONTAL = 0x01000000; /** Push object to x-axis position at the start of its container, not changing its size. */ - public static final int START = RELATIVE_HORIZONTAL_DIRECTION | LEFT; + public static final int START = RELATIVE_LAYOUT_DIRECTION | LEFT; /** Push object to x-axis position at the end of its container, not changing its size. */ - public static final int END = RELATIVE_HORIZONTAL_DIRECTION | RIGHT; + public static final int END = RELATIVE_LAYOUT_DIRECTION | RIGHT; /** * Binary mask for the horizontal gravity and script specific direction bit. @@ -352,7 +354,7 @@ public class Gravity public static int getAbsoluteGravity(int gravity, boolean isRtl) { int result = gravity; // If layout is script specific and gravity is horizontal relative (START or END) - if ((result & RELATIVE_HORIZONTAL_DIRECTION) > 0) { + if ((result & RELATIVE_LAYOUT_DIRECTION) > 0) { if ((result & Gravity.START) == Gravity.START) { // Remove the START bit result &= ~START; @@ -376,7 +378,7 @@ public class Gravity } // Don't need the script specific bit any more, so remove it as we are converting to // absolute values (LEFT or RIGHT) - result &= ~RELATIVE_HORIZONTAL_DIRECTION; + result &= ~RELATIVE_LAYOUT_DIRECTION; } return result; } diff --git a/core/java/android/view/View.java b/core/java/android/view/View.java index 51eb13b6a7ea..70218acaa938 100644 --- a/core/java/android/view/View.java +++ b/core/java/android/view/View.java @@ -951,51 +951,51 @@ public class View implements Drawable.Callback2, KeyEvent.Callback, Accessibilit /** * Horizontal direction of this view is from Left to Right. - * Use with {@link #setHorizontalDirection}. + * Use with {@link #setLayoutDirection}. * {@hide} */ - public static final int HORIZONTAL_DIRECTION_LTR = 0x00000000; + public static final int LAYOUT_DIRECTION_LTR = 0x00000000; /** * Horizontal direction of this view is from Right to Left. - * Use with {@link #setHorizontalDirection}. + * Use with {@link #setLayoutDirection}. * {@hide} */ - public static final int HORIZONTAL_DIRECTION_RTL = 0x40000000; + public static final int LAYOUT_DIRECTION_RTL = 0x40000000; /** * Horizontal direction of this view is inherited from its parent. - * Use with {@link #setHorizontalDirection}. + * Use with {@link #setLayoutDirection}. * {@hide} */ - public static final int HORIZONTAL_DIRECTION_INHERIT = 0x80000000; + public static final int LAYOUT_DIRECTION_INHERIT = 0x80000000; /** * Horizontal direction of this view is from deduced from the default language - * script for the locale. Use with {@link #setHorizontalDirection}. + * script for the locale. Use with {@link #setLayoutDirection}. * {@hide} */ - public static final int HORIZONTAL_DIRECTION_LOCALE = 0xC0000000; + public static final int LAYOUT_DIRECTION_LOCALE = 0xC0000000; /** * Mask for use with setFlags indicating bits used for horizontalDirection. * {@hide} */ - static final int HORIZONTAL_DIRECTION_MASK = 0xC0000000; + static final int LAYOUT_DIRECTION_MASK = 0xC0000000; /* * Array of horizontal direction flags for mapping attribute "horizontalDirection" to correct * flag value. * {@hide} */ - private static final int[] HORIZONTAL_DIRECTION_FLAGS = { HORIZONTAL_DIRECTION_LTR, - HORIZONTAL_DIRECTION_RTL, HORIZONTAL_DIRECTION_INHERIT, HORIZONTAL_DIRECTION_LOCALE}; + private static final int[] LAYOUT_DIRECTION_FLAGS = {LAYOUT_DIRECTION_LTR, + LAYOUT_DIRECTION_RTL, LAYOUT_DIRECTION_INHERIT, LAYOUT_DIRECTION_LOCALE}; /** * Default horizontalDirection. * {@hide} */ - private static final int HORIZONTAL_DIRECTION_DEFAULT = HORIZONTAL_DIRECTION_INHERIT; + private static final int LAYOUT_DIRECTION_DEFAULT = LAYOUT_DIRECTION_INHERIT; /** * View flag indicating whether {@link #addFocusables(ArrayList, int, int)} @@ -2464,7 +2464,7 @@ public class View implements Drawable.Callback2, KeyEvent.Callback, Accessibilit public View(Context context) { mContext = context; mResources = context != null ? context.getResources() : null; - mViewFlags = SOUND_EFFECTS_ENABLED | HAPTIC_FEEDBACK_ENABLED | HORIZONTAL_DIRECTION_INHERIT; + mViewFlags = SOUND_EFFECTS_ENABLED | HAPTIC_FEEDBACK_ENABLED | LAYOUT_DIRECTION_INHERIT; mTouchSlop = ViewConfiguration.get(context).getScaledTouchSlop(); setOverScrollMode(OVER_SCROLL_IF_CONTENT_SCROLLS); } @@ -2662,18 +2662,18 @@ public class View implements Drawable.Callback2, KeyEvent.Callback, Accessibilit viewFlagMasks |= VISIBILITY_MASK; } break; - case com.android.internal.R.styleable.View_horizontalDirection: + case com.android.internal.R.styleable.View_layoutDirection: // Clear any HORIZONTAL_DIRECTION flag already set - viewFlagValues &= ~HORIZONTAL_DIRECTION_MASK; + viewFlagValues &= ~LAYOUT_DIRECTION_MASK; // Set the HORIZONTAL_DIRECTION flags depending on the value of the attribute - final int horizontalDirection = a.getInt(attr, -1); - if (horizontalDirection != -1) { - viewFlagValues |= HORIZONTAL_DIRECTION_FLAGS[horizontalDirection]; + final int layoutDirection = a.getInt(attr, -1); + if (layoutDirection != -1) { + viewFlagValues |= LAYOUT_DIRECTION_FLAGS[layoutDirection]; } else { - // Set to default (HORIZONTAL_DIRECTION_INHERIT) - viewFlagValues |= HORIZONTAL_DIRECTION_DEFAULT; + // Set to default (LAYOUT_DIRECTION_INHERIT) + viewFlagValues |= LAYOUT_DIRECTION_DEFAULT; } - viewFlagMasks |= HORIZONTAL_DIRECTION_MASK; + viewFlagMasks |= LAYOUT_DIRECTION_MASK; break; case com.android.internal.R.styleable.View_drawingCacheQuality: final int cacheQuality = a.getInt(attr, 0); @@ -4256,38 +4256,38 @@ public class View implements Drawable.Callback2, KeyEvent.Callback, Accessibilit } /** - * Returns the horizontal direction for this view. + * Returns the layout direction for this view. * - * @return One of {@link #HORIZONTAL_DIRECTION_LTR}, - * {@link #HORIZONTAL_DIRECTION_RTL}, - * {@link #HORIZONTAL_DIRECTION_INHERIT} or - * {@link #HORIZONTAL_DIRECTION_LOCALE}. - * @attr ref android.R.styleable#View_horizontalDirection + * @return One of {@link #LAYOUT_DIRECTION_LTR}, + * {@link #LAYOUT_DIRECTION_RTL}, + * {@link #LAYOUT_DIRECTION_INHERIT} or + * {@link #LAYOUT_DIRECTION_LOCALE}. + * @attr ref android.R.styleable#View_layoutDirection * @hide */ @ViewDebug.ExportedProperty(category = "layout", mapping = { - @ViewDebug.IntToString(from = HORIZONTAL_DIRECTION_LTR, to = "LTR"), - @ViewDebug.IntToString(from = HORIZONTAL_DIRECTION_RTL, to = "RTL"), - @ViewDebug.IntToString(from = HORIZONTAL_DIRECTION_INHERIT, to = "INHERIT"), - @ViewDebug.IntToString(from = HORIZONTAL_DIRECTION_LOCALE, to = "LOCALE") + @ViewDebug.IntToString(from = LAYOUT_DIRECTION_LTR, to = "LTR"), + @ViewDebug.IntToString(from = LAYOUT_DIRECTION_RTL, to = "RTL"), + @ViewDebug.IntToString(from = LAYOUT_DIRECTION_INHERIT, to = "INHERIT"), + @ViewDebug.IntToString(from = LAYOUT_DIRECTION_LOCALE, to = "LOCALE") }) - public int getHorizontalDirection() { - return mViewFlags & HORIZONTAL_DIRECTION_MASK; + public int getLayoutDirection() { + return mViewFlags & LAYOUT_DIRECTION_MASK; } /** - * Set the horizontal direction for this view. + * Set the layout direction for this view. * - * @param horizontalDirection One of {@link #HORIZONTAL_DIRECTION_LTR}, - * {@link #HORIZONTAL_DIRECTION_RTL}, - * {@link #HORIZONTAL_DIRECTION_INHERIT} or - * {@link #HORIZONTAL_DIRECTION_LOCALE}. - * @attr ref android.R.styleable#View_horizontalDirection + * @param layoutDirection One of {@link #LAYOUT_DIRECTION_LTR}, + * {@link #LAYOUT_DIRECTION_RTL}, + * {@link #LAYOUT_DIRECTION_INHERIT} or + * {@link #LAYOUT_DIRECTION_LOCALE}. + * @attr ref android.R.styleable#View_layoutDirection * @hide */ @RemotableViewMethod - public void setHorizontalDirection(int horizontalDirection) { - setFlags(horizontalDirection, HORIZONTAL_DIRECTION_MASK); + public void setLayoutDirection(int layoutDirection) { + setFlags(layoutDirection, LAYOUT_DIRECTION_MASK); } /** @@ -6103,7 +6103,7 @@ public class View implements Drawable.Callback2, KeyEvent.Callback, Accessibilit } } - if ((changed & HORIZONTAL_DIRECTION_MASK) != 0) { + if ((changed & LAYOUT_DIRECTION_MASK) != 0) { requestLayout(); } } @@ -8658,24 +8658,24 @@ public class View implements Drawable.Callback2, KeyEvent.Callback, Accessibilit mPrivateFlags &= ~AWAKEN_SCROLL_BARS_ON_ATTACH; } jumpDrawablesToCurrentState(); - resolveHorizontalDirection(); + resolveLayoutDirection(); } /** * Resolving the layout direction. LTR is set initially. * We are supposing here that the parent directionality will be resolved before its children */ - private void resolveHorizontalDirection() { + private void resolveLayoutDirection() { mPrivateFlags2 &= ~RESOLVED_LAYOUT_RTL; - switch (getHorizontalDirection()) { - case HORIZONTAL_DIRECTION_INHERIT: + 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).isLayoutRtl()) { mPrivateFlags2 |= RESOLVED_LAYOUT_RTL; } break; - case HORIZONTAL_DIRECTION_RTL: + case LAYOUT_DIRECTION_RTL: mPrivateFlags2 |= RESOLVED_LAYOUT_RTL; break; } diff --git a/core/java/android/view/ViewConfiguration.java b/core/java/android/view/ViewConfiguration.java index 36bb046c0f68..5919150dbfae 100644 --- a/core/java/android/view/ViewConfiguration.java +++ b/core/java/android/view/ViewConfiguration.java @@ -110,7 +110,21 @@ public class ViewConfiguration { * double-tap. */ private static final int DOUBLE_TAP_TIMEOUT = 300; - + + /** + * Defines the maximum duration in milliseconds between a touch pad + * touch and release for a given touch to be considered a tap (click) as + * opposed to a hover movement gesture. + */ + private static final int HOVER_TAP_TIMEOUT = 150; + + /** + * Defines the maximum distance in pixels that a touch pad touch can move + * before being released for it to be considered a tap (click) as opposed + * to a hover movement gesture. + */ + private static final int HOVER_TAP_SLOP = 20; + /** * Defines the duration in milliseconds we want to display zoom controls in response * to a user panning within an application. @@ -369,7 +383,7 @@ public class ViewConfiguration { public static int getTapTimeout() { return TAP_TIMEOUT; } - + /** * @return the duration in milliseconds we will wait to see if a touch event * is a jump tap. If the user does not move within this interval, it is @@ -387,7 +401,27 @@ public class ViewConfiguration { public static int getDoubleTapTimeout() { return DOUBLE_TAP_TIMEOUT; } - + + /** + * @return the maximum duration in milliseconds between a touch pad + * touch and release for a given touch to be considered a tap (click) as + * opposed to a hover movement gesture. + * @hide + */ + public static int getHoverTapTimeout() { + return HOVER_TAP_TIMEOUT; + } + + /** + * @return the maximum distance in pixels that a touch pad touch can move + * before being released for it to be considered a tap (click) as opposed + * to a hover movement gesture. + * @hide + */ + public static int getHoverTapSlop() { + return HOVER_TAP_SLOP; + } + /** * @return Inset in pixels to look for touchable content when the user touches the edge of the * screen diff --git a/core/java/android/view/inputmethod/InputMethodSubtype.java b/core/java/android/view/inputmethod/InputMethodSubtype.java index 20cf93d36067..9d84c3e2ba63 100644 --- a/core/java/android/view/inputmethod/InputMethodSubtype.java +++ b/core/java/android/view/inputmethod/InputMethodSubtype.java @@ -28,6 +28,7 @@ import java.util.Arrays; import java.util.HashMap; import java.util.HashSet; import java.util.List; +import java.util.Locale; /** * This class is used to specify meta information of a subtype contained in an input method. @@ -54,10 +55,11 @@ public final class InputMethodSubtype implements Parcelable { * @param nameId The name of the subtype * @param iconId The icon of the subtype * @param locale The locale supported by the subtype - * @param modeId The mode supported by the subtype + * @param mode The mode supported by the subtype * @param extraValue The extra value of the subtype */ - InputMethodSubtype(int nameId, int iconId, String locale, String mode, String extraValue) { + public InputMethodSubtype( + int nameId, int iconId, String locale, String mode, String extraValue) { this(nameId, iconId, locale, mode, extraValue, false); } @@ -66,19 +68,20 @@ public final class InputMethodSubtype implements Parcelable { * @param nameId The name of the subtype * @param iconId The icon of the subtype * @param locale The locale supported by the subtype - * @param modeId The mode supported by the subtype + * @param mode The mode supported by the subtype * @param extraValue The extra value of the subtype * @param isAuxiliary true when this subtype is one shot subtype. */ - InputMethodSubtype(int nameId, int iconId, String locale, String mode, String extraValue, + public InputMethodSubtype(int nameId, int iconId, String locale, String mode, String extraValue, boolean isAuxiliary) { mSubtypeNameResId = nameId; mSubtypeIconResId = iconId; mSubtypeLocale = locale != null ? locale : ""; mSubtypeMode = mode != null ? mode : ""; mSubtypeExtraValue = extraValue != null ? extraValue : ""; - mSubtypeHashCode = hashCodeInternal(mSubtypeLocale, mSubtypeMode, mSubtypeExtraValue); mIsAuxiliary = isAuxiliary; + mSubtypeHashCode = hashCodeInternal(mSubtypeLocale, mSubtypeMode, mSubtypeExtraValue, + mIsAuxiliary); } InputMethodSubtype(Parcel source) { @@ -91,8 +94,9 @@ public final class InputMethodSubtype implements Parcelable { mSubtypeMode = s != null ? s : ""; s = source.readString(); mSubtypeExtraValue = s != null ? s : ""; - mSubtypeHashCode = hashCodeInternal(mSubtypeLocale, mSubtypeMode, mSubtypeExtraValue); mIsAuxiliary = (source.readInt() == 1); + mSubtypeHashCode = hashCodeInternal(mSubtypeLocale, mSubtypeMode, mSubtypeExtraValue, + mIsAuxiliary); } /** @@ -151,16 +155,17 @@ public final class InputMethodSubtype implements Parcelable { */ public CharSequence getDisplayName( Context context, String packageName, ApplicationInfo appInfo) { - final String locale = context.getResources().getConfiguration().locale.getDisplayName(); + final Locale locale = constructLocaleFromString(mSubtypeLocale); + final String localeStr = locale != null ? locale.getDisplayName() : mSubtypeLocale; if (mSubtypeNameResId == 0) { - return locale; + return localeStr; } final String subtypeName = context.getPackageManager().getText( packageName, mSubtypeNameResId, appInfo).toString(); if (!TextUtils.isEmpty(subtypeName)) { - return String.format(subtypeName, locale); + return String.format(subtypeName, localeStr); } else { - return locale; + return localeStr; } } @@ -218,7 +223,8 @@ public final class InputMethodSubtype implements Parcelable { && (subtype.getMode().equals(getMode())) && (subtype.getIconResId() == getIconResId()) && (subtype.getLocale().equals(getLocale())) - && (subtype.getExtraValue().equals(getExtraValue())); + && (subtype.getExtraValue().equals(getExtraValue())) + && (subtype.isAuxiliary() == isAuxiliary()); } return false; } @@ -251,8 +257,25 @@ public final class InputMethodSubtype implements Parcelable { } }; - private static int hashCodeInternal(String locale, String mode, String extraValue) { - return Arrays.hashCode(new Object[] {locale, mode, extraValue}); + private static Locale constructLocaleFromString(String localeStr) { + if (TextUtils.isEmpty(localeStr)) + return null; + String[] localeParams = localeStr.split("_", 3); + // The length of localeStr is guaranteed to always return a 1 <= value <= 3 + // because localeStr is not empty. + if (localeParams.length == 1) { + return new Locale(localeParams[0]); + } else if (localeParams.length == 2) { + return new Locale(localeParams[0], localeParams[1]); + } else if (localeParams.length == 3) { + return new Locale(localeParams[0], localeParams[1], localeParams[2]); + } + return null; + } + + private static int hashCodeInternal(String locale, String mode, String extraValue, + boolean isAuxiliary) { + return Arrays.hashCode(new Object[] {locale, mode, extraValue, isAuxiliary}); } /** diff --git a/core/java/android/webkit/WebView.java b/core/java/android/webkit/WebView.java index a68ca600f823..4aad02256fc9 100644 --- a/core/java/android/webkit/WebView.java +++ b/core/java/android/webkit/WebView.java @@ -6461,7 +6461,7 @@ public class WebView extends AbsoluteLayout if (hscroll != 0 || vscroll != 0) { final int vdelta = (int) (vscroll * getVerticalScrollFactor()); final int hdelta = (int) (hscroll * getHorizontalScrollFactor()); - if (pinScrollBy(hdelta, vdelta, true, 0)) { + if (pinScrollBy(hdelta, vdelta, false, 0)) { return true; } } diff --git a/core/java/android/widget/LinearLayout.java b/core/java/android/widget/LinearLayout.java index 38767351fdc2..0cdbc5b53f1d 100644 --- a/core/java/android/widget/LinearLayout.java +++ b/core/java/android/widget/LinearLayout.java @@ -132,8 +132,8 @@ public class LinearLayout extends ViewGroup { equals = Gravity.CENTER, name = "CENTER"), @ViewDebug.FlagToString(mask = Gravity.FILL, equals = Gravity.FILL, name = "FILL"), - @ViewDebug.FlagToString(mask = Gravity.RELATIVE_HORIZONTAL_DIRECTION, - equals = Gravity.RELATIVE_HORIZONTAL_DIRECTION, name = "RELATIVE") + @ViewDebug.FlagToString(mask = Gravity.RELATIVE_LAYOUT_DIRECTION, + equals = Gravity.RELATIVE_LAYOUT_DIRECTION, name = "RELATIVE") }) private int mGravity = Gravity.START | Gravity.TOP; diff --git a/core/java/com/android/internal/util/Objects.java b/core/java/com/android/internal/util/Objects.java index 598a079c84af..2664182c5ea8 100644 --- a/core/java/com/android/internal/util/Objects.java +++ b/core/java/com/android/internal/util/Objects.java @@ -16,34 +16,47 @@ package com.android.internal.util; +import java.util.Arrays; + /** * Object utility methods. */ public class Objects { /** - * Ensures the given object isn't {@code null}. + * Determines whether two possibly-null objects are equal. Returns: + * + * <ul> + * <li>{@code true} if {@code a} and {@code b} are both null. + * <li>{@code true} if {@code a} and {@code b} are both non-null and they are + * equal according to {@link Object#equals(Object)}. + * <li>{@code false} in all other situations. + * </ul> * - * @return the given object - * @throws NullPointerException if the object is null + * <p>This assumes that any non-null objects passed to this function conform + * to the {@code equals()} contract. */ - public static <T> T nonNull(T t) { - if (t == null) { - throw new NullPointerException(); - } - return t; + public static boolean equal(Object a, Object b) { + return a == b || (a != null && a.equals(b)); } /** - * Ensures the given object isn't {@code null}. + * Generates a hash code for multiple values. The hash code is generated by + * calling {@link Arrays#hashCode(Object[])}. * - * @return the given object - * @throws NullPointerException if the object is null + * <p>This is useful for implementing {@link Object#hashCode()}. For example, + * in an object that has three properties, {@code x}, {@code y}, and + * {@code z}, one could write: + * <pre> + * public int hashCode() { + * return Objects.hashCode(getX(), getY(), getZ()); + * }</pre> + * + * <b>Warning</b>: When a single object is supplied, the returned hash code + * does not equal the hash code of that object. */ - public static <T> T nonNull(T t, String message) { - if (t == null) { - throw new NullPointerException(message); - } - return t; + public static int hashCode(Object... objects) { + return Arrays.hashCode(objects); } + } diff --git a/core/java/com/android/internal/util/Preconditions.java b/core/java/com/android/internal/util/Preconditions.java new file mode 100644 index 000000000000..a53a9c0c6eb6 --- /dev/null +++ b/core/java/com/android/internal/util/Preconditions.java @@ -0,0 +1,57 @@ +/* + * 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.internal.util; + +/** + * Simple static methods to be called at the start of your own methods to verify + * correct arguments and state. + */ +public class Preconditions { + + /** + * Ensures that an object reference passed as a parameter to the calling + * method is not null. + * + * @param reference an object reference + * @return the non-null reference that was validated + * @throws NullPointerException if {@code reference} is null + */ + public static <T> T checkNotNull(T reference) { + if (reference == null) { + throw new NullPointerException(); + } + return reference; + } + + /** + * Ensures that an object reference passed as a parameter to the calling + * method is not null. + * + * @param reference an object reference + * @param errorMessage the exception message to use if the check fails; will + * be converted to a string using {@link String#valueOf(Object)} + * @return the non-null reference that was validated + * @throws NullPointerException if {@code reference} is null + */ + public static <T> T checkNotNull(T reference, Object errorMessage) { + if (reference == null) { + throw new NullPointerException(String.valueOf(errorMessage)); + } + return reference; + } + +} diff --git a/core/jni/android_app_backup_FullBackup.cpp b/core/jni/android_app_backup_FullBackup.cpp index ecfe5ff1a0ca..b36fa3e2f6b2 100644 --- a/core/jni/android_app_backup_FullBackup.cpp +++ b/core/jni/android_app_backup_FullBackup.cpp @@ -73,6 +73,8 @@ static struct { static int backupToTar(JNIEnv* env, jobject clazz, jstring packageNameObj, jstring domainObj, jstring linkdomain, jstring rootpathObj, jstring pathObj, jobject dataOutputObj) { + int ret; + // Extract the various strings, allowing for null object pointers const char* packagenamechars = env->GetStringUTFChars(packageNameObj, NULL); const char* rootchars = env->GetStringUTFChars(rootpathObj, NULL); diff --git a/core/res/res/layout/volume_adjust.xml b/core/res/res/layout/volume_adjust.xml index b0ca3e8e71ff..7303003f00b7 100644 --- a/core/res/res/layout/volume_adjust.xml +++ b/core/res/res/layout/volume_adjust.xml @@ -15,22 +15,21 @@ --> <FrameLayout xmlns:android="http://schemas.android.com/apk/res/android" - android:layout_width="wrap_content" - android:layout_height="wrap_content" - android:gravity="left"> - + android:layout_width="480dp" + android:layout_height="wrap_content"> <LinearLayout - android:layout_width="wrap_content" + android:layout_width="match_parent" android:layout_height="wrap_content" - android:layout_marginTop="80dip" + android:layout_marginTop="80dp" android:background="@android:drawable/dialog_full_holo_dark" android:orientation="horizontal" > <LinearLayout android:id="@+id/slider_group" - android:layout_width="wrap_content" + android:layout_width="0dp" android:layout_height="wrap_content" + android:layout_weight="1" android:orientation="vertical" > <!-- Sliders go here --> @@ -56,9 +55,6 @@ android:background="?attr/selectableItemBackground" android:src="@drawable/ic_sysbar_quicksettings" /> - - </LinearLayout> - -</FrameLayout> - + </LinearLayout> +</FrameLayout>
\ No newline at end of file diff --git a/core/res/res/layout/volume_adjust_item.xml b/core/res/res/layout/volume_adjust_item.xml index e841d878af0c..beb511db72c1 100644 --- a/core/res/res/layout/volume_adjust_item.xml +++ b/core/res/res/layout/volume_adjust_item.xml @@ -15,7 +15,7 @@ --> <LinearLayout xmlns:android="http://schemas.android.com/apk/res/android" - android:layout_width="wrap_content" + android:layout_width="match_parent" android:layout_height="80dip" android:orientation="horizontal" android:layout_marginTop="8dip" @@ -34,8 +34,9 @@ <SeekBar style="?android:attr/seekBarStyle" android:id="@+id/seekbar" - android:layout_width="300dip" + android:layout_width="300dp" android:layout_height="wrap_content" + android:layout_weight="1" android:padding="16dip" android:layout_marginLeft="8dip" android:layout_marginRight="8dip" /> diff --git a/core/res/res/values/attrs.xml b/core/res/res/values/attrs.xml index 9c5562779ece..4a7c6909ba56 100755 --- a/core/res/res/values/attrs.xml +++ b/core/res/res/values/attrs.xml @@ -1191,9 +1191,9 @@ edge, a right gravity will clip the left edge, and neither will clip both edges. --> <flag name="clip_horizontal" value="0x08" /> <!-- Push object to the beginning of its container, not changing its size. --> - <flag name="before" value="0x00800003" /> + <flag name="start" value="0x00800003" /> <!-- Push object to the end of its container, not changing its size. --> - <flag name="after" value="0x00800005" /> + <flag name="end" value="0x00800005" /> </attr> <!-- Standard orientation constant. --> @@ -1932,11 +1932,11 @@ </attr> <!-- Defines the direction of layout drawing. This typically is associated with writing - direction of the language script used. The possible values are Left-to-Right, - Right-to-Left, Locale and Inherit from parent view. If there is nothing to inherit, - Locale is used. Locale fallsback to 'en-US'. Left-to-Right is the direction used in - 'en-US'. The default for this attribute is 'inherit'. --> - <attr name="horizontalDirection"> + direction of the language script used. The possible values are "ltr" for Left-to-Right, + "rtl" for Right-to-Left, "locale" and "inherit" from parent view. If there is nothing + to inherit, "locale" is used. "locale" falls back to "en-US". "ltr" is the direction + used in "en-US". The default for this attribute is "inherit". --> + <attr name="layoutDirection"> <!-- Left-to-Right --> <enum name="ltr" value="0" /> <!-- Right-to-Left --> @@ -5156,6 +5156,8 @@ <!-- number of megabytes of storage MTP should reserve for free storage (used for emulated storage that is shared with system's data partition) --> <attr name="mtpReserve" format="integer" /> + <!-- true if the storage can be shared via USB mass storage --> + <attr name="allowMassStorage" format="boolean" /> </declare-styleable> </resources> diff --git a/core/res/res/values/public.xml b/core/res/res/values/public.xml index 81f888de98c6..55312e32815e 100644 --- a/core/res/res/values/public.xml +++ b/core/res/res/values/public.xml @@ -1657,6 +1657,9 @@ <public type="attr" name="compatibleWidthLimitDp" /> <public type="attr" name="largestWidthLimitDp" /> + <public type="style" name="Theme.Holo.Light.NoActionBar" /> + <public type="style" name="Theme.Holo.Light.NoActionBar.Fullscreen" /> + <!-- =============================================================== Resources added in version 13 of the platform (Ice Cream Sandwich) =============================================================== --> @@ -1666,7 +1669,6 @@ <public type="attr" name="state_drag_hovered" /> <public type="attr" name="stopWithTask" /> - <public type="style" name="Theme.Holo.Light.NoActionBar" /> <public type="style" name="TextAppearance.SuggestionHighlight" /> <public type="style" name="Theme.Holo.SplitActionBarWhenNarrow" /> <public type="style" name="Theme.Holo.Light.SplitActionBarWhenNarrow" /> @@ -1675,7 +1677,8 @@ <public type="attr" name="textEditSuggestionsBottomWindowLayout" /> <public type="attr" name="textEditSuggestionsTopWindowLayout" /> <public type="attr" name="textEditSuggestionItemLayout" /> - <public type="attr" name="horizontalDirection" /> + + <public type="attr" name="layoutDirection" /> <public type="attr" name="fullBackupAgent" /> <public type="attr" name="suggestionsEnabled" /> @@ -1703,4 +1706,5 @@ <public type="attr" name="notificationTimeout" /> <public type="attr" name="accessibilityFlags" /> <public type="attr" name="canRetrieveWindowContent" /> + </resources> diff --git a/core/res/res/values/strings.xml b/core/res/res/values/strings.xml index 29c23a683df2..65a1e44abe5d 100755 --- a/core/res/res/values/strings.xml +++ b/core/res/res/values/strings.xml @@ -1866,7 +1866,7 @@ <!-- Do not translate. WebView User Agent string --> <string name="web_user_agent" translatable="false">Mozilla/5.0 (Linux; U; <xliff:g id="x">Android %s</xliff:g>) - AppleWebKit/534.24 (KHTML, like Gecko) Version/4.0 <xliff:g id="mobile">%s</xliff:g>Safari/534.24</string> + AppleWebKit/534.27 (KHTML, like Gecko) Version/4.0 <xliff:g id="mobile">%s</xliff:g>Safari/534.27</string> <!-- Do not translate. WebView User Agent targeted content --> <string name="web_user_agent_target_content" translatable="false">"Mobile "</string> diff --git a/core/tests/coretests/Android.mk b/core/tests/coretests/Android.mk index b02d9044bd4d..00f47facb576 100644 --- a/core/tests/coretests/Android.mk +++ b/core/tests/coretests/Android.mk @@ -12,7 +12,7 @@ LOCAL_SRC_FILES := \ $(call all-java-files-under, EnabledTestApp/src) LOCAL_DX_FLAGS := --core-library -LOCAL_STATIC_JAVA_LIBRARIES := core-tests android-common frameworks-core-util-lib +LOCAL_STATIC_JAVA_LIBRARIES := core-tests android-common frameworks-core-util-lib mockwebserver LOCAL_JAVA_LIBRARIES := android.test.runner LOCAL_PACKAGE_NAME := FrameworksCoreTests diff --git a/core/tests/coretests/src/android/net/NetworkStatsHistoryTest.java b/core/tests/coretests/src/android/net/NetworkStatsHistoryTest.java index eb63c0d66e66..0b72c3c5b0d4 100644 --- a/core/tests/coretests/src/android/net/NetworkStatsHistoryTest.java +++ b/core/tests/coretests/src/android/net/NetworkStatsHistoryTest.java @@ -16,8 +16,6 @@ package android.net; -import static android.net.ConnectivityManager.TYPE_MOBILE; -import static android.net.NetworkStatsHistory.UID_ALL; 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; @@ -51,7 +49,7 @@ public class NetworkStatsHistoryTest extends TestCase { public void testRecordSingleBucket() throws Exception { final long BUCKET_SIZE = HOUR_IN_MILLIS; - stats = buildStats(BUCKET_SIZE); + stats = new NetworkStatsHistory(BUCKET_SIZE); // record data into narrow window to get single bucket stats.recordData(TEST_START, TEST_START + SECOND_IN_MILLIS, 1024L, 2048L); @@ -62,7 +60,7 @@ public class NetworkStatsHistoryTest extends TestCase { public void testRecordEqualBuckets() throws Exception { final long bucketDuration = HOUR_IN_MILLIS; - stats = buildStats(bucketDuration); + stats = new NetworkStatsHistory(bucketDuration); // split equally across two buckets final long recordStart = TEST_START + (bucketDuration / 2); @@ -75,7 +73,7 @@ public class NetworkStatsHistoryTest extends TestCase { public void testRecordTouchingBuckets() throws Exception { final long BUCKET_SIZE = 15 * MINUTE_IN_MILLIS; - stats = buildStats(BUCKET_SIZE); + stats = new NetworkStatsHistory(BUCKET_SIZE); // split almost completely into middle bucket, but with a few minutes // overlap into neighboring buckets. total record is 20 minutes. @@ -94,7 +92,7 @@ public class NetworkStatsHistoryTest extends TestCase { public void testRecordGapBuckets() throws Exception { final long BUCKET_SIZE = HOUR_IN_MILLIS; - stats = buildStats(BUCKET_SIZE); + stats = new NetworkStatsHistory(BUCKET_SIZE); // record some data today and next week with large gap final long firstStart = TEST_START; @@ -122,7 +120,7 @@ public class NetworkStatsHistoryTest extends TestCase { public void testRecordOverlapBuckets() throws Exception { final long BUCKET_SIZE = HOUR_IN_MILLIS; - stats = buildStats(BUCKET_SIZE); + stats = new NetworkStatsHistory(BUCKET_SIZE); // record some data in one bucket, and another overlapping buckets stats.recordData(TEST_START, TEST_START + SECOND_IN_MILLIS, 256L, 256L); @@ -135,9 +133,80 @@ public class NetworkStatsHistoryTest extends TestCase { assertBucket(stats, 1, 512L, 512L); } + public void testRecordEntireGapIdentical() throws Exception { + final long[] total = new long[2]; + + // first, create two separate histories far apart + final NetworkStatsHistory stats1 = new NetworkStatsHistory(HOUR_IN_MILLIS); + stats1.recordData(TEST_START, TEST_START + 2 * HOUR_IN_MILLIS, 2000L, 1000L); + + final long TEST_START_2 = TEST_START + DAY_IN_MILLIS; + final NetworkStatsHistory stats2 = new NetworkStatsHistory(HOUR_IN_MILLIS); + stats2.recordData(TEST_START_2, TEST_START_2 + 2 * HOUR_IN_MILLIS, 1000L, 500L); + + // combine together with identical bucket size + stats = new NetworkStatsHistory(HOUR_IN_MILLIS); + stats.recordEntireHistory(stats1); + stats.recordEntireHistory(stats2); + + // first verify that totals match up + stats.getTotalData(TEST_START - WEEK_IN_MILLIS, TEST_START + WEEK_IN_MILLIS, total); + assertTotalEquals(total, 3000L, 1500L); + + // now inspect internal buckets + assertBucket(stats, 0, 1000L, 500L); + assertBucket(stats, 1, 1000L, 500L); + assertBucket(stats, 2, 500L, 250L); + assertBucket(stats, 3, 500L, 250L); + } + + public void testRecordEntireOverlapVaryingBuckets() throws Exception { + final long[] total = new long[2]; + + // create history just over hour bucket boundary + final NetworkStatsHistory stats1 = new NetworkStatsHistory(HOUR_IN_MILLIS); + stats1.recordData(TEST_START, TEST_START + MINUTE_IN_MILLIS * 60, 600L, 600L); + + final long TEST_START_2 = TEST_START + MINUTE_IN_MILLIS; + final NetworkStatsHistory stats2 = new NetworkStatsHistory(MINUTE_IN_MILLIS); + stats2.recordData(TEST_START_2, TEST_START_2 + MINUTE_IN_MILLIS * 5, 50L, 50L); + + // combine together with minute bucket size + stats = new NetworkStatsHistory(MINUTE_IN_MILLIS); + stats.recordEntireHistory(stats1); + stats.recordEntireHistory(stats2); + + // first verify that totals match up + stats.getTotalData(TEST_START - WEEK_IN_MILLIS, TEST_START + WEEK_IN_MILLIS, total); + assertTotalEquals(total, 650L, 650L); + + // now inspect internal buckets + assertBucket(stats, 0, 10L, 10L); + assertBucket(stats, 1, 20L, 20L); + assertBucket(stats, 2, 20L, 20L); + assertBucket(stats, 3, 20L, 20L); + assertBucket(stats, 4, 20L, 20L); + assertBucket(stats, 5, 20L, 20L); + assertBucket(stats, 6, 10L, 10L); + + // now combine using 15min buckets + stats = new NetworkStatsHistory(HOUR_IN_MILLIS / 4); + stats.recordEntireHistory(stats1); + stats.recordEntireHistory(stats2); + + // first verify that totals match up + stats.getTotalData(TEST_START - WEEK_IN_MILLIS, TEST_START + WEEK_IN_MILLIS, total); + assertTotalEquals(total, 650L, 650L); + + // and inspect buckets + assertBucket(stats, 0, 200L, 200L); + assertBucket(stats, 1, 150L, 150L); + assertBucket(stats, 2, 150L, 150L); + assertBucket(stats, 3, 150L, 150L); + } + public void testRemove() throws Exception { - final long BUCKET_SIZE = HOUR_IN_MILLIS; - stats = buildStats(BUCKET_SIZE); + stats = new NetworkStatsHistory(HOUR_IN_MILLIS); // record some data across 24 buckets stats.recordData(TEST_START, TEST_START + DAY_IN_MILLIS, 24L, 24L); @@ -165,13 +234,44 @@ public class NetworkStatsHistoryTest extends TestCase { assertEquals(0, stats.bucketCount); } + public void testTotalData() throws Exception { + final long BUCKET_SIZE = HOUR_IN_MILLIS; + stats = new NetworkStatsHistory(BUCKET_SIZE); + + // record uniform data across day + stats.recordData(TEST_START, TEST_START + DAY_IN_MILLIS, 2400L, 4800L); + + final long[] total = new long[2]; + + // verify that total outside range is 0 + stats.getTotalData(TEST_START - WEEK_IN_MILLIS, TEST_START - DAY_IN_MILLIS, total); + assertTotalEquals(total, 0, 0); + + // verify total in first hour + stats.getTotalData(TEST_START, TEST_START + HOUR_IN_MILLIS, total); + assertTotalEquals(total, 100, 200); + + // verify total across 1.5 hours + stats.getTotalData(TEST_START, TEST_START + (long) (1.5 * HOUR_IN_MILLIS), total); + assertTotalEquals(total, 150, 300); + + // verify total beyond end + stats.getTotalData(TEST_START + (23 * HOUR_IN_MILLIS), TEST_START + WEEK_IN_MILLIS, total); + assertTotalEquals(total, 100, 200); + + // verify everything total + stats.getTotalData(TEST_START - WEEK_IN_MILLIS, TEST_START + WEEK_IN_MILLIS, total); + assertTotalEquals(total, 2400, 4800); + + } + @Suppress public void testFuzzing() throws Exception { try { // fuzzing with random events, looking for crashes final Random r = new Random(); for (int i = 0; i < 500; i++) { - stats = buildStats(r.nextLong()); + stats = new NetworkStatsHistory(r.nextLong()); for (int j = 0; j < 10000; j++) { if (r.nextBoolean()) { // add range @@ -191,10 +291,6 @@ public class NetworkStatsHistoryTest extends TestCase { } } - private static NetworkStatsHistory buildStats(long bucketSize) { - return new NetworkStatsHistory(TYPE_MOBILE, null, UID_ALL, bucketSize); - } - private static void assertConsistent(NetworkStatsHistory stats) { // verify timestamps are monotonic for (int i = 1; i < stats.bucketCount; i++) { @@ -202,6 +298,11 @@ public class NetworkStatsHistoryTest extends TestCase { } } + private static void assertTotalEquals(long[] total, long rx, long tx) { + assertEquals("unexpected rx", rx, total[0]); + assertEquals("unexpected tx", tx, total[1]); + } + private static void assertBucket(NetworkStatsHistory stats, int index, long rx, long tx) { assertEquals("unexpected rx", rx, stats.rx[index]); assertEquals("unexpected tx", tx, stats.tx[index]); diff --git a/core/tests/coretests/src/android/net/http/AbstractProxyTest.java b/core/tests/coretests/src/android/net/http/AbstractProxyTest.java index ee8941439d6e..ee4ce95202fd 100644 --- a/core/tests/coretests/src/android/net/http/AbstractProxyTest.java +++ b/core/tests/coretests/src/android/net/http/AbstractProxyTest.java @@ -16,6 +16,10 @@ package android.net.http; +import com.google.mockwebserver.MockResponse; +import com.google.mockwebserver.MockWebServer; +import com.google.mockwebserver.RecordedRequest; +import com.google.mockwebserver.SocketPolicy; import java.io.IOException; import java.io.InputStreamReader; import java.io.Reader; @@ -33,10 +37,6 @@ import org.apache.http.conn.params.ConnRouteParams; import org.apache.http.conn.scheme.Scheme; import org.apache.http.conn.ssl.AllowAllHostnameVerifier; import org.apache.http.conn.ssl.SSLSocketFactory; -import tests.http.MockResponse; -import tests.http.MockWebServer; -import tests.http.RecordedRequest; -import tests.http.SocketPolicy; public abstract class AbstractProxyTest extends TestCase { diff --git a/core/tests/coretests/src/android/net/http/CookiesTest.java b/core/tests/coretests/src/android/net/http/CookiesTest.java index e736bc9f6e93..29e590f74612 100644 --- a/core/tests/coretests/src/android/net/http/CookiesTest.java +++ b/core/tests/coretests/src/android/net/http/CookiesTest.java @@ -16,6 +16,9 @@ package android.net.http; +import com.google.mockwebserver.MockResponse; +import com.google.mockwebserver.MockWebServer; +import com.google.mockwebserver.RecordedRequest; import java.io.ByteArrayOutputStream; import java.io.IOException; import java.net.URISyntaxException; @@ -30,9 +33,6 @@ import org.apache.http.client.HttpClient; import org.apache.http.client.methods.HttpGet; import org.apache.http.conn.params.ConnRoutePNames; import org.apache.http.impl.client.DefaultHttpClient; -import tests.http.MockResponse; -import tests.http.MockWebServer; -import tests.http.RecordedRequest; public final class CookiesTest extends TestCase { diff --git a/core/tests/coretests/src/android/net/http/DefaultHttpClientTest.java b/core/tests/coretests/src/android/net/http/DefaultHttpClientTest.java index ad3ec3def37d..da772984849c 100644 --- a/core/tests/coretests/src/android/net/http/DefaultHttpClientTest.java +++ b/core/tests/coretests/src/android/net/http/DefaultHttpClientTest.java @@ -16,6 +16,12 @@ package android.net.http; +import com.google.mockwebserver.MockResponse; +import com.google.mockwebserver.MockWebServer; +import com.google.mockwebserver.SocketPolicy; +import static com.google.mockwebserver.SocketPolicy.DISCONNECT_AT_END; +import static com.google.mockwebserver.SocketPolicy.SHUTDOWN_INPUT_AT_END; +import static com.google.mockwebserver.SocketPolicy.SHUTDOWN_OUTPUT_AT_END; import java.io.IOException; import java.io.InputStreamReader; import java.io.Reader; @@ -24,12 +30,6 @@ import junit.framework.TestCase; import org.apache.http.HttpResponse; import org.apache.http.client.methods.HttpGet; import org.apache.http.impl.client.DefaultHttpClient; -import tests.http.MockResponse; -import tests.http.MockWebServer; -import tests.http.SocketPolicy; -import static tests.http.SocketPolicy.DISCONNECT_AT_END; -import static tests.http.SocketPolicy.SHUTDOWN_INPUT_AT_END; -import static tests.http.SocketPolicy.SHUTDOWN_OUTPUT_AT_END; public final class DefaultHttpClientTest extends TestCase { diff --git a/docs/html/guide/topics/graphics/renderscript.jd b/docs/html/guide/topics/graphics/renderscript.jd index 60bffdb1a49a..180322f55435 100644 --- a/docs/html/guide/topics/graphics/renderscript.jd +++ b/docs/html/guide/topics/graphics/renderscript.jd @@ -630,7 +630,7 @@ int root(int launchID) { <dt><code>ScriptC_helloworld</code></dt> <dd>This class is generated by the Android build tools and is the reflected version of the - <code>helloworld.rs</code> Renderscript. It provides a a high level entry point into the + <code>helloworld.rs</code> Renderscript. It provides a high level entry point into the <code>helloworld.rs</code> native code by defining the corresponding methods that you can call from the traditional framework APIs.</dd> diff --git a/include/ui/Input.h b/include/ui/Input.h index 3b5aba4df9a7..c9f694a8f0e9 100644 --- a/include/ui/Input.h +++ b/include/ui/Input.h @@ -655,11 +655,6 @@ private: // Oldest sample to consider when calculating the velocity. static const nsecs_t MAX_AGE = 200 * 1000000; // 200 ms - // When the total duration of the window of samples being averaged is less - // than the window size, the resulting velocity is scaled to reduce the impact - // of overestimation in short traces. - static const nsecs_t MIN_WINDOW = 100 * 1000000; // 100 ms - // The minimum duration between samples when estimating velocity. static const nsecs_t MIN_DURATION = 10 * 1000000; // 10 ms diff --git a/keystore/java/android/security/Credentials.java b/keystore/java/android/security/Credentials.java index 6b69b8a3f581..fd6c22c2b945 100644 --- a/keystore/java/android/security/Credentials.java +++ b/keystore/java/android/security/Credentials.java @@ -20,8 +20,19 @@ import android.content.ActivityNotFoundException; import android.content.Context; import android.content.Intent; import android.util.Log; - +import com.android.org.bouncycastle.openssl.PEMReader; +import com.android.org.bouncycastle.openssl.PEMWriter; +import java.io.ByteArrayInputStream; +import java.io.ByteArrayOutputStream; +import java.io.IOException; +import java.io.InputStreamReader; +import java.io.OutputStreamWriter; +import java.io.Reader; +import java.io.Writer; +import java.nio.charset.Charsets; import java.security.KeyPair; +import java.util.ArrayList; +import java.util.List; /** * {@hide} @@ -60,6 +71,40 @@ public class Credentials { /** Data type for PKCS12. */ public static final String PKCS12 = "PKCS12"; + /** + * Convert objects to a PEM format, which is used for + * CA_CERTIFICATE, USER_CERTIFICATE, and USER_PRIVATE_KEY + * entries. + */ + public static byte[] convertToPem(Object... objects) throws IOException { + ByteArrayOutputStream bao = new ByteArrayOutputStream(); + Writer writer = new OutputStreamWriter(bao, Charsets.US_ASCII); + PEMWriter pw = new PEMWriter(writer); + for (Object o : objects) { + pw.writeObject(o); + } + pw.close(); + return bao.toByteArray(); + } + /** + * Convert objects from PEM format, which is used for + * CA_CERTIFICATE, USER_CERTIFICATE, and USER_PRIVATE_KEY + * entries. + */ + public static List<Object> convertFromPem(byte[] bytes) throws IOException { + ByteArrayInputStream bai = new ByteArrayInputStream(bytes); + Reader reader = new InputStreamReader(bai, Charsets.US_ASCII); + PEMReader pr = new PEMReader(reader); + + List<Object> result = new ArrayList<Object>(); + Object o; + while ((o = pr.readObject()) != null) { + result.add(o); + } + pr.close(); + return result; + } + private static Credentials singleton; public static Credentials getInstance() { diff --git a/keystore/java/android/security/KeyChain.java b/keystore/java/android/security/KeyChain.java index ec820cf77de9..ba784edeed8e 100644 --- a/keystore/java/android/security/KeyChain.java +++ b/keystore/java/android/security/KeyChain.java @@ -34,6 +34,7 @@ import java.io.ByteArrayInputStream; import java.io.Closeable; import java.io.IOException; import java.security.KeyFactory; +import java.security.KeyPair; import java.security.NoSuchAlgorithmException; import java.security.PrivateKey; import java.security.cert.Certificate; @@ -185,11 +186,9 @@ public final class KeyChain { throw new IllegalArgumentException("bytes == null"); } try { - KeyFactory keyFactory = KeyFactory.getInstance("RSA"); - return keyFactory.generatePrivate(new PKCS8EncodedKeySpec(bytes)); - } catch (NoSuchAlgorithmException e) { - throw new AssertionError(e); - } catch (InvalidKeySpecException e) { + KeyPair keyPair = (KeyPair) Credentials.convertFromPem(bytes).get(0); + return keyPair.getPrivate(); + } catch (IOException e) { throw new AssertionError(e); } } diff --git a/libs/ui/Input.cpp b/libs/ui/Input.cpp index e95dbe4745d7..0af7f8043c35 100644 --- a/libs/ui/Input.cpp +++ b/libs/ui/Input.cpp @@ -700,7 +700,6 @@ bool MotionEvent::isTouchEvent(int32_t source, int32_t action) { const uint32_t VelocityTracker::HISTORY_SIZE; const nsecs_t VelocityTracker::MAX_AGE; -const nsecs_t VelocityTracker::MIN_WINDOW; const nsecs_t VelocityTracker::MIN_DURATION; VelocityTracker::VelocityTracker() { @@ -891,14 +890,6 @@ bool VelocityTracker::getVelocity(uint32_t id, float* outVx, float* outVy) const // Make sure we used at least one sample. if (samplesUsed != 0) { - // Scale the velocity linearly if the window of samples is small. - nsecs_t totalDuration = newestMovement.eventTime - oldestMovement.eventTime; - if (totalDuration < MIN_WINDOW) { - float scale = float(totalDuration) / float(MIN_WINDOW); - accumVx *= scale; - accumVy *= scale; - } - *outVx = accumVx; *outVy = accumVy; return true; diff --git a/media/tests/MediaFrameworkTest/src/com/android/mediaframeworktest/MediaFrameworkTest.java b/media/tests/MediaFrameworkTest/src/com/android/mediaframeworktest/MediaFrameworkTest.java index 79fd2cb741e0..e7f98de19d77 100644 --- a/media/tests/MediaFrameworkTest/src/com/android/mediaframeworktest/MediaFrameworkTest.java +++ b/media/tests/MediaFrameworkTest/src/com/android/mediaframeworktest/MediaFrameworkTest.java @@ -123,63 +123,6 @@ public class MediaFrameworkTest extends Activity implements SurfaceHolder.Callba intent.setDataAndType(path, mimetype); startActivity(intent); } - - @Override public boolean onKeyDown(int keyCode, KeyEvent event) { - switch (keyCode) { - case KeyEvent.KEYCODE_0: - MediaPlayer mp = new MediaPlayer(); - try{ - mp.setDataSource(MediaNames.VIDEO_RTSP3GP); - Log.v("emily","awb " + testfilepath); - mp.setDisplay(mSurfaceView.getHolder()); - mp.prepare(); - mp.start(); - }catch (Exception e){} - break; - - //start the music player intent with the test URL from PV - case KeyEvent.KEYCODE_1: - startPlayback(MediaNames.STREAM_MP3_1); - break; - - case KeyEvent.KEYCODE_2: - startPlayback(MediaNames.STREAM_MP3_2); - break; - - case KeyEvent.KEYCODE_3: - startPlayback(MediaNames.STREAM_MP3_3); - break; - - case KeyEvent.KEYCODE_4: - startPlayback(MediaNames.STREAM_MP3_4); - break; - - case KeyEvent.KEYCODE_5: - startPlayback(MediaNames.STREAM_MP3_5); - break; - - case KeyEvent.KEYCODE_6: - startPlayback(MediaNames.STREAM_MP3_6); - break; - - case KeyEvent.KEYCODE_7: - startPlayback(MediaNames.STREAM_MP3_7); - break; - - case KeyEvent.KEYCODE_8: - startPlayback(MediaNames.STREAM_MP3_8); - break; - - case KeyEvent.KEYCODE_9: - startPlayback(MediaNames.STREAM_MP3_9); - break; - - - - } - return super.onKeyDown(keyCode, event); - - } public static boolean checkStreamingServer() throws Exception { InetAddress address = InetAddress.getByAddress(MediaNames.STREAM_SERVER); diff --git a/media/tests/MediaFrameworkTest/src/com/android/mediaframeworktest/MediaNames.java b/media/tests/MediaFrameworkTest/src/com/android/mediaframeworktest/MediaNames.java index ca6e99927042..d6e134679bfe 100755 --- a/media/tests/MediaFrameworkTest/src/com/android/mediaframeworktest/MediaNames.java +++ b/media/tests/MediaFrameworkTest/src/com/android/mediaframeworktest/MediaNames.java @@ -50,19 +50,9 @@ public class MediaNames { public static final long PAUSE_WAIT_TIME = 3000; public static final long WAIT_TIME = 2000; public static final long WAIT_SNAPSHOT_TIME = 5000; - - //Streaming Video - public static final String VIDEO_HTTP3GP = "http://pvs.pv.com/jj/lipsync0.3gp"; - public static final String VIDEO_RTSP3GP = "rtsp://63.241.31.203/public/jj/md.3gp"; - public static final String VIDEO_RTSP3GP2 = "rtsp://pvs.pv.com/public/live_dvd1.3gp"; - public static final String VIDEO_RTSP3GP3 = - "rtsp://ehug.rtsp-youtube.l.google.com/" + - "Ci4LENy73wIaJQmeRVCJq4HuQBMYDSANFEIJbXYtZ29vZ2xlSARSB2RldGFpbHMM/0/0/0/video.3gp"; - //public static final String VIDEO_RTSP3GP = "rtsp://193.159.241.21/sp/alizee05.3gp"; - + //local video public static final String VIDEO_MP4 = "/sdcard/media_api/video/MPEG4_320_AAC_64.mp4"; - public static final String VIDEO_LONG_3GP = "/sdcard/media_api/video/radiohead.3gp"; public static final String VIDEO_SHORT_3GP = "/sdcard/media_api/video/short.3gp"; public static final String VIDEO_LARGE_SIZE_3GP = "/sdcard/media_api/video/border_large.3gp"; public static final String VIDEO_H263_AAC = "/sdcard/media_api/video/H263_56_AAC_24.3gp"; @@ -73,205 +63,9 @@ public class MediaNames { public static final String VIDEO_HIGHRES_H263 = "/sdcard/media_api/video/H263_500_AMRNB_12.3gp"; public static final String VIDEO_HIGHRES_MP4 = "/sdcard/media_api/video/H264_500_AAC_128.3gp"; - //ringtone - public static final String ringtone = "/sdcard/media_api/ringtones/F1_NewVoicemail.mp3"; - - //streaming mp3 - public static final String STREAM_MP3_1 = - "http://wms.pv.com:7070/MediaDownloadContent/mp3/chadthi_jawani_128kbps.mp3"; - public static final String STREAM_MP3_2 = - "http://wms.pv.com:7070/MediaDownloadContent/mp3/dualStereo.mp3"; - public static final String STREAM_MP3_3 = - "http://wms.pv.com:7070/mediadownloadcontent/UserUploads/15%20Keep%20Holding%20On.mp3"; - public static final String STREAM_MP3_4 = - "http://wms.pv.com:7070/mediadownloadcontent/UserUploads/1%20-%20Apologize.mp3"; - public static final String STREAM_MP3_5 = - "http://wms.pv.com:7070/mediadownloadcontent/UserUploads/" + - "03%20You're%20Gonna%20Miss%20This.mp3"; - public static final String STREAM_MP3_6 = - "http://wms.pv.com:7070/mediadownloadcontent/UserUploads" + - "/02%20Looney%20Tunes%20%C3%82%C2%B7%20Light%20Cavalry%20Overture%20(LP%20Version).mp3"; - public static final String STREAM_MP3_7 = - "http://wms.pv.com:7070/mediadownloadcontent/UserUploads" + - "/01%20Love%20Song%20(Album%20Version).mp3"; - public static final String STREAM_MP3_8 = - "http://wms.pv.com:7070/MediaDownloadContent/UserUploads/1%20-%20Apologize.mp3"; - public static final String STREAM_MP3_9 = - "http://wms.pv.com:7070/MediaDownloadContent/UserUploads" + - "/1%20-%20Smile%20(Explicit%20Version).mp3"; - public static final String STREAM_MP3_10 = - "http://wms.pv.com:7070/MediaDownloadContent/UserUploads/beefcake.mp3"; - - //Sonivox - public static String MIDIFILES[] = { - "/sdcard/media_api/music/Leadsol.mxmf", - "/sdcard/media_api/music/abba.imy", "/sdcard/media_api/music/ants.mid", - "/sdcard/media_api/music/greensleeves.rtttl", "/sdcard/media_api/music/test.ota"}; - - //Performance measurement - public static String[] WAVFILES = { - "/sdcard/media_api/music_perf/WAV/M1F1-AlawWE-AFsp.wav", - "/sdcard/media_api/music_perf/WAV/M1F1-float64-AFsp.wav", - "/sdcard/media_api/music_perf/WAV/song.wav", - "/sdcard/media_api/music_perf/WAV/WAVEtest.wav", - "/sdcard/media_api/music_perf/WAV/WAVEtest_out.wav", - "/sdcard/media_api/music_perf/WAV/test_out.wav"}; - - public static String[] AMRNBFILES = { - "/sdcard/media_api/music_perf/AMR/AI_AMR-NB_5.9kbps_6.24kbps_8khz_mono_NMC.amr", - "/sdcard/media_api/music_perf/AMR/AI_AMR-NB_5.15kbps_5.46kbps_8khz_mono_NMC.amr", - "/sdcard/media_api/music_perf/AMR/AI_AMR-NB_7.4kbps_7.80kbps_8khz_mono_NMC.amr", - "/sdcard/media_api/music_perf/AMR/AI_AMR-NB_7.95kbps_9.6kbps_8khz_mono_NMC.amr", - "/sdcard/media_api/music_perf/AMR/AI_AMR-NB_10.2kbps_10.48kbps_8khz_mono_NMC.amr"}; - - public static String[] AMRWBFILES = { - "/sdcard/media_api/music_perf/AMRWB/NIN_AMR-WB_15.85kbps_16kbps.amr", - "/sdcard/media_api/music_perf/AMRWB/NIN_AMR-WB_18.25kbps_18kbps.amr", - "/sdcard/media_api/music_perf/AMRWB/NIN_AMR-WB_19.85kbps_20kbps.amr", - "/sdcard/media_api/music_perf/AMRWB/NIN_AMR-WB_23.05kbps_23kbps.amr", - "/sdcard/media_api/music_perf/AMRWB/NIN_AMR-WB_23.85kbps_24kbps.amr", - "/sdcard/media_api/music_perf/AMRWB/PD_AMR-WB_19.85kbps_20kbps.amr", - "/sdcard/media_api/music_perf/AMRWB/PD_AMR-WB_23.05kbps_23kbps.amr", - "/sdcard/media_api/music_perf/AMRWB/PD_AMR-WB_23.85kbps_24kbps.amr", - "/sdcard/media_api/music_perf/AMRWB/WC_AMR-WB_23.05kbps_23kbps.amr", - "/sdcard/media_api/music_perf/AMRWB/WC_AMR-WB_23.85kbps_24kbps.amr", }; - - public static String[] MP3FILES = { - "/sdcard/media_api/music_perf/MP3/NIN_56kbps_32khz_stereo_VBR_MCA.MP3", - "/sdcard/media_api/music_perf/MP3/NIN_80kbps_32khz_stereo_VBR_MCA.mp3", - "/sdcard/media_api/music_perf/MP3/NIN_80kbps_44.1khz_stereo_VBR_MCA.mp3", - "/sdcard/media_api/music_perf/MP3/NIN_80kbps_48khz_stereo_VBR_MCA.mp3", - "/sdcard/media_api/music_perf/MP3/NIN_112kbps_32khz_stereo_VBR_MCA.mp3", - "/sdcard/media_api/music_perf/MP3/NIN_112kbps_44.1khz_stereo_VBR_MCA.mp3", - "/sdcard/media_api/music_perf/MP3/NIN_112kbps_48khz_stereo_VBR_MCA.mp3", - "/sdcard/media_api/music_perf/MP3/NIN_192kbps_32khz_mono_CBR_MCA.mp3", - "/sdcard/media_api/music_perf/MP3/NIN_192kbps_44.1khz_mono_CBR_MCA.mp3", - "/sdcard/media_api/music_perf/MP3/NIN_192kbps_48khz_mono_CBR_MCA.mp3", - "/sdcard/media_api/music_perf/MP3/NIN_256kbps_44.1khz_mono_CBR_MCA.mp3", - "/sdcard/media_api/music_perf/MP3/NIN_256kbps_48khz_mono_CBR_MCA.mp3", - "/sdcard/media_api/music_perf/MP3/PD_112kbps_32khz_stereo_VBR_MCA.mp3", - "/sdcard/media_api/music_perf/MP3/PD_112kbps_44.1khz_stereo_VBR_MCA.mp3", - "/sdcard/media_api/music_perf/MP3/PD_112kbps_48khz_stereo_VBR_MCA.mp3", - "/sdcard/media_api/music_perf/MP3/PD_192kbps_32khz_mono_CBR_DPA.mp3", - "/sdcard/media_api/music_perf/MP3/PD_256kbps_44.1khz_mono_CBR_DPA.mp3", - "/sdcard/media_api/music_perf/MP3/PD_256kbps_48khz_mono_CBR_MCA.mp3", - "/sdcard/media_api/music_perf/MP3/WC_256kbps_44.1khz_mono_CBR_DPA.mp3", - "/sdcard/media_api/music_perf/MP3/WC_256kbps_48khz_mono_CBR_DPA.mp3", - "/sdcard/media_api/music_perf/regular_album_photo/Apologize.mp3", - "/sdcard/media_api/music_perf/regular_album_photo/Because_Of_You.mp3", - "/sdcard/media_api/music_perf/regular_album_photo/Complicated.mp3", - "/sdcard/media_api/music_perf/regular_album_photo/Glamorous.mp3", - "/sdcard/media_api/music_perf/regular_album_photo/Im_With_You.mp3", - "/sdcard/media_api/music_perf/regular_album_photo/Smile.mp3", - "/sdcard/media_api/music_perf/regular_album_photo/Suddenly_I_See.mp3", - "/sdcard/media_api/music_perf/regular_album_photo/When You Say Nothing At All.mp3", - "/sdcard/media_api/music_perf/regular_album_photo/my_happy_ending.mp3"}; - - public static String[] AACFILES = { - "/sdcard/media_api/music_perf/AAC/AI_AAC_24kbps_12khz_Mono_1pCBR_SSE.mp4", - "/sdcard/media_api/music_perf/AAC/AI_AAC_56kbps_22.05khz_Stereo_1pCBR_SSE.mp4", - "/sdcard/media_api/music_perf/AAC/AI_AAC_56kbps_32khz_Stereo_CBR_SSE.mp4", - "/sdcard/media_api/music_perf/AAC/AI_AAC_56kbps_44.1khz_Stereo_1pCBR_SSE.mp4", - "/sdcard/media_api/music_perf/AAC/AI_AAC_80kbps_32khz_Stereo_1pCBR_SSE.mp4", - "/sdcard/media_api/music_perf/AAC/AI_AAC_80kbps_32khz_Stereo_CBR_SSE.mp4", - "/sdcard/media_api/music_perf/AAC/NIN_AAC_56kbps_22.05khz_Stereo_1pCBR_SSE.mp4", - "/sdcard/media_api/music_perf/AAC/NIN_AAC_56kbps_32khz_Stereo_CBR_SSE.mp4", - "/sdcard/media_api/music_perf/AAC/NIN_AAC_56kbps_44.1khz_Stereo_1pCBR_SSE.mp4", - "/sdcard/media_api/music_perf/AAC/NIN_AAC_80kbps_32khz_Stereo_1pCBR_SSE.mp4", - "/sdcard/media_api/music_perf/AAC/NIN_AAC_80kbps_32khz_Stereo_CBR_SSE.mp4", - "/sdcard/media_api/music_perf/AAC/PD_AAC_56kbps_22.05khz_Stereo_1pCBR_SSE.mp4", - "/sdcard/media_api/music_perf/AAC/PD_AAC_56kbps_32khz_Stereo_CBR_SSE.mp4", - "/sdcard/media_api/music_perf/AAC/PD_AAC_56kbps_44.1khz_Stereo_1pCBR_SSE.mp4", - "/sdcard/media_api/music_perf/AAC/PD_AAC_80kbps_32khz_Stereo_1pCBR_SSE.mp4", - "/sdcard/media_api/music_perf/AAC/PD_AAC_80kbps_32khz_Stereo_CBR_SSE.mp4", - "/sdcard/media_api/music_perf/AAC/PV_AAC_56kbps_22.05khz_Stereo_1pCBR_SSE.mp4", - "/sdcard/media_api/music_perf/AAC/PV_AAC_56kbps_32khz_Stereo_CBR_SSE.mp4", - "/sdcard/media_api/music_perf/AAC/PV_AAC_56kbps_44.1khz_Stereo_1pCBR_SSE.mp4", - "/sdcard/media_api/music_perf/AAC/PV_AAC_80kbps_32khz_Stereo_1pCBR_SSE.mp4", - "/sdcard/media_api/music_perf/AAC/PV_AAC_80kbps_32khz_Stereo_CBR_SSE.mp4", - "/sdcard/media_api/music_perf/AAC/WC_AAC_56kbps_22.05khz_Stereo_1pCBR_SSE.mp4", - "/sdcard/media_api/music_perf/AAC/WC_AAC_56kbps_32khz_Stereo_CBR_SSE.mp4", - "/sdcard/media_api/music_perf/AAC/WC_AAC_56kbps_44.1khz_Stereo_1pCBR_SSE.mp4", - "/sdcard/media_api/music_perf/AAC/WC_AAC_80kbps_32khz_Stereo_1pCBR_SSE.mp4", - "/sdcard/media_api/music_perf/AAC/WC_AAC_80kbps_32khz_Stereo_CBR_SSE.mp4", - }; - - public static String[] VIDEOFILES = { "/sdcard/media_api/video_perf/AI_CTO_Mpeg4_32kbps_10fps_SQCIF_128x96+AAC_8kbps_8khz_mono_QTE.mp4", - "/sdcard/media_api/video_perf/AI_CTO_Mpeg4_32kbps_12fps_SQCIF_128x96+AAC_8kbps_8khz_mono_QTE.mp4", - "/sdcard/media_api/video_perf/AI_CTO_Mpeg4_32kbps_15fps_SQCIF_128x96+AAC_8kbps_8khz_mono_QTE.mp4", - "/sdcard/media_api/video_perf/AI_CTO_Mpeg4_32kbps_5fps_SQCIF_128x96+AAC_8kbps_8khz_mono_QTE.mp4", - "/sdcard/media_api/video_perf/AI_CTO_Mpeg4_32kbps_5fps_SQCIF_128x96+AAC_8kbps_8khz_mono_SSE.mp4", - "/sdcard/media_api/video_perf/AI_CTO_Mpeg4_32kbps_7.5fps_SQCIF_128x96+AAC_8kbps_8khz_mono_QTE.mp4", - "/sdcard/media_api/video_perf/AI_WMV_1024kbps_20fps_QCIF_176x144_noaudio_SSE.wmv", - "/sdcard/media_api/video_perf/AI_WMV_1024kbps_25fps_QCIF_176x144_noaudio_SSE.wmv", - "/sdcard/media_api/video_perf/Chicken.wmv", - "/sdcard/media_api/video_perf/MP_qcif_15fps_100kbps_48kHz_192kbps_30secs.wmv", - "/sdcard/media_api/video_perf/NIN_CTO_H264_123kbps_5fps_QCIF_176x144+AMR_12.2kbps_8khz_mono_QTE.3gp", - "/sdcard/media_api/video_perf/NIN_CTO_H264_96kbps_10.2fps_QCIF_176x144+AMR_12.2kbps_8khz_mono_QTE.3gp", - "/sdcard/media_api/video_perf/NIN_CTO_H264_96kbps_12fps_QCIF_176x144+AMR_12.2kbps_8khz_mono_QTE.3gp", - "/sdcard/media_api/video_perf/NIN_CTO_H264_96kbps_15fps_QCIF_176x144+AMR_12.2kbps_8khz_mono_QTE.3gp", - "/sdcard/media_api/video_perf/NIN_CTO_Mpeg4_123kbps_15fps_QCIF_176x144+AAC_32kbps_22khz_mono_SSE.3gp", - "/sdcard/media_api/video_perf/NIN_CTO_Mpeg4_123kbps_7.5fps_QCIF_176x144+AAC_32kbps_22khz_stereo_SSE.3gp", - "/sdcard/media_api/video_perf/NIN_CTO_Mpeg4_128kbps_10fps_QCIF_176x144+AAC+_32kbps_48khz_stereo_SSE.3gp", - "/sdcard/media_api/video_perf/NIN_CTO_Mpeg4_128kbps_12fps_QCIF_176x144+AAC+_32kbps_48khz_stereo_SSE.3gp", - "/sdcard/media_api/video_perf/NIN_CTO_Mpeg4_128kbps_15fps_QCIF_176x144+AAC+_32kbps_48khz_stereo_SSE.3gp", - "/sdcard/media_api/video_perf/NIN_CTO_Mpeg4_128kbps_5fps_QCIF_176x144+AAC+_32kbps_48khz_stereo_SSE.3gp", - "/sdcard/media_api/video_perf/NIN_CTO_Mpeg4_128kbps_7.5fps_QCIF_176x144+AAC+_32kbps_48khz_stereo_SSE.3gp", - "/sdcard/media_api/video_perf/NIN_H263_128kbps_10fps_QCIF_174x144_noaudio_SSE.mp4", - "/sdcard/media_api/video_perf/NIN_H263_128kbps_15fps_QCIF_174x144_noaudio_SSE.mp4", - "/sdcard/media_api/video_perf/NIN_H263_48kbps_10fps_QCIF_174x144_noaudio_SSE.3gp", - "/sdcard/media_api/video_perf/NIN_H263_48kbps_12fps_QCIF_174x144_noaudio_SSE.3gp", - "/sdcard/media_api/video_perf/NIN_H264_123kbps_15fps_QCIF_176x144+AAC_32kbps_22khz_stereo_SSE.3gp", - "/sdcard/media_api/video_perf/NIN_H264_123kbps_7.5fps_QCIF_176x144+AAC_32kbps_22khz_stereo_SSE.3gp", - "/sdcard/media_api/video_perf/PV_H264_2000kbps_20fps_CIF_352x288+AAC_96kbps_48khz_stereo_SSE.mp4", - "/sdcard/media_api/video_perf/PV_H264_2000kbps_25fps_CIF_352x288+AAC_96kbps_48khz_stereo_SSE.mp4", - "/sdcard/media_api/video_perf/PV_H264_2000kbps_30fps_CIF_352x288+AAC_128kbps_48khz_stereo_SSE.mp4", - "/sdcard/media_api/video_perf/Stevie-1.wmv", - "/sdcard/media_api/video_perf/WC_H264_1600kbps_20fps_QCIF_176x144+AAC_96kbps_48khz_mono_SSE.mp4", - "/sdcard/media_api/video_perf/WC_H264_1600kbps_25fps_QCIF_176x144+AAC_96kbps_48khz_mono_SSE.mp4", - "/sdcard/media_api/video_perf/WC_H264_1600kbps_30fps_QCIF_176x144+AAC_96kbps_48khz_mono_SSE.mp4", - "/sdcard/media_api/video_perf/bugs.wmv", - "/sdcard/media_api/video_perf/niceday.wmv", - "/sdcard/media_api/video_perf/eaglesatopnflpe.wmv", - - }; - - //wma - only support up to wma 9 - public static String[] WMASUPPORTED = { - "/sdcard/media_api/music_perf/WMASUPPORTED/AI_WMA9.2_32kbps_44.1khz_mono_CBR_DPA.wma", - "/sdcard/media_api/music_perf/WMASUPPORTED/AI_WMA9.2_48kbps_44.1khz_mono_CBR_DPA.wma", - "/sdcard/media_api/music_perf/WMASUPPORTED/NIN_WMA9.2_32kbps_44.1khz_mono_CBR_DPA.wma", - "/sdcard/media_api/music_perf/WMASUPPORTED/NIN_WMA9.2_48kbps_44.1khz_mono_CBR_DPA.wma", - "/sdcard/media_api/music_perf/WMASUPPORTED/PD_WMA9.2_32kbps_44.1khz_mono_CBR_DPA.wma", - "/sdcard/media_api/music_perf/WMASUPPORTED/PD_WMA9.2_48kbps_44.1khz_mono_CBR_DPA.wma", - "/sdcard/media_api/music_perf/WMASUPPORTED/PV_WMA9.2_32kbps_44.1khz_mono_CBR_DPA.wma", - "/sdcard/media_api/music_perf/WMASUPPORTED/PV_WMA9.2_48kbps_44.1khz_mono_CBR_DPA.wma", - "/sdcard/media_api/music_perf/WMASUPPORTED/WC_WMA9.2_32kbps_44.1khz_mono_CBR_DPA.wma", - "/sdcard/media_api/music_perf/WMASUPPORTED/WC_WMA9.2_48kbps_44.1khz_mono_CBR_DPA.wma" - - }; - - public static String[] WMAUNSUPPORTED = { - "/sdcard/media_api/music_perf/WMAUNSUPPORTED/AI_WMA10_127kbps_48khz_stereo_CBR_DPA.wma", - "/sdcard/media_api/music_perf/WMAUNSUPPORTED/AI_WMA10_128kbps_44.1khz_stereo_2pVBR_DPA.wma", - "/sdcard/media_api/music_perf/WMAUNSUPPORTED/AI_WMA10_128kbps_48khz_stereo_2pVBR_DPA.wma", - "/sdcard/media_api/music_perf/WMAUNSUPPORTED/AI_WMA10_128kbps_88khz_stereo_CBR_DPA.wma", - "/sdcard/media_api/music_perf/WMAUNSUPPORTED/AI_WMA10_128kbps_96khz_stereo_CBR_DPA.wma", - "/sdcard/media_api/music_perf/WMAUNSUPPORTED/AI_WMA10_192kbps_44.1khz_stereo_2pVBR_DPA.wma", - "/sdcard/media_api/music_perf/WMAUNSUPPORTED/AI_WMA10_192kbps_88khz_stereo_CBR_DPA.wma", - "/sdcard/media_api/music_perf/WMAUNSUPPORTED/AI_WMA10_192kbps_96khz_stereo_CBR_DPA.wma", - "/sdcard/media_api/music_perf/WMAUNSUPPORTED/AI_WMA10_256kbps_44khz_stereo_CBR_DPA.wma", - "/sdcard/media_api/music_perf/WMAUNSUPPORTED/AI_WMA10_256kbps_48khz_stereo_CBR_DPA.wma", - "/sdcard/media_api/music_perf/WMAUNSUPPORTED/AI_WMA10_256kbps_88khz_stereo_CBR_DPA.wma", - "/sdcard/media_api/music_perf/WMAUNSUPPORTED/AI_WMA10_256kbps_96khz_stereo_CBR_DPA.wma", - "/sdcard/media_api/music_perf/WMAUNSUPPORTED/AI_WMA10_384kbps_44khz_stereo_CBR_DPA.wma", - "/sdcard/media_api/music_perf/WMAUNSUPPORTED/AI_WMA10_384kbps_48khz_stereo_CBR_DPA.wma", - "/sdcard/media_api/music_perf/WMAUNSUPPORTED/AI_WMA10_384kbps_88khz_stereo_CBR_DPA.wma" - }; - //Media Recorder public static final String RECORDER_OUTPUT = "/sdcard/media_api/recorderOutput.amr"; - + //video thumbnail public static final String THUMBNAIL_OUTPUT = "/sdcard/media_api/videoThumbnail.png"; public static final String GOLDEN_THUMBNAIL_OUTPUT = "/sdcard/media_api/goldenThumbnail.png"; @@ -318,7 +112,7 @@ public class MediaNames { "/sdcard/media_api/metadata/test34.wmv", "/sdcard/media_api/metadata/test_metadata.mp4", }; - + public static final String[] METADATA_RETRIEVAL_TEST_FILES = { // Raw AAC is not supported // "/sdcard/media_api/test_raw.aac", @@ -405,54 +199,8 @@ public class MediaNames { null, null, null, null, null, null, "295", "1", null} }; - public static final String META_DATA_OTHERS [][] = { - {"/sdcard/media_api/metaDataTestMedias/3GP/cat.3gp", null, null, null, - null, null, "20080309T002415.000Z", null, - null, null, "63916", "2", null}, - {"/sdcard/media_api/metaDataTestMedias/AMR/AMR_NB.amr", null, null, null, - null, null, null, null, - null, null, "126540", "1", null}, - {"/sdcard/media_api/metaDataTestMedias/AMRWB/AMR_WB.amr", null, null, null, - null, null, null, null, - null, null, "231180", "1", null}, - {"/sdcard/media_api/metaDataTestMedias/M4A/Jaws Of Life_ver1.m4a", "1/8", "Suspended Animation", - "John Petrucci", null, null, "20070510T125223.000Z", - "12", "Jaws Of Life", "2005", "449329", "1", "m4a composer"}, - {"/sdcard/media_api/metaDataTestMedias/M4V/sample_iPod.m4v", null, null, - null, null, null, "20051220T202015.000Z", - null, null, null, "85500", "2", null}, - {"/sdcard/media_api/metaDataTestMedias/MIDI/MIDI.mid", null, "Suspended Animation", - "John Petrucci", null, null, "20070510T125223.000Z", - null, null, "2005", "231180", "1", null}, - {"/sdcard/media_api/metaDataTestMedias/MP4/kung_fu_panda_h264.mp4", "2/0", "mp4 album Kung Fu Panda", - "mp4 artist Kung Fu Panda", null, null, "20080517T091451.000Z", - "40", "Kung Fu Panda", "2008", "128521", "2", "mp4 composer"}, - {"/sdcard/media_api/metaDataTestMedias/OGG/Ring_Classic_02.ogg", null, "Suspended Animation", - "John Petrucci", null, null, "20070510T125223.000Z", - null, null, "2005", "231180", "1", null}, - {"/sdcard/media_api/metaDataTestMedias/OGG/When You Say Nothing At All.ogg", - null, "Suspended Animation", "John Petrucci", - null, null, "20070510T125223.000Z", null, null, "2005", "231180", "1", null}, - {"/sdcard/media_api/metaDataTestMedias/WAV/Im With You.wav", null, null, - null, null, null, null, - null, null, null, "224000", "1", null}, - {"/sdcard/media_api/metaDataTestMedias/WMA/WMA9.wma", "6", "Ten Songs in the Key of Betrayal", - "Alien Crime Syndicate", "Alien Crime Syndicate", - "wma 9 Composer", "20040521T175729.483Z", - "Rock", "Run for the Money", "2004", "134479", "1", null}, - {"/sdcard/media_api/metaDataTestMedias/WMA/WMA10.wma", "09", "wma 10 Album", - "wma 10 Album Artist", "wma 10 Artist", "wma 10 Composer", "20070705T063625.097Z", - "Acid Jazz", "wma 10 Title", "2010", "126574", "1", null}, - {"/sdcard/media_api/metaDataTestMedias/WMV/bugs.wmv", "8", "wmv 9 Album", - null, "wmv 9 Artist ", null, "20051122T155247.540Z", - null, "Looney Tunes - Hare-Breadth Hurry", "2005", "193482", "2", null}, - {"/sdcard/media_api/metaDataTestMedias/WMV/clips_ver7.wmv", "50", "wmv 7 Album", - null, "Hallau Shoots & Company", null, "20020226T170045.891Z", - null, "CODEC Shootout", "1986", "43709", "2", null} - }; - //output recorded video - + public static final String RECORDED_HVGA_H263 = "/sdcard/HVGA_H263.3gp"; public static final String RECORDED_QVGA_H263 = "/sdcard/QVGA_H263.3gp"; public static final String RECORDED_SQVGA_H263 = "/sdcard/SQVGA_H263.3gp"; @@ -474,22 +222,7 @@ public class MediaNames { public static final long RECORDED_TIME = 5000; public static final long VALID_VIDEO_DURATION = 2000; - - //Videos for the mediaplayer stress test - public static String[] H263_STRESS = { - "/sdcard/media_api/video_stress/h263/H263_CIF.3gp", - "/sdcard/media_api/video_stress/h263/H263_QCIF.3gp", - "/sdcard/media_api/video_stress/h263/H263_QVGA.3gp", - "/sdcard/media_api/video_stress/h263/H263_SQVGA.3gp" - }; - - public static String[] MPEG4_STRESS = { - "/sdcard/media_api/video_stress/h263/mpeg4_CIF.mp4", - "/sdcard/media_api/video_stress/h263/mpeg4_QCIF.3gp", - "/sdcard/media_api/video_stress/h263/mpeg4_QVGA.3gp", - "/sdcard/media_api/video_stress/h263/mpeg4_SQVGA.mp4" - }; - + //Streaming test files public static final byte [] STREAM_SERVER = new byte[] {(byte)75,(byte)17,(byte)48,(byte)204}; public static final String STREAM_H264_480_360_1411k = diff --git a/media/tests/MediaFrameworkTest/src/com/android/mediaframeworktest/functional/MediaMetadataTest.java b/media/tests/MediaFrameworkTest/src/com/android/mediaframeworktest/functional/MediaMetadataTest.java index 6ded74df6e4e..00e0a5244bce 100644 --- a/media/tests/MediaFrameworkTest/src/com/android/mediaframeworktest/functional/MediaMetadataTest.java +++ b/media/tests/MediaFrameworkTest/src/com/android/mediaframeworktest/functional/MediaMetadataTest.java @@ -47,14 +47,8 @@ public class MediaMetadataTest extends AndroidTestCase { CORRUPTED_ID3V2_TYER, CORRUPTED_ID3V2_TYER_2, CORRUPTED_ID3V2_TIT } - public static enum NON_MP3_TEST_FILE{ - THREE3GP, AMRNB, AMRWB, M4A1, M4V, MIDI, - H264, OGG1, OGG2, WAV, WMA9, WMA10, WMV9, WMV7 - } - public static METADATA_EXPECTEDRESULT meta; public static MP3_TEST_FILE mp3_test_file; - public static NON_MP3_TEST_FILE non_mp3_test_file; @MediumTest public static void testID3V1V2Metadata() throws Exception { @@ -116,88 +110,11 @@ public class MediaMetadataTest extends AndroidTestCase { validateMetatData(mp3_test_file.CORRUPTED_ID3V2_TIT.ordinal(), MediaNames.META_DATA_MP3); } - @MediumTest - public static void test3gp_Metadata() throws Exception { - validateMetatData(non_mp3_test_file.THREE3GP.ordinal(), MediaNames.META_DATA_OTHERS); - } - - @MediumTest - public static void testAmr_Metadata() throws Exception { - validateMetatData(non_mp3_test_file.AMRNB.ordinal(), MediaNames.META_DATA_OTHERS); - } - - @MediumTest - public static void testAmrWb_Metadata() throws Exception { - validateMetatData(non_mp3_test_file.AMRWB.ordinal(), MediaNames.META_DATA_OTHERS); - } - - @MediumTest - public static void testM4A1_Metadata() throws Exception { - validateMetatData(non_mp3_test_file.M4A1.ordinal(), MediaNames.META_DATA_OTHERS); - } - - @MediumTest - public static void testM4v_Metadata() throws Exception { - validateMetatData(non_mp3_test_file.M4V.ordinal(), MediaNames.META_DATA_OTHERS); - } - - @MediumTest - public static void testH264_Metadata() throws Exception { - validateMetatData(non_mp3_test_file.H264.ordinal(), MediaNames.META_DATA_OTHERS); - } - //bug# 1440489 - @Suppress - @MediumTest - public static void testOgg1_Metadata() throws Exception { - validateMetatData(non_mp3_test_file.OGG1.ordinal(), MediaNames.META_DATA_OTHERS); - } - - @Suppress - @MediumTest - public static void testOgg2_Metadata() throws Exception { - validateMetatData(non_mp3_test_file.OGG2.ordinal(), MediaNames.META_DATA_OTHERS); - } - - @Suppress - @MediumTest - public static void testMidi_Metadata() throws Exception { - validateMetatData(non_mp3_test_file.MIDI.ordinal(), MediaNames.META_DATA_OTHERS); - } - - @MediumTest - public static void testWav_Metadata() throws Exception { - validateMetatData(non_mp3_test_file.WAV.ordinal(), MediaNames.META_DATA_OTHERS); - } - - @Suppress - @MediumTest - public static void testWma9_Metadata() throws Exception { - validateMetatData(non_mp3_test_file.WMA9.ordinal(), MediaNames.META_DATA_OTHERS); - } - - @Suppress - @MediumTest - public static void testWma10_Metadata() throws Exception { - validateMetatData(non_mp3_test_file.WMA10.ordinal(), MediaNames.META_DATA_OTHERS); - } - - @Suppress - @MediumTest - public static void testWmv9_Metadata() throws Exception { - validateMetatData(non_mp3_test_file.WMV9.ordinal(), MediaNames.META_DATA_OTHERS); - } - - @Suppress - @MediumTest - public static void testWmv10_Metadata() throws Exception { - validateMetatData(non_mp3_test_file.WMV7.ordinal(), MediaNames.META_DATA_OTHERS); - } private static void validateMetatData(int fileIndex, String meta_data_file[][]) { Log.v(TAG, "filePath = "+ meta_data_file[fileIndex][0]); if ((meta_data_file[fileIndex][0].endsWith("wma") && !MediaProfileReader.getWMAEnable()) || (meta_data_file[fileIndex][0].endsWith("wmv") && !MediaProfileReader.getWMVEnable())) { - Log.v(TAG, "Skip test since windows media is not supported"); return; } String value = null; diff --git a/media/tests/MediaFrameworkTest/src/com/android/mediaframeworktest/functional/MediaPlayerApiTest.java b/media/tests/MediaFrameworkTest/src/com/android/mediaframeworktest/functional/MediaPlayerApiTest.java index d22025cadee2..3a9564d75a0a 100644 --- a/media/tests/MediaFrameworkTest/src/com/android/mediaframeworktest/functional/MediaPlayerApiTest.java +++ b/media/tests/MediaFrameworkTest/src/com/android/mediaframeworktest/functional/MediaPlayerApiTest.java @@ -369,13 +369,7 @@ public class MediaPlayerApiTest extends ActivityInstrumentationTestCase<MediaFra boolean isSeek = CodecTest.videoSeekTo(MediaNames.VIDEO_MP4); assertTrue("Local MP4 SeekTo", isSeek); } - - @LargeTest - public void testVideoLong3gpSeekTo() throws Exception { - boolean isSeek = CodecTest.videoSeekTo(MediaNames.VIDEO_LONG_3GP); - assertTrue("Local 3gp SeekTo", isSeek); - } - + @LargeTest public void testVideoH263AACSeekTo() throws Exception { boolean isSeek = CodecTest.videoSeekTo(MediaNames.VIDEO_H263_AAC); diff --git a/media/tests/MediaFrameworkTest/src/com/android/mediaframeworktest/performance/MediaPlayerPerformance.java b/media/tests/MediaFrameworkTest/src/com/android/mediaframeworktest/performance/MediaPlayerPerformance.java index 34affa7b1cb7..3b5b9a3b4e05 100644 --- a/media/tests/MediaFrameworkTest/src/com/android/mediaframeworktest/performance/MediaPlayerPerformance.java +++ b/media/tests/MediaFrameworkTest/src/com/android/mediaframeworktest/performance/MediaPlayerPerformance.java @@ -100,109 +100,6 @@ public class MediaPlayerPerformance extends ActivityInstrumentationTestCase2<Med MediaTestUtil.getNativeHeapDump(this.getName() + "_after"); } - public void createDB() { - mDB = SQLiteDatabase.openOrCreateDatabase("/sdcard/perf.db", null); - mDB.execSQL("CREATE TABLE IF NOT EXISTS perfdata (_id INTEGER PRIMARY KEY," + - "file TEXT," + "setdatatime LONG," + "preparetime LONG," + - "playtime LONG" + ");"); - //clean the table before adding new data - mDB.execSQL("DELETE FROM perfdata"); - } - - public void audioPlaybackStartupTime(String[] testFile) { - long t1 = 0; - long t2 = 0; - long t3 = 0; - long t4 = 0; - long setDataSourceDuration = 0; - long prepareDuration = 0; - long startDuration = 0; - long totalSetDataTime = 0; - long totalPrepareTime = 0; - long totalStartDuration = 0; - - int numberOfFiles = testFile.length; - Log.v(TAG, "File length " + numberOfFiles); - for (int k = 0; k < numberOfFiles; k++) { - MediaPlayer mp = new MediaPlayer(); - try { - t1 = SystemClock.uptimeMillis(); - FileInputStream fis = new FileInputStream(testFile[k]); - FileDescriptor fd = fis.getFD(); - mp.setDataSource(fd); - fis.close(); - t2 = SystemClock.uptimeMillis(); - mp.prepare(); - t3 = SystemClock.uptimeMillis(); - mp.start(); - t4 = SystemClock.uptimeMillis(); - } catch (Exception e) { - Log.v(TAG, e.toString()); - } - setDataSourceDuration = t2 - t1; - prepareDuration = t3 - t2; - startDuration = t4 - t3; - totalSetDataTime = totalSetDataTime + setDataSourceDuration; - totalPrepareTime = totalPrepareTime + prepareDuration; - totalStartDuration = totalStartDuration + startDuration; - mDB.execSQL("INSERT INTO perfdata (file, setdatatime, preparetime," + - " playtime) VALUES (" + '"' + testFile[k] + '"' + ',' + - setDataSourceDuration + ',' + prepareDuration + - ',' + startDuration + ");"); - Log.v(TAG, "File name " + testFile[k]); - mp.stop(); - mp.release(); - } - Log.v(TAG, "setDataSource average " + totalSetDataTime / numberOfFiles); - Log.v(TAG, "prepare average " + totalPrepareTime / numberOfFiles); - Log.v(TAG, "start average " + totalStartDuration / numberOfFiles); - - } - - @Suppress - public void testStartUpTime() throws Exception { - createDB(); - audioPlaybackStartupTime(MediaNames.MP3FILES); - audioPlaybackStartupTime(MediaNames.AACFILES); - - //close the database after all transactions - if (mDB.isOpen()) { - mDB.close(); - } - } - - public void wmametadatautility(String[] testFile) { - long t1 = 0; - long t2 = 0; - long sum = 0; - long duration = 0; - MediaMetadataRetriever retriever = new MediaMetadataRetriever(); - String value; - for (int i = 0, n = testFile.length; i < n; ++i) { - try { - t1 = SystemClock.uptimeMillis(); - retriever.setDataSource(testFile[i]); - value = retriever.extractMetadata(MediaMetadataRetriever.METADATA_KEY_ALBUM); - value = retriever.extractMetadata(MediaMetadataRetriever.METADATA_KEY_ARTIST); - value = retriever.extractMetadata(MediaMetadataRetriever.METADATA_KEY_COMPOSER); - value = retriever.extractMetadata(MediaMetadataRetriever.METADATA_KEY_GENRE); - value = retriever.extractMetadata(MediaMetadataRetriever.METADATA_KEY_TITLE); - value = retriever.extractMetadata(MediaMetadataRetriever.METADATA_KEY_YEAR); - value = - retriever - .extractMetadata(MediaMetadataRetriever.METADATA_KEY_CD_TRACK_NUMBER); - t2 = SystemClock.uptimeMillis(); - duration = t2 - t1; - Log.v(TAG, "Time taken = " + duration); - sum = sum + duration; - } catch (Exception e) { - Log.v(TAG, e.getMessage()); - } - - } - Log.v(TAG, "Average duration = " + sum / testFile.length); - } - private void initializeMessageLooper() { final ConditionVariable startDone = new ConditionVariable(); new Thread() { @@ -421,13 +318,6 @@ public class MediaPlayerPerformance extends ActivityInstrumentationTestCase2<Med return true; } - @Suppress - public void testWmaParseTime() throws Exception { - // createDB(); - wmametadatautility(MediaNames.WMASUPPORTED); - } - - // Test case 1: Capture the memory usage after every 20 h263 playback @LargeTest public void testH263VideoPlaybackMemoryUsage() throws Exception { diff --git a/packages/BackupRestoreConfirmation/src/com/android/backupconfirm/BackupRestoreConfirmation.java b/packages/BackupRestoreConfirmation/src/com/android/backupconfirm/BackupRestoreConfirmation.java index 52bfc2850681..ed413e693701 100644 --- a/packages/BackupRestoreConfirmation/src/com/android/backupconfirm/BackupRestoreConfirmation.java +++ b/packages/BackupRestoreConfirmation/src/com/android/backupconfirm/BackupRestoreConfirmation.java @@ -60,6 +60,7 @@ public class BackupRestoreConfirmation extends Activity { IBackupManager mBackupManager; FullObserver mObserver; int mToken; + boolean mDidAcknowledge; TextView mStatusView; Button mAllowButton; @@ -70,6 +71,7 @@ public class BackupRestoreConfirmation extends Activity { Context mContext; ObserverHandler(Context context) { mContext = context; + mDidAcknowledge = false; } @Override @@ -157,11 +159,7 @@ public class BackupRestoreConfirmation extends Activity { mAllowButton.setOnClickListener(new View.OnClickListener() { @Override public void onClick(View v) { - try { - mBackupManager.acknowledgeFullBackupOrRestore(mToken, true, mObserver); - } catch (RemoteException e) { - // TODO: bail gracefully if we can't contact the backup manager - } + sendAcknowledgement(mToken, true, mObserver); mAllowButton.setEnabled(false); mDenyButton.setEnabled(false); } @@ -170,11 +168,7 @@ public class BackupRestoreConfirmation extends Activity { mDenyButton.setOnClickListener(new View.OnClickListener() { @Override public void onClick(View v) { - try { - mBackupManager.acknowledgeFullBackupOrRestore(mToken, false, mObserver); - } catch (RemoteException e) { - // TODO: bail gracefully if we can't contact the backup manager - } + sendAcknowledgement(mToken, false, mObserver); mAllowButton.setEnabled(false); mDenyButton.setEnabled(false); } @@ -187,14 +181,21 @@ public class BackupRestoreConfirmation extends Activity { // We explicitly equate departure from the UI with refusal. This includes the // implicit configuration-changed stop/restart cycle. - try { - mBackupManager.acknowledgeFullBackupOrRestore(mToken, false, null); - } catch (RemoteException e) { - // if this fails we'll still time out with no acknowledgment - } + sendAcknowledgement(mToken, false, null); finish(); } + void sendAcknowledgement(int token, boolean allow, IFullBackupRestoreObserver observer) { + if (!mDidAcknowledge) { + mDidAcknowledge = true; + try { + mBackupManager.acknowledgeFullBackupOrRestore(mToken, true, mObserver); + } catch (RemoteException e) { + // TODO: bail gracefully if we can't contact the backup manager + } + } + } + /** * The observer binder for showing backup/restore progress. This binder just bounces * the notifications onto the main thread. diff --git a/packages/SharedStorageBackup/Android.mk b/packages/SharedStorageBackup/Android.mk new file mode 100644 index 000000000000..1d4f4da70d0d --- /dev/null +++ b/packages/SharedStorageBackup/Android.mk @@ -0,0 +1,33 @@ +# +# 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. +# + +LOCAL_PATH := $(call my-dir) +include $(CLEAR_VARS) + +LOCAL_MODULE_TAGS := optional + +LOCAL_SRC_FILES := $(call all-java-files-under, src) + +LOCAL_PROGUARD_FLAG_FILES := proguard.flags + +LOCAL_PACKAGE_NAME := SharedStorageBackup +LOCAL_CERTIFICATE := platform + +include $(BUILD_PACKAGE) + +######################## +include $(call all-makefiles-under,$(LOCAL_PATH)) + diff --git a/packages/SharedStorageBackup/AndroidManifest.xml b/packages/SharedStorageBackup/AndroidManifest.xml new file mode 100644 index 000000000000..258059c54fce --- /dev/null +++ b/packages/SharedStorageBackup/AndroidManifest.xml @@ -0,0 +1,29 @@ +<?xml version="1.0" encoding="utf-8"?> +<!-- +** Copyright 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. +--> + +<manifest xmlns:android="http://schemas.android.com/apk/res/android" + package="com.android.sharedstoragebackup" > + + <uses-permission android:name="android.permission.WRITE_EXTERNAL_STORAGE" /> + <uses-permission android:name="android.permission.WRITE_MEDIA_STORAGE" /> + + <application android:allowClearUserData="false" + android:permission="android.permission.CONFIRM_FULL_BACKUP" + android:fullBackupAgent=".SharedStorageAgent" + android:allowBackup="false" > + </application> +</manifest> diff --git a/packages/SharedStorageBackup/proguard.flags b/packages/SharedStorageBackup/proguard.flags new file mode 100644 index 000000000000..f43cb819b190 --- /dev/null +++ b/packages/SharedStorageBackup/proguard.flags @@ -0,0 +1 @@ +-keep class com.android.sharedstoragebackup.SharedStorageAgent diff --git a/packages/SharedStorageBackup/src/com/android/sharedstoragebackup/SharedStorageAgent.java b/packages/SharedStorageBackup/src/com/android/sharedstoragebackup/SharedStorageAgent.java new file mode 100644 index 000000000000..b02ca2e761ec --- /dev/null +++ b/packages/SharedStorageBackup/src/com/android/sharedstoragebackup/SharedStorageAgent.java @@ -0,0 +1,93 @@ +package com.android.sharedstoragebackup; + +import android.app.backup.FullBackup; +import android.app.backup.FullBackupAgent; +import android.app.backup.BackupDataInput; +import android.app.backup.BackupDataOutput; +import android.content.Context; +import android.os.Environment; +import android.os.ParcelFileDescriptor; +import android.os.storage.StorageManager; +import android.os.storage.StorageVolume; +import android.util.Slog; + +import java.io.File; +import java.io.IOException; + +public class SharedStorageAgent extends FullBackupAgent { + static final String TAG = "SharedStorageAgent"; + static final boolean DEBUG = true; + + StorageVolume[] mVolumes; + + @Override + public void onCreate() { + StorageManager mgr = (StorageManager) getSystemService(Context.STORAGE_SERVICE); + if (mgr != null) { + mVolumes = mgr.getVolumeList(); + } else { + Slog.e(TAG, "Unable to access Storage Manager"); + } + } + + @Override + public void onBackup(ParcelFileDescriptor oldState, BackupDataOutput data, + ParcelFileDescriptor newState) throws IOException { + // If there are shared-storage volumes available, run the inherited directory- + // hierarchy backup process on them. By convention in the Storage Manager, the + // "primary" shared storage volume is first in the list. + if (mVolumes != null) { + for (int i = 0; i < mVolumes.length; i++) { + StorageVolume v = mVolumes[i]; + // Express the contents of volume N this way in the tar stream: + // shared/N/path/to/file + // The restore will then extract to the given volume + String domain = FullBackup.SHARED_PREFIX + i; + processTree(null, domain, v.getPath(), null, data); + } + } + } + + /** + * Incremental onRestore() implementation is not used. + */ + @Override + public void onRestore(BackupDataInput data, int appVersionCode, ParcelFileDescriptor newState) + throws IOException { + } + + /** + * Full restore of one file to shared storage + */ + @Override + public void onRestoreFile(ParcelFileDescriptor data, long size, + int type, String domain, String relpath, long mode, long mtime) + throws IOException { + Slog.d(TAG, "Shared restore: [ " + domain + " : " + relpath + "]"); + + File outFile = null; + + // The file path must be in the semantic form [number]/path/to/file... + int slash = relpath.indexOf('/'); + if (slash > 0) { + try { + int i = Integer.parseInt(relpath.substring(0, slash)); + if (i <= mVolumes.length) { + outFile = new File(mVolumes[i].getPath(), relpath.substring(slash + 1)); + if (DEBUG) Slog.i(TAG, " => " + outFile.getAbsolutePath()); + } else { + Slog.w(TAG, "Cannot restore data for unavailable volume " + i); + } + } catch (NumberFormatException e) { + if (DEBUG) Slog.w(TAG, "Bad volume number token: " + relpath.substring(0, slash)); + } + } else { + if (DEBUG) Slog.i(TAG, "Can't find volume-number token"); + } + if (outFile == null) { + Slog.e(TAG, "Skipping data with malformed path " + relpath); + } + + FullBackup.restoreToFile(data, size, type, mode, mtime, outFile, false); + } +} diff --git a/packages/SystemUI/res/drawable-sw600dp-hdpi/ic_sysbar_zoom_default.png b/packages/SystemUI/res/drawable-sw600dp-hdpi/ic_sysbar_zoom_default.png Binary files differnew file mode 100644 index 000000000000..e2584e3a2f9a --- /dev/null +++ b/packages/SystemUI/res/drawable-sw600dp-hdpi/ic_sysbar_zoom_default.png diff --git a/packages/SystemUI/res/drawable-sw600dp-hdpi/ic_sysbar_zoom_pressed.png b/packages/SystemUI/res/drawable-sw600dp-hdpi/ic_sysbar_zoom_pressed.png Binary files differnew file mode 100644 index 000000000000..58b85102d9b3 --- /dev/null +++ b/packages/SystemUI/res/drawable-sw600dp-hdpi/ic_sysbar_zoom_pressed.png diff --git a/packages/SystemUI/res/drawable-sw600dp-mdpi/ic_sysbar_zoom_default.png b/packages/SystemUI/res/drawable-sw600dp-mdpi/ic_sysbar_zoom_default.png Binary files differnew file mode 100644 index 000000000000..2795c345f62e --- /dev/null +++ b/packages/SystemUI/res/drawable-sw600dp-mdpi/ic_sysbar_zoom_default.png diff --git a/packages/SystemUI/res/drawable-sw600dp-mdpi/ic_sysbar_zoom_pressed.png b/packages/SystemUI/res/drawable-sw600dp-mdpi/ic_sysbar_zoom_pressed.png Binary files differnew file mode 100644 index 000000000000..bbed6a6467d6 --- /dev/null +++ b/packages/SystemUI/res/drawable-sw600dp-mdpi/ic_sysbar_zoom_pressed.png diff --git a/packages/SystemUI/res/drawable/ic_sysbar_zoom.xml b/packages/SystemUI/res/drawable/ic_sysbar_zoom.xml new file mode 100644 index 000000000000..977e00205e6f --- /dev/null +++ b/packages/SystemUI/res/drawable/ic_sysbar_zoom.xml @@ -0,0 +1,21 @@ +<?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. +--> + +<selector xmlns:android="http://schemas.android.com/apk/res/android"> + <item android:state_pressed="true" android:drawable="@drawable/ic_sysbar_zoom_pressed" /> + <item android:drawable="@drawable/ic_sysbar_zoom_default" /> +</selector> + diff --git a/packages/SystemUI/res/layout-sw600dp/status_bar.xml b/packages/SystemUI/res/layout-sw600dp/status_bar.xml index d9f3f2324499..707a8cb56f73 100644 --- a/packages/SystemUI/res/layout-sw600dp/status_bar.xml +++ b/packages/SystemUI/res/layout-sw600dp/status_bar.xml @@ -75,6 +75,13 @@ systemui:keyCode="82" android:visibility="invisible" /> + <com.android.systemui.statusbar.policy.CompatModeButton + android:id="@+id/compat_button" + android:layout_width="80dip" + android:layout_height="match_parent" + android:src="@drawable/ic_sysbar_zoom" + android:visibility="invisible" + /> </LinearLayout> <!-- fake space bar zone --> diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/policy/CompatModeButton.java b/packages/SystemUI/src/com/android/systemui/statusbar/policy/CompatModeButton.java new file mode 100644 index 000000000000..9b44f7893cde --- /dev/null +++ b/packages/SystemUI/src/com/android/systemui/statusbar/policy/CompatModeButton.java @@ -0,0 +1,63 @@ +/* + * 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. + */ + +package com.android.systemui.statusbar.policy; + +import android.app.ActivityManager; +import android.content.Context; +import android.content.res.TypedArray; +import android.os.RemoteException; +import android.util.AttributeSet; +import android.util.Slog; +import android.view.View; +import android.widget.ImageView; + +import com.android.systemui.R; + +public class CompatModeButton extends ImageView implements View.OnClickListener { + private static final String TAG = "StatusBar.CompatModeButton"; + + private ActivityManager mAM; + + public CompatModeButton(Context context, AttributeSet attrs) { + this(context, attrs, 0); + } + + public CompatModeButton(Context context, AttributeSet attrs, int defStyle) { + super(context, attrs); + + setClickable(true); + + mAM = (ActivityManager) context.getSystemService(Context.ACTIVITY_SERVICE); + + setOnClickListener(this); + + refresh(); + } + + @Override + public void onClick(View v) { + mAM.setFrontActivityScreenCompatMode(ActivityManager.COMPAT_MODE_TOGGLE); + } + + public void refresh() { + setVisibility( + (mAM.getFrontActivityScreenCompatMode() == ActivityManager.COMPAT_MODE_NEVER) + ? View.GONE + : View.VISIBLE + ); + } +} diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/policy/NetworkController.java b/packages/SystemUI/src/com/android/systemui/statusbar/policy/NetworkController.java index 70a78df493e4..3175a99fb482 100644 --- a/packages/SystemUI/src/com/android/systemui/statusbar/policy/NetworkController.java +++ b/packages/SystemUI/src/com/android/systemui/statusbar/policy/NetworkController.java @@ -446,12 +446,14 @@ public class NetworkController extends BroadcastReceiver { } boolean isCdmaEri() { - final int iconIndex = mServiceState.getCdmaEriIconIndex(); - if (iconIndex != EriInfo.ROAMING_INDICATOR_OFF) { - final int iconMode = mServiceState.getCdmaEriIconMode(); - if (iconMode == EriInfo.ROAMING_ICON_MODE_NORMAL - || iconMode == EriInfo.ROAMING_ICON_MODE_FLASH) { - return true; + if (mServiceState != null) { + final int iconIndex = mServiceState.getCdmaEriIconIndex(); + if (iconIndex != EriInfo.ROAMING_INDICATOR_OFF) { + final int iconMode = mServiceState.getCdmaEriIconMode(); + if (iconMode == EriInfo.ROAMING_ICON_MODE_NORMAL + || iconMode == EriInfo.ROAMING_ICON_MODE_FLASH) { + return true; + } } } return false; @@ -854,7 +856,7 @@ public class NetworkController extends BroadcastReceiver { pw.print(" mDataActivity="); pw.println(mDataActivity); pw.print(" mServiceState="); - pw.println(mServiceState.toString()); + pw.println(mServiceState); pw.print(" mNetworkName="); pw.println(mNetworkName); pw.print(" mNetworkNameDefault="); diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/tablet/TabletStatusBar.java b/packages/SystemUI/src/com/android/systemui/statusbar/tablet/TabletStatusBar.java index 5b5801df8f46..ffb45ca5abcf 100644 --- a/packages/SystemUI/src/com/android/systemui/statusbar/tablet/TabletStatusBar.java +++ b/packages/SystemUI/src/com/android/systemui/statusbar/tablet/TabletStatusBar.java @@ -69,6 +69,7 @@ import com.android.systemui.R; import com.android.systemui.statusbar.*; import com.android.systemui.statusbar.policy.BatteryController; import com.android.systemui.statusbar.policy.BluetoothController; +import com.android.systemui.statusbar.policy.CompatModeButton; import com.android.systemui.statusbar.policy.LocationController; import com.android.systemui.statusbar.policy.NetworkController; import com.android.systemui.statusbar.policy.Prefs; @@ -963,6 +964,10 @@ public class TabletStatusBar extends StatusBar implements // See above re: lights-out policy for legacy apps. if (visible) setLightsOn(true); + + // XXX: HACK: not sure if this is the best way to catch a new activity that might require a + // change in compatibility features, but it's a start. + ((CompatModeButton) mBarContents.findViewById(R.id.compat_button)).refresh(); } public void setImeWindowStatus(IBinder token, int vis, int backDisposition) { diff --git a/services/camera/libcameraservice/CameraHardwareInterface.h b/services/camera/libcameraservice/CameraHardwareInterface.h index f9fa30e47ddb..7a18831f6fc4 100644 --- a/services/camera/libcameraservice/CameraHardwareInterface.h +++ b/services/camera/libcameraservice/CameraHardwareInterface.h @@ -438,18 +438,23 @@ private: } static void __data_cb(int32_t msg_type, - const camera_memory_t *data, + const camera_memory_t *data, unsigned int index, void *user) { LOGV("%s", __FUNCTION__); CameraHardwareInterface *__this = static_cast<CameraHardwareInterface *>(user); sp<CameraHeapMemory> mem(static_cast<CameraHeapMemory *>(data->handle)); - __this->mDataCb(msg_type, mem, __this->mCbUser); + if (index >= mem->mNumBufs) { + LOGE("%s: invalid buffer index %d, max allowed is %d", __FUNCTION__, + index, mem->mNumBufs); + return; + } + __this->mDataCb(msg_type, mem->mBuffers[index], __this->mCbUser); } static void __data_cb_timestamp(nsecs_t timestamp, int32_t msg_type, - const camera_memory_t *data, + const camera_memory_t *data, unsigned index, void *user) { LOGV("%s", __FUNCTION__); @@ -459,38 +464,85 @@ private: // drop all references, it will be destroyed (as well as the enclosed // MemoryHeapBase. sp<CameraHeapMemory> mem(static_cast<CameraHeapMemory *>(data->handle)); - __this->mDataCbTimestamp(timestamp, msg_type, mem, __this->mCbUser); + if (index >= mem->mNumBufs) { + LOGE("%s: invalid buffer index %d, max allowed is %d", __FUNCTION__, + index, mem->mNumBufs); + return; + } + __this->mDataCbTimestamp(timestamp, msg_type, mem->mBuffers[index], __this->mCbUser); } // This is a utility class that combines a MemoryHeapBase and a MemoryBase // in one. Since we tend to use them in a one-to-one relationship, this is // handy. - class CameraHeapMemory : public MemoryBase { + class CameraHeapMemory : public RefBase { public: - CameraHeapMemory(size_t size) : - MemoryBase(new MemoryHeapBase(size), 0, size) + CameraHeapMemory(int fd, size_t buf_size, uint_t num_buffers = 1) : + mBufSize(buf_size), + mNumBufs(num_buffers) + { + mHeap = new MemoryHeapBase(fd, buf_size * num_buffers); + commonInitialization(); + } + + CameraHeapMemory(size_t buf_size, uint_t num_buffers = 1) : + mBufSize(buf_size), + mNumBufs(num_buffers) { - handle.data = getHeap()->base(); - handle.size = size; + mHeap = new MemoryHeapBase(buf_size * num_buffers); + commonInitialization(); + } + + void commonInitialization() + { + handle.data = mHeap->base(); + handle.size = mBufSize * mNumBufs; handle.handle = this; + + mBuffers = new sp<MemoryBase>[mNumBufs]; + for (uint_t i = 0; i < mNumBufs; i++) + mBuffers[i] = new MemoryBase(mHeap, + i * mBufSize, + mBufSize); + + handle.release = __put_memory; } + virtual ~CameraHeapMemory() + { + delete [] mBuffers; + } + + size_t mBufSize; + uint_t mNumBufs; + sp<MemoryHeapBase> mHeap; + sp<MemoryBase> *mBuffers; + camera_memory_t handle; }; - static camera_memory_t* __get_memory(size_t size, - void *user __attribute__((unused))) + static camera_memory_t* __get_memory(int fd, size_t buf_size, uint_t num_bufs, + void *user __attribute__((unused))) { - // We allocate the object here, but we do not assign it to a strong - // pointer yet. The HAL will pass it back to us via the data callback - // or the data-timestamp callback, and from there on we will wrap it - // within a strong pointer. - - CameraHeapMemory *mem = new CameraHeapMemory(size); + CameraHeapMemory *mem; + if (fd < 0) + mem = new CameraHeapMemory(buf_size, num_bufs); + else + mem = new CameraHeapMemory(fd, buf_size, num_bufs); + mem->incStrong(mem); return &mem->handle; } + static void __put_memory(camera_memory_t *data) + { + if (!data) + return; + + CameraHeapMemory *mem = static_cast<CameraHeapMemory *>(data->handle); + mem->decStrong(mem); + } + static ANativeWindow *__to_anw(void *user) { CameraHardwareInterface *__this = @@ -541,7 +593,7 @@ private: static int __set_buffer_count(struct preview_stream_ops* w, int count) { ANativeWindow *a = anw(w); - return native_window_set_buffer_count(a, count); + return native_window_set_buffer_count(a, count); } static int __set_buffers_geometry(struct preview_stream_ops* w, diff --git a/services/input/InputReader.cpp b/services/input/InputReader.cpp index c42e3abcd4f3..09b24a8ef1ed 100644 --- a/services/input/InputReader.cpp +++ b/services/input/InputReader.cpp @@ -200,23 +200,6 @@ static int32_t calculateEdgeFlagsUsingPointerBounds( return edgeFlags; } -static void clampPositionUsingPointerBounds( - const sp<PointerControllerInterface>& pointerController, float* x, float* y) { - float minX, minY, maxX, maxY; - if (pointerController->getBounds(&minX, &minY, &maxX, &maxY)) { - if (*x < minX) { - *x = minX; - } else if (*x > maxX) { - *x = maxX; - } - if (*y < minY) { - *y = minY; - } else if (*y > maxY) { - *y = maxY; - } - } -} - static float calculateCommonVector(float a, float b) { if (a > 0 && b > 0) { return a < b ? a : b; @@ -787,8 +770,8 @@ void InputReader::dump(String8& dump) { mConfig.pointerGestureTapSlop); dump.appendFormat(INDENT3 "MultitouchSettleInterval: %0.1fms\n", mConfig.pointerGestureMultitouchSettleInterval * 0.000001f); - dump.appendFormat(INDENT3 "MultitouchMinSpeed: %0.1fpx/s\n", - mConfig.pointerGestureMultitouchMinSpeed); + dump.appendFormat(INDENT3 "MultitouchMinDistance: %0.1fpx\n", + mConfig.pointerGestureMultitouchMinDistance); dump.appendFormat(INDENT3 "SwipeTransitionAngleCosine: %0.1f\n", mConfig.pointerGestureSwipeTransitionAngleCosine); dump.appendFormat(INDENT3 "SwipeMaxWidthRatio: %0.1f\n", @@ -3509,11 +3492,18 @@ void TouchInputMapper::dispatchPointerGestures(nsecs_t when, uint32_t policyFlag cancelPreviousGesture = false; } - // Switch pointer presentation. - mPointerController->setPresentation( - mParameters.gestureMode == Parameters::GESTURE_MODE_SPOTS - ? PointerControllerInterface::PRESENTATION_SPOT - : PointerControllerInterface::PRESENTATION_POINTER); + // Update the pointer presentation and spots. + if (mParameters.gestureMode == Parameters::GESTURE_MODE_SPOTS) { + mPointerController->setPresentation(PointerControllerInterface::PRESENTATION_SPOT); + if (finishPreviousGesture || cancelPreviousGesture) { + mPointerController->clearSpots(); + } + mPointerController->setSpots(mPointerGesture.currentGestureCoords, + mPointerGesture.currentGestureIdToIndex, + mPointerGesture.currentGestureIdBits); + } else { + mPointerController->setPresentation(PointerControllerInterface::PRESENTATION_POINTER); + } // Show or hide the pointer if needed. switch (mPointerGesture.currentGestureMode) { @@ -3712,12 +3702,6 @@ bool TouchInputMapper::preparePointerGestures(nsecs_t when, mPointerGesture.currentGestureIdBits.clear(); mPointerGesture.pointerVelocityControl.reset(); - - if (mParameters.gestureMode == Parameters::GESTURE_MODE_SPOTS) { - mPointerGesture.spotGesture = PointerControllerInterface::SPOT_GESTURE_NEUTRAL; - mPointerGesture.spotIdBits.clear(); - moveSpotsLocked(); - } return true; } } @@ -3798,22 +3782,18 @@ bool TouchInputMapper::preparePointerGestures(nsecs_t when, if (isQuietTime) { // Case 1: Quiet time. (QUIET) #if DEBUG_GESTURES - LOGD("Gestures: QUIET for next %0.3fms", - (mPointerGesture.quietTime + QUIET_INTERVAL - when) * 0.000001f); + LOGD("Gestures: QUIET for next %0.3fms", (mPointerGesture.quietTime + + mConfig->pointerGestureQuietInterval - when) * 0.000001f); #endif - *outFinishPreviousGesture = true; + if (mPointerGesture.lastGestureMode != PointerGesture::QUIET) { + *outFinishPreviousGesture = true; + } mPointerGesture.activeGestureId = -1; mPointerGesture.currentGestureMode = PointerGesture::QUIET; mPointerGesture.currentGestureIdBits.clear(); mPointerGesture.pointerVelocityControl.reset(); - - if (mParameters.gestureMode == Parameters::GESTURE_MODE_SPOTS) { - mPointerGesture.spotGesture = PointerControllerInterface::SPOT_GESTURE_NEUTRAL; - mPointerGesture.spotIdBits.clear(); - moveSpotsLocked(); - } } else if (isPointerDown(mCurrentTouch.buttonState)) { // Case 2: Button is pressed. (BUTTON_CLICK_OR_DRAG) // The pointer follows the active touch point. @@ -3899,32 +3879,11 @@ bool TouchInputMapper::preparePointerGestures(nsecs_t when, mPointerGesture.currentGestureCoords[0].setAxisValue(AMOTION_EVENT_AXIS_X, x); mPointerGesture.currentGestureCoords[0].setAxisValue(AMOTION_EVENT_AXIS_Y, y); mPointerGesture.currentGestureCoords[0].setAxisValue(AMOTION_EVENT_AXIS_PRESSURE, 1.0f); - - if (mParameters.gestureMode == Parameters::GESTURE_MODE_SPOTS) { - if (activeTouchId >= 0) { - // Collapse all spots into one point at the pointer location. - mPointerGesture.spotGesture = PointerControllerInterface::SPOT_GESTURE_BUTTON_DRAG; - mPointerGesture.spotIdBits.clear(); - for (uint32_t i = 0; i < mCurrentTouch.pointerCount; i++) { - uint32_t id = mCurrentTouch.pointers[i].id; - mPointerGesture.spotIdBits.markBit(id); - mPointerGesture.spotIdToIndex[id] = i; - mPointerGesture.spotCoords[i] = mPointerGesture.currentGestureCoords[0]; - } - } else { - // No fingers. Generate a spot at the pointer location so the - // anchor appears to be pressed. - mPointerGesture.spotGesture = PointerControllerInterface::SPOT_GESTURE_BUTTON_CLICK; - mPointerGesture.spotIdBits.clear(); - mPointerGesture.spotIdBits.markBit(0); - mPointerGesture.spotIdToIndex[0] = 0; - mPointerGesture.spotCoords[0] = mPointerGesture.currentGestureCoords[0]; - } - moveSpotsLocked(); - } } else if (mCurrentTouch.pointerCount == 0) { // Case 3. No fingers down and button is not pressed. (NEUTRAL) - *outFinishPreviousGesture = true; + if (mPointerGesture.lastGestureMode != PointerGesture::NEUTRAL) { + *outFinishPreviousGesture = true; + } // Watch for taps coming out of HOVER or TAP_DRAG mode. // Checking for taps after TAP_DRAG allows us to detect double-taps. @@ -3965,15 +3924,6 @@ bool TouchInputMapper::preparePointerGestures(nsecs_t when, mPointerGesture.currentGestureCoords[0].setAxisValue( AMOTION_EVENT_AXIS_PRESSURE, 1.0f); - if (mParameters.gestureMode == Parameters::GESTURE_MODE_SPOTS) { - mPointerGesture.spotGesture = PointerControllerInterface::SPOT_GESTURE_TAP; - mPointerGesture.spotIdBits.clear(); - mPointerGesture.spotIdBits.markBit(lastActiveTouchId); - mPointerGesture.spotIdToIndex[lastActiveTouchId] = 0; - mPointerGesture.spotCoords[0] = mPointerGesture.currentGestureCoords[0]; - moveSpotsLocked(); - } - tapped = true; } else { #if DEBUG_GESTURES @@ -3999,12 +3949,6 @@ bool TouchInputMapper::preparePointerGestures(nsecs_t when, mPointerGesture.activeGestureId = -1; mPointerGesture.currentGestureMode = PointerGesture::NEUTRAL; mPointerGesture.currentGestureIdBits.clear(); - - if (mParameters.gestureMode == Parameters::GESTURE_MODE_SPOTS) { - mPointerGesture.spotGesture = PointerControllerInterface::SPOT_GESTURE_NEUTRAL; - mPointerGesture.spotIdBits.clear(); - moveSpotsLocked(); - } } } else if (mCurrentTouch.pointerCount == 1) { // Case 4. Exactly one finger down, button is not pressed. (HOVER or TAP_DRAG) @@ -4067,7 +4011,9 @@ bool TouchInputMapper::preparePointerGestures(nsecs_t when, #if DEBUG_GESTURES LOGD("Gestures: HOVER"); #endif - *outFinishPreviousGesture = true; + if (mPointerGesture.lastGestureMode != PointerGesture::HOVER) { + *outFinishPreviousGesture = true; + } mPointerGesture.activeGestureId = 0; down = false; } @@ -4094,16 +4040,6 @@ bool TouchInputMapper::preparePointerGestures(nsecs_t when, mPointerGesture.tapX = x; mPointerGesture.tapY = y; } - - if (mParameters.gestureMode == Parameters::GESTURE_MODE_SPOTS) { - mPointerGesture.spotGesture = down ? PointerControllerInterface::SPOT_GESTURE_DRAG - : PointerControllerInterface::SPOT_GESTURE_HOVER; - mPointerGesture.spotIdBits.clear(); - mPointerGesture.spotIdBits.markBit(activeTouchId); - mPointerGesture.spotIdToIndex[activeTouchId] = 0; - mPointerGesture.spotCoords[0] = mPointerGesture.currentGestureCoords[0]; - moveSpotsLocked(); - } } else { // Case 5. At least two fingers down, button is not pressed. (PRESS, SWIPE or FREEFORM) // We need to provide feedback for each finger that goes down so we cannot wait @@ -4131,8 +4067,8 @@ bool TouchInputMapper::preparePointerGestures(nsecs_t when, // Reset the gesture. #if DEBUG_GESTURES LOGD("Gestures: Resetting gesture since additional pointers went down for MULTITOUCH, " - "settle time remaining %0.3fms", - (mPointerGesture.firstTouchTime + MULTITOUCH_SETTLE_INTERVAL - when) + "settle time remaining %0.3fms", (mPointerGesture.firstTouchTime + + mConfig->pointerGestureMultitouchSettleInterval - when) * 0.000001f); #endif *outCancelPreviousGesture = true; @@ -4147,101 +4083,134 @@ bool TouchInputMapper::preparePointerGestures(nsecs_t when, mPointerGesture.referenceIdBits.clear(); mPointerGesture.pointerVelocityControl.reset(); - if (settled && mParameters.gestureMode == Parameters::GESTURE_MODE_SPOTS - && mLastTouch.idBits.hasBit(mPointerGesture.activeTouchId)) { - // The spot is already visible and has settled, use it as the reference point - // for the gesture. Other spots will be positioned relative to this one. + // Use the centroid and pointer location as the reference points for the gesture. #if DEBUG_GESTURES - LOGD("Gestures: Using active spot as reference for MULTITOUCH, " - "settle time expired %0.3fms ago", - (when - mPointerGesture.firstTouchTime - MULTITOUCH_SETTLE_INTERVAL) - * 0.000001f); + LOGD("Gestures: Using centroid as reference for MULTITOUCH, " + "settle time remaining %0.3fms", (mPointerGesture.firstTouchTime + + mConfig->pointerGestureMultitouchSettleInterval - when) + * 0.000001f); #endif - const PointerData& d = mLastTouch.pointers[mLastTouch.idToIndex[ - mPointerGesture.activeTouchId]]; - mPointerGesture.referenceTouchX = d.x; - mPointerGesture.referenceTouchY = d.y; - const PointerCoords& c = mPointerGesture.spotCoords[mPointerGesture.spotIdToIndex[ - mPointerGesture.activeTouchId]]; - mPointerGesture.referenceGestureX = c.getAxisValue(AMOTION_EVENT_AXIS_X); - mPointerGesture.referenceGestureY = c.getAxisValue(AMOTION_EVENT_AXIS_Y); + mCurrentTouch.getCentroid(&mPointerGesture.referenceTouchX, + &mPointerGesture.referenceTouchY); + mPointerController->getPosition(&mPointerGesture.referenceGestureX, + &mPointerGesture.referenceGestureY); + } + + // Clear the reference deltas for fingers not yet included in the reference calculation. + for (BitSet32 idBits(mCurrentTouch.idBits.value & ~mPointerGesture.referenceIdBits.value); + !idBits.isEmpty(); ) { + uint32_t id = idBits.firstMarkedBit(); + idBits.clearBit(id); + + mPointerGesture.referenceDeltas[id].dx = 0; + mPointerGesture.referenceDeltas[id].dy = 0; + } + mPointerGesture.referenceIdBits = mCurrentTouch.idBits; + + // Add delta for all fingers and calculate a common movement delta. + float commonDeltaX = 0, commonDeltaY = 0; + BitSet32 commonIdBits(mLastTouch.idBits.value & mCurrentTouch.idBits.value); + for (BitSet32 idBits(commonIdBits); !idBits.isEmpty(); ) { + bool first = (idBits == commonIdBits); + uint32_t id = idBits.firstMarkedBit(); + idBits.clearBit(id); + + const PointerData& cpd = mCurrentTouch.pointers[mCurrentTouch.idToIndex[id]]; + const PointerData& lpd = mLastTouch.pointers[mLastTouch.idToIndex[id]]; + PointerGesture::Delta& delta = mPointerGesture.referenceDeltas[id]; + delta.dx += cpd.x - lpd.x; + delta.dy += cpd.y - lpd.y; + + if (first) { + commonDeltaX = delta.dx; + commonDeltaY = delta.dy; } else { - // Use the centroid and pointer location as the reference points for the gesture. -#if DEBUG_GESTURES - LOGD("Gestures: Using centroid as reference for MULTITOUCH, " - "settle time remaining %0.3fms", - (mPointerGesture.firstTouchTime + MULTITOUCH_SETTLE_INTERVAL - when) - * 0.000001f); -#endif - mCurrentTouch.getCentroid(&mPointerGesture.referenceTouchX, - &mPointerGesture.referenceTouchY); - mPointerController->getPosition(&mPointerGesture.referenceGestureX, - &mPointerGesture.referenceGestureY); + commonDeltaX = calculateCommonVector(commonDeltaX, delta.dx); + commonDeltaY = calculateCommonVector(commonDeltaY, delta.dy); } } + // Consider transitions from PRESS to SWIPE or MULTITOUCH. if (mPointerGesture.currentGestureMode == PointerGesture::PRESS) { - float d; - if (mCurrentTouch.pointerCount > 2) { - // There are more than two pointers, switch to FREEFORM. + float dist[MAX_POINTER_ID + 1]; + int32_t distOverThreshold = 0; + for (BitSet32 idBits(mPointerGesture.referenceIdBits); !idBits.isEmpty(); ) { + uint32_t id = idBits.firstMarkedBit(); + idBits.clearBit(id); + + PointerGesture::Delta& delta = mPointerGesture.referenceDeltas[id]; + dist[id] = hypotf(delta.dx * mLocked.pointerGestureXZoomScale, + delta.dy * mLocked.pointerGestureYZoomScale); + if (dist[id] > mConfig->pointerGestureMultitouchMinDistance) { + distOverThreshold += 1; + } + } + + // Only transition when at least two pointers have moved further than + // the minimum distance threshold. + if (distOverThreshold >= 2) { + float d; + if (mCurrentTouch.pointerCount > 2) { + // There are more than two pointers, switch to FREEFORM. #if DEBUG_GESTURES - LOGD("Gestures: PRESS transitioned to FREEFORM, number of pointers %d > 2", - mCurrentTouch.pointerCount); + LOGD("Gestures: PRESS transitioned to FREEFORM, number of pointers %d > 2", + mCurrentTouch.pointerCount); #endif - *outCancelPreviousGesture = true; - mPointerGesture.currentGestureMode = PointerGesture::FREEFORM; - } else if (((d = distance( - mCurrentTouch.pointers[0].x, mCurrentTouch.pointers[0].y, - mCurrentTouch.pointers[1].x, mCurrentTouch.pointers[1].y)) - > mLocked.pointerGestureMaxSwipeWidth)) { - // There are two pointers but they are too far apart, switch to FREEFORM. + *outCancelPreviousGesture = true; + mPointerGesture.currentGestureMode = PointerGesture::FREEFORM; + } else if (((d = distance( + mCurrentTouch.pointers[0].x, mCurrentTouch.pointers[0].y, + mCurrentTouch.pointers[1].x, mCurrentTouch.pointers[1].y)) + > mLocked.pointerGestureMaxSwipeWidth)) { + // There are two pointers but they are too far apart for a SWIPE, + // switch to FREEFORM. #if DEBUG_GESTURES - LOGD("Gestures: PRESS transitioned to FREEFORM, distance %0.3f > %0.3f", - d, mLocked.pointerGestureMaxSwipeWidth); + LOGD("Gestures: PRESS transitioned to FREEFORM, distance %0.3f > %0.3f", + d, mLocked.pointerGestureMaxSwipeWidth); #endif - *outCancelPreviousGesture = true; - mPointerGesture.currentGestureMode = PointerGesture::FREEFORM; - } else { - // There are two pointers. Wait for both pointers to start moving - // before deciding whether this is a SWIPE or FREEFORM gesture. - uint32_t id1 = mCurrentTouch.pointers[0].id; - uint32_t id2 = mCurrentTouch.pointers[1].id; - - float vx1, vy1, vx2, vy2; - mPointerGesture.velocityTracker.getVelocity(id1, &vx1, &vy1); - mPointerGesture.velocityTracker.getVelocity(id2, &vx2, &vy2); - - float speed1 = hypotf(vx1, vy1); - float speed2 = hypotf(vx2, vy2); - if (speed1 >= mConfig->pointerGestureMultitouchMinSpeed - && speed2 >= mConfig->pointerGestureMultitouchMinSpeed) { - // Calculate the dot product of the velocity vectors. - // When the vectors are oriented in approximately the same direction, - // the angle betweeen them is near zero and the cosine of the angle - // approches 1.0. Recall that dot(v1, v2) = cos(angle) * mag(v1) * mag(v2). - float dot = vx1 * vx2 + vy1 * vy2; - float cosine = dot / (speed1 * speed2); // denominator always > 0 - if (cosine >= mConfig->pointerGestureSwipeTransitionAngleCosine) { - // Pointers are moving in the same direction. Switch to SWIPE. + *outCancelPreviousGesture = true; + mPointerGesture.currentGestureMode = PointerGesture::FREEFORM; + } else { + // There are two pointers. Wait for both pointers to start moving + // before deciding whether this is a SWIPE or FREEFORM gesture. + uint32_t id1 = mCurrentTouch.pointers[0].id; + uint32_t id2 = mCurrentTouch.pointers[1].id; + float dist1 = dist[id1]; + float dist2 = dist[id2]; + if (dist1 >= mConfig->pointerGestureMultitouchMinDistance + && dist2 >= mConfig->pointerGestureMultitouchMinDistance) { + // Calculate the dot product of the displacement vectors. + // When the vectors are oriented in approximately the same direction, + // the angle betweeen them is near zero and the cosine of the angle + // approches 1.0. Recall that dot(v1, v2) = cos(angle) * mag(v1) * mag(v2). + PointerGesture::Delta& delta1 = mPointerGesture.referenceDeltas[id1]; + PointerGesture::Delta& delta2 = mPointerGesture.referenceDeltas[id2]; + float dot = delta1.dx * delta2.dx + delta1.dy * delta2.dy; + float cosine = dot / (dist1 * dist2); // denominator always > 0 + if (cosine >= mConfig->pointerGestureSwipeTransitionAngleCosine) { + // Pointers are moving in the same direction. Switch to SWIPE. #if DEBUG_GESTURES - LOGD("Gestures: PRESS transitioned to SWIPE, " - "speed1 %0.3f >= %0.3f, speed2 %0.3f >= %0.3f, " - "cosine %0.3f >= %0.3f", - speed1, MULTITOUCH_MIN_SPEED, speed2, MULTITOUCH_MIN_SPEED, - cosine, SWIPE_TRANSITION_ANGLE_COSINE); + LOGD("Gestures: PRESS transitioned to SWIPE, " + "dist1 %0.3f >= %0.3f, dist2 %0.3f >= %0.3f, " + "cosine %0.3f >= %0.3f", + dist1, mConfig->pointerGestureMultitouchMinDistance, + dist2, mConfig->pointerGestureMultitouchMinDistance, + cosine, mConfig->pointerGestureSwipeTransitionAngleCosine); #endif - mPointerGesture.currentGestureMode = PointerGesture::SWIPE; - } else { - // Pointers are moving in different directions. Switch to FREEFORM. + mPointerGesture.currentGestureMode = PointerGesture::SWIPE; + } else { + // Pointers are moving in different directions. Switch to FREEFORM. #if DEBUG_GESTURES - LOGD("Gestures: PRESS transitioned to FREEFORM, " - "speed1 %0.3f >= %0.3f, speed2 %0.3f >= %0.3f, " - "cosine %0.3f < %0.3f", - speed1, MULTITOUCH_MIN_SPEED, speed2, MULTITOUCH_MIN_SPEED, - cosine, SWIPE_TRANSITION_ANGLE_COSINE); + LOGD("Gestures: PRESS transitioned to FREEFORM, " + "dist1 %0.3f >= %0.3f, dist2 %0.3f >= %0.3f, " + "cosine %0.3f < %0.3f", + dist1, mConfig->pointerGestureMultitouchMinDistance, + dist2, mConfig->pointerGestureMultitouchMinDistance, + cosine, mConfig->pointerGestureSwipeTransitionAngleCosine); #endif - *outCancelPreviousGesture = true; - mPointerGesture.currentGestureMode = PointerGesture::FREEFORM; + *outCancelPreviousGesture = true; + mPointerGesture.currentGestureMode = PointerGesture::FREEFORM; + } } } } @@ -4258,67 +4227,28 @@ bool TouchInputMapper::preparePointerGestures(nsecs_t when, } } - // Clear the reference deltas for fingers not yet included in the reference calculation. - for (BitSet32 idBits(mCurrentTouch.idBits.value & ~mPointerGesture.referenceIdBits.value); - !idBits.isEmpty(); ) { - uint32_t id = idBits.firstMarkedBit(); - idBits.clearBit(id); - - mPointerGesture.referenceDeltas[id].dx = 0; - mPointerGesture.referenceDeltas[id].dy = 0; - } - mPointerGesture.referenceIdBits = mCurrentTouch.idBits; - - // Move the reference points based on the overall group motion of the fingers. - // The objective is to calculate a vector delta that is common to the movement - // of all fingers. - BitSet32 commonIdBits(mLastTouch.idBits.value & mCurrentTouch.idBits.value); - if (!commonIdBits.isEmpty()) { - float commonDeltaX = 0, commonDeltaY = 0; - for (BitSet32 idBits(commonIdBits); !idBits.isEmpty(); ) { - bool first = (idBits == commonIdBits); + // Move the reference points based on the overall group motion of the fingers + // except in PRESS mode while waiting for a transition to occur. + if (mPointerGesture.currentGestureMode != PointerGesture::PRESS + && (commonDeltaX || commonDeltaY)) { + for (BitSet32 idBits(mPointerGesture.referenceIdBits); !idBits.isEmpty(); ) { uint32_t id = idBits.firstMarkedBit(); idBits.clearBit(id); - const PointerData& cpd = mCurrentTouch.pointers[mCurrentTouch.idToIndex[id]]; - const PointerData& lpd = mLastTouch.pointers[mLastTouch.idToIndex[id]]; PointerGesture::Delta& delta = mPointerGesture.referenceDeltas[id]; - delta.dx += cpd.x - lpd.x; - delta.dy += cpd.y - lpd.y; - - if (first) { - commonDeltaX = delta.dx; - commonDeltaY = delta.dy; - } else { - commonDeltaX = calculateCommonVector(commonDeltaX, delta.dx); - commonDeltaY = calculateCommonVector(commonDeltaY, delta.dy); - } + delta.dx = 0; + delta.dy = 0; } - if (commonDeltaX || commonDeltaY) { - for (BitSet32 idBits(commonIdBits); !idBits.isEmpty(); ) { - uint32_t id = idBits.firstMarkedBit(); - idBits.clearBit(id); - - PointerGesture::Delta& delta = mPointerGesture.referenceDeltas[id]; - delta.dx = 0; - delta.dy = 0; - } - - mPointerGesture.referenceTouchX += commonDeltaX; - mPointerGesture.referenceTouchY += commonDeltaY; + mPointerGesture.referenceTouchX += commonDeltaX; + mPointerGesture.referenceTouchY += commonDeltaY; - commonDeltaX *= mLocked.pointerGestureXMovementScale; - commonDeltaY *= mLocked.pointerGestureYMovementScale; - mPointerGesture.pointerVelocityControl.move(when, &commonDeltaX, &commonDeltaY); + commonDeltaX *= mLocked.pointerGestureXMovementScale; + commonDeltaY *= mLocked.pointerGestureYMovementScale; + mPointerGesture.pointerVelocityControl.move(when, &commonDeltaX, &commonDeltaY); - mPointerGesture.referenceGestureX += commonDeltaX; - mPointerGesture.referenceGestureY += commonDeltaY; - - clampPositionUsingPointerBounds(mPointerController, - &mPointerGesture.referenceGestureX, - &mPointerGesture.referenceGestureY); - } + mPointerGesture.referenceGestureX += commonDeltaX; + mPointerGesture.referenceGestureY += commonDeltaY; } // Report gestures. @@ -4344,10 +4274,6 @@ bool TouchInputMapper::preparePointerGestures(nsecs_t when, mPointerGesture.currentGestureCoords[0].setAxisValue(AMOTION_EVENT_AXIS_Y, mPointerGesture.referenceGestureY); mPointerGesture.currentGestureCoords[0].setAxisValue(AMOTION_EVENT_AXIS_PRESSURE, 1.0f); - - if (mParameters.gestureMode == Parameters::GESTURE_MODE_SPOTS) { - mPointerGesture.spotGesture = PointerControllerInterface::SPOT_GESTURE_PRESS; - } } else if (mPointerGesture.currentGestureMode == PointerGesture::SWIPE) { // SWIPE mode. #if DEBUG_GESTURES @@ -4370,10 +4296,6 @@ bool TouchInputMapper::preparePointerGestures(nsecs_t when, mPointerGesture.currentGestureCoords[0].setAxisValue(AMOTION_EVENT_AXIS_Y, mPointerGesture.referenceGestureY); mPointerGesture.currentGestureCoords[0].setAxisValue(AMOTION_EVENT_AXIS_PRESSURE, 1.0f); - - if (mParameters.gestureMode == Parameters::GESTURE_MODE_SPOTS) { - mPointerGesture.spotGesture = PointerControllerInterface::SPOT_GESTURE_SWIPE; - } } else if (mPointerGesture.currentGestureMode == PointerGesture::FREEFORM) { // FREEFORM mode. #if DEBUG_GESTURES @@ -4475,33 +4397,6 @@ bool TouchInputMapper::preparePointerGestures(nsecs_t when, "activeGestureId=%d", mPointerGesture.activeGestureId); #endif } - - if (mParameters.gestureMode == Parameters::GESTURE_MODE_SPOTS) { - mPointerGesture.spotGesture = PointerControllerInterface::SPOT_GESTURE_FREEFORM; - } - } - - // Update spot locations for PRESS, SWIPE and FREEFORM. - // We use the same calculation as we do to calculate the gesture pointers - // for FREEFORM so that the spots smoothly track gestures. - if (mParameters.gestureMode == Parameters::GESTURE_MODE_SPOTS) { - mPointerGesture.spotIdBits.clear(); - for (uint32_t i = 0; i < mCurrentTouch.pointerCount; i++) { - uint32_t id = mCurrentTouch.pointers[i].id; - mPointerGesture.spotIdBits.markBit(id); - mPointerGesture.spotIdToIndex[id] = i; - - float x = (mCurrentTouch.pointers[i].x - mPointerGesture.referenceTouchX) - * mLocked.pointerGestureXZoomScale + mPointerGesture.referenceGestureX; - float y = (mCurrentTouch.pointers[i].y - mPointerGesture.referenceTouchY) - * mLocked.pointerGestureYZoomScale + mPointerGesture.referenceGestureY; - - mPointerGesture.spotCoords[i].clear(); - mPointerGesture.spotCoords[i].setAxisValue(AMOTION_EVENT_AXIS_X, x); - mPointerGesture.spotCoords[i].setAxisValue(AMOTION_EVENT_AXIS_Y, y); - mPointerGesture.spotCoords[i].setAxisValue(AMOTION_EVENT_AXIS_PRESSURE, 1.0f); - } - moveSpotsLocked(); } } @@ -4544,11 +4439,6 @@ bool TouchInputMapper::preparePointerGestures(nsecs_t when, return true; } -void TouchInputMapper::moveSpotsLocked() { - mPointerController->setSpots(mPointerGesture.spotGesture, - mPointerGesture.spotCoords, mPointerGesture.spotIdToIndex, mPointerGesture.spotIdBits); -} - void TouchInputMapper::dispatchMotion(nsecs_t when, uint32_t policyFlags, uint32_t source, int32_t action, int32_t flags, int32_t metaState, int32_t buttonState, int32_t edgeFlags, const PointerProperties* properties, const PointerCoords* coords, diff --git a/services/input/InputReader.h b/services/input/InputReader.h index 1d4ad873c1dd..36cd89cfb576 100644 --- a/services/input/InputReader.h +++ b/services/input/InputReader.h @@ -101,8 +101,8 @@ struct InputReaderConfiguration { nsecs_t pointerGestureMultitouchSettleInterval; // The transition from PRESS to SWIPE or FREEFORM gesture mode is made when - // both of the pointers are moving at least this fast. - float pointerGestureMultitouchMinSpeed; // in pixels per second + // at least two pointers have moved at least this far from their starting place. + float pointerGestureMultitouchMinDistance; // in pixels // The transition from PRESS to SWIPE gesture mode can only occur when the // cosine of the angle between the two vectors is greater than or equal to than this value @@ -134,7 +134,7 @@ struct InputReaderConfiguration { filterTouchEvents(false), filterJumpyTouchEvents(false), virtualKeyQuietTime(0), - pointerVelocityControlParameters(1.0f, 80.0f, 400.0f, 4.0f), + pointerVelocityControlParameters(1.0f, 500.0f, 3000.0f, 3.0f), wheelVelocityControlParameters(1.0f, 15.0f, 50.0f, 4.0f), pointerGestureQuietInterval(100 * 1000000LL), // 100 ms pointerGestureDragMinSwitchSpeed(50), // 50 pixels per second @@ -142,10 +142,10 @@ struct InputReaderConfiguration { pointerGestureTapDragInterval(150 * 1000000LL), // 150 ms pointerGestureTapSlop(10.0f), // 10 pixels pointerGestureMultitouchSettleInterval(100 * 1000000LL), // 100 ms - pointerGestureMultitouchMinSpeed(150.0f), // 150 pixels per second + pointerGestureMultitouchMinDistance(15), // 15 pixels pointerGestureSwipeTransitionAngleCosine(0.5f), // cosine of 45degrees - pointerGestureSwipeMaxWidthRatio(0.333f), - pointerGestureMovementSpeedRatio(0.3f), + pointerGestureSwipeMaxWidthRatio(0.25f), + pointerGestureMovementSpeedRatio(0.8f), pointerGestureZoomSpeedRatio(0.3f) { } }; @@ -1140,12 +1140,6 @@ private: PointerProperties lastGestureProperties[MAX_POINTERS]; PointerCoords lastGestureCoords[MAX_POINTERS]; - // Pointer coords and ids for the current spots. - PointerControllerInterface::SpotGesture spotGesture; - BitSet32 spotIdBits; // same set of ids as touch ids - uint32_t spotIdToIndex[MAX_POINTER_ID + 1]; - PointerCoords spotCoords[MAX_POINTERS]; - // Time the pointer gesture last went down. nsecs_t downTime; @@ -1192,8 +1186,6 @@ private: currentGestureIdBits.clear(); lastGestureMode = NEUTRAL; lastGestureIdBits.clear(); - spotGesture = PointerControllerInterface::SPOT_GESTURE_NEUTRAL; - spotIdBits.clear(); downTime = 0; velocityTracker.clear(); resetTap(); @@ -1219,7 +1211,6 @@ private: void dispatchPointerGestures(nsecs_t when, uint32_t policyFlags, bool isTimeout); bool preparePointerGestures(nsecs_t when, bool* outCancelPreviousGesture, bool* outFinishPreviousGesture, bool isTimeout); - void moveSpotsLocked(); // Dispatches a motion event. // If the changedId is >= 0 and the action is POINTER_DOWN or POINTER_UP, the diff --git a/services/input/PointerController.cpp b/services/input/PointerController.cpp index c18ebcfccaae..12c7cba16cca 100644 --- a/services/input/PointerController.cpp +++ b/services/input/PointerController.cpp @@ -240,15 +240,15 @@ void PointerController::setPresentation(Presentation presentation) { } } -void PointerController::setSpots(SpotGesture spotGesture, - const PointerCoords* spotCoords, const uint32_t* spotIdToIndex, BitSet32 spotIdBits) { +void PointerController::setSpots(const PointerCoords* spotCoords, + const uint32_t* spotIdToIndex, BitSet32 spotIdBits) { #if DEBUG_POINTER_UPDATES - LOGD("setSpots: spotGesture=%d", spotGesture); + LOGD("setSpots: idBits=%08x", spotIdBits.value); for (BitSet32 idBits(spotIdBits); !idBits.isEmpty(); ) { uint32_t id = idBits.firstMarkedBit(); idBits.clearBit(id); const PointerCoords& c = spotCoords[spotIdToIndex[id]]; - LOGD(" spot %d: position=(%0.3f, %0.3f), pressure=%0.3f", id, + LOGD(" spot %d: position=(%0.3f, %0.3f), pressure=%0.3f", id, c.getAxisValue(AMOTION_EVENT_AXIS_X), c.getAxisValue(AMOTION_EVENT_AXIS_Y), c.getAxisValue(AMOTION_EVENT_AXIS_PRESSURE)); diff --git a/services/input/PointerController.h b/services/input/PointerController.h index 1c21db16d3a3..700ef7295fe7 100644 --- a/services/input/PointerController.h +++ b/services/input/PointerController.h @@ -90,38 +90,6 @@ public: /* Sets the mode of the pointer controller. */ virtual void setPresentation(Presentation presentation) = 0; - // Describes the current gesture. - enum SpotGesture { - // No gesture. - // Do not display any spots. - SPOT_GESTURE_NEUTRAL, - // Tap at current location. - // Briefly display one spot at the tapped location. - SPOT_GESTURE_TAP, - // Drag at current location. - // Display spot at pressed location. - SPOT_GESTURE_DRAG, - // Button pressed but no finger is down. - // Display spot at pressed location. - SPOT_GESTURE_BUTTON_CLICK, - // Button pressed and a finger is down. - // Display spot at pressed location. - SPOT_GESTURE_BUTTON_DRAG, - // One finger down and hovering. - // Display spot at the hovered location. - SPOT_GESTURE_HOVER, - // Two fingers down but not sure in which direction they are moving so we consider - // it a press at the pointer location. - // Display two spots near the pointer location. - SPOT_GESTURE_PRESS, - // Two fingers down and moving in same direction. - // Display two spots near the pointer location. - SPOT_GESTURE_SWIPE, - // Two or more fingers down and moving in arbitrary directions. - // Display two or more spots near the pointer location, one for each finger. - SPOT_GESTURE_FREEFORM, - }; - /* Sets the spots for the current gesture. * The spots are not subject to the inactivity timeout like the pointer * itself it since they are expected to remain visible for so long as @@ -131,8 +99,7 @@ public: * For spotCoords, pressure != 0 indicates that the spot's location is being * pressed (not hovering). */ - virtual void setSpots(SpotGesture spotGesture, - const PointerCoords* spotCoords, const uint32_t* spotIdToIndex, + virtual void setSpots(const PointerCoords* spotCoords, const uint32_t* spotIdToIndex, BitSet32 spotIdBits) = 0; /* Removes all spots. */ @@ -198,8 +165,8 @@ public: virtual void unfade(Transition transition); virtual void setPresentation(Presentation presentation); - virtual void setSpots(SpotGesture spotGesture, - const PointerCoords* spotCoords, const uint32_t* spotIdToIndex, BitSet32 spotIdBits); + virtual void setSpots(const PointerCoords* spotCoords, + const uint32_t* spotIdToIndex, BitSet32 spotIdBits); virtual void clearSpots(); void setDisplaySize(int32_t width, int32_t height); diff --git a/services/input/tests/InputReader_test.cpp b/services/input/tests/InputReader_test.cpp index 00b4222f68b8..d04c9e7db145 100644 --- a/services/input/tests/InputReader_test.cpp +++ b/services/input/tests/InputReader_test.cpp @@ -101,8 +101,8 @@ private: virtual void setPresentation(Presentation presentation) { } - virtual void setSpots(SpotGesture spotGesture, - const PointerCoords* spotCoords, const uint32_t* spotIdToIndex, BitSet32 spotIdBits) { + virtual void setSpots(const PointerCoords* spotCoords, + const uint32_t* spotIdToIndex, BitSet32 spotIdBits) { } virtual void clearSpots() { diff --git a/services/java/com/android/server/BackupManagerService.java b/services/java/com/android/server/BackupManagerService.java index 7c6d3c144346..c28732e8d2b4 100644 --- a/services/java/com/android/server/BackupManagerService.java +++ b/services/java/com/android/server/BackupManagerService.java @@ -134,6 +134,7 @@ class BackupManagerService extends IBackupManager.Stub { // Timeout intervals for agent backup & restore operations static final long TIMEOUT_BACKUP_INTERVAL = 30 * 1000; static final long TIMEOUT_FULL_BACKUP_INTERVAL = 5 * 60 * 1000; + static final long TIMEOUT_SHARED_BACKUP_INTERVAL = 30 * 60 * 1000; static final long TIMEOUT_RESTORE_INTERVAL = 60 * 1000; // User confirmation timeout for a full backup/restore operation @@ -1691,7 +1692,7 @@ class BackupManagerService extends IBackupManager.Stub { public void run() { final List<PackageInfo> packagesToBackup; - Slog.i(TAG, "--- Performing full-dataset restore ---"); + Slog.i(TAG, "--- Performing full-dataset backup ---"); sendStartBackup(); // doAllApps supersedes the package set if any @@ -1720,64 +1721,23 @@ class BackupManagerService extends IBackupManager.Stub { } } - // Now back up the app data via the agent mechanism PackageInfo pkg = null; try { + // Now back up the app data via the agent mechanism int N = packagesToBackup.size(); for (int i = 0; i < N; i++) { pkg = packagesToBackup.get(i); + backupOnePackage(pkg); + } - Slog.d(TAG, "Binding to full backup agent : " + pkg.packageName); - - IBackupAgent agent = bindToAgentSynchronous(pkg.applicationInfo, - IApplicationThread.BACKUP_MODE_FULL); - if (agent != null) { - try { - ApplicationInfo app = pkg.applicationInfo; - boolean sendApk = mIncludeApks - && ((app.flags & ApplicationInfo.FLAG_FORWARD_LOCK) == 0) - && ((app.flags & ApplicationInfo.FLAG_SYSTEM) == 0 || - (app.flags & ApplicationInfo.FLAG_UPDATED_SYSTEM_APP) != 0); - - sendOnBackupPackage(pkg.packageName); - - { - BackupDataOutput output = new BackupDataOutput( - mOutputFile.getFileDescriptor()); - - if (DEBUG) Slog.d(TAG, "Writing manifest for " + pkg.packageName); - writeAppManifest(pkg, mManifestFile, sendApk); - FullBackup.backupToTar(pkg.packageName, null, null, - mFilesDir.getAbsolutePath(), - mManifestFile.getAbsolutePath(), - output); - } - - if (DEBUG) Slog.d(TAG, "Calling doBackup()"); - final int token = generateToken(); - prepareOperationTimeout(token, TIMEOUT_FULL_BACKUP_INTERVAL); - agent.doBackup(null, mOutputFile, null, sendApk, - token, mBackupManagerBinder); - boolean success = waitUntilOperationComplete(token); - if (!success) { - Slog.d(TAG, "Full backup failed on package " + pkg.packageName); - } else { - if (DEBUG) Slog.d(TAG, "Full backup success: " + pkg.packageName); - } - } catch (IOException e) { - Slog.e(TAG, "Error backing up " + pkg.packageName, e); - } - } else { - Slog.w(TAG, "Unable to bind to full agent for " + pkg.packageName); - } - tearDown(pkg); + // Finally, shared storage if requested + if (mIncludeShared) { + backupSharedStorage(); } } catch (RemoteException e) { Slog.e(TAG, "App died during full backup"); } finally { - if (pkg != null) { - tearDown(pkg); - } + tearDown(pkg); try { mOutputFile.close(); } catch (IOException e) { @@ -1796,6 +1756,79 @@ class BackupManagerService extends IBackupManager.Stub { } } + private void backupOnePackage(PackageInfo pkg) throws RemoteException { + Slog.d(TAG, "Binding to full backup agent : " + pkg.packageName); + + IBackupAgent agent = bindToAgentSynchronous(pkg.applicationInfo, + IApplicationThread.BACKUP_MODE_FULL); + if (agent != null) { + try { + ApplicationInfo app = pkg.applicationInfo; + boolean sendApk = mIncludeApks + && ((app.flags & ApplicationInfo.FLAG_FORWARD_LOCK) == 0) + && ((app.flags & ApplicationInfo.FLAG_SYSTEM) == 0 || + (app.flags & ApplicationInfo.FLAG_UPDATED_SYSTEM_APP) != 0); + + sendOnBackupPackage(pkg.packageName); + + { + BackupDataOutput output = new BackupDataOutput( + mOutputFile.getFileDescriptor()); + + if (DEBUG) Slog.d(TAG, "Writing manifest for " + pkg.packageName); + writeAppManifest(pkg, mManifestFile, sendApk); + FullBackup.backupToTar(pkg.packageName, null, null, + mFilesDir.getAbsolutePath(), + mManifestFile.getAbsolutePath(), + output); + } + + if (DEBUG) Slog.d(TAG, "Calling doBackup()"); + final int token = generateToken(); + prepareOperationTimeout(token, TIMEOUT_FULL_BACKUP_INTERVAL); + agent.doBackup(null, mOutputFile, null, sendApk, + token, mBackupManagerBinder); + if (!waitUntilOperationComplete(token)) { + Slog.e(TAG, "Full backup failed on package " + pkg.packageName); + } else { + if (DEBUG) Slog.d(TAG, "Full backup success: " + pkg.packageName); + } + } catch (IOException e) { + Slog.e(TAG, "Error backing up " + pkg.packageName, e); + } + } else { + Slog.w(TAG, "Unable to bind to full agent for " + pkg.packageName); + } + tearDown(pkg); + } + + private void backupSharedStorage() throws RemoteException { + PackageInfo pkg = null; + try { + pkg = mPackageManager.getPackageInfo("com.android.sharedstoragebackup", 0); + IBackupAgent agent = bindToAgentSynchronous(pkg.applicationInfo, + IApplicationThread.BACKUP_MODE_FULL); + if (agent != null) { + sendOnBackupPackage("Shared storage"); + + final int token = generateToken(); + prepareOperationTimeout(token, TIMEOUT_SHARED_BACKUP_INTERVAL); + agent.doBackup(null, mOutputFile, null, false, token, mBackupManagerBinder); + if (!waitUntilOperationComplete(token)) { + Slog.e(TAG, "Full backup failed on shared storage"); + } else { + if (DEBUG) Slog.d(TAG, "Full shared storage backup success"); + } + } else { + Slog.e(TAG, "Could not bind to shared storage backup agent"); + } + } catch (NameNotFoundException e) { + Slog.e(TAG, "Shared storage backup package not found"); + } finally { + tearDown(pkg); + } + } + private void writeAppManifest(PackageInfo pkg, File manifestFile, boolean withApk) throws IOException { // Manifest format. All data are strings ending in LF: @@ -1836,23 +1869,24 @@ class BackupManagerService extends IBackupManager.Stub { } private void tearDown(PackageInfo pkg) { - final ApplicationInfo app = pkg.applicationInfo; - try { - // unbind and tidy up even on timeout or failure, just in case - mActivityManager.unbindBackupAgent(app); - - // The agent was running with a stub Application object, so shut it down. - // !!! We hardcode the confirmation UI's package name here rather than use a - // manifest flag! TODO something less direct. - if (app.uid != Process.SYSTEM_UID - && !pkg.packageName.equals("com.android.backupconfirm")) { - if (DEBUG) Slog.d(TAG, "Backup complete, killing host process"); - mActivityManager.killApplicationProcess(app.processName, app.uid); - } else { - if (DEBUG) Slog.d(TAG, "Not killing after restore: " + app.processName); + if (pkg != null) { + final ApplicationInfo app = pkg.applicationInfo; + if (app != null) { + try { + // unbind and tidy up even on timeout or failure, just in case + mActivityManager.unbindBackupAgent(app); + + // The agent was running with a stub Application object, so shut it down. + if (app.uid != Process.SYSTEM_UID) { + if (DEBUG) Slog.d(TAG, "Backup complete, killing host process"); + mActivityManager.killApplicationProcess(app.processName, app.uid); + } else { + if (DEBUG) Slog.d(TAG, "Not killing after restore: " + app.processName); + } + } catch (RemoteException e) { + Slog.d(TAG, "Lost app trying to shut down"); + } } - } catch (RemoteException e) { - Slog.d(TAG, "Lost app trying to shut down"); } } @@ -1949,6 +1983,7 @@ class BackupManagerService extends IBackupManager.Stub { // with a whitelist of packages known to be unclearable. mClearedPackages.add("android"); mClearedPackages.add("com.android.providers.settings"); + } class RestoreFileRunnable implements Runnable { @@ -1988,6 +2023,11 @@ class BackupManagerService extends IBackupManager.Stub { Slog.i(TAG, "--- Performing full-dataset restore ---"); sendStartRestore(); + // Are we able to restore shared-storage data? + if (Environment.getExternalStorageState().equals(Environment.MEDIA_MOUNTED)) { + mPackagePolicies.put("com.android.sharedstoragebackup", RestorePolicy.ACCEPT); + } + try { byte[] buffer = new byte[32 * 1024]; FileInputStream instream = new FileInputStream(mInputFile.getFileDescriptor()); @@ -2707,7 +2747,9 @@ class BackupManagerService extends IBackupManager.Stub { info.path, 0, FullBackup.SHARED_PREFIX.length())) { // File in shared storage. !!! TODO: implement this. info.path = info.path.substring(FullBackup.SHARED_PREFIX.length()); + info.packageName = "com.android.sharedstoragebackup"; info.domain = FullBackup.SHARED_STORAGE_TOKEN; + if (DEBUG) Slog.i(TAG, "File in shared storage: " + info.path); } else if (FullBackup.APPS_PREFIX.regionMatches(0, info.path, 0, FullBackup.APPS_PREFIX.length())) { // App content! Parse out the package name and domain @@ -2817,6 +2859,7 @@ class BackupManagerService extends IBackupManager.Stub { final int end = offset + maxChars; for (int i = offset; i < end; i++) { final byte b = data[i]; + // Numeric fields in tar can terminate with either NUL or SPC if (b == 0 || b == ' ') break; if (b < '0' || b > ('0' + radix - 1)) { throw new IOException("Invalid number in header"); @@ -2829,8 +2872,8 @@ class BackupManagerService extends IBackupManager.Stub { String extractString(byte[] data, int offset, int maxChars) throws IOException { final int end = offset + maxChars; int eos = offset; - // tar string fields can end with either NUL or SPC - while (eos < end && data[eos] != 0 && data[eos] != ' ') eos++; + // tar string fields terminate early with a NUL + while (eos < end && data[eos] != 0) eos++; return new String(data, offset, eos-offset, "US-ASCII"); } diff --git a/services/java/com/android/server/ConnectivityService.java b/services/java/com/android/server/ConnectivityService.java index dd76eb8c705c..e3d4c45cf62a 100644 --- a/services/java/com/android/server/ConnectivityService.java +++ b/services/java/com/android/server/ConnectivityService.java @@ -16,6 +16,7 @@ package com.android.server; +import static android.Manifest.permission.READ_PHONE_STATE; import static android.Manifest.permission.UPDATE_DEVICE_STATS; import static android.net.ConnectivityManager.isNetworkTypeValid; import static android.net.NetworkPolicyManager.RULE_ALLOW_ALL; @@ -38,6 +39,7 @@ import android.net.MobileDataStateTracker; import android.net.NetworkConfig; import android.net.NetworkInfo; import android.net.NetworkInfo.DetailedState; +import android.net.NetworkState; import android.net.NetworkStateTracker; import android.net.NetworkUtils; import android.net.Proxy; @@ -65,6 +67,7 @@ import android.util.SparseIntArray; import com.android.internal.telephony.Phone; import com.android.server.connectivity.Tethering; +import com.google.android.collect.Lists; import java.io.FileDescriptor; import java.io.IOException; @@ -458,6 +461,12 @@ public class ConnectivityService extends IConnectivityManager.Stub { mTethering.getTetherableBluetoothRegexs().length != 0) && mTethering.getUpstreamIfaceRegexs().length != 0); + try { + nmService.registerObserver(mTethering); + } catch (RemoteException e) { + loge("Error registering observer :" + e); + } + if (DBG) { mInetLog = new ArrayList(); } @@ -557,25 +566,32 @@ public class ConnectivityService extends IConnectivityManager.Stub { */ private boolean isNetworkBlocked(NetworkInfo info, int uid) { synchronized (mUidRules) { - return isNetworkBlockedLocked(info, uid); + // 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); + + if (networkIsPaid && (uidRules & RULE_REJECT_PAID) != 0) { + return true; + } + + // no restrictive rules; network is visible + return false; } } /** - * Check if UID is blocked from using the given {@link NetworkInfo}. + * Return a filtered version of the given {@link NetworkInfo}, potentially + * marked {@link DetailedState#BLOCKED} based on + * {@link #isNetworkBlocked(NetworkInfo, int)}. */ - private boolean isNetworkBlockedLocked(NetworkInfo info, int uid) { - // 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); - - if (networkIsPaid && (uidRules & RULE_REJECT_PAID) != 0) { - return true; + private NetworkInfo filterNetworkInfo(NetworkInfo info, int uid) { + if (isNetworkBlocked(info, uid)) { + // network is blocked; clone and override state + info = new NetworkInfo(info); + info.setDetailedState(DetailedState.BLOCKED, null, null); } - - // no restrictive rules; network is visible - return false; + return info; } /** @@ -610,12 +626,7 @@ public class ConnectivityService extends IConnectivityManager.Stub { if (isNetworkTypeValid(networkType)) { final NetworkStateTracker tracker = mNetTrackers[networkType]; if (tracker != null) { - info = tracker.getNetworkInfo(); - if (isNetworkBlocked(info, uid)) { - // network is blocked; clone and override state - info = new NetworkInfo(info); - info.setDetailedState(DetailedState.BLOCKED, null, null); - } + info = filterNetworkInfo(tracker.getNetworkInfo(), uid); } } return info; @@ -625,22 +636,15 @@ public class ConnectivityService extends IConnectivityManager.Stub { public NetworkInfo[] getAllNetworkInfo() { enforceAccessPermission(); final int uid = Binder.getCallingUid(); - final NetworkInfo[] result = new NetworkInfo[mNetworksDefined]; - int i = 0; + final ArrayList<NetworkInfo> result = Lists.newArrayList(); synchronized (mUidRules) { for (NetworkStateTracker tracker : mNetTrackers) { if (tracker != null) { - NetworkInfo info = tracker.getNetworkInfo(); - if (isNetworkBlockedLocked(info, uid)) { - // network is blocked; clone and override state - info = new NetworkInfo(info); - info.setDetailedState(DetailedState.BLOCKED, null, null); - } - result[i++] = info; + result.add(filterNetworkInfo(tracker.getNetworkInfo(), uid)); } } } - return result; + return result.toArray(new NetworkInfo[result.size()]); } /** @@ -668,6 +672,23 @@ public class ConnectivityService extends IConnectivityManager.Stub { return null; } + @Override + public NetworkState[] getAllNetworkState() { + enforceAccessPermission(); + final int uid = Binder.getCallingUid(); + final ArrayList<NetworkState> result = Lists.newArrayList(); + synchronized (mUidRules) { + for (NetworkStateTracker tracker : mNetTrackers) { + if (tracker != null) { + final NetworkInfo info = filterNetworkInfo(tracker.getNetworkInfo(), uid); + result.add(new NetworkState( + info, tracker.getLinkProperties(), tracker.getLinkCapabilities())); + } + } + } + return result.toArray(new NetworkState[result.size()]); + } + public boolean setRadios(boolean turnOn) { boolean result = true; enforceChangePermission(); diff --git a/services/java/com/android/server/MountService.java b/services/java/com/android/server/MountService.java index f78dca96d243..c86f96237f44 100644 --- a/services/java/com/android/server/MountService.java +++ b/services/java/com/android/server/MountService.java @@ -1121,16 +1121,20 @@ class MountService extends IMountService.Stub implements INativeDaemonConnectorC com.android.internal.R.styleable.Storage_emulated, false); int mtpReserve = a.getInt( com.android.internal.R.styleable.Storage_mtpReserve, 0); + boolean allowMassStorage = a.getBoolean( + com.android.internal.R.styleable.Storage_allowMassStorage, false); Slog.d(TAG, "got storage path: " + path + " description: " + description + " primary: " + primary + " removable: " + removable + - " emulated: " + emulated + " mtpReserve: " + mtpReserve); + " emulated: " + emulated + " mtpReserve: " + mtpReserve + + " allowMassStorage: " + allowMassStorage); if (path == null || description == null) { Slog.e(TAG, "path or description is null in readStorageList"); } else { String pathString = path.toString(); StorageVolume volume = new StorageVolume(pathString, - description.toString(), removable, emulated, mtpReserve); + description.toString(), removable, emulated, + mtpReserve, allowMassStorage); if (primary) { if (mPrimaryVolume == null) { mPrimaryVolume = volume; diff --git a/services/java/com/android/server/SystemBackupAgent.java b/services/java/com/android/server/SystemBackupAgent.java index 99c8af62d1d6..08c66996128e 100644 --- a/services/java/com/android/server/SystemBackupAgent.java +++ b/services/java/com/android/server/SystemBackupAgent.java @@ -138,7 +138,7 @@ public class SystemBackupAgent extends BackupAgentHelper { if (outFile == null) { Slog.w(TAG, "Skipping unrecognized system file: [ " + domain + " : " + path + " ]"); } - FullBackup.restoreToFile(data, size, type, mode, mtime, outFile); + FullBackup.restoreToFile(data, size, type, mode, mtime, outFile, true); if (restoredWallpaper) { WallpaperManagerService wallpaper = diff --git a/services/java/com/android/server/SystemServer.java b/services/java/com/android/server/SystemServer.java index 596cbac4937f..fd03e5f8930a 100644 --- a/services/java/com/android/server/SystemServer.java +++ b/services/java/com/android/server/SystemServer.java @@ -305,6 +305,7 @@ class ServerThread extends Thread { Slog.i(TAG, "Connectivity Service"); connectivity = new ConnectivityService(context, networkManagement, networkPolicy); ServiceManager.addService(Context.CONNECTIVITY_SERVICE, connectivity); + networkStats.bindConnectivityManager(connectivity); } catch (Throwable e) { Slog.e(TAG, "Failure starting Connectivity Service", e); } diff --git a/services/java/com/android/server/WifiService.java b/services/java/com/android/server/WifiService.java index 41ec63a53c90..4e2501b460e7 100644 --- a/services/java/com/android/server/WifiService.java +++ b/services/java/com/android/server/WifiService.java @@ -321,6 +321,13 @@ public class WifiService extends IWifiManager.Stub { } break; } + case AsyncChannel.CMD_CHANNEL_DISCONNECTED: { + Slog.e(TAG, "WifiStateMachine channel lost, msg.arg1 =" + msg.arg1); + mWifiStateMachineChannel = null; + //Re-establish connection to state machine + mWsmChannel.connect(mContext, this, mWifiStateMachine.getHandler()); + break; + } default: { Slog.d(TAG, "WifiStateMachineHandler.handleMessage ignoring msg=" + msg); break; @@ -593,7 +600,12 @@ public class WifiService extends IWifiManager.Stub { */ public WifiConfiguration getWifiApConfiguration() { enforceAccessPermission(); - return mWifiStateMachine.syncGetWifiApConfiguration(mWifiStateMachineChannel); + if (mWifiStateMachineChannel != null) { + return mWifiStateMachine.syncGetWifiApConfiguration(mWifiStateMachineChannel); + } else { + Slog.e(TAG, "mWifiStateMachineChannel is not initialized"); + return null; + } } /** diff --git a/services/java/com/android/server/WifiWatchdogService.java b/services/java/com/android/server/WifiWatchdogService.java index 6ef6963c8a69..6c7895e35b14 100644 --- a/services/java/com/android/server/WifiWatchdogService.java +++ b/services/java/com/android/server/WifiWatchdogService.java @@ -36,16 +36,21 @@ import android.provider.Settings; import android.text.TextUtils; import android.util.Slog; +import java.io.BufferedInputStream; +import java.io.InputStream; import java.io.IOException; import java.net.DatagramPacket; import java.net.DatagramSocket; +import java.net.HttpURLConnection; import java.net.InetAddress; import java.net.SocketException; import java.net.SocketTimeoutException; import java.net.UnknownHostException; +import java.net.URL; import java.util.Collection; import java.util.List; import java.util.Random; +import java.util.Scanner; /** * {@link WifiWatchdogService} monitors the initial connection to a Wi-Fi @@ -195,6 +200,34 @@ public class WifiWatchdogService { return Settings.Secure.getInt(mContentResolver, Settings.Secure.WIFI_WATCHDOG_PING_DELAY_MS, 250); } + + /** + * @see android.provider.Settings.Secure#WIFI_WATCHDOG_WALLED_GARDEN_TEST_ENABLED + */ + private Boolean isWalledGardenTestEnabled() { + return Settings.Secure.getInt(mContentResolver, + Settings.Secure.WIFI_WATCHDOG_WALLED_GARDEN_TEST_ENABLED, 1) == 1; + } + + /** + * @see android.provider.Settings.Secure#WIFI_WATCHDOG_WALLED_GARDEN_URL + */ + private String getWalledGardenUrl() { + String url = Settings.Secure.getString(mContentResolver, + Settings.Secure.WIFI_WATCHDOG_WALLED_GARDEN_URL); + if (TextUtils.isEmpty(url)) return "http://www.google.com/"; + return url; + } + + /** + * @see android.provider.Settings.Secure#WIFI_WATCHDOG_WALLED_GARDEN_PATTERN + */ + private String getWalledGardenPattern() { + String pattern = Settings.Secure.getString(mContentResolver, + Settings.Secure.WIFI_WATCHDOG_WALLED_GARDEN_PATTERN); + if (TextUtils.isEmpty(pattern)) return "<title>.*Google.*</title>"; + return pattern; + } /** * @see android.provider.Settings.Secure#WIFI_WATCHDOG_ACCEPTABLE_PACKET_LOSS_PERCENTAGE @@ -509,7 +542,7 @@ public class WifiWatchdogService { // This access point does not require a watchdog, so queue idle on the main thread mHandler.idle(); } - mHandler.checkWalledGarden(ssid); + if (isWalledGardenTestEnabled()) mHandler.checkWalledGarden(ssid); } /** @@ -1118,19 +1151,35 @@ public class WifiWatchdogService { } } + /** + * DNS based detection techniques do not work at all hotspots. The one sure way to check + * a walled garden is to see if a URL fetch on a known address fetches the data we + * expect + */ private boolean isWalledGardenConnection() { - //One way to detect a walled garden is to see if multiple DNS queries - //resolve to the same IP address + InputStream in = null; + HttpURLConnection urlConnection = null; try { - String host1 = "www.google.com"; - String host2 = "www.android.com"; - String address1 = InetAddress.getByName(host1).getHostAddress(); - String address2 = InetAddress.getByName(host2).getHostAddress(); - if (address1.equals(address2)) return true; - } catch (UnknownHostException e) { + URL url = new URL(getWalledGardenUrl()); + urlConnection = (HttpURLConnection) url.openConnection(); + in = new BufferedInputStream(urlConnection.getInputStream()); + Scanner scanner = new Scanner(in); + if (scanner.findInLine(getWalledGardenPattern()) != null) { + return false; + } else { + return true; + } + } catch (IOException e) { return false; + } finally { + if (in != null) { + try { + in.close(); + } catch (IOException e) { + } + } + if (urlConnection != null) urlConnection.disconnect(); } - return false; } private void handleWalledGardenCheck(String ssid) { diff --git a/services/java/com/android/server/accessibility/TouchExplorer.java b/services/java/com/android/server/accessibility/TouchExplorer.java index 4c7f595de111..aab189a68cda 100644 --- a/services/java/com/android/server/accessibility/TouchExplorer.java +++ b/services/java/com/android/server/accessibility/TouchExplorer.java @@ -226,8 +226,9 @@ public class TouchExplorer implements Explorer { // Send a hover for every finger down so the user gets feedback // where she is currently touching. mSendHoverDelayed.forceSendAndRemove(); - mSendHoverDelayed.post(event, MotionEvent.ACTION_HOVER_ENTER, 1, policyFlags, - DELAY_SEND_HOVER_MOVE); + final int pointerIdBits = (1 << event.getActionIndex()); + mSendHoverDelayed.post(event, MotionEvent.ACTION_HOVER_ENTER, pointerIdBits, + policyFlags, DELAY_SEND_HOVER_MOVE); } break; case MotionEvent.ACTION_POINTER_DOWN: { switch (activePointerCount) { @@ -350,11 +351,12 @@ public class TouchExplorer implements Explorer { if (isDraggingGesture(event)) { // Two pointers moving in the same direction within // a given distance perform a drag. - mCurrentState = STATE_DRAGGING; + mCurrentState = STATE_DRAGGING; if (mTouchExploreGestureInProgress) { sendAccessibilityEvent(TYPE_TOUCH_EXPLORATION_GESTURE_END); mTouchExploreGestureInProgress = false; } + mLastTouchExploreEvent = null; mDraggingPointerId = pointerId; sendMotionEvent(event, MotionEvent.ACTION_DOWN, pointerIdBits, policyFlags); @@ -365,6 +367,7 @@ public class TouchExplorer implements Explorer { sendAccessibilityEvent(TYPE_TOUCH_EXPLORATION_GESTURE_END); mTouchExploreGestureInProgress = false; } + mLastTouchExploreEvent = null; sendDownForAllActiveNotInjectedPointers(event, policyFlags); } } break; @@ -382,6 +385,7 @@ public class TouchExplorer implements Explorer { sendAccessibilityEvent(TYPE_TOUCH_EXPLORATION_GESTURE_END); mTouchExploreGestureInProgress = false; } + mLastTouchExploreEvent = null; sendDownForAllActiveNotInjectedPointers(event, policyFlags); } } @@ -728,7 +732,7 @@ public class TouchExplorer implements Explorer { /** * Sends an event. * - * @param event The event to send. + * @param prototype The prototype from which to create the injected events. * @param action The action of the event. * @param pointerIdBits The bits of the pointers to send. * @param policyFlags The policy flags associated with the event. @@ -979,7 +983,7 @@ public class TouchExplorer implements Explorer { case MotionEvent.ACTION_DOWN: { // New gesture so restart tracking injected down pointers. mInjectedPointersDown = 0; - handleReceivedPointerDown(0, event); + handleReceivedPointerDown(event.getActionIndex(), event); } break; case MotionEvent.ACTION_POINTER_DOWN: { handleReceivedPointerDown(event.getActionIndex(), event); @@ -988,7 +992,7 @@ public class TouchExplorer implements Explorer { handleReceivedPointerMove(event); } break; case MotionEvent.ACTION_UP: { - handleReceivedPointerUp(0, event); + handleReceivedPointerUp(event.getActionIndex(), event); } break; case MotionEvent.ACTION_POINTER_UP: { handleReceivedPointerUp(event.getActionIndex(), event); @@ -1008,13 +1012,13 @@ public class TouchExplorer implements Explorer { final int action = event.getActionMasked(); switch (action) { case MotionEvent.ACTION_DOWN: { - handleInjectedPointerDown(0, event); + handleInjectedPointerDown(event.getActionIndex(), event); } break; case MotionEvent.ACTION_POINTER_DOWN: { handleInjectedPointerDown(event.getActionIndex(), event); } break; case MotionEvent.ACTION_UP: { - handleInjectedPointerUp(0, event); + handleInjectedPointerUp(event.getActionIndex(), event); } break; case MotionEvent.ACTION_POINTER_UP: { handleInjectedPointerUp(event.getActionIndex(), event); diff --git a/services/java/com/android/server/am/ActivityManagerService.java b/services/java/com/android/server/am/ActivityManagerService.java index c2a8a1db01ab..da9f1d6844b5 100644 --- a/services/java/com/android/server/am/ActivityManagerService.java +++ b/services/java/com/android/server/am/ActivityManagerService.java @@ -92,7 +92,6 @@ import android.os.FileObserver; import android.os.FileUtils; import android.os.Handler; import android.os.IBinder; -import android.os.IInterface; import android.os.IPermissionController; import android.os.Looper; import android.os.Message; diff --git a/services/java/com/android/server/connectivity/Tethering.java b/services/java/com/android/server/connectivity/Tethering.java index 7ea05916c2fd..5fa26ef5f03e 100644 --- a/services/java/com/android/server/connectivity/Tethering.java +++ b/services/java/com/android/server/connectivity/Tethering.java @@ -129,13 +129,6 @@ public class Tethering extends INetworkManagementEventObserver.Stub { mNMService = nmService; mLooper = looper; - // register for notifications from NetworkManagement Service - try { - mNMService.registerObserver(this); - } catch (RemoteException e) { - Log.e(TAG, "Error registering observer :" + e); - } - mIfaces = new HashMap<String, TetherInterfaceSM>(); // make our own thread so we don't anr the system diff --git a/services/java/com/android/server/net/InterfaceIdentity.java b/services/java/com/android/server/net/InterfaceIdentity.java new file mode 100644 index 000000000000..ff86581effc9 --- /dev/null +++ b/services/java/com/android/server/net/InterfaceIdentity.java @@ -0,0 +1,73 @@ +/* + * 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 new file mode 100644 index 000000000000..79feb95f3463 --- /dev/null +++ b/services/java/com/android/server/net/NetworkIdentity.java @@ -0,0 +1,208 @@ +/* + * 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.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 = in.readUTF(); + 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); + out.writeUTF(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_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); + } + +} diff --git a/services/java/com/android/server/net/NetworkPolicyManagerService.java b/services/java/com/android/server/net/NetworkPolicyManagerService.java index 17c7161d06d6..1a90a92449d3 100644 --- a/services/java/com/android/server/net/NetworkPolicyManagerService.java +++ b/services/java/com/android/server/net/NetworkPolicyManagerService.java @@ -89,6 +89,8 @@ public class NetworkPolicyManagerService extends INetworkPolicyManager.Stub { // TODO: keep record of billing cycle details, and limit rules // TODO: keep map of interfaces-to-billing-relationship + // TODO: dispatch callbacks through handler when locked + public NetworkPolicyManagerService(Context context, IActivityManager activityManager, IPowerManager powerManager, INetworkStatsService networkStats) { mContext = checkNotNull(context, "missing context"); diff --git a/services/java/com/android/server/net/NetworkStatsService.java b/services/java/com/android/server/net/NetworkStatsService.java index d9c1f25b7293..3892de81319b 100644 --- a/services/java/com/android/server/net/NetworkStatsService.java +++ b/services/java/com/android/server/net/NetworkStatsService.java @@ -1,5 +1,5 @@ /* - * Copyright (C) 2008 The Android Open Source Project + * 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. @@ -16,12 +16,23 @@ package com.android.server.net; +import static android.Manifest.permission.CONNECTIVITY_INTERNAL; import static android.Manifest.permission.DUMP; import static android.Manifest.permission.SHUTDOWN; import static android.Manifest.permission.UPDATE_DEVICE_STATS; -import static android.net.ConnectivityManager.TYPE_MOBILE; -import static android.net.ConnectivityManager.TYPE_WIFI; +import static android.net.ConnectivityManager.CONNECTIVITY_ACTION; +import static android.net.NetworkStats.IFACE_ALL; import static android.net.NetworkStats.UID_ALL; +import static android.provider.Settings.Secure.NETSTATS_DETAIL_BUCKET_DURATION; +import static android.provider.Settings.Secure.NETSTATS_DETAIL_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_SUMMARY_BUCKET_DURATION; +import static android.provider.Settings.Secure.NETSTATS_SUMMARY_MAX_HISTORY; +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 com.android.internal.util.Preconditions.checkNotNull; import android.app.AlarmManager; import android.app.IAlarmManager; @@ -30,29 +41,33 @@ import android.content.BroadcastReceiver; import android.content.Context; import android.content.Intent; import android.content.IntentFilter; +import android.content.pm.ApplicationInfo; +import android.net.IConnectivityManager; import android.net.INetworkStatsService; -import android.net.LinkProperties; +import android.net.NetworkInfo; +import android.net.NetworkState; import android.net.NetworkStats; import android.net.NetworkStatsHistory; -import android.net.wifi.WifiManager; import android.os.Handler; import android.os.HandlerThread; import android.os.INetworkManagementService; import android.os.RemoteException; import android.os.SystemClock; +import android.provider.Settings; import android.telephony.TelephonyManager; -import android.text.format.DateUtils; import android.util.NtpTrustedTime; import android.util.Slog; +import android.util.SparseArray; import android.util.TrustedTime; -import com.android.internal.telephony.Phone; -import com.android.internal.telephony.TelephonyIntents; +import com.google.android.collect.Lists; import com.google.android.collect.Maps; import java.io.FileDescriptor; import java.io.PrintWriter; +import java.util.ArrayList; import java.util.HashMap; +import java.util.HashSet; /** * Collect and persist detailed network statistics, and provide this data to @@ -67,39 +82,49 @@ public class NetworkStatsService extends INetworkStatsService.Stub { private final IAlarmManager mAlarmManager; private final TrustedTime mTime; + private IConnectivityManager mConnManager; + private static final String ACTION_NETWORK_STATS_POLL = "com.android.server.action.NETWORK_STATS_POLL"; private PendingIntent mPollIntent; - // TODO: move tweakable params to Settings.Secure // TODO: listen for kernel push events through netd instead of polling private static final long KB_IN_BYTES = 1024; + private static final long MB_IN_BYTES = 1024 * KB_IN_BYTES; + private static final long GB_IN_BYTES = 1024 * MB_IN_BYTES; - private static final long POLL_INTERVAL = AlarmManager.INTERVAL_FIFTEEN_MINUTES; - private static final long SUMMARY_BUCKET_DURATION = 6 * DateUtils.HOUR_IN_MILLIS; - private static final long SUMMARY_MAX_HISTORY = 90 * DateUtils.DAY_IN_MILLIS; - - // TODO: remove these high-frequency testing values -// private static final long POLL_INTERVAL = 5 * DateUtils.SECOND_IN_MILLIS; -// private static final long SUMMARY_BUCKET_DURATION = 10 * DateUtils.SECOND_IN_MILLIS; -// private static final long SUMMARY_MAX_HISTORY = 2 * DateUtils.MINUTE_IN_MILLIS; + private LongSecureSetting mPollInterval = new LongSecureSetting( + NETSTATS_POLL_INTERVAL, 15 * MINUTE_IN_MILLIS); + private LongSecureSetting mPersistThreshold = new LongSecureSetting( + NETSTATS_PERSIST_THRESHOLD, 64 * KB_IN_BYTES); - /** Minimum delta required to persist to disk. */ - private static final long SUMMARY_PERSIST_THRESHOLD = 64 * KB_IN_BYTES; + private LongSecureSetting mSummaryBucketDuration = new LongSecureSetting( + NETSTATS_SUMMARY_BUCKET_DURATION, 6 * HOUR_IN_MILLIS); + private LongSecureSetting mSummaryMaxHistory = new LongSecureSetting( + NETSTATS_SUMMARY_MAX_HISTORY, 90 * DAY_IN_MILLIS); + private LongSecureSetting mDetailBucketDuration = new LongSecureSetting( + NETSTATS_DETAIL_BUCKET_DURATION, 6 * HOUR_IN_MILLIS); + private LongSecureSetting mDetailMaxHistory = new LongSecureSetting( + NETSTATS_DETAIL_MAX_HISTORY, 90 * DAY_IN_MILLIS); - private static final long TIME_CACHE_MAX_AGE = DateUtils.DAY_IN_MILLIS; + private static final long TIME_CACHE_MAX_AGE = DAY_IN_MILLIS; private final Object mStatsLock = new Object(); /** Set of active ifaces during this boot. */ - private HashMap<String, InterfaceInfo> mActiveIface = Maps.newHashMap(); + private HashMap<String, InterfaceIdentity> mActiveIface = Maps.newHashMap(); + /** Set of historical stats for known ifaces. */ - private HashMap<InterfaceInfo, NetworkStatsHistory> mIfaceStats = Maps.newHashMap(); + private HashMap<InterfaceIdentity, NetworkStatsHistory> mSummaryStats = Maps.newHashMap(); + /** Set of historical stats for known UIDs. */ + private SparseArray<NetworkStatsHistory> mDetailStats = new SparseArray<NetworkStatsHistory>(); - private NetworkStats mLastPollStats; - private NetworkStats mLastPersistStats; + private NetworkStats mLastSummaryPoll; + private NetworkStats mLastSummaryPersist; + + private NetworkStats mLastDetailPoll; private final HandlerThread mHandlerThread; private final Handler mHandler; @@ -132,13 +157,9 @@ public class NetworkStatsService extends INetworkStatsService.Stub { readStatsLocked(); // watch other system services that claim interfaces - // TODO: protect incoming broadcast with permissions check. - // TODO: consider migrating this to ConnectivityService, but it might - // cause a circular dependency. - final IntentFilter interfaceFilter = new IntentFilter(); - interfaceFilter.addAction(TelephonyIntents.ACTION_ANY_DATA_CONNECTION_STATE_CHANGED); - interfaceFilter.addAction(WifiManager.NETWORK_STATE_CHANGED_ACTION); - mContext.registerReceiver(mInterfaceReceiver, interfaceFilter); + final IntentFilter ifaceFilter = new IntentFilter(); + ifaceFilter.addAction(CONNECTIVITY_ACTION); + mContext.registerReceiver(mIfaceReceiver, ifaceFilter, CONNECTIVITY_INTERNAL, mHandler); // listen for periodic polling events final IntentFilter pollFilter = new IntentFilter(ACTION_NETWORK_STATS_POLL); @@ -155,9 +176,13 @@ public class NetworkStatsService extends INetworkStatsService.Stub { } } + public void bindConnectivityManager(IConnectivityManager connManager) { + mConnManager = checkNotNull(connManager, "missing IConnectivityManager"); + } + /** * Clear any existing {@link #ACTION_NETWORK_STATS_POLL} alarms, and - * reschedule based on current {@link #POLL_INTERVAL} value. + * reschedule based on current {@link #mPollInterval} value. */ private void registerPollAlarmLocked() throws RemoteException { if (mPollIntent != null) { @@ -169,49 +194,72 @@ public class NetworkStatsService extends INetworkStatsService.Stub { final long currentRealtime = SystemClock.elapsedRealtime(); mAlarmManager.setInexactRepeating( - AlarmManager.ELAPSED_REALTIME, currentRealtime, POLL_INTERVAL, mPollIntent); + AlarmManager.ELAPSED_REALTIME, currentRealtime, mPollInterval.get(), mPollIntent); } @Override - public NetworkStatsHistory[] getNetworkStatsSummary(int networkType) { - // TODO: return history for requested types - return null; + public NetworkStatsHistory getHistoryForNetwork(int networkTemplate) { + // TODO: create relaxed permission for reading stats + mContext.enforceCallingOrSelfPermission(UPDATE_DEVICE_STATS, TAG); + + synchronized (mStatsLock) { + // combine all interfaces that match template + final String subscriberId = getActiveSubscriberId(); + final NetworkStatsHistory combined = new NetworkStatsHistory( + mSummaryBucketDuration.get()); + for (InterfaceIdentity ident : mSummaryStats.keySet()) { + final NetworkStatsHistory history = mSummaryStats.get(ident); + if (ident.matchesTemplate(networkTemplate, subscriberId)) { + combined.recordEntireHistory(history); + } + } + return combined; + } } @Override - public NetworkStatsHistory getNetworkStatsUid(int uid) { + public NetworkStatsHistory getHistoryForUid(int uid, int networkTemplate) { + // TODO: create relaxed permission for reading stats + mContext.enforceCallingOrSelfPermission(UPDATE_DEVICE_STATS, TAG); + // TODO: return history for requested uid return null; } + @Override + public NetworkStats getSummaryPerUid(long start, long end, int networkTemplate) { + // TODO: create relaxed permission for reading stats + mContext.enforceCallingOrSelfPermission(UPDATE_DEVICE_STATS, TAG); + + // TODO: apply networktemplate once granular uid stats are stored. + + synchronized (mStatsLock) { + final int size = mDetailStats.size(); + final NetworkStats.Builder stats = new NetworkStats.Builder(end - start, size); + + final long[] total = new long[2]; + for (int i = 0; i < size; i++) { + final int uid = mDetailStats.keyAt(i); + final NetworkStatsHistory history = mDetailStats.valueAt(i); + history.getTotalData(start, end, total); + stats.addEntry(IFACE_ALL, uid, total[0], total[1]); + } + return stats.build(); + } + } + /** - * Receiver that watches for other system components that claim network + * Receiver that watches for {@link IConnectivityManager} to claim network * interfaces. Used to associate {@link TelephonyManager#getSubscriberId()} * with mobile interfaces. */ - private BroadcastReceiver mInterfaceReceiver = new BroadcastReceiver() { + private BroadcastReceiver mIfaceReceiver = new BroadcastReceiver() { @Override public void onReceive(Context context, Intent intent) { - final String action = intent.getAction(); - if (TelephonyIntents.ACTION_ANY_DATA_CONNECTION_STATE_CHANGED.equals(action)) { - final LinkProperties prop = intent.getParcelableExtra( - Phone.DATA_LINK_PROPERTIES_KEY); - final String iface = prop != null ? prop.getInterfaceName() : null; - if (iface != null) { - final TelephonyManager teleManager = (TelephonyManager) context - .getSystemService(Context.TELEPHONY_SERVICE); - final InterfaceInfo info = new InterfaceInfo( - iface, TYPE_MOBILE, teleManager.getSubscriberId()); - reportActiveInterface(info); - } - } else if (WifiManager.NETWORK_STATE_CHANGED_ACTION.equals(action)) { - final LinkProperties prop = intent.getParcelableExtra( - WifiManager.EXTRA_LINK_PROPERTIES); - final String iface = prop != null ? prop.getInterfaceName() : null; - if (iface != null) { - final InterfaceInfo info = new InterfaceInfo(iface, TYPE_WIFI, null); - reportActiveInterface(info); - } + // on background handler thread, and verified CONNECTIVITY_INTERNAL + // permission above. + synchronized (mStatsLock) { + updateIfacesLocked(); } } }; @@ -219,8 +267,8 @@ public class NetworkStatsService extends INetworkStatsService.Stub { private BroadcastReceiver mPollReceiver = new BroadcastReceiver() { @Override public void onReceive(Context context, Intent intent) { - // already running on background handler, network/io is safe, and - // caller verified to have UPDATE_DEVICE_STATS permission above. + // on background handler thread, and verified UPDATE_DEVICE_STATS + // permission above. synchronized (mStatsLock) { // TODO: acquire wakelock while performing poll performPollLocked(); @@ -231,13 +279,49 @@ public class NetworkStatsService extends INetworkStatsService.Stub { private BroadcastReceiver mShutdownReceiver = new BroadcastReceiver() { @Override public void onReceive(Context context, Intent intent) { - // persist stats during clean shutdown + // verified SHUTDOWN permission above. synchronized (mStatsLock) { writeStatsLocked(); } } }; + /** + * 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}. + */ + private void updateIfacesLocked() { + if (LOGD) Slog.v(TAG, "updateIfacesLocked()"); + + // 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. + // TODO: verify that we only poll summary stats, not uid details + performPollLocked(); + + final NetworkState[] states; + try { + states = mConnManager.getAllNetworkState(); + } catch (RemoteException e) { + Slog.w(TAG, "problem reading network state"); + return; + } + + // rebuild active interfaces based on connected networks + mActiveIface.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); + ident.add(NetworkIdentity.buildNetworkIdentity(mContext, state)); + } + } + } + private void performPollLocked() { if (LOGD) Slog.v(TAG, "performPollLocked()"); @@ -250,58 +334,110 @@ public class NetworkStatsService extends INetworkStatsService.Stub { final long currentTime = mTime.hasCache() ? mTime.currentTimeMillis() : System.currentTimeMillis(); - final NetworkStats current; + final NetworkStats summary; + final NetworkStats detail; try { - current = mNetworkManager.getNetworkStatsSummary(); + summary = mNetworkManager.getNetworkStatsSummary(); + detail = mNetworkManager.getNetworkStatsDetail(); } catch (RemoteException e) { Slog.w(TAG, "problem reading network stats"); return; } - // update historical usage with delta since last poll - final NetworkStats pollDelta = computeStatsDelta(mLastPollStats, current); - final long timeStart = currentTime - pollDelta.elapsedRealtime; - for (String iface : pollDelta.getKnownIfaces()) { - final InterfaceInfo info = mActiveIface.get(iface); - if (info == null) { - if (LOGD) Slog.w(TAG, "unknown interface " + iface + ", ignoring stats"); + performSummaryPollLocked(summary, currentTime); + performDetailPollLocked(detail, currentTime); + + // decide if enough has changed to trigger persist + final NetworkStats persistDelta = computeStatsDelta(mLastSummaryPersist, summary); + final long persistThreshold = mPersistThreshold.get(); + for (String iface : persistDelta.getUniqueIfaces()) { + final int index = persistDelta.findIndex(iface, UID_ALL); + if (persistDelta.rx[index] > persistThreshold + || persistDelta.tx[index] > persistThreshold) { + writeStatsLocked(); + mLastSummaryPersist = summary; + break; + } + } + } + + /** + * Update {@link #mSummaryStats} historical usage. + */ + private void performSummaryPollLocked(NetworkStats summary, long currentTime) { + final ArrayList<String> unknownIface = Lists.newArrayList(); + + final NetworkStats delta = computeStatsDelta(mLastSummaryPoll, summary); + final long timeStart = currentTime - delta.elapsedRealtime; + final long maxHistory = mSummaryMaxHistory.get(); + for (String iface : delta.getUniqueIfaces()) { + final InterfaceIdentity ident = mActiveIface.get(iface); + if (ident == null) { + unknownIface.add(iface); continue; } - final int index = pollDelta.findIndex(iface, UID_ALL); - final long rx = pollDelta.rx[index]; - final long tx = pollDelta.tx[index]; + final int index = delta.findIndex(iface, UID_ALL); + final long rx = delta.rx[index]; + final long tx = delta.tx[index]; - final NetworkStatsHistory history = findOrCreateHistoryLocked(info); + final NetworkStatsHistory history = findOrCreateSummaryLocked(ident); history.recordData(timeStart, currentTime, rx, tx); - history.removeBucketsBefore(currentTime - SUMMARY_MAX_HISTORY); + history.removeBucketsBefore(currentTime - maxHistory); } + mLastSummaryPoll = summary; - mLastPollStats = current; + if (LOGD && unknownIface.size() > 0) { + Slog.w(TAG, "unknown interfaces " + unknownIface.toString() + ", ignoring those stats"); + } + } - // decide if enough has changed to trigger persist - final NetworkStats persistDelta = computeStatsDelta(mLastPersistStats, current); - for (String iface : persistDelta.getKnownIfaces()) { - final int index = persistDelta.findIndex(iface, UID_ALL); - if (persistDelta.rx[index] > SUMMARY_PERSIST_THRESHOLD - || persistDelta.tx[index] > SUMMARY_PERSIST_THRESHOLD) { - writeStatsLocked(); - mLastPersistStats = current; - break; - } + /** + * Update {@link #mDetailStats} historical usage. + */ + private void performDetailPollLocked(NetworkStats detail, long currentTime) { + final NetworkStats delta = computeStatsDelta(mLastDetailPoll, detail); + final long timeStart = currentTime - delta.elapsedRealtime; + final long maxHistory = mDetailMaxHistory.get(); + for (int uid : delta.getUniqueUids()) { + final int index = delta.findIndex(IFACE_ALL, uid); + final long rx = delta.rx[index]; + final long tx = delta.tx[index]; + + final NetworkStatsHistory history = findOrCreateDetailLocked(uid); + history.recordData(timeStart, currentTime, rx, tx); + history.removeBucketsBefore(currentTime - maxHistory); } + mLastDetailPoll = detail; } - private NetworkStatsHistory findOrCreateHistoryLocked(InterfaceInfo info) { - NetworkStatsHistory stats = mIfaceStats.get(info); + private NetworkStatsHistory findOrCreateSummaryLocked(InterfaceIdentity ident) { + NetworkStatsHistory stats = mSummaryStats.get(ident); if (stats == null) { - stats = new NetworkStatsHistory( - info.networkType, info.identity, UID_ALL, SUMMARY_BUCKET_DURATION); - mIfaceStats.put(info, stats); + stats = new NetworkStatsHistory(mSummaryBucketDuration.get()); + mSummaryStats.put(ident, stats); } return stats; } + private NetworkStatsHistory findOrCreateDetailLocked(int uid) { + NetworkStatsHistory stats = mDetailStats.get(uid); + if (stats == null) { + stats = new NetworkStatsHistory(mDetailBucketDuration.get()); + mDetailStats.put(uid, stats); + } + return stats; + } + + private InterfaceIdentity findOrCreateInterfaceLocked(String iface) { + InterfaceIdentity ident = mActiveIface.get(iface); + if (ident == null) { + ident = new InterfaceIdentity(); + mActiveIface.put(iface, ident); + } + return ident; + } + private void readStatsLocked() { if (LOGD) Slog.v(TAG, "readStatsLocked()"); // TODO: read historical stats from disk using AtomicFile @@ -310,70 +446,96 @@ public class NetworkStatsService extends INetworkStatsService.Stub { private void writeStatsLocked() { if (LOGD) Slog.v(TAG, "writeStatsLocked()"); // TODO: persist historical stats to disk using AtomicFile + // TODO: consider duplicating stats and releasing lock while writing } @Override protected void dump(FileDescriptor fd, PrintWriter pw, String[] args) { mContext.enforceCallingOrSelfPermission(DUMP, TAG); - pw.println("Active interfaces:"); - for (InterfaceInfo info : mActiveIface.values()) { - info.dump(" ", pw); + final HashSet<String> argSet = new HashSet<String>(); + for (String arg : args) { + argSet.add(arg); } - pw.println("Known historical stats:"); - for (NetworkStatsHistory stats : mIfaceStats.values()) { - stats.dump(" ", pw); + synchronized (mStatsLock) { + // TODO: remove this testing code, since it corrupts stats + if (argSet.contains("generate")) { + generateRandomLocked(); + pw.println("Generated stub stats"); + return; + } + + pw.println("Active interfaces:"); + for (String iface : mActiveIface.keySet()) { + final InterfaceIdentity ident = mActiveIface.get(iface); + pw.print(" iface="); pw.print(iface); + pw.print(" ident="); pw.println(ident.toString()); + } + + pw.println("Known historical stats:"); + for (InterfaceIdentity ident : mSummaryStats.keySet()) { + final NetworkStatsHistory stats = mSummaryStats.get(ident); + pw.print(" ident="); pw.println(ident.toString()); + stats.dump(" ", pw); + } + + if (argSet.contains("detail")) { + pw.println("Known detail stats:"); + for (int i = 0; i < mDetailStats.size(); i++) { + final int uid = mDetailStats.keyAt(i); + final NetworkStatsHistory stats = mDetailStats.valueAt(i); + pw.print(" UID="); pw.println(uid); + stats.dump(" ", pw); + } + } } } /** - * Details for a well-known network interface, including its name, network - * type, and billing relationship identity (such as IMSI). + * @deprecated only for temporary testing */ - private static class InterfaceInfo { - public final String iface; - public final int networkType; - public final String identity; - - public InterfaceInfo(String iface, int networkType, String identity) { - this.iface = iface; - this.networkType = networkType; - this.identity = identity; + @Deprecated + private void generateRandomLocked() { + long end = System.currentTimeMillis(); + long start = end - mSummaryMaxHistory.get(); + long rx = 3 * GB_IN_BYTES; + long tx = 2 * GB_IN_BYTES; + + mSummaryStats.clear(); + for (InterfaceIdentity ident : mActiveIface.values()) { + final NetworkStatsHistory stats = findOrCreateSummaryLocked(ident); + stats.generateRandom(start, end, rx, tx); } - @Override - public int hashCode() { - final int prime = 31; - int result = 1; - result = prime * result + ((identity == null) ? 0 : identity.hashCode()); - result = prime * result + ((iface == null) ? 0 : iface.hashCode()); - result = prime * result + networkType; - return result; - } + end = System.currentTimeMillis(); + start = end - mDetailMaxHistory.get(); + rx = 500 * MB_IN_BYTES; + tx = 100 * MB_IN_BYTES; - @Override - public boolean equals(Object obj) { - if (obj instanceof InterfaceInfo) { - final InterfaceInfo info = (InterfaceInfo) obj; - return equal(iface, info.iface) && networkType == info.networkType - && equal(identity, info.identity); - } - return false; + mDetailStats.clear(); + for (ApplicationInfo info : mContext.getPackageManager().getInstalledApplications(0)) { + final int uid = info.uid; + final NetworkStatsHistory stats = findOrCreateDetailLocked(uid); + stats.generateRandom(start, end, rx, tx); } + } + + private class LongSecureSetting { + private String mKey; + private long mDefaultValue; - public void dump(String prefix, PrintWriter pw) { - pw.print(prefix); - pw.print("InterfaceInfo: iface="); pw.print(iface); - pw.print(" networkType="); pw.print(networkType); - pw.print(" identity="); pw.println(identity); + public LongSecureSetting(String key, long defaultValue) { + mKey = key; + mDefaultValue = defaultValue; } - } - private void reportActiveInterface(InterfaceInfo info) { - synchronized (mStatsLock) { - // TODO: when interface redefined, port over historical stats - mActiveIface.put(info.iface, info); + public long get() { + if (mContext != null) { + return Settings.Secure.getLong(mContext.getContentResolver(), mKey, mDefaultValue); + } else { + return mDefaultValue; + } } } @@ -389,15 +551,10 @@ public class NetworkStatsService extends INetworkStatsService.Stub { } } - private static boolean equal(Object a, Object b) { - return a == b || (a != null && a.equals(b)); - } - - private static <T> T checkNotNull(T value, String message) { - if (value == null) { - throw new NullPointerException(message); - } - return value; + private String getActiveSubscriberId() { + final TelephonyManager telephony = (TelephonyManager) mContext.getSystemService( + Context.TELEPHONY_SERVICE); + return telephony.getSubscriberId(); } } diff --git a/services/java/com/android/server/wm/InputManager.java b/services/java/com/android/server/wm/InputManager.java index 3095c37799d8..ee69311b0950 100644 --- a/services/java/com/android/server/wm/InputManager.java +++ b/services/java/com/android/server/wm/InputManager.java @@ -617,8 +617,13 @@ public class InputManager { } @SuppressWarnings("unused") - public int getTapTimeout() { - return ViewConfiguration.getTapTimeout(); + public int getHoverTapTimeout() { + return ViewConfiguration.getHoverTapTimeout(); + } + + @SuppressWarnings("unused") + public int getHoverTapSlop() { + return ViewConfiguration.getHoverTapSlop(); } @SuppressWarnings("unused") @@ -632,11 +637,6 @@ public class InputManager { } @SuppressWarnings("unused") - public int getTouchSlop() { - return ViewConfiguration.get(mContext).getScaledTouchSlop(); - } - - @SuppressWarnings("unused") public int getMaxEventsPerSecond() { int result = 0; try { diff --git a/services/java/com/android/server/wm/WindowManagerService.java b/services/java/com/android/server/wm/WindowManagerService.java index d95d4c543939..819c16eb08a8 100644 --- a/services/java/com/android/server/wm/WindowManagerService.java +++ b/services/java/com/android/server/wm/WindowManagerService.java @@ -153,6 +153,7 @@ public class WindowManagerService extends IWindowManager.Stub static final boolean DEBUG_WINDOW_MOVEMENT = false; static final boolean DEBUG_TOKEN_MOVEMENT = false; static final boolean DEBUG_ORIENTATION = false; + static final boolean DEBUG_APP_ORIENTATION = false; static final boolean DEBUG_CONFIGURATION = false; static final boolean DEBUG_APP_TRANSITIONS = false; static final boolean DEBUG_STARTING_WINDOW = false; @@ -427,6 +428,7 @@ public class WindowManagerService extends IWindowManager.Stub boolean mWindowsFreezingScreen = false; long mFreezeGcPending = 0; int mAppsFreezingScreen = 0; + int mLastWindowForcedOrientation = ActivityInfo.SCREEN_ORIENTATION_UNSPECIFIED; int mLayoutSeq = 0; @@ -3187,6 +3189,15 @@ public class WindowManagerService extends IWindowManager.Stub } public int getOrientationFromWindowsLocked() { + if (mDisplayFrozen || mOpeningApps.size() > 0 || mClosingApps.size() > 0) { + // If the display is frozen, some activities may be in the middle + // of restarting, and thus have removed their old window. If the + // window has the flag to hide the lock screen, then the lock screen + // can re-appear and inflict its own orientation on us. Keep the + // orientation stable until this all settles down. + return mLastWindowForcedOrientation; + } + int pos = mWindows.size() - 1; while (pos >= 0) { WindowState wtoken = mWindows.get(pos); @@ -3194,7 +3205,7 @@ public class WindowManagerService extends IWindowManager.Stub if (wtoken.mAppToken != null) { // We hit an application window. so the orientation will be determined by the // app window. No point in continuing further. - return ActivityInfo.SCREEN_ORIENTATION_UNSPECIFIED; + return (mLastWindowForcedOrientation=ActivityInfo.SCREEN_ORIENTATION_UNSPECIFIED); } if (!wtoken.isVisibleLw() || !wtoken.mPolicyVisibilityAfterAnim) { continue; @@ -3204,10 +3215,10 @@ public class WindowManagerService extends IWindowManager.Stub (req == ActivityInfo.SCREEN_ORIENTATION_BEHIND)){ continue; } else { - return req; + return (mLastWindowForcedOrientation=req); } } - return ActivityInfo.SCREEN_ORIENTATION_UNSPECIFIED; + return (mLastWindowForcedOrientation=ActivityInfo.SCREEN_ORIENTATION_UNSPECIFIED); } public int getOrientationFromAppTokensLocked() { @@ -3220,16 +3231,23 @@ public class WindowManagerService extends IWindowManager.Stub while (pos >= 0) { AppWindowToken wtoken = mAppTokens.get(pos); pos--; + + if (DEBUG_APP_ORIENTATION) Slog.v(TAG, "Checking app orientation: " + wtoken); + // if we're about to tear down this window and not seek for // the behind activity, don't use it for orientation if (!findingBehind && (!wtoken.hidden && wtoken.hiddenRequested)) { + if (DEBUG_ORIENTATION) Slog.v(TAG, "Skipping " + wtoken + + " -- going to hide"); continue; } if (!haveGroup) { // We ignore any hidden applications on the top. if (wtoken.hiddenRequested || wtoken.willBeHidden) { + if (DEBUG_ORIENTATION) Slog.v(TAG, "Skipping " + wtoken + + " -- hidden on top"); continue; } haveGroup = true; @@ -3243,6 +3261,8 @@ public class WindowManagerService extends IWindowManager.Stub // user's orientation. if (lastOrientation != ActivityInfo.SCREEN_ORIENTATION_BEHIND && lastFullscreen) { + if (DEBUG_ORIENTATION) Slog.v(TAG, "Done at " + wtoken + + " -- end of group, return " + lastOrientation); return lastOrientation; } } @@ -3253,16 +3273,21 @@ public class WindowManagerService extends IWindowManager.Stub lastFullscreen = wtoken.appFullscreen; if (lastFullscreen && or != ActivityInfo.SCREEN_ORIENTATION_BEHIND) { + if (DEBUG_ORIENTATION) Slog.v(TAG, "Done at " + wtoken + + " -- full screen, return " + or); return or; } // If this application has requested an explicit orientation, // then use it. if (or != ActivityInfo.SCREEN_ORIENTATION_UNSPECIFIED && or != ActivityInfo.SCREEN_ORIENTATION_BEHIND) { + if (DEBUG_ORIENTATION) Slog.v(TAG, "Done at " + wtoken + + " -- explicitly set, return " + or); return or; } findingBehind |= (or == ActivityInfo.SCREEN_ORIENTATION_BEHIND); } + if (DEBUG_ORIENTATION) Slog.v(TAG, "No app is requesting an orientation"); return ActivityInfo.SCREEN_ORIENTATION_UNSPECIFIED; } @@ -3335,15 +3360,6 @@ public class WindowManagerService extends IWindowManager.Stub * android.os.IBinder) */ boolean updateOrientationFromAppTokensLocked(boolean inTransaction) { - if (mDisplayFrozen || mOpeningApps.size() > 0 || mClosingApps.size() > 0) { - // If the display is frozen, some activities may be in the middle - // of restarting, and thus have removed their old window. If the - // window has the flag to hide the lock screen, then the lock screen - // can re-appear and inflict its own orientation on us. Keep the - // orientation stable until this all settles down. - return false; - } - boolean changed = false; long ident = Binder.clearCallingIdentity(); try { @@ -8939,9 +8955,10 @@ public class WindowManagerService extends IWindowManager.Stub pw.print(" mAppsFreezingScreen="); pw.print(mAppsFreezingScreen); pw.print(" mWaitingForConfig="); pw.println(mWaitingForConfig); pw.print(" mRotation="); pw.print(mRotation); - pw.print(" mForcedAppOrientation="); pw.print(mForcedAppOrientation); pw.print(" mRequestedRotation="); pw.print(mRequestedRotation); pw.print(" mAltOrientation="); pw.println(mAltOrientation); + pw.print(" mLastWindowForcedOrientation"); pw.print(mLastWindowForcedOrientation); + pw.print(" mForcedAppOrientation="); pw.println(mForcedAppOrientation); pw.print(" mDeferredRotation="); pw.print(mDeferredRotation); pw.print(", mDeferredRotationAnimFlags="); pw.println(mDeferredRotationAnimFlags); pw.print(" mAnimationPending="); pw.print(mAnimationPending); diff --git a/services/jni/com_android_server_InputManager.cpp b/services/jni/com_android_server_InputManager.cpp index 881882f104c2..7c5084fda650 100644 --- a/services/jni/com_android_server_InputManager.cpp +++ b/services/jni/com_android_server_InputManager.cpp @@ -56,7 +56,7 @@ namespace android { // The exponent used to calculate the pointer speed scaling factor. // The scaling factor is calculated as 2 ^ (speed * exponent), // where the speed ranges from -7 to + 7 and is supplied by the user. -static const float POINTER_SPEED_EXPONENT = 1.0f / 3; +static const float POINTER_SPEED_EXPONENT = 1.0f / 4; static struct { jmethodID notifyConfigurationChanged; @@ -76,10 +76,10 @@ static struct { jmethodID getKeyRepeatTimeout; jmethodID getKeyRepeatDelay; jmethodID getMaxEventsPerSecond; - jmethodID getTapTimeout; + jmethodID getHoverTapTimeout; + jmethodID getHoverTapSlop; jmethodID getDoubleTapTimeout; jmethodID getLongPressTimeout; - jmethodID getTouchSlop; jmethodID getPointerLayer; jmethodID getPointerIcon; } gCallbacksClassInfo; @@ -410,32 +410,32 @@ void NativeInputManager::getReaderConfiguration(InputReaderConfiguration* outCon env->DeleteLocalRef(excludedDeviceNames); } - jint tapTimeout = env->CallIntMethod(mCallbacksObj, - gCallbacksClassInfo.getTapTimeout); - if (!checkAndClearExceptionFromCallback(env, "getTapTimeout")) { + jint hoverTapTimeout = env->CallIntMethod(mCallbacksObj, + gCallbacksClassInfo.getHoverTapTimeout); + if (!checkAndClearExceptionFromCallback(env, "getHoverTapTimeout")) { jint doubleTapTimeout = env->CallIntMethod(mCallbacksObj, gCallbacksClassInfo.getDoubleTapTimeout); if (!checkAndClearExceptionFromCallback(env, "getDoubleTapTimeout")) { jint longPressTimeout = env->CallIntMethod(mCallbacksObj, gCallbacksClassInfo.getLongPressTimeout); if (!checkAndClearExceptionFromCallback(env, "getLongPressTimeout")) { - outConfig->pointerGestureTapInterval = milliseconds_to_nanoseconds(tapTimeout); + outConfig->pointerGestureTapInterval = milliseconds_to_nanoseconds(hoverTapTimeout); // We must ensure that the tap-drag interval is significantly shorter than // the long-press timeout because the tap is held down for the entire duration // of the double-tap timeout. jint tapDragInterval = max(min(longPressTimeout - 100, - doubleTapTimeout), tapTimeout); + doubleTapTimeout), hoverTapTimeout); outConfig->pointerGestureTapDragInterval = milliseconds_to_nanoseconds(tapDragInterval); } } } - jint touchSlop = env->CallIntMethod(mCallbacksObj, - gCallbacksClassInfo.getTouchSlop); - if (!checkAndClearExceptionFromCallback(env, "getTouchSlop")) { - outConfig->pointerGestureTapSlop = touchSlop; + jint hoverTapSlop = env->CallIntMethod(mCallbacksObj, + gCallbacksClassInfo.getHoverTapSlop); + if (!checkAndClearExceptionFromCallback(env, "getHoverTapSlop")) { + outConfig->pointerGestureTapSlop = hoverTapSlop; } { // acquire lock @@ -1394,8 +1394,11 @@ int register_android_server_InputManager(JNIEnv* env) { GET_METHOD_ID(gCallbacksClassInfo.getKeyRepeatDelay, clazz, "getKeyRepeatDelay", "()I"); - GET_METHOD_ID(gCallbacksClassInfo.getTapTimeout, clazz, - "getTapTimeout", "()I"); + GET_METHOD_ID(gCallbacksClassInfo.getHoverTapTimeout, clazz, + "getHoverTapTimeout", "()I"); + + GET_METHOD_ID(gCallbacksClassInfo.getHoverTapSlop, clazz, + "getHoverTapSlop", "()I"); GET_METHOD_ID(gCallbacksClassInfo.getDoubleTapTimeout, clazz, "getDoubleTapTimeout", "()I"); @@ -1403,9 +1406,6 @@ int register_android_server_InputManager(JNIEnv* env) { GET_METHOD_ID(gCallbacksClassInfo.getLongPressTimeout, clazz, "getLongPressTimeout", "()I"); - GET_METHOD_ID(gCallbacksClassInfo.getTouchSlop, clazz, - "getTouchSlop", "()I"); - GET_METHOD_ID(gCallbacksClassInfo.getMaxEventsPerSecond, clazz, "getMaxEventsPerSecond", "()I"); diff --git a/telephony/java/android/telephony/TelephonyManager.java b/telephony/java/android/telephony/TelephonyManager.java index 8732e2199890..7272b0838d08 100644 --- a/telephony/java/android/telephony/TelephonyManager.java +++ b/telephony/java/android/telephony/TelephonyManager.java @@ -469,6 +469,46 @@ public class TelephonyManager { } } + /** Unknown network class. {@hide} */ + public static final int NETWORK_CLASS_UNKNOWN = 0; + /** Class of broadly defined "2G" networks. {@hide} */ + public static final int NETWORK_CLASS_2_G = 1; + /** Class of broadly defined "3G" networks. {@hide} */ + public static final int NETWORK_CLASS_3_G = 2; + /** Class of broadly defined "4G" networks. {@hide} */ + public static final int NETWORK_CLASS_4_G = 3; + + /** + * Return general class of network type, such as "3G" or "4G". In cases + * where classification is contentious, this method is conservative. + * + * @hide + */ + public static int getNetworkClass(int networkType) { + switch (networkType) { + case NETWORK_TYPE_GPRS: + case NETWORK_TYPE_EDGE: + case NETWORK_TYPE_CDMA: + case NETWORK_TYPE_1xRTT: + case NETWORK_TYPE_IDEN: + return NETWORK_CLASS_2_G; + case NETWORK_TYPE_UMTS: + case NETWORK_TYPE_EVDO_0: + case NETWORK_TYPE_EVDO_A: + case NETWORK_TYPE_HSDPA: + case NETWORK_TYPE_HSUPA: + case NETWORK_TYPE_HSPA: + case NETWORK_TYPE_EVDO_B: + case NETWORK_TYPE_EHRPD: + case NETWORK_TYPE_HSPAP: + return NETWORK_CLASS_3_G; + case NETWORK_TYPE_LTE: + return NETWORK_CLASS_4_G; + default: + return NETWORK_CLASS_UNKNOWN; + } + } + /** * Returns a string representation of the radio technology (network type) * currently in use on the device. @@ -477,7 +517,12 @@ public class TelephonyManager { * @hide pending API council review */ public String getNetworkTypeName() { - switch (getNetworkType()) { + return getNetworkTypeName(getNetworkType()); + } + + /** {@hide} */ + public static String getNetworkTypeName(int type) { + switch (type) { case NETWORK_TYPE_GPRS: return "GPRS"; case NETWORK_TYPE_EDGE: diff --git a/telephony/java/com/android/internal/telephony/BaseCommands.java b/telephony/java/com/android/internal/telephony/BaseCommands.java index 0c4581b578b4..8427d1439895 100644 --- a/telephony/java/com/android/internal/telephony/BaseCommands.java +++ b/telephony/java/com/android/internal/telephony/BaseCommands.java @@ -96,8 +96,10 @@ public abstract class BaseCommands implements CommandsInterface { protected Registrant mRestrictedStateRegistrant; protected Registrant mGsmBroadcastSmsRegistrant; - // Network Mode received from PhoneFactory - protected int mNetworkMode; + // Preferred network type received from PhoneFactory. + // This is used when establishing a connection to the + // vendor ril so it starts up in the correct mode. + protected int mPreferredNetworkType; // CDMA subscription received from PhoneFactory protected int mCdmaSubscription; // Type of Phone, GSM or CDMA. Set by CDMAPhone or GSMPhone. diff --git a/telephony/java/com/android/internal/telephony/RIL.java b/telephony/java/com/android/internal/telephony/RIL.java index 2c04b30f1cf0..572bbaa9d4c6 100644 --- a/telephony/java/com/android/internal/telephony/RIL.java +++ b/telephony/java/com/android/internal/telephony/RIL.java @@ -611,14 +611,14 @@ public final class RIL extends BaseCommands implements CommandsInterface { //***** Constructors - public RIL(Context context, int networkMode, int cdmaSubscription) { + public RIL(Context context, int preferredNetworkType, int cdmaSubscription) { super(context); if (RILJ_LOGD) { - riljLog("RIL(context, networkMode=" + networkMode + + riljLog("RIL(context, preferredNetworkType=" + preferredNetworkType + " cdmaSubscription=" + cdmaSubscription + ")"); } mCdmaSubscription = cdmaSubscription; - mNetworkMode = networkMode; + mPreferredNetworkType = preferredNetworkType; mPhoneType = RILConstants.NO_PHONE; PowerManager pm = (PowerManager)context.getSystemService(Context.POWER_SERVICE); @@ -1813,6 +1813,8 @@ public final class RIL extends BaseCommands implements CommandsInterface { rr.mp.writeInt(1); rr.mp.writeInt(networkType); + mPreferredNetworkType = networkType; + if (RILJ_LOGD) riljLog(rr.serialString() + "> " + requestToString(rr.mRequest) + " : " + networkType); @@ -2222,7 +2224,7 @@ public final class RIL extends BaseCommands implements CommandsInterface { case RIL_REQUEST_STK_HANDLE_CALL_SETUP_REQUESTED_FROM_SIM: ret = responseInts(p); break; case RIL_REQUEST_EXPLICIT_CALL_TRANSFER: ret = responseVoid(p); break; case RIL_REQUEST_SET_PREFERRED_NETWORK_TYPE: ret = responseVoid(p); break; - case RIL_REQUEST_GET_PREFERRED_NETWORK_TYPE: ret = responseInts(p); break; + case RIL_REQUEST_GET_PREFERRED_NETWORK_TYPE: ret = responseGetPreferredNetworkType(p); break; case RIL_REQUEST_GET_NEIGHBORING_CELL_IDS: ret = responseCellList(p); break; case RIL_REQUEST_SET_LOCATION_UPDATES: ret = responseVoid(p); break; case RIL_REQUEST_CDMA_SET_SUBSCRIPTION_SOURCE: ret = responseVoid(p); break; @@ -2737,7 +2739,7 @@ public final class RIL extends BaseCommands implements CommandsInterface { // Initial conditions setRadioPower(false, null); - setPreferredNetworkType(mNetworkMode, null); + setPreferredNetworkType(mPreferredNetworkType, null); setCdmaSubscriptionSource(mCdmaSubscription, null); notifyRegistrantsRilConnectionChanged(((int[])ret)[0]); break; @@ -3161,6 +3163,18 @@ public final class RIL extends BaseCommands implements CommandsInterface { return response; } + private Object responseGetPreferredNetworkType(Parcel p) { + int [] response = (int[]) responseInts(p); + + if (response.length >= 1) { + // Since this is the response for getPreferredNetworkType + // we'll assume that it should be the value we want the + // vendor ril to take if we reestablish a connection to it. + mPreferredNetworkType = response[0]; + } + return response; + } + private Object responseGmsBroadcastConfig(Parcel p) { int num; ArrayList<SmsBroadcastConfigInfo> response; diff --git a/tests/BiDiTests/res/layout/frame_layout_ltr.xml b/tests/BiDiTests/res/layout/frame_layout_ltr.xml index bd324e3c0cad..61fd06e08532 100644 --- a/tests/BiDiTests/res/layout/frame_layout_ltr.xml +++ b/tests/BiDiTests/res/layout/frame_layout_ltr.xml @@ -18,7 +18,7 @@ android:id="@+id/frame_layout_ltr" android:layout_width="match_parent" android:layout_height="match_parent" - android:horizontalDirection="ltr" + android:layoutDirection="ltr" android:background="#FF000000"> <FrameLayout @@ -52,28 +52,28 @@ <FrameLayout android:layout_width="100dp" android:layout_height="100dp" - android:layout_gravity="top|before" + android:layout_gravity="top|start" android:background="#FFFFFFFF"> </FrameLayout> <FrameLayout android:layout_width="100dp" android:layout_height="100dp" - android:layout_gravity="top|after" + android:layout_gravity="top|end" android:background="#FFFFFF00"> </FrameLayout> <FrameLayout android:layout_width="100dp" android:layout_height="100dp" - android:layout_gravity="bottom|before" + android:layout_gravity="bottom|start" android:background="#FFFFFFFF"> </FrameLayout> <FrameLayout android:layout_width="100dp" android:layout_height="100dp" - android:layout_gravity="bottom|after" + android:layout_gravity="bottom|end" android:background="#FFFFFF00"> </FrameLayout> diff --git a/tests/BiDiTests/res/layout/frame_layout_rtl.xml b/tests/BiDiTests/res/layout/frame_layout_rtl.xml index 814f5e137b3f..598b41a233a2 100644 --- a/tests/BiDiTests/res/layout/frame_layout_rtl.xml +++ b/tests/BiDiTests/res/layout/frame_layout_rtl.xml @@ -18,7 +18,7 @@ android:id="@+id/frame_layout_ltr" android:layout_width="match_parent" android:layout_height="match_parent" - android:horizontalDirection="rtl" + android:layoutDirection="rtl" android:background="#FF000000"> <FrameLayout @@ -52,28 +52,28 @@ <FrameLayout android:layout_width="100dp" android:layout_height="100dp" - android:layout_gravity="top|before" + android:layout_gravity="top|start" android:background="#FFFFFFFF"> </FrameLayout> <FrameLayout android:layout_width="100dp" android:layout_height="100dp" - android:layout_gravity="top|after" + android:layout_gravity="top|end" android:background="#FFFFFF00"> </FrameLayout> <FrameLayout android:layout_width="100dp" android:layout_height="100dp" - android:layout_gravity="bottom|before" + android:layout_gravity="bottom|start" android:background="#FFFFFFFF"> </FrameLayout> <FrameLayout android:layout_width="100dp" android:layout_height="100dp" - android:layout_gravity="bottom|after" + android:layout_gravity="bottom|end" android:background="#FFFFFF00"> </FrameLayout> diff --git a/tests/BiDiTests/res/layout/linear_layout_ltr.xml b/tests/BiDiTests/res/layout/linear_layout_ltr.xml index c5a8d47d6e62..d4386f2d0102 100644 --- a/tests/BiDiTests/res/layout/linear_layout_ltr.xml +++ b/tests/BiDiTests/res/layout/linear_layout_ltr.xml @@ -19,7 +19,7 @@ android:orientation="vertical" android:layout_width="match_parent" android:layout_height="match_parent" - android:horizontalDirection="ltr"> + android:layoutDirection="ltr"> <LinearLayout xmlns:android="http://schemas.android.com/apk/res/android" android:orientation="horizontal" @@ -51,7 +51,7 @@ android:orientation="horizontal" android:layout_width="match_parent" android:layout_height="wrap_content" - android:horizontalDirection="inherit"> + android:layoutDirection="inherit"> <Button android:layout_height="wrap_content" android:layout_width="wrap_content" @@ -78,7 +78,7 @@ android:orientation="horizontal" android:layout_width="match_parent" android:layout_height="wrap_content" - android:horizontalDirection="ltr"> + android:layoutDirection="ltr"> <Button android:layout_height="wrap_content" android:layout_width="wrap_content" @@ -105,7 +105,7 @@ android:orientation="horizontal" android:layout_width="match_parent" android:layout_height="wrap_content" - android:horizontalDirection="rtl"> + android:layoutDirection="rtl"> <Button android:layout_height="wrap_content" android:layout_width="wrap_content" @@ -132,7 +132,7 @@ android:orientation="vertical" android:layout_width="match_parent" android:layout_height="wrap_content" - android:horizontalDirection="inherit"> + android:layoutDirection="inherit"> <Button android:layout_height="wrap_content" android:layout_width="wrap_content" @@ -159,7 +159,7 @@ android:orientation="vertical" android:layout_width="match_parent" android:layout_height="wrap_content" - android:horizontalDirection="ltr"> + android:layoutDirection="ltr"> <Button android:layout_height="wrap_content" android:layout_width="wrap_content" @@ -186,7 +186,7 @@ android:orientation="vertical" android:layout_width="match_parent" android:layout_height="wrap_content" - android:horizontalDirection="rtl"> + android:layoutDirection="rtl"> <Button android:layout_height="wrap_content" android:layout_width="wrap_content" diff --git a/tests/BiDiTests/res/layout/linear_layout_rtl.xml b/tests/BiDiTests/res/layout/linear_layout_rtl.xml index 1494fec54ac9..9d0726386c3d 100644 --- a/tests/BiDiTests/res/layout/linear_layout_rtl.xml +++ b/tests/BiDiTests/res/layout/linear_layout_rtl.xml @@ -19,7 +19,7 @@ android:orientation="vertical" android:layout_width="match_parent" android:layout_height="match_parent" - android:horizontalDirection="rtl"> + android:layoutDirection="rtl"> <LinearLayout xmlns:android="http://schemas.android.com/apk/res/android" android:orientation="horizontal" @@ -51,7 +51,7 @@ android:orientation="horizontal" android:layout_width="match_parent" android:layout_height="wrap_content" - android:horizontalDirection="inherit"> + android:layoutDirection="inherit"> <Button android:layout_height="wrap_content" android:layout_width="wrap_content" @@ -78,7 +78,7 @@ android:orientation="horizontal" android:layout_width="match_parent" android:layout_height="wrap_content" - android:horizontalDirection="ltr"> + android:layoutDirection="ltr"> <Button android:layout_height="wrap_content" android:layout_width="wrap_content" @@ -105,7 +105,7 @@ android:orientation="horizontal" android:layout_width="match_parent" android:layout_height="wrap_content" - android:horizontalDirection="rtl"> + android:layoutDirection="rtl"> <Button android:layout_height="wrap_content" android:layout_width="wrap_content" @@ -132,7 +132,7 @@ android:orientation="vertical" android:layout_width="match_parent" android:layout_height="wrap_content" - android:horizontalDirection="inherit"> + android:layoutDirection="inherit"> <Button android:layout_height="wrap_content" android:layout_width="wrap_content" @@ -159,7 +159,7 @@ android:orientation="vertical" android:layout_width="match_parent" android:layout_height="wrap_content" - android:horizontalDirection="ltr"> + android:layoutDirection="ltr"> <Button android:layout_height="wrap_content" android:layout_width="wrap_content" @@ -186,7 +186,7 @@ android:orientation="vertical" android:layout_width="match_parent" android:layout_height="wrap_content" - android:horizontalDirection="rtl"> + android:layoutDirection="rtl"> <Button android:layout_height="wrap_content" android:layout_width="wrap_content" diff --git a/tests/BiDiTests/res/layout/relative_layout_ltr.xml b/tests/BiDiTests/res/layout/relative_layout_ltr.xml index 53ae7c34ba28..d789707dfe62 100644 --- a/tests/BiDiTests/res/layout/relative_layout_ltr.xml +++ b/tests/BiDiTests/res/layout/relative_layout_ltr.xml @@ -18,7 +18,7 @@ android:id="@+id/frame_layout_ltr" android:layout_width="match_parent" android:layout_height="match_parent" - android:horizontalDirection="ltr" + android:layoutDirection="ltr" android:background="#FF000000"> <RelativeLayout @@ -52,28 +52,28 @@ <RelativeLayout android:layout_width="100dp" android:layout_height="100dp" - android:layout_gravity="top|before" + android:layout_gravity="top|start" android:background="#FFFFFFFF"> </RelativeLayout> <RelativeLayout android:layout_width="100dp" android:layout_height="100dp" - android:layout_gravity="top|after" + android:layout_gravity="top|end" android:background="#FFFFFF00"> </RelativeLayout> <RelativeLayout android:layout_width="100dp" android:layout_height="100dp" - android:layout_gravity="bottom|before" + android:layout_gravity="bottom|start" android:background="#FFFFFFFF"> </RelativeLayout> <RelativeLayout android:layout_width="100dp" android:layout_height="100dp" - android:layout_gravity="bottom|after" + android:layout_gravity="bottom|end" android:background="#FFFFFF00"> </RelativeLayout> diff --git a/tests/BiDiTests/res/layout/relative_layout_ltr_2.xml b/tests/BiDiTests/res/layout/relative_layout_ltr_2.xml index 93d1b4ba1676..a13ef8bfc0ad 100644 --- a/tests/BiDiTests/res/layout/relative_layout_ltr_2.xml +++ b/tests/BiDiTests/res/layout/relative_layout_ltr_2.xml @@ -19,7 +19,7 @@ android:orientation="vertical" android:layout_width="match_parent" android:layout_height="match_parent" - android:horizontalDirection="ltr"> + android:layoutDirection="ltr"> <RelativeLayout xmlns:android="http://schemas.android.com/apk/res/android" android:layout_width="fill_parent" @@ -58,7 +58,7 @@ android:layout_height="wrap_content" android:background="#FF000000" android:padding="10px" - android:horizontalDirection="ltr"> + android:layoutDirection="ltr"> <TextView android:id="@+id/label_2" android:layout_width="fill_parent" @@ -91,7 +91,7 @@ android:layout_height="wrap_content" android:background="#FF000000" android:padding="10px" - android:horizontalDirection="inherit"> + android:layoutDirection="inherit"> <TextView android:id="@+id/label_3" android:layout_width="fill_parent" @@ -124,7 +124,7 @@ android:layout_height="wrap_content" android:background="#FF000000" android:padding="10px" - android:horizontalDirection="rtl"> + android:layoutDirection="rtl"> <TextView android:id="@+id/label_4" android:layout_width="fill_parent" diff --git a/tests/BiDiTests/res/layout/relative_layout_rtl.xml b/tests/BiDiTests/res/layout/relative_layout_rtl.xml index 4b87752a92b1..580892474206 100644 --- a/tests/BiDiTests/res/layout/relative_layout_rtl.xml +++ b/tests/BiDiTests/res/layout/relative_layout_rtl.xml @@ -18,7 +18,7 @@ android:id="@+id/frame_layout_rtl" android:layout_width="match_parent" android:layout_height="match_parent" - android:horizontalDirection="rtl" + android:layoutDirection="rtl" android:background="#FF000000"> <RelativeLayout @@ -52,28 +52,28 @@ <RelativeLayout android:layout_width="100dp" android:layout_height="100dp" - android:layout_gravity="top|before" + android:layout_gravity="top|start" android:background="#FFFFFFFF"> </RelativeLayout> <RelativeLayout android:layout_width="100dp" android:layout_height="100dp" - android:layout_gravity="top|after" + android:layout_gravity="top|end" android:background="#FFFFFF00"> </RelativeLayout> <RelativeLayout android:layout_width="100dp" android:layout_height="100dp" - android:layout_gravity="bottom|before" + android:layout_gravity="bottom|start" android:background="#FFFFFFFF"> </RelativeLayout> <RelativeLayout android:layout_width="100dp" android:layout_height="100dp" - android:layout_gravity="bottom|after" + android:layout_gravity="bottom|end" android:background="#FFFFFF00"> </RelativeLayout> diff --git a/tests/BiDiTests/res/layout/relative_layout_rtl_2.xml b/tests/BiDiTests/res/layout/relative_layout_rtl_2.xml index 8418e72f58ab..1a6b3d54a04d 100644 --- a/tests/BiDiTests/res/layout/relative_layout_rtl_2.xml +++ b/tests/BiDiTests/res/layout/relative_layout_rtl_2.xml @@ -19,7 +19,7 @@ android:orientation="vertical" android:layout_width="match_parent" android:layout_height="match_parent" - android:horizontalDirection="rtl"> + android:layoutDirection="rtl"> <RelativeLayout xmlns:android="http://schemas.android.com/apk/res/android" android:layout_width="fill_parent" @@ -58,7 +58,7 @@ android:layout_height="wrap_content" android:background="#FF000000" android:padding="10px" - android:horizontalDirection="ltr"> + android:layoutDirection="ltr"> <TextView android:id="@+id/label_2" android:layout_width="fill_parent" @@ -91,7 +91,7 @@ android:layout_height="wrap_content" android:background="#FF000000" android:padding="10px" - android:horizontalDirection="inherit"> + android:layoutDirection="inherit"> <TextView android:id="@+id/label_3" android:layout_width="fill_parent" @@ -124,7 +124,7 @@ android:layout_height="wrap_content" android:background="#FF000000" android:padding="10px" - android:horizontalDirection="rtl"> + android:layoutDirection="rtl"> <TextView android:id="@+id/label_4" android:layout_width="fill_parent" diff --git a/tests/BiDiTests/res/layout/table_layout_ltr.xml b/tests/BiDiTests/res/layout/table_layout_ltr.xml index f44de8ed9880..8e1891e7014c 100644 --- a/tests/BiDiTests/res/layout/table_layout_ltr.xml +++ b/tests/BiDiTests/res/layout/table_layout_ltr.xml @@ -19,7 +19,7 @@ android:orientation="vertical" android:layout_width="match_parent" android:layout_height="match_parent" - android:horizontalDirection="ltr"> + android:layoutDirection="ltr"> <TableLayout xmlns:android="http://schemas.android.com/apk/res/android" android:layout_width="wrap_content" @@ -72,7 +72,7 @@ android:layout_width="wrap_content" android:layout_height="wrap_content" android:stretchColumns="1,2" - android:horizontalDirection="inherit"> + android:layoutDirection="inherit"> <TableRow> <Button android:layout_height="wrap_content" @@ -120,7 +120,7 @@ android:layout_width="wrap_content" android:layout_height="wrap_content" android:stretchColumns="1,2" - android:horizontalDirection="ltr"> + android:layoutDirection="ltr"> <TableRow> <Button android:layout_height="wrap_content" @@ -168,7 +168,7 @@ android:layout_width="wrap_content" android:layout_height="wrap_content" android:stretchColumns="1,2" - android:horizontalDirection="rtl"> + android:layoutDirection="rtl"> <TableRow> <Button android:layout_height="wrap_content" diff --git a/tests/BiDiTests/res/layout/table_layout_rtl.xml b/tests/BiDiTests/res/layout/table_layout_rtl.xml index 84270baa8b97..bd664e4900db 100644 --- a/tests/BiDiTests/res/layout/table_layout_rtl.xml +++ b/tests/BiDiTests/res/layout/table_layout_rtl.xml @@ -19,7 +19,7 @@ android:orientation="vertical" android:layout_width="match_parent" android:layout_height="match_parent" - android:horizontalDirection="rtl"> + android:layoutDirection="rtl"> <TableLayout xmlns:android="http://schemas.android.com/apk/res/android" android:layout_width="wrap_content" @@ -72,7 +72,7 @@ android:layout_width="wrap_content" android:layout_height="wrap_content" android:stretchColumns="1,2" - android:horizontalDirection="inherit"> + android:layoutDirection="inherit"> <TableRow> <Button android:layout_height="wrap_content" @@ -120,7 +120,7 @@ android:layout_width="wrap_content" android:layout_height="wrap_content" android:stretchColumns="1,2" - android:horizontalDirection="ltr"> + android:layoutDirection="ltr"> <TableRow> <Button android:layout_height="wrap_content" @@ -168,7 +168,7 @@ android:layout_width="wrap_content" android:layout_height="wrap_content" android:stretchColumns="1,2" - android:horizontalDirection="rtl"> + android:layoutDirection="rtl"> <TableRow> <Button android:layout_height="wrap_content" diff --git a/tools/layoutlib/bridge/src/android/graphics/Canvas_Delegate.java b/tools/layoutlib/bridge/src/android/graphics/Canvas_Delegate.java index 922cd4c977fb..8e3ed9327736 100644 --- a/tools/layoutlib/bridge/src/android/graphics/Canvas_Delegate.java +++ b/tools/layoutlib/bridge/src/android/graphics/Canvas_Delegate.java @@ -425,7 +425,7 @@ public final class Canvas_Delegate { AffineTransform matrixTx = matrixDelegate.getAffineTransform(); // combine them so that the given matrix is applied after. - currentTx.preConcatenate(matrixTx); + currentTx.concatenate(matrixTx); // give it to the graphics2D as a new matrix replacing all previous transform snapshot.setTransform(currentTx); @@ -717,7 +717,7 @@ public final class Canvas_Delegate { /*package*/ static void native_drawCircle(int nativeCanvas, float cx, float cy, float radius, int paint) { native_drawOval(nativeCanvas, - new RectF(cx - radius, cy - radius, radius*2, radius*2), + new RectF(cx - radius, cy - radius, radius, radius), paint); } diff --git a/tools/layoutlib/bridge/src/android/os/HandlerThread_Delegate.java b/tools/layoutlib/bridge/src/android/os/HandlerThread_Delegate.java new file mode 100644 index 000000000000..afbe97c06ebb --- /dev/null +++ b/tools/layoutlib/bridge/src/android/os/HandlerThread_Delegate.java @@ -0,0 +1,80 @@ +/* + * 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.os; + +import com.android.layoutlib.bridge.android.BridgeContext; +import com.android.layoutlib.bridge.impl.RenderAction; +import com.android.tools.layoutlib.annotations.LayoutlibDelegate; + +import java.util.ArrayList; +import java.util.HashMap; +import java.util.List; +import java.util.Map; + +/** + * Delegate overriding selected methods of android.os.HandlerThread + * + * Through the layoutlib_create tool, selected methods of Handler have been replaced + * by calls to methods of the same name in this delegate class. + * + * + */ +public class HandlerThread_Delegate { + + private static Map<BridgeContext, List<HandlerThread>> sThreads = + new HashMap<BridgeContext, List<HandlerThread>>(); + + public static void cleanUp(BridgeContext context) { + List<HandlerThread> list = sThreads.get(context); + if (list != null) { + for (HandlerThread thread : list) { + thread.quit(); + } + + list.clear(); + sThreads.remove(context); + } + } + + // -------- Delegate methods + + @LayoutlibDelegate + /*package*/ static void run(HandlerThread theThread) { + // record the thread so that it can be quit() on clean up. + BridgeContext context = RenderAction.getCurrentContext(); + List<HandlerThread> list = sThreads.get(context); + if (list == null) { + list = new ArrayList<HandlerThread>(); + sThreads.put(context, list); + } + + list.add(theThread); + + // ---- START DEFAULT IMPLEMENTATION. + + theThread.mTid = Process.myTid(); + Looper.prepare(); + synchronized (theThread) { + theThread.mLooper = Looper.myLooper(); + theThread.notifyAll(); + } + Process.setThreadPriority(theThread.mPriority); + theThread.onLooperPrepared(); + Looper.loop(); + theThread.mTid = -1; + } +} diff --git a/tools/layoutlib/bridge/src/android/view/LayoutInflater_Delegate.java b/tools/layoutlib/bridge/src/android/view/LayoutInflater_Delegate.java index 0f3cf571cf8d..3ef328802ec4 100644 --- a/tools/layoutlib/bridge/src/android/view/LayoutInflater_Delegate.java +++ b/tools/layoutlib/bridge/src/android/view/LayoutInflater_Delegate.java @@ -22,7 +22,10 @@ import com.android.tools.layoutlib.annotations.LayoutlibDelegate; import org.xmlpull.v1.XmlPullParser; import org.xmlpull.v1.XmlPullParserException; +import android.content.res.TypedArray; +import android.content.res.XmlResourceParser; import android.util.AttributeSet; +import android.util.Xml; import java.io.IOException; @@ -35,6 +38,8 @@ import java.io.IOException; */ public class LayoutInflater_Delegate { + public static boolean sIsInInclude = false; + /** * Recursive method used to descend down the xml hierarchy and instantiate * views, instantiate their children, and then call onFinishInflate(). @@ -94,4 +99,128 @@ public class LayoutInflater_Delegate { } } } + + @LayoutlibDelegate + public static void parseInclude( + LayoutInflater thisInflater, + XmlPullParser parser, View parent, AttributeSet attrs) + throws XmlPullParserException, IOException { + + int type; + + if (parent instanceof ViewGroup) { + final int layout = attrs.getAttributeResourceValue(null, "layout", 0); + if (layout == 0) { + final String value = attrs.getAttributeValue(null, "layout"); + if (value == null) { + throw new InflateException("You must specifiy a layout in the" + + " include tag: <include layout=\"@layout/layoutID\" />"); + } else { + throw new InflateException("You must specifiy a valid layout " + + "reference. The layout ID " + value + " is not valid."); + } + } else { + final XmlResourceParser childParser = + thisInflater.getContext().getResources().getLayout(layout); + + try { + final AttributeSet childAttrs = Xml.asAttributeSet(childParser); + + while ((type = childParser.next()) != XmlPullParser.START_TAG && + type != XmlPullParser.END_DOCUMENT) { + // Empty. + } + + if (type != XmlPullParser.START_TAG) { + throw new InflateException(childParser.getPositionDescription() + + ": No start tag found!"); + } + + final String childName = childParser.getName(); + + if (LayoutInflater.TAG_MERGE.equals(childName)) { + // Inflate all children. + thisInflater.rInflate(childParser, parent, childAttrs, false); + } else { + final View view = thisInflater.createViewFromTag(parent, childName, childAttrs); + final ViewGroup group = (ViewGroup) parent; + + // We try to load the layout params set in the <include /> tag. If + // they don't exist, we will rely on the layout params set in the + // included XML file. + // During a layoutparams generation, a runtime exception is thrown + // if either layout_width or layout_height is missing. We catch + // this exception and set localParams accordingly: true means we + // successfully loaded layout params from the <include /> tag, + // false means we need to rely on the included layout params. + ViewGroup.LayoutParams params = null; + try { + // ---- START CHANGES + sIsInInclude = true; + // ---- END CHANGES + + params = group.generateLayoutParams(attrs); + + } catch (RuntimeException e) { + // ---- START CHANGES + sIsInInclude = false; + // ---- END CHANGES + + params = group.generateLayoutParams(childAttrs); + } finally { + // ---- START CHANGES + sIsInInclude = false; + // ---- END CHANGES + + if (params != null) { + view.setLayoutParams(params); + } + } + + // Inflate all children. + thisInflater.rInflate(childParser, view, childAttrs, true); + + // Attempt to override the included layout's android:id with the + // one set on the <include /> tag itself. + TypedArray a = thisInflater.mContext.obtainStyledAttributes(attrs, + com.android.internal.R.styleable.View, 0, 0); + int id = a.getResourceId(com.android.internal.R.styleable.View_id, View.NO_ID); + // While we're at it, let's try to override android:visibility. + int visibility = a.getInt(com.android.internal.R.styleable.View_visibility, -1); + a.recycle(); + + if (id != View.NO_ID) { + view.setId(id); + } + + switch (visibility) { + case 0: + view.setVisibility(View.VISIBLE); + break; + case 1: + view.setVisibility(View.INVISIBLE); + break; + case 2: + view.setVisibility(View.GONE); + break; + } + + group.addView(view); + } + } finally { + childParser.close(); + } + } + } else { + throw new InflateException("<include /> can only be used inside of a ViewGroup"); + } + + final int currentDepth = parser.getDepth(); + while (((type = parser.next()) != XmlPullParser.END_TAG || + parser.getDepth() > currentDepth) && type != XmlPullParser.END_DOCUMENT) { + // Empty + } + } + + } diff --git a/tools/layoutlib/bridge/src/com/android/layoutlib/bridge/Bridge.java b/tools/layoutlib/bridge/src/com/android/layoutlib/bridge/Bridge.java index 144ec428a752..3ba32573aec8 100644 --- a/tools/layoutlib/bridge/src/com/android/layoutlib/bridge/Bridge.java +++ b/tools/layoutlib/bridge/src/com/android/layoutlib/bridge/Bridge.java @@ -196,7 +196,7 @@ public final class Bridge extends com.android.ide.common.rendering.api.Bridge { Capability.UNBOUND_RENDERING, Capability.CUSTOM_BACKGROUND_COLOR, Capability.RENDER, - //Capability.LAYOUT_ONLY, // disable to run on ADT 10.0 which doesn't include this. + Capability.LAYOUT_ONLY, Capability.EMBEDDED_LAYOUT, Capability.VIEW_MANIPULATION, Capability.PLAY_ANIMATION, diff --git a/tools/layoutlib/bridge/src/com/android/layoutlib/bridge/android/BridgeContext.java b/tools/layoutlib/bridge/src/com/android/layoutlib/bridge/android/BridgeContext.java index e5360289f977..4fa924d0da57 100644 --- a/tools/layoutlib/bridge/src/com/android/layoutlib/bridge/android/BridgeContext.java +++ b/tools/layoutlib/bridge/src/com/android/layoutlib/bridge/android/BridgeContext.java @@ -71,7 +71,6 @@ import java.io.File; import java.io.FileInputStream; import java.io.FileNotFoundException; import java.io.FileOutputStream; -import java.io.FileReader; import java.io.IOException; import java.io.InputStream; import java.util.HashMap; @@ -342,7 +341,7 @@ public final class BridgeContext extends Activity { try { KXmlParser parser = new KXmlParser(); parser.setFeature(XmlPullParser.FEATURE_PROCESS_NAMESPACES, true); - parser.setInput(new FileReader(xml)); + parser.setInput(new FileInputStream(xml), "UTF-8"); //$NON-NLS-1$); // set the resource ref to have correct view cookies mBridgeInflater.setResourceReference(resource); @@ -514,14 +513,13 @@ public final class BridgeContext extends Activity { BridgeTypedArray ta = ((BridgeResources) mSystemResources).newTypeArray(attrs.length, isPlatformFile); - // resolve the defStyleAttr value into a IStyleResourceValue - StyleResourceValue defStyleValues = null; - // look for a custom style. String customStyle = null; if (set != null) { customStyle = set.getAttributeValue(null /* namespace*/, "style"); } + + StyleResourceValue customStyleValues = null; if (customStyle != null) { ResourceValue item = mRenderResources.findResValue(customStyle, false /*forceFrameworkOnly*/); @@ -530,75 +528,76 @@ public final class BridgeContext extends Activity { item = mRenderResources.resolveResValue(item); if (item instanceof StyleResourceValue) { - defStyleValues = (StyleResourceValue)item; + customStyleValues = (StyleResourceValue)item; } } - if (defStyleValues == null) { - if (defStyleAttr != 0) { - // get the name from the int. - String defStyleName = searchAttr(defStyleAttr); + // resolve the defStyleAttr value into a IStyleResourceValue + StyleResourceValue defStyleValues = null; - if (defaultPropMap != null) { - defaultPropMap.put("style", defStyleName); - } + if (defStyleAttr != 0) { + // get the name from the int. + String defStyleName = searchAttr(defStyleAttr); - // look for the style in the current theme, and its parent: - ResourceValue item = mRenderResources.findItemInTheme(defStyleName); + if (defaultPropMap != null) { + defaultPropMap.put("style", defStyleName); + } - if (item != null) { - // item is a reference to a style entry. Search for it. - item = mRenderResources.findResValue(item.getValue(), - false /*forceFrameworkOnly*/); + // look for the style in the current theme, and its parent: + ResourceValue item = mRenderResources.findItemInTheme(defStyleName); - if (item instanceof StyleResourceValue) { - defStyleValues = (StyleResourceValue)item; - } - } else { - Bridge.getLog().error(null, - String.format( - "Failed to find style '%s' in current theme", defStyleName), - null /*data*/); - } - } else if (defStyleRes != 0) { - Pair<ResourceType, String> value = Bridge.resolveResourceId(defStyleRes); - if (value == null) { - value = mProjectCallback.resolveResourceId(defStyleRes); + if (item != null) { + // item is a reference to a style entry. Search for it. + item = mRenderResources.findResValue(item.getValue(), + false /*forceFrameworkOnly*/); + + if (item instanceof StyleResourceValue) { + defStyleValues = (StyleResourceValue)item; } + } else { + Bridge.getLog().error(null, + String.format( + "Failed to find style '%s' in current theme", defStyleName), + null /*data*/); + } + } else if (defStyleRes != 0) { + Pair<ResourceType, String> value = Bridge.resolveResourceId(defStyleRes); + if (value == null) { + value = mProjectCallback.resolveResourceId(defStyleRes); + } - if (value != null) { - if (value.getFirst() == ResourceType.STYLE) { - // look for the style in the current theme, and its parent: - ResourceValue item = mRenderResources.findItemInTheme(value.getSecond()); - if (item != null) { - if (item instanceof StyleResourceValue) { - if (defaultPropMap != null) { - defaultPropMap.put("style", item.getName()); - } - - defStyleValues = (StyleResourceValue)item; + if (value != null) { + if (value.getFirst() == ResourceType.STYLE) { + // look for the style in the current theme, and its parent: + ResourceValue item = mRenderResources.findItemInTheme(value.getSecond()); + if (item != null) { + if (item instanceof StyleResourceValue) { + if (defaultPropMap != null) { + defaultPropMap.put("style", item.getName()); } - } else { - Bridge.getLog().error(null, - String.format( - "Style with id 0x%x (resolved to '%s') does not exist.", - defStyleRes, value.getSecond()), - null /*data*/); + + defStyleValues = (StyleResourceValue)item; } } else { Bridge.getLog().error(null, String.format( - "Resouce id 0x%x is not of type STYLE (instead %s)", - defStyleRes, value.getFirst().toString()), + "Style with id 0x%x (resolved to '%s') does not exist.", + defStyleRes, value.getSecond()), null /*data*/); } } else { Bridge.getLog().error(null, String.format( - "Failed to find style with id 0x%x in current theme", - defStyleRes), + "Resouce id 0x%x is not of type STYLE (instead %s)", + defStyleRes, value.getFirst().toString()), null /*data*/); } + } else { + Bridge.getLog().error(null, + String.format( + "Failed to find style with id 0x%x in current theme", + defStyleRes), + null /*data*/); } } @@ -623,8 +622,13 @@ public final class BridgeContext extends Activity { if (value == null) { ResourceValue resValue = null; - // look for the value in the defStyle first (and its parent if needed) - if (defStyleValues != null) { + // look for the value in the custom style first (and its parent if needed) + if (customStyleValues != null) { + resValue = mRenderResources.findItemInStyle(customStyleValues, name); + } + + // then look for the value in the default Style (and its parent if needed) + if (resValue == null && defStyleValues != null) { resValue = mRenderResources.findItemInStyle(defStyleValues, name); } diff --git a/tools/layoutlib/bridge/src/com/android/layoutlib/bridge/android/BridgeInflater.java b/tools/layoutlib/bridge/src/com/android/layoutlib/bridge/android/BridgeInflater.java index d6bbebd807d0..7c90a312c6ad 100644 --- a/tools/layoutlib/bridge/src/com/android/layoutlib/bridge/android/BridgeInflater.java +++ b/tools/layoutlib/bridge/src/com/android/layoutlib/bridge/android/BridgeInflater.java @@ -36,7 +36,7 @@ import android.view.View; import android.view.ViewGroup; import java.io.File; -import java.io.FileReader; +import java.io.FileInputStream; /** * Custom implementation of {@link LayoutInflater} to handle custom views. @@ -177,7 +177,7 @@ public final class BridgeInflater extends LayoutInflater { try { KXmlParser parser = new KXmlParser(); parser.setFeature(XmlPullParser.FEATURE_PROCESS_NAMESPACES, true); - parser.setInput(new FileReader(f)); + parser.setInput(new FileInputStream(f), "UTF-8"); //$NON-NLS-1$ BridgeXmlBlockParser bridgeParser = new BridgeXmlBlockParser( parser, bridgeContext, false); diff --git a/tools/layoutlib/bridge/src/com/android/layoutlib/bridge/android/BridgeResources.java b/tools/layoutlib/bridge/src/com/android/layoutlib/bridge/android/BridgeResources.java index 273e493fa376..345f053582d7 100644 --- a/tools/layoutlib/bridge/src/com/android/layoutlib/bridge/android/BridgeResources.java +++ b/tools/layoutlib/bridge/src/com/android/layoutlib/bridge/android/BridgeResources.java @@ -46,7 +46,6 @@ import android.view.ViewGroup.LayoutParams; import java.io.File; import java.io.FileInputStream; import java.io.FileNotFoundException; -import java.io.FileReader; import java.io.InputStream; /** @@ -244,7 +243,7 @@ public final class BridgeResources extends Resources { // give that to our XmlBlockParser parser = new KXmlParser(); parser.setFeature(XmlPullParser.FEATURE_PROCESS_NAMESPACES, true); - parser.setInput(new FileReader(xml)); + parser.setInput(new FileInputStream(xml), "UTF-8"); //$NON-NLS-1$); } } @@ -282,7 +281,7 @@ public final class BridgeResources extends Resources { // give that to our XmlBlockParser parser = new KXmlParser(); parser.setFeature(XmlPullParser.FEATURE_PROCESS_NAMESPACES, true); - parser.setInput(new FileReader(xml)); + parser.setInput(new FileInputStream(xml), "UTF-8"); //$NON-NLS-1$); return new BridgeXmlBlockParser(parser, mContext, mPlatformResourceFlag[0]); } @@ -501,7 +500,7 @@ public final class BridgeResources extends Resources { try { KXmlParser parser = new KXmlParser(); parser.setFeature(XmlPullParser.FEATURE_PROCESS_NAMESPACES, true); - parser.setInput(new FileReader(f)); + parser.setInput(new FileInputStream(f), "UTF-8"); //$NON-NLS-1$); return new BridgeXmlBlockParser(parser, mContext, mPlatformResourceFlag[0]); } catch (XmlPullParserException e) { @@ -536,7 +535,7 @@ public final class BridgeResources extends Resources { try { KXmlParser parser = new KXmlParser(); parser.setFeature(XmlPullParser.FEATURE_PROCESS_NAMESPACES, true); - parser.setInput(new FileReader(f)); + parser.setInput(new FileInputStream(f), "UTF-8"); //$NON-NLS-1$); return new BridgeXmlBlockParser(parser, mContext, mPlatformResourceFlag[0]); } catch (XmlPullParserException e) { diff --git a/tools/layoutlib/bridge/src/com/android/layoutlib/bridge/android/BridgeTypedArray.java b/tools/layoutlib/bridge/src/com/android/layoutlib/bridge/android/BridgeTypedArray.java index b9f769f2af06..d4600a14f42e 100644 --- a/tools/layoutlib/bridge/src/com/android/layoutlib/bridge/android/BridgeTypedArray.java +++ b/tools/layoutlib/bridge/src/com/android/layoutlib/bridge/android/BridgeTypedArray.java @@ -36,10 +36,11 @@ import android.content.res.TypedArray; import android.graphics.drawable.Drawable; import android.util.DisplayMetrics; import android.util.TypedValue; +import android.view.LayoutInflater_Delegate; import android.view.ViewGroup.LayoutParams; import java.io.File; -import java.io.FileReader; +import java.io.FileInputStream; import java.util.Arrays; import java.util.Map; @@ -315,7 +316,7 @@ public final class BridgeTypedArray extends TypedArray { try { KXmlParser parser = new KXmlParser(); parser.setFeature(XmlPullParser.FEATURE_PROCESS_NAMESPACES, true); - parser.setInput(new FileReader(f)); + parser.setInput(new FileInputStream(f), "UTF-8"); //$NON-NLS-1$); BridgeXmlBlockParser blockParser = new BridgeXmlBlockParser( parser, mContext, resValue.isFramework()); @@ -471,40 +472,23 @@ public final class BridgeTypedArray extends TypedArray { */ @Override public int getDimensionPixelSize(int index, int defValue) { - if (mResourceData[index] == null) { - return defValue; - } + try { + return getDimension(index); + } catch (RuntimeException e) { + if (mResourceData[index] != null) { + String s = mResourceData[index].getValue(); - String s = mResourceData[index].getValue(); + if (s != null) { + // looks like we were unable to resolve the dimension value + Bridge.getLog().warning(LayoutLog.TAG_RESOURCES_FORMAT, + String.format( + "\"%1$s\" in attribute \"%2$s\" is not a valid format.", + s, mNames[index]), null /*data*/); + } + } - if (s == null) { - return defValue; - } else if (s.equals(BridgeConstants.MATCH_PARENT) || - s.equals(BridgeConstants.FILL_PARENT)) { - return LayoutParams.MATCH_PARENT; - } else if (s.equals(BridgeConstants.WRAP_CONTENT)) { - return LayoutParams.WRAP_CONTENT; - } else if (RenderResources.REFERENCE_NULL.equals(s)) { return defValue; } - - if (ResourceHelper.stringToFloat(s, mValue)) { - float f = mValue.getDimension(mBridgeResources.mMetrics); - - final int res = (int)(f+0.5f); - if (res != 0) return res; - if (f == 0) return 0; - if (f > 0) return 1; - return defValue; // this is basically unreachable. - } - - // looks like we were unable to resolve the dimension value - Bridge.getLog().warning(LayoutLog.TAG_RESOURCES_FORMAT, - String.format( - "\"%1$s\" in attribute \"%2$s\" is not a valid format.", - s, mNames[index]), null /*data*/); - - return defValue; } /** @@ -521,7 +505,20 @@ public final class BridgeTypedArray extends TypedArray { */ @Override public int getLayoutDimension(int index, String name) { - return getDimensionPixelSize(index, 0); + try { + // this will throw an exception + return getDimension(index); + } catch (RuntimeException e) { + + if (LayoutInflater_Delegate.sIsInInclude) { + throw new RuntimeException(); + } + + Bridge.getLog().warning(LayoutLog.TAG_RESOURCES_FORMAT, + "You must supply a " + name + " attribute.", null); + + return 0; + } } @Override @@ -529,6 +526,36 @@ public final class BridgeTypedArray extends TypedArray { return getDimensionPixelSize(index, defValue); } + private int getDimension(int index) { + if (mResourceData[index] == null) { + throw new RuntimeException(); + } + + String s = mResourceData[index].getValue(); + + if (s == null) { + throw new RuntimeException(); + } else if (s.equals(BridgeConstants.MATCH_PARENT) || + s.equals(BridgeConstants.FILL_PARENT)) { + return LayoutParams.MATCH_PARENT; + } else if (s.equals(BridgeConstants.WRAP_CONTENT)) { + return LayoutParams.WRAP_CONTENT; + } else if (RenderResources.REFERENCE_NULL.equals(s)) { + throw new RuntimeException(); + } + + if (ResourceHelper.stringToFloat(s, mValue)) { + float f = mValue.getDimension(mBridgeResources.mMetrics); + + final int res = (int)(f+0.5f); + if (res != 0) return res; + if (f == 0) return 0; + if (f > 0) return 1; + } + + throw new RuntimeException(); + } + /** * Retrieve a fractional unit attribute at <var>index</var>. * diff --git a/tools/layoutlib/bridge/src/com/android/layoutlib/bridge/bars/CustomBar.java b/tools/layoutlib/bridge/src/com/android/layoutlib/bridge/bars/CustomBar.java index 0c4b0d3bbda7..060e6ee077bb 100644 --- a/tools/layoutlib/bridge/src/com/android/layoutlib/bridge/bars/CustomBar.java +++ b/tools/layoutlib/bridge/src/com/android/layoutlib/bridge/bars/CustomBar.java @@ -73,7 +73,7 @@ abstract class CustomBar extends LinearLayout { parser.setFeature(XmlPullParser.FEATURE_PROCESS_NAMESPACES, true); parser.setInput( getClass().getResourceAsStream(layoutPath), - "UTF8"); + "UTF8"); //$NON-NLS-1$ BridgeXmlBlockParser bridgeParser = new BridgeXmlBlockParser( parser, (BridgeContext) context, false /*platformFile*/); diff --git a/tools/layoutlib/bridge/src/com/android/layoutlib/bridge/impl/RenderAction.java b/tools/layoutlib/bridge/src/com/android/layoutlib/bridge/impl/RenderAction.java index 8e80c2117115..6194f5d757cb 100644 --- a/tools/layoutlib/bridge/src/com/android/layoutlib/bridge/impl/RenderAction.java +++ b/tools/layoutlib/bridge/src/com/android/layoutlib/bridge/impl/RenderAction.java @@ -29,6 +29,7 @@ import com.android.layoutlib.bridge.Bridge; import com.android.layoutlib.bridge.android.BridgeContext; import com.android.resources.ResourceType; +import android.os.HandlerThread_Delegate; import android.util.DisplayMetrics; import java.util.concurrent.TimeUnit; @@ -228,6 +229,10 @@ public abstract class RenderAction<T extends RenderParams> extends FrameworkReso private void tearDown() { // Make sure to remove static references, otherwise we could not unload the lib mContext.disposeResources(); + + // quit HandlerThread created during this session. + HandlerThread_Delegate.cleanUp(sCurrentContext); + sCurrentContext = null; Bridge.setLog(null); diff --git a/tools/layoutlib/bridge/src/com/android/layoutlib/bridge/impl/ResourceHelper.java b/tools/layoutlib/bridge/src/com/android/layoutlib/bridge/impl/ResourceHelper.java index 96ab30f1c2ff..e5efa4e55424 100644 --- a/tools/layoutlib/bridge/src/com/android/layoutlib/bridge/impl/ResourceHelper.java +++ b/tools/layoutlib/bridge/src/com/android/layoutlib/bridge/impl/ResourceHelper.java @@ -44,7 +44,6 @@ import android.util.TypedValue; import java.io.File; import java.io.FileInputStream; -import java.io.FileReader; import java.io.IOException; import java.io.InputStream; import java.net.MalformedURLException; @@ -124,7 +123,7 @@ public final class ResourceHelper { // providing an XmlPullParser KXmlParser parser = new KXmlParser(); parser.setFeature(XmlPullParser.FEATURE_PROCESS_NAMESPACES, true); - parser.setInput(new FileReader(f)); + parser.setInput(new FileInputStream(f), "UTF-8"); //$NON-NLS-1$); BridgeXmlBlockParser blockParser = new BridgeXmlBlockParser( parser, context, resValue.isFramework()); @@ -206,7 +205,7 @@ public final class ResourceHelper { // let the framework inflate the Drawable from the XML file. KXmlParser parser = new KXmlParser(); parser.setFeature(XmlPullParser.FEATURE_PROCESS_NAMESPACES, true); - parser.setInput(new FileReader(f)); + parser.setInput(new FileInputStream(f), "UTF-8"); //$NON-NLS-1$); BridgeXmlBlockParser blockParser = new BridgeXmlBlockParser( parser, context, value.isFramework()); diff --git a/tools/layoutlib/bridge/tests/src/com/android/layoutlib/bridge/android/BridgeXmlBlockParserTest.java b/tools/layoutlib/bridge/tests/src/com/android/layoutlib/bridge/android/BridgeXmlBlockParserTest.java index 3252fb497714..70d5446265e5 100644 --- a/tools/layoutlib/bridge/tests/src/com/android/layoutlib/bridge/android/BridgeXmlBlockParserTest.java +++ b/tools/layoutlib/bridge/tests/src/com/android/layoutlib/bridge/android/BridgeXmlBlockParserTest.java @@ -44,7 +44,7 @@ public class BridgeXmlBlockParserTest extends TestCase { InputStream input = this.getClass().getClassLoader().getResourceAsStream( "com/android/layoutlib/testdata/layout1.xml"); - parser.setInput(input, null /*encoding*/); + parser.setInput(input, "UTF-8"); //$NON-NLS-1$ assertEquals(XmlPullParser.START_DOCUMENT, parser.next()); diff --git a/tools/layoutlib/create/src/com/android/tools/layoutlib/create/CreateInfo.java b/tools/layoutlib/create/src/com/android/tools/layoutlib/create/CreateInfo.java index eff6bbc893b1..5c60318686bb 100644 --- a/tools/layoutlib/create/src/com/android/tools/layoutlib/create/CreateInfo.java +++ b/tools/layoutlib/create/src/com/android/tools/layoutlib/create/CreateInfo.java @@ -99,8 +99,10 @@ public final class CreateInfo implements ICreateInfo { "android.content.res.Resources$Theme#resolveAttribute", "android.graphics.BitmapFactory#finishDecode", "android.os.Handler#sendMessageAtTime", + "android.os.HandlerThread#run", "android.os.Build#getString", "android.view.LayoutInflater#rInflate", + "android.view.LayoutInflater#parseInclude", "android.view.View#isInEditMode", "com.android.internal.util.XmlUtils#convertValueToInt", // TODO: comment out once DelegateClass is working |