diff options
87 files changed, 2894 insertions, 1123 deletions
diff --git a/api/current.txt b/api/current.txt index ab48c8d0a6be..a57423730de7 100644 --- a/api/current.txt +++ b/api/current.txt @@ -10862,6 +10862,7 @@ package android.hardware.camera2 { public abstract class CameraMetadata { method public abstract T get(android.hardware.camera2.CameraMetadata.Key<T>); + method public java.util.List<android.hardware.camera2.CameraMetadata.Key<?>> getKeys(); field public static final int COLOR_CORRECTION_MODE_FAST = 1; // 0x1 field public static final int COLOR_CORRECTION_MODE_HIGH_QUALITY = 2; // 0x2 field public static final int COLOR_CORRECTION_MODE_TRANSFORM_MATRIX = 0; // 0x0 @@ -10987,6 +10988,8 @@ package android.hardware.camera2 { public final class CameraProperties extends android.hardware.camera2.CameraMetadata { method public T get(android.hardware.camera2.CameraMetadata.Key<T>); + method public java.util.List<android.hardware.camera2.CameraMetadata.Key<?>> getAvailableCaptureRequestKeys(); + method public java.util.List<android.hardware.camera2.CameraMetadata.Key<?>> getAvailableCaptureResultKeys(); field public static final android.hardware.camera2.CameraMetadata.Key CONTROL_AE_AVAILABLE_ANTIBANDING_MODES; field public static final android.hardware.camera2.CameraMetadata.Key CONTROL_AE_AVAILABLE_TARGET_FPS_RANGES; field public static final android.hardware.camera2.CameraMetadata.Key CONTROL_AE_COMPENSATION_RANGE; @@ -11131,6 +11134,7 @@ package android.hardware.camera2 { field public static final android.hardware.camera2.CameraMetadata.Key SENSOR_SENSITIVITY; field public static final android.hardware.camera2.CameraMetadata.Key SENSOR_TEMPERATURE; field public static final android.hardware.camera2.CameraMetadata.Key SENSOR_TIMESTAMP; + field public static final android.hardware.camera2.CameraMetadata.Key STATISTICS_FACES; field public static final android.hardware.camera2.CameraMetadata.Key STATISTICS_FACE_DETECT_MODE; field public static final android.hardware.camera2.CameraMetadata.Key STATISTICS_FACE_IDS; field public static final android.hardware.camera2.CameraMetadata.Key STATISTICS_FACE_LANDMARKS; @@ -11146,14 +11150,16 @@ package android.hardware.camera2 { field public static final android.hardware.camera2.CameraMetadata.Key TONEMAP_MODE; } - public static class CaptureResult.Face { - ctor public CaptureResult.Face(); + public final class Face { method public android.graphics.Rect getBounds(); method public int getId(); - method public android.graphics.Point getLeftEye(); - method public android.graphics.Point getMouth(); - method public android.graphics.Point getRightEye(); + method public android.graphics.Point getLeftEyePosition(); + method public android.graphics.Point getMouthPosition(); + method public android.graphics.Point getRightEyePosition(); method public int getScore(); + field public static final int ID_UNSUPPORTED = -1; // 0xffffffff + field public static final int SCORE_MAX = 100; // 0x64 + field public static final int SCORE_MIN = 1; // 0x1 } public final class Rational { @@ -12334,7 +12340,7 @@ package android.media { field public static final int EULER_Z = 2; // 0x2 } - public abstract interface Image implements java.lang.AutoCloseable { + public abstract class Image implements java.lang.AutoCloseable { method public abstract void close(); method public abstract int getFormat(); method public abstract int getHeight(); @@ -12343,23 +12349,23 @@ package android.media { method public abstract int getWidth(); } - public static abstract interface Image.Plane { + public static abstract class Image.Plane { method public abstract java.nio.ByteBuffer getBuffer(); method public abstract int getPixelStride(); method public abstract int getRowStride(); } - public final class ImageReader implements java.lang.AutoCloseable { - ctor public ImageReader(int, int, int, int); + public class ImageReader implements java.lang.AutoCloseable { + method public android.media.Image acquireLatestImage(); + method public android.media.Image acquireNextImage(); method public void close(); method public int getHeight(); method public int getImageFormat(); method public int getMaxImages(); - method public android.media.Image getNextImage(); method public android.view.Surface getSurface(); method public int getWidth(); - method public void releaseImage(android.media.Image); - method public void setImageAvailableListener(android.media.ImageReader.OnImageAvailableListener, android.os.Handler); + method public static android.media.ImageReader newInstance(int, int, int, int); + method public void setOnImageAvailableListener(android.media.ImageReader.OnImageAvailableListener, android.os.Handler); } public static abstract interface ImageReader.OnImageAvailableListener { @@ -15211,9 +15217,9 @@ package android.nfc { field public static final java.lang.String EXTRA_NDEF_MESSAGES = "android.nfc.extra.NDEF_MESSAGES"; field public static final java.lang.String EXTRA_READER_PRESENCE_CHECK_DELAY = "presence"; field public static final java.lang.String EXTRA_TAG = "android.nfc.extra.TAG"; - field public static final int FLAG_READER_KOVIO = 16; // 0x10 field public static final int FLAG_READER_NFC_A = 1; // 0x1 field public static final int FLAG_READER_NFC_B = 2; // 0x2 + field public static final int FLAG_READER_NFC_BARCODE = 16; // 0x10 field public static final int FLAG_READER_NFC_F = 4; // 0x4 field public static final int FLAG_READER_NFC_V = 8; // 0x8 field public static final int FLAG_READER_NO_PLATFORM_SOUNDS = 256; // 0x100 diff --git a/cmds/am/src/com/android/commands/am/Am.java b/cmds/am/src/com/android/commands/am/Am.java index d78572b561d7..c18f54259678 100644 --- a/cmds/am/src/com/android/commands/am/Am.java +++ b/cmds/am/src/com/android/commands/am/Am.java @@ -100,6 +100,7 @@ public class Am extends BaseCommand { " am monitor [--gdb <port>]\n" + " am hang [--allow-restart]\n" + " am restart\n" + + " am idle-maintenance\n" + " am screen-compat [on|off] <PACKAGE>\n" + " am to-uri [INTENT]\n" + " am to-intent-uri [INTENT]\n" + @@ -189,6 +190,8 @@ public class Am extends BaseCommand { "\n" + "am restart: restart the user-space system.\n" + "\n" + + "am idle-maintenance: perform idle maintenance now.\n" + + "\n" + "am screen-compat: control screen compatibility mode of <PACKAGE>.\n" + "\n" + "am to-uri: print the given Intent specification as a URI.\n" + @@ -295,6 +298,8 @@ public class Am extends BaseCommand { runHang(); } else if (op.equals("restart")) { runRestart(); + } else if (op.equals("idle-maintenance")) { + runIdleMaintenance(); } else if (op.equals("screen-compat")) { runScreenCompat(); } else if (op.equals("to-uri")) { @@ -1393,6 +1398,20 @@ public class Am extends BaseCommand { mAm.restart(); } + private void runIdleMaintenance() throws Exception { + String opt; + while ((opt=nextOption()) != null) { + System.err.println("Error: Unknown option: " + opt); + return; + } + + System.out.println("Performing idle maintenance..."); + Intent intent = new Intent( + "com.android.server.IdleMaintenanceService.action.FORCE_IDLE_MAINTENANCE"); + mAm.broadcastIntent(null, intent, null, null, 0, null, null, null, + android.app.AppOpsManager.OP_NONE, true, false, UserHandle.USER_ALL); + } + private void runScreenCompat() throws Exception { String mode = nextArgRequired(); boolean enabled; diff --git a/core/java/android/app/ActivityManagerNative.java b/core/java/android/app/ActivityManagerNative.java index 370db3117663..a727b076e9bb 100644 --- a/core/java/android/app/ActivityManagerNative.java +++ b/core/java/android/app/ActivityManagerNative.java @@ -1995,6 +1995,13 @@ public abstract class ActivityManagerNative extends Binder implements IActivityM reply.writeParcelableArray(uris, 0); return true; } + + case PERFORM_IDLE_MAINTENANCE_TRANSACTION: { + data.enforceInterface(IActivityManager.descriptor); + performIdleMaintenance(); + reply.writeNoException(); + return true; + } } return super.onTransact(code, data, reply, flags); @@ -4578,5 +4585,15 @@ class ActivityManagerProxy implements IActivityManager return uris; } + public void performIdleMaintenance() throws RemoteException { + Parcel data = Parcel.obtain(); + Parcel reply = Parcel.obtain(); + data.writeInterfaceToken(IActivityManager.descriptor); + mRemote.transact(PERFORM_IDLE_MAINTENANCE_TRANSACTION, data, reply, 0); + reply.readException(); + data.recycle(); + reply.recycle(); + } + private IBinder mRemote; } diff --git a/core/java/android/app/IActivityManager.java b/core/java/android/app/IActivityManager.java index b2ae298944f4..25c02df80840 100644 --- a/core/java/android/app/IActivityManager.java +++ b/core/java/android/app/IActivityManager.java @@ -403,6 +403,8 @@ public interface IActivityManager extends IInterface { String sourcePackage, String targetPackage, int modeFlags, int modeMask) throws RemoteException; + public void performIdleMaintenance() throws RemoteException; + /* * Private non-Binder interfaces */ @@ -685,4 +687,5 @@ public interface IActivityManager extends IInterface { int REPORT_ACTIVITY_FULLY_DRAWN_TRANSACTION = IBinder.FIRST_CALL_TRANSACTION+176; int RESTART_TRANSACTION = IBinder.FIRST_CALL_TRANSACTION+177; int GET_GRANTED_URI_PERMISSIONS_TRANSACTION = IBinder.FIRST_CALL_TRANSACTION+178; + int PERFORM_IDLE_MAINTENANCE_TRANSACTION = IBinder.FIRST_CALL_TRANSACTION+179; } diff --git a/core/java/android/content/Context.java b/core/java/android/content/Context.java index 92a9c7c84ab1..02ccaa5ace22 100644 --- a/core/java/android/content/Context.java +++ b/core/java/android/content/Context.java @@ -2132,13 +2132,8 @@ public abstract class Context { public static final String UPDATE_LOCK_SERVICE = "updatelock"; /** - * Use with {@link #getSystemService} to retrieve a {@link - * android.net.NetworkManagementService} for handling management of - * system network services - * + * Constant for the internal network management service, not really a Context service. * @hide - * @see #getSystemService - * @see android.net.NetworkManagementService */ public static final String NETWORKMANAGEMENT_SERVICE = "network_management"; @@ -2327,7 +2322,7 @@ public abstract class Context { * android.hardware.SerialManager} for access to serial ports. * * @see #getSystemService - * @see android.harware.SerialManager + * @see android.hardware.SerialManager * * @hide */ @@ -2353,17 +2348,6 @@ public abstract class Context { /** * Use with {@link #getSystemService} to retrieve a - * {@link android.os.SchedulingPolicyService} for managing scheduling policy. - * - * @see #getSystemService - * @see android.os.SchedulingPolicyService - * - * @hide - */ - public static final String SCHEDULING_POLICY_SERVICE = "scheduling_policy"; - - /** - * Use with {@link #getSystemService} to retrieve a * {@link android.os.UserManager} for managing users on devices that support multiple users. * * @see #getSystemService diff --git a/core/java/android/content/pm/PackageParser.java b/core/java/android/content/pm/PackageParser.java index 4494e691ccfa..2e25177e49a9 100644 --- a/core/java/android/content/pm/PackageParser.java +++ b/core/java/android/content/pm/PackageParser.java @@ -1441,18 +1441,29 @@ public class PackageParser { */ boolean required = true; // Optional <uses-permission> not supported + int maxSdkVersion = 0; + TypedValue val = sa.peekValue( + com.android.internal.R.styleable.AndroidManifestUsesPermission_maxSdkVersion); + if (val != null) { + if (val.type >= TypedValue.TYPE_FIRST_INT && val.type <= TypedValue.TYPE_LAST_INT) { + maxSdkVersion = val.data; + } + } + sa.recycle(); - if (name != null) { - int index = pkg.requestedPermissions.indexOf(name); - if (index == -1) { - pkg.requestedPermissions.add(name.intern()); - pkg.requestedPermissionsRequired.add(required ? Boolean.TRUE : Boolean.FALSE); - } else { - if (pkg.requestedPermissionsRequired.get(index) != required) { - outError[0] = "conflicting <uses-permission> entries"; - mParseError = PackageManager.INSTALL_PARSE_FAILED_MANIFEST_MALFORMED; - return false; + if ((maxSdkVersion == 0) || (maxSdkVersion >= Build.VERSION.RESOURCES_SDK_INT)) { + if (name != null) { + int index = pkg.requestedPermissions.indexOf(name); + if (index == -1) { + pkg.requestedPermissions.add(name.intern()); + pkg.requestedPermissionsRequired.add(required ? Boolean.TRUE : Boolean.FALSE); + } else { + if (pkg.requestedPermissionsRequired.get(index) != required) { + outError[0] = "conflicting <uses-permission> entries"; + mParseError = PackageManager.INSTALL_PARSE_FAILED_MANIFEST_MALFORMED; + return false; + } } } } diff --git a/core/java/android/hardware/camera2/CameraMetadata.java b/core/java/android/hardware/camera2/CameraMetadata.java index ec23f082f2f5..7f4ba4f1551a 100644 --- a/core/java/android/hardware/camera2/CameraMetadata.java +++ b/core/java/android/hardware/camera2/CameraMetadata.java @@ -18,12 +18,25 @@ package android.hardware.camera2; import android.hardware.camera2.impl.CameraMetadataNative; +import java.lang.reflect.Field; +import java.util.ArrayList; +import java.util.Collections; +import java.util.List; + /** * The base class for camera controls and information. * + * <p> * This class defines the basic key/value map used for querying for camera * characteristics or capture results, and for setting camera request * parameters. + * </p> + * + * <p> + * All instances of CameraMetadata are immutable. The list of keys with {@link #getKeys()} + * never changes, nor do the values returned by any key with {@link #get} throughout + * the lifetime of the object. + * </p> * * @see CameraDevice * @see CameraManager @@ -38,9 +51,14 @@ public abstract class CameraMetadata { } /** - * Get a camera metadata field value. The field definitions can be + * Get a camera metadata field value. + * + * <p>The field definitions can be * found in {@link CameraProperties}, {@link CaptureResult}, and - * {@link CaptureRequest}. + * {@link CaptureRequest}.</p> + * + * <p>Querying the value for the same key more than once will return a value + * which is equal to the previous queried value.</p> * * @throws IllegalArgumentException if the key was not valid * @@ -49,6 +67,54 @@ public abstract class CameraMetadata { */ public abstract <T> T get(Key<T> key); + /** + * Returns a list of the keys contained in this map. + * + * <p>The list returned is not modifiable, so any attempts to modify it will throw + * a {@code UnsupportedOperationException}.</p> + * + * <p>All values retrieved by a key from this list with {@link #get} are guaranteed to be + * non-{@code null}. Each key is only listed once in the list. The order of the keys + * is undefined.</p> + * + * @return List of the keys contained in this map. + */ + public List<Key<?>> getKeys() { + return Collections.unmodifiableList(getKeysStatic(this.getClass(), this)); + } + + /** + * Return a list of all the Key<?> that are declared as a field inside of the class + * {@code type}. + * + * <p> + * Optionally, if {@code instance} is not null, then filter out any keys with null values. + * </p> + */ + /*package*/ static ArrayList<Key<?>> getKeysStatic(Class<? extends CameraMetadata> type, + CameraMetadata instance) { + ArrayList<Key<?>> keyList = new ArrayList<Key<?>>(); + + Field[] fields = type.getDeclaredFields(); + for (Field field : fields) { + if (field.getDeclaringClass().isAssignableFrom(Key.class)) { + Key<?> key; + try { + key = (Key<?>) field.get(instance); + } catch (IllegalAccessException e) { + throw new AssertionError("Can't get IllegalAccessException", e); + } catch (IllegalArgumentException e) { + throw new AssertionError("Can't get IllegalArgumentException", e); + } + if (instance == null || instance.get(key) != null) { + keyList.add(key); + } + } + } + + return keyList; + } + public static class Key<T> { private boolean mHasTag; diff --git a/core/java/android/hardware/camera2/CameraProperties.java b/core/java/android/hardware/camera2/CameraProperties.java index 45c009fbe925..58a1ee3175df 100644 --- a/core/java/android/hardware/camera2/CameraProperties.java +++ b/core/java/android/hardware/camera2/CameraProperties.java @@ -18,6 +18,9 @@ package android.hardware.camera2; import android.hardware.camera2.impl.CameraMetadataNative; +import java.util.Collections; +import java.util.List; + /** * <p>The properties describing a * {@link CameraDevice CameraDevice}.</p> @@ -32,6 +35,8 @@ import android.hardware.camera2.impl.CameraMetadataNative; public final class CameraProperties extends CameraMetadata { private final CameraMetadataNative mProperties; + private List<Key<?>> mAvailableRequestKeys; + private List<Key<?>> mAvailableResultKeys; /** * Takes ownership of the passed-in properties object @@ -46,6 +51,75 @@ public final class CameraProperties extends CameraMetadata { return mProperties.get(key); } + /** + * Returns the list of keys supported by this {@link CameraDevice} for querying + * with a {@link CaptureRequest}. + * + * <p>The list returned is not modifiable, so any attempts to modify it will throw + * a {@code UnsupportedOperationException}.</p> + * + * <p>Each key is only listed once in the list. The order of the keys is undefined.</p> + * + * <p>Note that there is no {@code getAvailableCameraPropertiesKeys()} -- use + * {@link #getKeys()} instead.</p> + * + * @return List of keys supported by this CameraDevice for CaptureRequests. + */ + public List<Key<?>> getAvailableCaptureRequestKeys() { + if (mAvailableRequestKeys == null) { + mAvailableRequestKeys = getAvailableKeyList(CaptureRequest.class); + } + return mAvailableRequestKeys; + } + + /** + * Returns the list of keys supported by this {@link CameraDevice} for querying + * with a {@link CaptureResult}. + * + * <p>The list returned is not modifiable, so any attempts to modify it will throw + * a {@code UnsupportedOperationException}.</p> + * + * <p>Each key is only listed once in the list. The order of the keys is undefined.</p> + * + * <p>Note that there is no {@code getAvailableCameraPropertiesKeys()} -- use + * {@link #getKeys()} instead.</p> + * + * @return List of keys supported by this CameraDevice for CaptureResults. + */ + public List<Key<?>> getAvailableCaptureResultKeys() { + if (mAvailableResultKeys == null) { + mAvailableResultKeys = getAvailableKeyList(CaptureResult.class); + } + return mAvailableResultKeys; + } + + /** + * Returns the list of keys supported by this {@link CameraDevice} by metadataClass. + * + * <p>The list returned is not modifiable, so any attempts to modify it will throw + * a {@code UnsupportedOperationException}.</p> + * + * <p>Each key is only listed once in the list. The order of the keys is undefined.</p> + * + * @param metadataClass The subclass of CameraMetadata that you want to get the keys for. + * + * @return List of keys supported by this CameraDevice for metadataClass. + * + * @throws IllegalArgumentException if metadataClass is not a subclass of CameraMetadata + */ + private <T extends CameraMetadata> List<Key<?>> getAvailableKeyList(Class<T> metadataClass) { + + if (metadataClass.equals(CameraMetadata.class)) { + throw new AssertionError( + "metadataClass must be a strict subclass of CameraMetadata"); + } else if (!CameraMetadata.class.isAssignableFrom(metadataClass)) { + throw new AssertionError( + "metadataClass must be a subclass of CameraMetadata"); + } + + return Collections.unmodifiableList(getKeysStatic(metadataClass, /*instance*/null)); + } + /*@O~@~@~@~@~@~@~@~@~@~@~@~@~@~@~@~@~@~@~@~@~@~@~@~@~@~@~@~@~@~@~@~@~@~ * The key entries below this point are generated from metadata * definitions in /system/media/camera/docs. Do not modify by hand or diff --git a/core/java/android/hardware/camera2/CaptureRequest.java b/core/java/android/hardware/camera2/CaptureRequest.java index 4608ab913fa7..3ec5ca03c782 100644 --- a/core/java/android/hardware/camera2/CaptureRequest.java +++ b/core/java/android/hardware/camera2/CaptureRequest.java @@ -176,7 +176,7 @@ public final class CaptureRequest extends CameraMetadata implements Parcelable { */ public final static class Builder { - private CaptureRequest mRequest; + private final CaptureRequest mRequest; /** * Initialize the builder using the template; the request takes diff --git a/core/java/android/hardware/camera2/CaptureResult.java b/core/java/android/hardware/camera2/CaptureResult.java index 3fcd2f9c147c..377e78aa5477 100644 --- a/core/java/android/hardware/camera2/CaptureResult.java +++ b/core/java/android/hardware/camera2/CaptureResult.java @@ -51,89 +51,6 @@ public final class CaptureResult extends CameraMetadata { return mResults.get(key); } - /** - * Describes a face detected in an image. - */ - public static class Face { - - /** - * <p>Bounds of the face. A rectangle relative to the sensor's - * {@link CameraProperties#SENSOR_INFO_ACTIVE_ARRAY_SIZE}, with (0,0) - * representing the top-left corner of the active array rectangle.</p> - */ - public Rect getBounds() { - return mBounds; - } - - /** <p>The confidence level for the detection of the face. The range is 1 to - * 100. 100 is the highest confidence.</p> - * - * <p>Depending on the device, even very low-confidence faces may be - * listed, so applications should filter out faces with low confidence, - * depending on the use case. For a typical point-and-shoot camera - * application that wishes to display rectangles around detected faces, - * filtering out faces with confidence less than 50 is recommended.</p> - * - */ - public int getScore() { - return mScore; - } - - /** - * An unique id per face while the face is visible to the tracker. If - * the face leaves the field-of-view and comes back, it will get a new - * id. This is an optional field, may not be supported on all devices. - * If not supported, id will always be set to -1. The optional fields - * are supported as a set. Either they are all valid, or none of them - * are. - */ - public int getId() { - return mId; - } - - /** - * The coordinates of the center of the left eye. The coordinates are in - * the same space as the ones for {@link #getBounds}. This is an - * optional field, may not be supported on all devices. If not - * supported, the value will always be set to null. The optional fields - * are supported as a set. Either they are all valid, or none of them - * are. - */ - public Point getLeftEye() { - return mLeftEye; - } - - /** - * The coordinates of the center of the right eye. The coordinates are - * in the same space as the ones for {@link #getBounds}.This is an - * optional field, may not be supported on all devices. If not - * supported, the value will always be set to null. The optional fields - * are supported as a set. Either they are all valid, or none of them - * are. - */ - public Point getRightEye() { - return mRightEye; - } - - /** - * The coordinates of the center of the mouth. The coordinates are in - * the same space as the ones for {@link #getBounds}. This is an optional - * field, may not be supported on all devices. If not supported, the - * value will always be set to null. The optional fields are supported - * as a set. Either they are all valid, or none of them are. - */ - public Point getMouth() { - return mMouth; - } - - private Rect mBounds; - private int mScore; - private int mId; - private Point mLeftEye; - private Point mRightEye; - private Point mMouth; - } - /*@O~@~@~@~@~@~@~@~@~@~@~@~@~@~@~@~@~@~@~@~@~@~@~@~@~@~@~@~@~@~@~@~@~@~ * The key entries below this point are generated from metadata * definitions in /system/media/camera/docs. Do not modify by hand or @@ -1003,4 +920,19 @@ public final class CaptureResult extends CameraMetadata { /*~@~@~@~@~@~@~@~@~@~@~@~@~@~@~@~@~@~@~@~@~@~@~@~@~@~@~@~@~@~@~@~@~@~@~@~ * End generated code *~@~@~@~@~@~@~@~@~@~@~@~@~@~@~@~@~@~@~@~@~@~@~@~@~@~@~@~@~@~@~@~@~@~O@*/ + + /** + * <p> + * List of the {@link Face Faces} detected through camera face detection + * in this result. + * </p> + * <p> + * Only available if {@link #STATISTICS_FACE_DETECT_MODE} {@code !=} + * {@link CameraMetadata#STATISTICS_FACE_DETECT_MODE_OFF OFF}. + * </p> + * + * @see Face + */ + public static final Key<Face[]> STATISTICS_FACES = + new Key<Face[]>("android.statistics.faces", Face[].class); } diff --git a/core/java/android/hardware/camera2/Face.java b/core/java/android/hardware/camera2/Face.java new file mode 100644 index 000000000000..6bfc5354359a --- /dev/null +++ b/core/java/android/hardware/camera2/Face.java @@ -0,0 +1,252 @@ +/* + * Copyright (C) 2013 The Android Open Source Project + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + + +package android.hardware.camera2; + +import android.graphics.Point; +import android.graphics.Rect; + +/** + * Describes a face detected in an image. + */ +public final class Face { + + /** + * The ID is {@code -1} when the optional set of fields is unsupported. + * + * @see Face#Face(Rect, int) + * @see #getId() + */ + public static final int ID_UNSUPPORTED = -1; + + /** + * The minimum possible value for the confidence level. + * + * @see #getScore() + */ + public static final int SCORE_MIN = 1; + + /** + * The maximum possible value for the confidence level. + * + * @see #getScore() + */ + public static final int SCORE_MAX = 100; + + private final Rect mBounds; + private final int mScore; + private final int mId; + private final Point mLeftEye; + private final Point mRightEye; + private final Point mMouth; + + /** + * Create a new face with all fields set. + * + * <p>The id, leftEyePosition, rightEyePosition, and mouthPosition are considered optional. + * If the id is {@value #ID_UNSUPPORTED} then the leftEyePosition, rightEyePosition, and + * mouthPositions are guaranteed to be {@code null}. Otherwise, each of leftEyePosition, + * rightEyePosition, and mouthPosition may be independently null or not-null.</p> + * + * @param bounds Bounds of the face. + * @param score Confidence level between {@value #SCORE_MIN}-{@value #SCORE_MAX}. + * @param id A unique ID per face visible to the tracker. + * @param leftEyePosition The position of the left eye. + * @param rightEyePosition The position of the right eye. + * @param mouthPosition The position of the mouth. + * + * @throws IllegalArgumentException + * if bounds is {@code null}, + * or if the confidence is not in the range of + * {@value #SCORE_MIN}-{@value #SCORE_MAX}, + * or if id is {@value #ID_UNSUPPORTED} and + * leftEyePosition/rightEyePosition/mouthPosition aren't all null, + * or else if id is negative. + * + * @hide + */ + public Face(Rect bounds, int score, int id, + Point leftEyePosition, Point rightEyePosition, Point mouthPosition) { + checkNotNull("bounds", bounds); + if (score < SCORE_MIN || score > SCORE_MAX) { + throw new IllegalArgumentException("Confidence out of range"); + } else if (id < 0 && id != ID_UNSUPPORTED) { + throw new IllegalArgumentException("Id out of range"); + } + if (id == ID_UNSUPPORTED) { + checkNull("leftEyePosition", leftEyePosition); + checkNull("rightEyePosition", rightEyePosition); + checkNull("mouthPosition", mouthPosition); + } + + mBounds = bounds; + mScore = score; + mId = id; + mLeftEye = leftEyePosition; + mRightEye = rightEyePosition; + mMouth = mouthPosition; + } + + /** + * Create a new face without the optional fields. + * + * <p>The id, leftEyePosition, rightEyePosition, and mouthPosition are considered optional. + * If the id is {@value #ID_UNSUPPORTED} then the leftEyePosition, rightEyePosition, and + * mouthPositions are guaranteed to be {@code null}. Otherwise, each of leftEyePosition, + * rightEyePosition, and mouthPosition may be independently null or not-null.</p> + * + * @param bounds Bounds of the face. + * @param score Confidence level between {@value #SCORE_MIN}-{@value #SCORE_MAX}. + * + * @throws IllegalArgumentException + * if bounds is {@code null}, + * or if the confidence is not in the range of + * {@value #SCORE_MIN}-{@value #SCORE_MAX}. + * + * @hide + */ + public Face(Rect bounds, int score) { + this(bounds, score, ID_UNSUPPORTED, + /*leftEyePosition*/null, /*rightEyePosition*/null, /*mouthPosition*/null); + } + + /** + * Bounds of the face. + * + * <p>A rectangle relative to the sensor's + * {@link CameraProperties#SENSOR_INFO_ACTIVE_ARRAY_SIZE}, with (0,0) + * representing the top-left corner of the active array rectangle.</p> + * + * <p>There is no constraints on the the Rectangle value other than it + * is not-{@code null}.</p> + */ + public Rect getBounds() { + return mBounds; + } + + /** + * The confidence level for the detection of the face. + * + * <p>The range is {@value #SCORE_MIN} to {@value #SCORE_MAX}. + * {@value #SCORE_MAX} is the highest confidence.</p> + * + * <p>Depending on the device, even very low-confidence faces may be + * listed, so applications should filter out faces with low confidence, + * depending on the use case. For a typical point-and-shoot camera + * application that wishes to display rectangles around detected faces, + * filtering out faces with confidence less than half of {@value #SCORE_MAX} + * is recommended.</p> + * + * @see #SCORE_MAX + * @see #SCORE_MIN + */ + public int getScore() { + return mScore; + } + + /** + * An unique id per face while the face is visible to the tracker. + * + * <p> + * If the face leaves the field-of-view and comes back, it will get a new + * id.</p> + * + * <p>This is an optional field, may not be supported on all devices. + * If the id is {@value #ID_UNSUPPORTED} then the leftEyePosition, rightEyePosition, and + * mouthPositions are guaranteed to be {@code null}. Otherwise, each of leftEyePosition, + * rightEyePosition, and mouthPosition may be independently null or not-null.</p> + * + * <p>This value will either be {@value #ID_UNSUPPORTED} or + * otherwise greater than {@code 0}.</p> + * + * @see #ID_UNSUPPORTED + */ + public int getId() { + return mId; + } + + /** + * The coordinates of the center of the left eye. + * + * <p>The coordinates are in + * the same space as the ones for {@link #getBounds}. This is an + * optional field, may not be supported on all devices. If not + * supported, the value will always be set to null. + * This value will always be null only if {@link #getId()} returns + * {@value #ID_UNSUPPORTED}.</p> + * + * @return The left eye position, or {@code null} if unknown. + */ + public Point getLeftEyePosition() { + return mLeftEye; + } + + /** + * The coordinates of the center of the right eye. + * + * <p>The coordinates are + * in the same space as the ones for {@link #getBounds}.This is an + * optional field, may not be supported on all devices. If not + * supported, the value will always be set to null. + * This value will always be null only if {@link #getId()} returns + * {@value #ID_UNSUPPORTED}.</p> + * + * @return The right eye position, or {@code null} if unknown. + */ + public Point getRightEyePosition() { + return mRightEye; + } + + /** + * The coordinates of the center of the mouth. + * + * <p>The coordinates are in + * the same space as the ones for {@link #getBounds}. This is an optional + * field, may not be supported on all devices. If not + * supported, the value will always be set to null. + * This value will always be null only if {@link #getId()} returns + * {@value #ID_UNSUPPORTED}.</p> them are. + * </p> + * + * @return The mouth position, or {@code null} if unknown. + */ + public Point getMouthPosition() { + return mMouth; + } + + /** + * Represent the Face as a string for debugging purposes. + */ + @Override + public String toString() { + return String.format("{ bounds: %s, score: %s, id: %d, " + + "leftEyePosition: %s, rightEyePosition: %s, mouthPosition: %s }", + mBounds, mScore, mId, mLeftEye, mRightEye, mMouth); + } + + private static void checkNotNull(String name, Object obj) { + if (obj == null) { + throw new IllegalArgumentException(name + " was required, but it was null"); + } + } + + private static void checkNull(String name, Object obj) { + if (obj != null) { + throw new IllegalArgumentException(name + " was required to be null, but it wasn't"); + } + } +} diff --git a/core/java/android/hardware/camera2/impl/CameraMetadataNative.java b/core/java/android/hardware/camera2/impl/CameraMetadataNative.java index 020d7b60f654..c13438a7f493 100644 --- a/core/java/android/hardware/camera2/impl/CameraMetadataNative.java +++ b/core/java/android/hardware/camera2/impl/CameraMetadataNative.java @@ -27,7 +27,6 @@ import java.nio.ByteBuffer; import java.nio.ByteOrder; import java.util.ArrayList; import java.util.HashMap; -import java.util.Map; /** * Implementation of camera metadata marshal/unmarshal across Binder to diff --git a/core/java/android/hardware/camera2/utils/CameraBinderDecorator.java b/core/java/android/hardware/camera2/utils/CameraBinderDecorator.java index 2c05c58d852c..d0b3ec4f76a2 100644 --- a/core/java/android/hardware/camera2/utils/CameraBinderDecorator.java +++ b/core/java/android/hardware/camera2/utils/CameraBinderDecorator.java @@ -19,6 +19,7 @@ package android.hardware.camera2.utils; import static android.hardware.camera2.CameraAccessException.CAMERA_DISABLED; import static android.hardware.camera2.CameraAccessException.CAMERA_DISCONNECTED; import static android.hardware.camera2.CameraAccessException.CAMERA_IN_USE; +import static android.hardware.camera2.CameraAccessException.MAX_CAMERAS_IN_USE; import static android.hardware.camera2.CameraAccessException.CAMERA_DEPRECATED_HAL; import android.os.DeadObjectException; @@ -50,6 +51,7 @@ public class CameraBinderDecorator { public static final int EBUSY = -16; public static final int ENODEV = -19; public static final int EOPNOTSUPP = -95; + public static final int EDQUOT = -122; private static class CameraBinderDecoratorListener implements Decorator.DecoratorListener { @@ -83,6 +85,9 @@ public class CameraBinderDecorator { case EBUSY: UncheckedThrow.throwAnyException(new CameraRuntimeException( CAMERA_IN_USE)); + case EDQUOT: + UncheckedThrow.throwAnyException(new CameraRuntimeException( + MAX_CAMERAS_IN_USE)); case ENODEV: UncheckedThrow.throwAnyException(new CameraRuntimeException( CAMERA_DISCONNECTED)); diff --git a/core/java/android/nfc/NfcAdapter.java b/core/java/android/nfc/NfcAdapter.java index 2a1890002acc..486e75a5ca30 100644 --- a/core/java/android/nfc/NfcAdapter.java +++ b/core/java/android/nfc/NfcAdapter.java @@ -227,9 +227,9 @@ public final class NfcAdapter { /** * Flag for use with {@link #enableReaderMode(Activity, ReaderCallback, int, Bundle)}. * <p> - * Setting this flag enables polling for Kovio technology. + * Setting this flag enables polling for NfcBarcode technology. */ - public static final int FLAG_READER_KOVIO = 0x10; + public static final int FLAG_READER_NFC_BARCODE = 0x10; /** * Flag for use with {@link #enableReaderMode(Activity, ReaderCallback, int, Bundle)}. diff --git a/core/java/android/provider/Settings.java b/core/java/android/provider/Settings.java index c1c826c35f90..c8312e3adec6 100644 --- a/core/java/android/provider/Settings.java +++ b/core/java/android/provider/Settings.java @@ -4365,6 +4365,9 @@ public final class Settings { /** @hide */ public static final String BAR_SERVICE_COMPONENT = "bar_service_component"; + /** @hide */ + public static final String TRANSIENT_NAV_CONFIRMATIONS = "transient_nav_confirmations"; + /** * This are the settings to be backed up. * diff --git a/core/java/android/transition/Fade.java b/core/java/android/transition/Fade.java index 12e0d73db389..4cc2c4280d74 100644 --- a/core/java/android/transition/Fade.java +++ b/core/java/android/transition/Fade.java @@ -91,6 +91,9 @@ public class Fade extends Visibility { return null; } final ObjectAnimator anim = ObjectAnimator.ofFloat(view, "alpha", startAlpha, endAlpha); + if (DBG) { + Log.d(LOG_TAG, "Created animator " + anim); + } if (listener != null) { anim.addListener(listener); anim.addPauseListener(listener); @@ -146,12 +149,41 @@ public class Fade extends Visibility { final View endView = endValues.view; if (DBG) { View startView = (startValues != null) ? startValues.view : null; - Log.d(LOG_TAG, "Fade.onDisappear: startView, startVis, endView, endVis = " + + Log.d(LOG_TAG, "Fade.onAppear: startView, startVis, endView, endVis = " + startView + ", " + startVisibility + ", " + endView + ", " + endVisibility); } // if alpha < 1, just fade it in from the current value if (endView.getAlpha() == 1.0f) { endView.setAlpha(0); + TransitionListener transitionListener = new TransitionListenerAdapter() { + boolean mCanceled = false; + float mPausedAlpha; + + @Override + public void onTransitionCancel(Transition transition) { + endView.setAlpha(1); + mCanceled = true; + } + + @Override + public void onTransitionEnd(Transition transition) { + if (!mCanceled) { + endView.setAlpha(1); + } + } + + @Override + public void onTransitionPause(Transition transition) { + mPausedAlpha = endView.getAlpha(); + endView.setAlpha(1); + } + + @Override + public void onTransitionResume(Transition transition) { + endView.setAlpha(mPausedAlpha); + } + }; + addListener(transitionListener); } return createAnimation(endView, endView.getAlpha(), 1, null); } diff --git a/core/java/android/transition/Transition.java b/core/java/android/transition/Transition.java index c588c6bf4c7a..60b47087fe44 100644 --- a/core/java/android/transition/Transition.java +++ b/core/java/android/transition/Transition.java @@ -1240,12 +1240,13 @@ public abstract class Transition implements Cloneable { View oldView = oldInfo.view; TransitionValues newValues = mEndValues.viewValues != null ? mEndValues.viewValues.get(oldView) : null; + if (newValues == null) { + newValues = mEndValues.idValues.get(oldView.getId()); + } if (oldValues != null) { // if oldValues null, then transition didn't care to stash values, // and won't get canceled - if (newValues == null) { - cancel = true; - } else { + if (newValues != null) { for (String key : oldValues.values.keySet()) { Object oldValue = oldValues.values.get(key); Object newValue = newValues.values.get(key); @@ -1451,6 +1452,8 @@ public abstract class Transition implements Cloneable { try { clone = (Transition) super.clone(); clone.mAnimators = new ArrayList<Animator>(); + clone.mStartValues = new TransitionValuesMaps(); + clone.mEndValues = new TransitionValuesMaps(); } catch (CloneNotSupportedException e) {} return clone; diff --git a/core/java/android/transition/TransitionManager.java b/core/java/android/transition/TransitionManager.java index 54d801eba443..44ca4e58fd94 100644 --- a/core/java/android/transition/TransitionManager.java +++ b/core/java/android/transition/TransitionManager.java @@ -22,6 +22,7 @@ import android.util.Log; import android.view.ViewGroup; import android.view.ViewTreeObserver; +import java.lang.ref.WeakReference; import java.util.ArrayList; /** @@ -68,8 +69,9 @@ public class TransitionManager { ArrayMap<Scene, Transition> mSceneTransitions = new ArrayMap<Scene, Transition>(); ArrayMap<Scene, ArrayMap<Scene, Transition>> mScenePairTransitions = new ArrayMap<Scene, ArrayMap<Scene, Transition>>(); - private static ThreadLocal<ArrayMap<ViewGroup, ArrayList<Transition>>> sRunningTransitions = - new ThreadLocal<ArrayMap<ViewGroup, ArrayList<Transition>>>(); + private static ThreadLocal<WeakReference<ArrayMap<ViewGroup, ArrayList<Transition>>>> + sRunningTransitions = + new ThreadLocal<WeakReference<ArrayMap<ViewGroup, ArrayList<Transition>>>>(); private static ArrayList<ViewGroup> sPendingTransitions = new ArrayList<ViewGroup>(); @@ -184,20 +186,24 @@ public class TransitionManager { } private static ArrayMap<ViewGroup, ArrayList<Transition>> getRunningTransitions() { - ArrayMap<ViewGroup, ArrayList<Transition>> runningTransitions = + WeakReference<ArrayMap<ViewGroup, ArrayList<Transition>>> runningTransitions = sRunningTransitions.get(); - if (runningTransitions == null) { - runningTransitions = new ArrayMap<ViewGroup, ArrayList<Transition>>(); + if (runningTransitions == null || runningTransitions.get() == null) { + ArrayMap<ViewGroup, ArrayList<Transition>> transitions = + new ArrayMap<ViewGroup, ArrayList<Transition>>(); + runningTransitions = new WeakReference<ArrayMap<ViewGroup, ArrayList<Transition>>>( + transitions); sRunningTransitions.set(runningTransitions); } - return runningTransitions; + return runningTransitions.get(); } private static void sceneChangeRunTransition(final ViewGroup sceneRoot, final Transition transition) { if (transition != null) { final ViewTreeObserver observer = sceneRoot.getViewTreeObserver(); - observer.addOnPreDrawListener(new ViewTreeObserver.OnPreDrawListener() { + final ViewTreeObserver.OnPreDrawListener listener = + new ViewTreeObserver.OnPreDrawListener() { public boolean onPreDraw() { sceneRoot.getViewTreeObserver().removeOnPreDrawListener(this); sPendingTransitions.remove(sceneRoot); @@ -236,7 +242,8 @@ public class TransitionManager { // values set on them again and avoid artifacts. return false; } - }); + }; + observer.addOnPreDrawListener(listener); } } @@ -342,23 +349,19 @@ public class TransitionManager { * value of null causes the TransitionManager to use the default transition. */ public static void beginDelayedTransition(final ViewGroup sceneRoot, Transition transition) { - - // TEMPORARY: disabling delayed transitions until a fix for the various ActionBar- - // triggered artifacts is found - -// if (!sPendingTransitions.contains(sceneRoot) && sceneRoot.isLaidOut()) { -// if (Transition.DBG) { -// Log.d(LOG_TAG, "beginDelayedTransition: root, transition = " + -// sceneRoot + ", " + transition); -// } -// sPendingTransitions.add(sceneRoot); -// if (transition == null) { -// transition = sDefaultTransition; -// } -// final Transition finalTransition = transition.clone(); -// sceneChangeSetup(sceneRoot, transition); -// Scene.setCurrentScene(sceneRoot, null); -// sceneChangeRunTransition(sceneRoot, finalTransition); -// } + if (!sPendingTransitions.contains(sceneRoot) && sceneRoot.isLaidOut()) { + if (Transition.DBG) { + Log.d(LOG_TAG, "beginDelayedTransition: root, transition = " + + sceneRoot + ", " + transition); + } + sPendingTransitions.add(sceneRoot); + if (transition == null) { + transition = sDefaultTransition; + } + final Transition transitionClone = transition.clone(); + sceneChangeSetup(sceneRoot, transitionClone); + Scene.setCurrentScene(sceneRoot, null); + sceneChangeRunTransition(sceneRoot, transitionClone); + } } } diff --git a/core/java/android/transition/Visibility.java b/core/java/android/transition/Visibility.java index 75d3e7c0b4e0..f49821fab1c3 100644 --- a/core/java/android/transition/Visibility.java +++ b/core/java/android/transition/Visibility.java @@ -65,9 +65,6 @@ public abstract class Visibility extends Transition { ViewGroup endParent; } - // Temporary structure, used in calculating state in setup() and play() - private VisibilityInfo mTmpVisibilityInfo = new VisibilityInfo(); - @Override public String[] getTransitionProperties() { return sTransitionProperties; @@ -161,7 +158,7 @@ public abstract class Visibility extends Transition { private VisibilityInfo getVisibilityChangeInfo(TransitionValues startValues, TransitionValues endValues) { - final VisibilityInfo visInfo = mTmpVisibilityInfo; + final VisibilityInfo visInfo = new VisibilityInfo(); visInfo.visibilityChange = false; visInfo.fadeIn = false; if (startValues != null) { diff --git a/core/java/android/view/ViewTreeObserver.java b/core/java/android/view/ViewTreeObserver.java index 730c4eb01cd0..ad8b51db4a1a 100644 --- a/core/java/android/view/ViewTreeObserver.java +++ b/core/java/android/view/ViewTreeObserver.java @@ -992,6 +992,8 @@ public final class ViewTreeObserver { mData = mDataCopy; } mDataCopy = null; + mAccess.mData.clear(); + mAccess.mSize = 0; } int size() { diff --git a/core/java/com/android/internal/app/PlatLogoActivity.java b/core/java/com/android/internal/app/PlatLogoActivity.java index 91c47d1450fa..76b857974bdb 100644 --- a/core/java/com/android/internal/app/PlatLogoActivity.java +++ b/core/java/com/android/internal/app/PlatLogoActivity.java @@ -30,6 +30,8 @@ import android.util.DisplayMetrics; import android.view.Gravity; import android.view.View; import android.view.ViewGroup; +import android.view.animation.AccelerateInterpolator; +import android.view.animation.AnticipateOvershootInterpolator; import android.view.animation.DecelerateInterpolator; import android.widget.FrameLayout; import android.widget.ImageView; @@ -41,6 +43,7 @@ public class PlatLogoActivity extends Activity { FrameLayout mContent; int mCount; final Handler mHandler = new Handler(); + static final int BGCOLOR = 0xffed1d24; @Override protected void onCreate(Bundle savedInstanceState) { @@ -53,6 +56,7 @@ public class PlatLogoActivity extends Activity { Typeface light = Typeface.create("sans-serif-light", Typeface.NORMAL); mContent = new FrameLayout(this); + mContent.setBackgroundColor(0xC0000000); final FrameLayout.LayoutParams lp = new FrameLayout.LayoutParams( FrameLayout.LayoutParams.WRAP_CONTENT, @@ -64,13 +68,16 @@ public class PlatLogoActivity extends Activity { logo.setScaleType(ImageView.ScaleType.CENTER_INSIDE); logo.setVisibility(View.INVISIBLE); + final View bg = new View(this); + bg.setBackgroundColor(BGCOLOR); + bg.setAlpha(0f); + final TextView letter = new TextView(this); letter.setTypeface(bold); letter.setTextSize(300); letter.setTextColor(0xFFFFFFFF); letter.setGravity(Gravity.CENTER); - letter.setShadowLayer(12*metrics.density, 0, 0, 0xC085F985); letter.setText(String.valueOf(Build.VERSION.RELEASE).substring(0, 1)); final int p = (int)(4 * metrics.density); @@ -81,11 +88,11 @@ public class PlatLogoActivity extends Activity { tv.setPadding(p, p, p, p); tv.setTextColor(0xFFFFFFFF); tv.setGravity(Gravity.CENTER); - tv.setShadowLayer(4 * metrics.density, 0, 2 * metrics.density, 0x66000000); tv.setTransformationMethod(new AllCapsTransformationMethod(this)); tv.setText("Android " + Build.VERSION.RELEASE); tv.setVisibility(View.INVISIBLE); + mContent.addView(bg); mContent.addView(letter, lp); mContent.addView(logo, lp); @@ -96,22 +103,52 @@ public class PlatLogoActivity extends Activity { mContent.addView(tv, lp2); mContent.setOnClickListener(new View.OnClickListener() { + int clicks; @Override public void onClick(View v) { + clicks++; + if (clicks >= 6) { + mContent.performLongClick(); + return; + } + letter.animate().cancel(); + final float offset = (int)letter.getRotation() % 360; + letter.animate() + .rotationBy((Math.random() > 0.5f ? 360 : -360) - offset) + .setInterpolator(new DecelerateInterpolator()) + .setDuration(700).start(); + } + }); + + mContent.setOnLongClickListener(new View.OnLongClickListener() { + @Override + public boolean onLongClick(View v) { if (logo.getVisibility() != View.VISIBLE) { - letter.animate().alpha(0.25f).scaleY(0.75f).scaleX(0.75f).setDuration(2000) + bg.setScaleX(0.01f); + bg.animate().alpha(1f).scaleX(1f).setStartDelay(500).start(); + letter.animate().alpha(0f).scaleY(0.5f).scaleX(0.5f) + .rotationBy(360) + .setInterpolator(new AccelerateInterpolator()) + .setDuration(1000) .start(); logo.setAlpha(0f); logo.setVisibility(View.VISIBLE); - logo.animate().alpha(1f).setDuration(1000).setStartDelay(500).start(); + logo.setScaleX(0.5f); + logo.setScaleY(0.5f); + logo.animate().alpha(1f).scaleX(1f).scaleY(1f) + .setDuration(1000).setStartDelay(500) + .setInterpolator(new AnticipateOvershootInterpolator()) + .start(); tv.setAlpha(0f); tv.setVisibility(View.VISIBLE); tv.animate().alpha(1f).setDuration(1000).setStartDelay(1000).start(); + return true; } + return false; } }); - mContent.setOnLongClickListener(new View.OnLongClickListener() { + logo.setOnLongClickListener(new View.OnLongClickListener() { @Override public boolean onLongClick(View v) { try { diff --git a/core/java/com/android/internal/app/ProcessStats.java b/core/java/com/android/internal/app/ProcessStats.java index 07854e20f8af..d3fe34edb1c2 100644 --- a/core/java/com/android/internal/app/ProcessStats.java +++ b/core/java/com/android/internal/app/ProcessStats.java @@ -24,6 +24,7 @@ import android.os.UserHandle; import android.text.format.DateFormat; import android.util.ArrayMap; import android.util.ArraySet; +import android.util.Log; import android.util.Slog; import android.util.SparseArray; import android.util.TimeUtils; @@ -969,6 +970,7 @@ public final class ProcessStats implements Parcelable { if (ps.isInUse()) { uids.valueAt(iu).resetSafely(now); } else { + uids.valueAt(iu).makeDead(); uids.removeAt(iu); } } @@ -986,6 +988,7 @@ public final class ProcessStats implements Parcelable { if (ps.isInUse() || ps.mCommonProcess.isInUse()) { pkgState.mProcesses.valueAt(iproc).resetSafely(now); } else { + pkgState.mProcesses.valueAt(iproc).makeDead(); pkgState.mProcesses.removeAt(iproc); } } @@ -2127,6 +2130,7 @@ public final class ProcessStats implements Parcelable { int mNumExcessiveCpu; boolean mMultiPackage; + boolean mDead; public long mTmpTotalTime; @@ -2230,6 +2234,18 @@ public final class ProcessStats implements Parcelable { mNumExcessiveCpu = 0; } + void makeDead() { + mDead = true; + } + + private void ensureNotDead() { + if (!mDead) { + return; + } + throw new IllegalStateException("ProcessState dead: name=" + mName + + " pkg=" + mPackage + " uid=" + mUid + " common.name=" + mCommonProcess.mName); + } + void writeToParcel(Parcel out, long now) { out.writeInt(mMultiPackage ? 1 : 0); out.writeInt(mDurationsTableSize); @@ -2271,6 +2287,7 @@ public final class ProcessStats implements Parcelable { } public void makeActive() { + ensureNotDead(); mActive = true; } @@ -2279,7 +2296,8 @@ public final class ProcessStats implements Parcelable { } public boolean isInUse() { - return mActive || mNumActiveServices > 0 || mNumStartedServices > 0; + return mActive || mNumActiveServices > 0 || mNumStartedServices > 0 + || mCurState != STATE_NOTHING; } /** @@ -2315,6 +2333,7 @@ public final class ProcessStats implements Parcelable { } void setState(int state, long now) { + ensureNotDead(); if (mCurState != state) { //Slog.i(TAG, "Setting state in " + mName + "/" + mPackage + ": " + state); commitStateTime(now); @@ -2392,6 +2411,7 @@ public final class ProcessStats implements Parcelable { } public void addPss(long pss, long uss, boolean always) { + ensureNotDead(); if (!always) { if (mLastPssState == mCurState && SystemClock.uptimeMillis() < (mLastPssTime+(30*1000))) { @@ -2453,6 +2473,7 @@ public final class ProcessStats implements Parcelable { } public void reportExcessiveWake(ArrayMap<String, ProcessState> pkgList) { + ensureNotDead(); mCommonProcess.mNumExcessiveWake++; if (!mCommonProcess.mMultiPackage) { return; @@ -2464,6 +2485,7 @@ public final class ProcessStats implements Parcelable { } public void reportExcessiveCpu(ArrayMap<String, ProcessState> pkgList) { + ensureNotDead(); mCommonProcess.mNumExcessiveCpu++; if (!mCommonProcess.mMultiPackage) { return; @@ -2489,9 +2511,17 @@ public final class ProcessStats implements Parcelable { return this; } - private ProcessState pullFixedProc(ArrayMap<String, ProcessState> pkgList, - int index) { + private ProcessState pullFixedProc(ArrayMap<String, ProcessState> pkgList, int index) { ProcessState proc = pkgList.valueAt(index); + if (mDead && proc.mCommonProcess != proc) { + // Somehow we try to continue to use a process state that is dead, because + // it was not being told it was active during the last commit. We can recover + // from this by generating a fresh new state, but this is bad because we + // are losing whatever data we had in the old process state. + Log.wtf(TAG, "Pulling dead proc: name=" + mName + " pkg=" + mPackage + + " uid=" + mUid + " common.name=" + mCommonProcess.mName); + proc = mStats.getProcessStateLocked(proc.mPackage, proc.mUid, proc.mName); + } if (proc.mMultiPackage) { // The array map is still pointing to a common process state // that is now shared across packages. Update it to point to diff --git a/core/jni/android/graphics/CreateJavaOutputStreamAdaptor.cpp b/core/jni/android/graphics/CreateJavaOutputStreamAdaptor.cpp index 2d06b681f73d..f773f59688fb 100644 --- a/core/jni/android/graphics/CreateJavaOutputStreamAdaptor.cpp +++ b/core/jni/android/graphics/CreateJavaOutputStreamAdaptor.cpp @@ -159,17 +159,26 @@ SkStream* CreateJavaInputStreamAdaptor(JNIEnv* env, jobject stream, } -static SkMemoryStream* adaptor_to_mem_stream(SkStream* adaptor) { - SkASSERT(adaptor != NULL); - SkDynamicMemoryWStream wStream; - const int bufferSize = 256 * 1024; // 256 KB, same as ViewStateSerializer. - uint8_t buffer[bufferSize]; - do { - size_t bytesRead = adaptor->read(buffer, bufferSize); - wStream.write(buffer, bytesRead); - } while (!adaptor->isAtEnd()); - SkAutoTUnref<SkData> data(wStream.copyToData()); - return new SkMemoryStream(data.get()); +static SkMemoryStream* adaptor_to_mem_stream(SkStream* stream) { + SkASSERT(stream != NULL); + size_t bufferSize = 4096; + size_t streamLen = 0; + size_t len; + char* data = (char*)sk_malloc_throw(bufferSize); + + while ((len = stream->read(data + streamLen, + bufferSize - streamLen)) != 0) { + streamLen += len; + if (streamLen == bufferSize) { + bufferSize *= 2; + data = (char*)sk_realloc_throw(data, bufferSize); + } + } + data = (char*)sk_realloc_throw(data, streamLen); + + SkMemoryStream* streamMem = new SkMemoryStream(); + streamMem->setMemoryOwned(data, streamLen); + return streamMem; } SkStreamRewindable* CopyJavaInputStream(JNIEnv* env, jobject stream, diff --git a/core/res/res/drawable-hdpi/stat_sys_adb_am.png b/core/res/res/drawable-hdpi/stat_sys_adb_am.png Binary files differindex 0c133390ed31..382557e55e91 100644 --- a/core/res/res/drawable-hdpi/stat_sys_adb_am.png +++ b/core/res/res/drawable-hdpi/stat_sys_adb_am.png diff --git a/core/res/res/drawable-mdpi/stat_sys_adb_am.png b/core/res/res/drawable-mdpi/stat_sys_adb_am.png Binary files differindex f0a50894de9e..4380035b5fbd 100644 --- a/core/res/res/drawable-mdpi/stat_sys_adb_am.png +++ b/core/res/res/drawable-mdpi/stat_sys_adb_am.png diff --git a/core/res/res/drawable-nodpi/platlogo.png b/core/res/res/drawable-nodpi/platlogo.png Binary files differindex 4fd0e3c53a5c..6351c2dea4a4 100644 --- a/core/res/res/drawable-nodpi/platlogo.png +++ b/core/res/res/drawable-nodpi/platlogo.png diff --git a/core/res/res/drawable-xhdpi/stat_sys_adb_am.png b/core/res/res/drawable-xhdpi/stat_sys_adb_am.png Binary files differindex 789a3f59d37f..3222a7601575 100644 --- a/core/res/res/drawable-xhdpi/stat_sys_adb_am.png +++ b/core/res/res/drawable-xhdpi/stat_sys_adb_am.png diff --git a/core/res/res/drawable-xxhdpi/stat_sys_adb_am.png b/core/res/res/drawable-xxhdpi/stat_sys_adb_am.png Binary files differindex a36fa36e1466..e01ad3860ab6 100644 --- a/core/res/res/drawable-xxhdpi/stat_sys_adb_am.png +++ b/core/res/res/drawable-xxhdpi/stat_sys_adb_am.png diff --git a/core/res/res/layout/toast_bar.xml b/core/res/res/layout/toast_bar.xml index b7443d55a149..a31d7cb14760 100644 --- a/core/res/res/layout/toast_bar.xml +++ b/core/res/res/layout/toast_bar.xml @@ -35,7 +35,7 @@ android:paddingRight="16dp" android:singleLine="true" android:textColor="@android:color/white" - android:textSize="16sp" /> + android:textSize="14sp" /> <LinearLayout android:id="@android:id/button1" diff --git a/core/res/res/values-land/refs.xml b/core/res/res/values-land/refs.xml deleted file mode 100644 index cda38cf2f2a4..000000000000 --- a/core/res/res/values-land/refs.xml +++ /dev/null @@ -1,20 +0,0 @@ -<?xml version="1.0" encoding="utf-8"?> -<!-- - * Copyright (c) 2013, 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. -*/ ---> -<resources> - <item type="string" name="transient_navigation_confirmation">@string/transient_navigation_confirmation_long</item> -</resources>
\ No newline at end of file diff --git a/core/res/res/values-sw600dp-port/refs.xml b/core/res/res/values-sw600dp-port/refs.xml deleted file mode 100644 index cda38cf2f2a4..000000000000 --- a/core/res/res/values-sw600dp-port/refs.xml +++ /dev/null @@ -1,20 +0,0 @@ -<?xml version="1.0" encoding="utf-8"?> -<!-- - * Copyright (c) 2013, 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. -*/ ---> -<resources> - <item type="string" name="transient_navigation_confirmation">@string/transient_navigation_confirmation_long</item> -</resources>
\ No newline at end of file diff --git a/core/res/res/values/attrs_manifest.xml b/core/res/res/values/attrs_manifest.xml index d2ada7a167ba..80810d549590 100644 --- a/core/res/res/values/attrs_manifest.xml +++ b/core/res/res/values/attrs_manifest.xml @@ -1048,6 +1048,11 @@ tag; often this is one of the {@link android.Manifest.permission standard system permissions}. --> <attr name="name" /> + <!-- Optional: specify the maximum version of the Android OS for which the + application wishes to request the permission. When running on a version + of Android higher than the number given here, the permission will not + be requested. --> + <attr name="maxSdkVersion" format="integer" /> <!-- Specify whether this permission is required for the application. The default is true, meaning the application requires the permission, and it must always be granted when it is installed. @@ -1129,7 +1134,7 @@ on. You can use this to ensure your application is filtered out of later versions of the platform when you know you have incompatibility with them. --> - <attr name="maxSdkVersion" format="integer" /> + <attr name="maxSdkVersion" /> </declare-styleable> <!-- The <code>library</code> tag declares that this apk is providing itself diff --git a/core/res/res/values/config.xml b/core/res/res/values/config.xml index d4b834af5ab0..2c1a3c1d2d15 100644 --- a/core/res/res/values/config.xml +++ b/core/res/res/values/config.xml @@ -1241,4 +1241,8 @@ To do this, add 21407 item to values-mcc214-mnc04/config.xml --> <string-array translatable="false" name="config_operatorConsideredNonRoaming"> </string-array> + + <!-- Threshold (in ms) under which a screen off / screen on will be considered a reset of the + transient navigation confirmation prompt.--> + <integer name="config_transient_navigation_confirmation_panic">5000</integer> </resources> diff --git a/core/res/res/values/strings.xml b/core/res/res/values/strings.xml index a03378e570f4..98368a162aaf 100644 --- a/core/res/res/values/strings.xml +++ b/core/res/res/values/strings.xml @@ -4344,9 +4344,7 @@ <!-- PIN entry dialog tells the user to not enter a PIN for a while. [CHAR LIMIT=none] --> <string name="restr_pin_try_later">Try again later</string> - <!-- Toast bar message when hiding the transient navigation bar [CHAR LIMIT=35] --> - <string name="transient_navigation_confirmation">Swipe edge of screen to reveal bar</string> + <!-- Toast bar message when hiding the transient navigation bar [CHAR LIMIT=45] --> + <string name="transient_navigation_confirmation">Swipe down from the top to exit full screen</string> - <!-- Longer version of toast bar message when hiding the transient navigation bar (if room) --> - <string name="transient_navigation_confirmation_long">Swipe from edge of screen to reveal system bar</string> </resources> diff --git a/core/res/res/values/symbols.xml b/core/res/res/values/symbols.xml index 4148f2048ea7..9cb0a371ce8d 100644 --- a/core/res/res/values/symbols.xml +++ b/core/res/res/values/symbols.xml @@ -301,6 +301,7 @@ <java-symbol type="integer" name="config_ntpThreshold" /> <java-symbol type="integer" name="config_ntpTimeout" /> <java-symbol type="integer" name="config_toastDefaultGravity" /> + <java-symbol type="integer" name="config_transient_navigation_confirmation_panic" /> <java-symbol type="integer" name="config_wifi_framework_scan_interval" /> <java-symbol type="integer" name="config_wifi_supplicant_scan_interval" /> <java-symbol type="integer" name="config_wifi_scan_interval_p2p_connected" /> @@ -885,7 +886,6 @@ <java-symbol type="string" name="write_fail_reason_cancelled" /> <java-symbol type="string" name="write_fail_reason_cannot_write" /> <java-symbol type="string" name="transient_navigation_confirmation" /> - <java-symbol type="string" name="transient_navigation_confirmation_long" /> <java-symbol type="string" name="ssl_ca_cert_noti_by_unknown" /> <java-symbol type="string" name="ssl_ca_cert_noti_managed" /> <java-symbol type="string" name="ssl_ca_cert_warning" /> diff --git a/core/tests/ConnectivityManagerTest/AndroidManifest.xml b/core/tests/ConnectivityManagerTest/AndroidManifest.xml index b76c8beb7756..16492682d03d 100644 --- a/core/tests/ConnectivityManagerTest/AndroidManifest.xml +++ b/core/tests/ConnectivityManagerTest/AndroidManifest.xml @@ -23,13 +23,6 @@ which is needed when building test cases. --> <application> <uses-library android:name="android.test.runner" /> - <activity android:name="ConnectivityManagerTestActivity" - android:label="@string/app_name"> - <intent-filter> - <action android:name="android.intent.action.MAIN" /> - <category android:name="android.intent.category.LAUNCHER" /> - </intent-filter> - </activity> </application> <!-- @@ -87,4 +80,6 @@ <uses-permission android:name="android.permission.INTERACT_ACROSS_USERS_FULL" /> <uses-permission android:name="android.permission.INTERACT_ACROSS_USERS" /> <uses-permission android:name="android.permission.INJECT_EVENTS" /> + <uses-permission android:name="android.permission.DEVICE_POWER" /> + </manifest> diff --git a/core/tests/ConnectivityManagerTest/src/com/android/connectivitymanagertest/AccessPointParserHelper.java b/core/tests/ConnectivityManagerTest/src/com/android/connectivitymanagertest/AccessPointParserHelper.java index 0461c0bfdc11..b942eb692e49 100644 --- a/core/tests/ConnectivityManagerTest/src/com/android/connectivitymanagertest/AccessPointParserHelper.java +++ b/core/tests/ConnectivityManagerTest/src/com/android/connectivitymanagertest/AccessPointParserHelper.java @@ -32,13 +32,11 @@ import android.net.wifi.WifiEnterpriseConfig; import android.net.LinkAddress; import android.net.LinkProperties; import android.net.RouteInfo; -import android.util.Log; import java.io.InputStream; import java.net.InetAddress; import java.net.UnknownHostException; import java.util.ArrayList; -import java.util.HashMap; import java.util.List; diff --git a/core/tests/ConnectivityManagerTest/src/com/android/connectivitymanagertest/ConnectivityManagerTestActivity.java b/core/tests/ConnectivityManagerTest/src/com/android/connectivitymanagertest/ConnectivityManagerTestBase.java index a0cb1bb890f1..30eda75d714f 100644 --- a/core/tests/ConnectivityManagerTest/src/com/android/connectivitymanagertest/ConnectivityManagerTestActivity.java +++ b/core/tests/ConnectivityManagerTest/src/com/android/connectivitymanagertest/ConnectivityManagerTestBase.java @@ -16,7 +16,7 @@ package com.android.connectivitymanagertest; -import android.app.Activity; +import android.app.KeyguardManager; import android.content.Context; import android.content.BroadcastReceiver; import android.content.Intent; @@ -26,21 +26,14 @@ import android.net.NetworkInfo; import android.net.NetworkInfo.State; import android.net.wifi.WifiConfiguration; import android.net.wifi.WifiManager; -import android.net.wifi.WifiInfo; -import android.net.wifi.ScanResult; import android.net.wifi.WifiConfiguration.KeyMgmt; -import android.os.Bundle; import android.os.Handler; -import android.os.IPowerManager; import android.os.Message; import android.os.PowerManager; -import android.os.ServiceManager; import android.os.SystemClock; -import android.os.UserHandle; -import android.provider.Settings; +import android.test.InstrumentationTestCase; import android.util.Log; import android.view.KeyEvent; -import android.widget.LinearLayout; import com.android.internal.util.AsyncChannel; @@ -52,13 +45,17 @@ import java.util.List; /** - * An activity registered with connectivity manager broadcast - * provides network connectivity information and - * can be used to set device states: Cellular, Wifi, Airplane mode. + * Base InstrumentationTestCase for Connectivity Manager (CM) test suite + * + * It registers connectivity manager broadcast and WiFi broadcast to provide + * network connectivity information, also provides a set of utility functions + * to modify and verify connectivity states. + * + * A CM test case should extend this base class. */ -public class ConnectivityManagerTestActivity extends Activity { +public class ConnectivityManagerTestBase extends InstrumentationTestCase { - public static final String LOG_TAG = "ConnectivityManagerTestActivity"; + public static final String LOG_TAG = "ConnectivityManagerTestBase"; public static final int WAIT_FOR_SCAN_RESULT = 10 * 1000; //10 seconds public static final int WIFI_SCAN_TIMEOUT = 50 * 1000; // 50 seconds public static final int SHORT_TIMEOUT = 5 * 1000; // 5 seconds @@ -94,14 +91,9 @@ public class ConnectivityManagerTestActivity extends Activity { private Context mContext; public boolean scanResultAvailable = false; - /* - * Control Wifi States - */ + /* Control Wifi States */ public WifiManager mWifiManager; - - /* - * Verify connectivity state - */ + /* Verify connectivity state */ public static final int NUM_NETWORK_TYPES = ConnectivityManager.MAX_NETWORK_TYPE + 1; NetworkState[] connectivityState = new NetworkState[NUM_NETWORK_TYPES]; @@ -208,26 +200,28 @@ public class ConnectivityManagerTestActivity extends Activity { } } - public ConnectivityManagerTestActivity() { + @Override + public void setUp() throws Exception { mState = State.UNKNOWN; scanResultAvailable = false; - } + mContext = getInstrumentation().getContext(); - @Override - protected void onCreate(Bundle savedInstanceState) { - super.onCreate(savedInstanceState); - log("onCreate, inst=" + Integer.toHexString(hashCode())); + // Get an instance of ConnectivityManager + mCM = (ConnectivityManager)mContext.getSystemService(Context.CONNECTIVITY_SERVICE); + // Get an instance of WifiManager + mWifiManager =(WifiManager)mContext.getSystemService(Context.WIFI_SERVICE); - // Create a simple layout - LinearLayout contentView = new LinearLayout(this); - contentView.setOrientation(LinearLayout.VERTICAL); - setContentView(contentView); - setTitle("ConnectivityManagerTestActivity"); + if (mWifiManager.isWifiApEnabled()) { + // if soft AP is enabled, disable it + mWifiManager.setWifiApEnabled(null, false); + log("Disable soft ap"); + } + initializeNetworkStates(); // register a connectivity receiver for CONNECTIVITY_ACTION; mConnectivityReceiver = new ConnectivityReceiver(); - registerReceiver(mConnectivityReceiver, + mContext.registerReceiver(mConnectivityReceiver, new IntentFilter(ConnectivityManager.CONNECTIVITY_ACTION)); mWifiReceiver = new WifiReceiver(); @@ -238,28 +232,15 @@ public class ConnectivityManagerTestActivity extends Activity { mIntentFilter.addAction(WifiManager.SUPPLICANT_CONNECTION_CHANGE_ACTION); mIntentFilter.addAction(WifiManager.WIFI_AP_STATE_CHANGED_ACTION); mIntentFilter.addAction(ConnectivityManager.ACTION_TETHER_STATE_CHANGED); - registerReceiver(mWifiReceiver, mIntentFilter); + mContext.registerReceiver(mWifiReceiver, mIntentFilter); - // Get an instance of ConnectivityManager - mCM = (ConnectivityManager)getSystemService(Context.CONNECTIVITY_SERVICE); - // Get an instance of WifiManager - mWifiManager =(WifiManager)getSystemService(Context.WIFI_SERVICE); - mContext = this; - - if (mWifiManager.isWifiApEnabled()) { - // if soft AP is enabled, disable it - mWifiManager.setWifiApEnabled(null, false); - log("Disable soft ap"); - } - - initializeNetworkStates(); log("Clear Wifi before we start the test."); removeConfiguredNetworksAndDisableWifi(); mWifiRegexs = mCM.getTetherableWifiRegexs(); } public List<WifiConfiguration> loadNetworkConfigurations() throws Exception { - InputStream in = getAssets().open(ACCESS_POINT_FILE); + InputStream in = mContext.getAssets().open(ACCESS_POINT_FILE); mParseHelper = new AccessPointParserHelper(in); return mParseHelper.getNetworkConfigurations(); } @@ -277,6 +258,12 @@ public class ConnectivityManagerTestActivity extends Activity { public void recordNetworkState(int networkType, State networkState) { log("record network state for network " + networkType + ", state is " + networkState); + if (connectivityState == null) { + log("ConnectivityState is null"); + } + if (connectivityState[networkType] == null) { + log("connectivityState[networkType] is null"); + } connectivityState[networkType].recordState(networkState); } @@ -503,7 +490,7 @@ public class ConnectivityManagerTestActivity extends Activity { public void turnScreenOff() { log("Turn screen off"); PowerManager pm = - (PowerManager) getSystemService(Context.POWER_SERVICE); + (PowerManager) mContext.getSystemService(Context.POWER_SERVICE); pm.goToSleep(SystemClock.uptimeMillis()); } @@ -511,8 +498,13 @@ public class ConnectivityManagerTestActivity extends Activity { public void turnScreenOn() { log("Turn screen on"); PowerManager pm = - (PowerManager) getSystemService(Context.POWER_SERVICE); + (PowerManager) mContext.getSystemService(Context.POWER_SERVICE); pm.wakeUp(SystemClock.uptimeMillis()); + // disable lock screen + KeyguardManager km = (KeyguardManager) mContext.getSystemService(Context.KEYGUARD_SERVICE); + if (km.inKeyguardRestrictedInputMode()) { + sendKeys(KeyEvent.KEYCODE_MENU); + } } /** @@ -607,7 +599,12 @@ public class ConnectivityManagerTestActivity extends Activity { mWifiManager.setWifiEnabled(true); sleep(SHORT_TIMEOUT); } + List<WifiConfiguration> wifiConfigList = mWifiManager.getConfiguredNetworks(); + if (wifiConfigList == null) { + log("no configuration list is null"); + return true; + } log("size of wifiConfigList: " + wifiConfigList.size()); for (WifiConfiguration wifiConfig: wifiConfigList) { log("remove wifi configuration: " + wifiConfig.networkId); @@ -656,57 +653,15 @@ public class ConnectivityManagerTestActivity extends Activity { } @Override - protected void onDestroy() { - super.onDestroy(); - + public void tearDown() throws Exception{ //Unregister receiver if (mConnectivityReceiver != null) { - unregisterReceiver(mConnectivityReceiver); + mContext.unregisterReceiver(mConnectivityReceiver); } if (mWifiReceiver != null) { - unregisterReceiver(mWifiReceiver); - } - log("onDestroy, inst=" + Integer.toHexString(hashCode())); - } - - @Override - public void onStart() { - super.onStart(); - mContext = this; - Bundle bundle = this.getIntent().getExtras(); - if (bundle != null){ - mPowerSsid = bundle.getString("power_ssid"); - } - } - //A thread to set the device into airplane mode then turn on wifi. - Thread setDeviceWifiAndAirplaneThread = new Thread(new Runnable() { - public void run() { - mCM.setAirplaneMode(true); - connectToWifi(mPowerSsid); - } - }); - - //A thread to set the device into wifi - Thread setDeviceInWifiOnlyThread = new Thread(new Runnable() { - public void run() { - connectToWifi(mPowerSsid); - } - }); - - @Override - public boolean onKeyDown(int keyCode, KeyEvent event) { - switch (keyCode) { - //This is a tricky way for the scripted monkey to - //set the device in wifi and wifi in airplane mode. - case KeyEvent.KEYCODE_1: - setDeviceWifiAndAirplaneThread.start(); - break; - - case KeyEvent.KEYCODE_2: - setDeviceInWifiOnlyThread.start(); - break; + mContext.unregisterReceiver(mWifiReceiver); } - return super.onKeyDown(keyCode, event); + super.tearDown(); } private void log(String message) { diff --git a/core/tests/ConnectivityManagerTest/src/com/android/connectivitymanagertest/ConnectivityManagerUnitTestRunner.java b/core/tests/ConnectivityManagerTest/src/com/android/connectivitymanagertest/ConnectivityManagerUnitTestRunner.java index 3a78f26bba39..0e57a006eed0 100644 --- a/core/tests/ConnectivityManagerTest/src/com/android/connectivitymanagertest/ConnectivityManagerUnitTestRunner.java +++ b/core/tests/ConnectivityManagerTest/src/com/android/connectivitymanagertest/ConnectivityManagerUnitTestRunner.java @@ -16,10 +16,8 @@ package com.android.connectivitymanagertest; -import android.os.Bundle; import android.test.InstrumentationTestRunner; import android.test.InstrumentationTestSuite; -import android.util.Log; import com.android.connectivitymanagertest.unit.WifiClientTest; import com.android.connectivitymanagertest.unit.WifiSoftAPTest; diff --git a/core/tests/ConnectivityManagerTest/src/com/android/connectivitymanagertest/functional/ConnectivityManagerMobileTest.java b/core/tests/ConnectivityManagerTest/src/com/android/connectivitymanagertest/functional/ConnectivityManagerMobileTest.java index 729e1d25160b..05462b4cdfc2 100644 --- a/core/tests/ConnectivityManagerTest/src/com/android/connectivitymanagertest/functional/ConnectivityManagerMobileTest.java +++ b/core/tests/ConnectivityManagerTest/src/com/android/connectivitymanagertest/functional/ConnectivityManagerMobileTest.java @@ -24,31 +24,24 @@ import android.net.wifi.WifiManager; import android.os.PowerManager; import android.os.PowerManager.WakeLock; import android.provider.Settings; -import android.test.ActivityInstrumentationTestCase2; import android.test.suitebuilder.annotation.LargeTest; import android.util.Log; -import com.android.connectivitymanagertest.ConnectivityManagerTestActivity; +import com.android.connectivitymanagertest.ConnectivityManagerTestBase; import com.android.connectivitymanagertest.ConnectivityManagerTestRunner; import com.android.connectivitymanagertest.NetworkState; public class ConnectivityManagerMobileTest extends - ActivityInstrumentationTestCase2<ConnectivityManagerTestActivity> { - private static final String LOG_TAG = "ConnectivityManagerMobileTest"; + ConnectivityManagerTestBase { + private static final String TAG = "ConnectivityManagerMobileTest"; private String mTestAccessPoint; - private ConnectivityManagerTestActivity cmActivity; private WakeLock wl; private boolean mWifiOnlyFlag; - public ConnectivityManagerMobileTest() { - super(ConnectivityManagerTestActivity.class); - } - @Override public void setUp() throws Exception { super.setUp(); - cmActivity = getActivity(); ConnectivityManagerTestRunner mRunner = (ConnectivityManagerTestRunner)getInstrumentation(); mTestAccessPoint = mRunner.mTestSsid; @@ -62,12 +55,12 @@ public class ConnectivityManagerMobileTest extends if (Settings.Global.getInt(getInstrumentation().getContext().getContentResolver(), Settings.Global.AIRPLANE_MODE_ON) == 1) { log("airplane is not disabled, disable it."); - cmActivity.mCM.setAirplaneMode(false); + mCM.setAirplaneMode(false); } if (!mWifiOnlyFlag) { - if (!cmActivity.waitForNetworkState(ConnectivityManager.TYPE_MOBILE, State.CONNECTED, - ConnectivityManagerTestActivity.LONG_TIMEOUT)) { + if (!waitForNetworkState(ConnectivityManager.TYPE_MOBILE, + State.CONNECTED, LONG_TIMEOUT)) { // Note: When the test fails in setUp(), tearDown is not called. In that case, // the activity is destroyed which blocks the next test at "getActivity()". // tearDown() is called here to avoid that situation. @@ -79,29 +72,22 @@ public class ConnectivityManagerMobileTest extends @Override public void tearDown() throws Exception { - cmActivity.finish(); - log("tear down ConnectivityManagerTestActivity"); wl.release(); - cmActivity.removeConfiguredNetworksAndDisableWifi(); - // if airplane mode is set, disable it. - if (Settings.Global.getInt(getInstrumentation().getContext().getContentResolver(), - Settings.Global.AIRPLANE_MODE_ON) == 1) { - log("disable airplane mode if it is enabled"); - cmActivity.mCM.setAirplaneMode(false); - } + removeConfiguredNetworksAndDisableWifi(); + mCM.setAirplaneMode(false); super.tearDown(); } // help function to verify 3G connection public void verifyCellularConnection() { - NetworkInfo extraNetInfo = cmActivity.mCM.getActiveNetworkInfo(); + NetworkInfo extraNetInfo = mCM.getActiveNetworkInfo(); assertEquals("network type is not MOBILE", ConnectivityManager.TYPE_MOBILE, extraNetInfo.getType()); assertTrue("not connected to cellular network", extraNetInfo.isConnected()); } private void log(String message) { - Log.v(LOG_TAG, message); + Log.v(TAG, message); } private void sleep(long sleeptime) { @@ -115,46 +101,46 @@ public class ConnectivityManagerMobileTest extends @LargeTest public void test3GToWifiNotification() { if (mWifiOnlyFlag) { - Log.v(LOG_TAG, this.getName() + " is excluded for wifi-only test"); + Log.v(TAG, this.getName() + " is excluded for wifi-only test"); return; } // Enable Wi-Fi to avoid initial UNKNOWN state - cmActivity.enableWifi(); - sleep(2 * ConnectivityManagerTestActivity.SHORT_TIMEOUT); + enableWifi(); + sleep(2 * SHORT_TIMEOUT); // Wi-Fi is disabled - cmActivity.disableWifi(); + disableWifi(); - assertTrue(cmActivity.waitForNetworkState(ConnectivityManager.TYPE_WIFI, - State.DISCONNECTED, ConnectivityManagerTestActivity.LONG_TIMEOUT)); - assertTrue(cmActivity.waitForNetworkState(ConnectivityManager.TYPE_MOBILE, - State.CONNECTED, ConnectivityManagerTestActivity.LONG_TIMEOUT)); + assertTrue(waitForNetworkState(ConnectivityManager.TYPE_WIFI, + State.DISCONNECTED, LONG_TIMEOUT)); + assertTrue(waitForNetworkState(ConnectivityManager.TYPE_MOBILE, + State.CONNECTED, LONG_TIMEOUT)); // Wait for 10 seconds for broadcasts to be sent out sleep(10 * 1000); // As Wifi stays in DISCONNETED, Mobile statys in CONNECTED, // the connectivity manager will not broadcast any network connectivity event for Wifi - NetworkInfo networkInfo = cmActivity.mCM.getNetworkInfo(ConnectivityManager.TYPE_MOBILE); - cmActivity.setStateTransitionCriteria(ConnectivityManager.TYPE_MOBILE, + NetworkInfo networkInfo = mCM.getNetworkInfo(ConnectivityManager.TYPE_MOBILE); + setStateTransitionCriteria(ConnectivityManager.TYPE_MOBILE, networkInfo.getState(), NetworkState.DO_NOTHING, State.CONNECTED); - networkInfo = cmActivity.mCM.getNetworkInfo(ConnectivityManager.TYPE_WIFI); - cmActivity.setStateTransitionCriteria(ConnectivityManager.TYPE_WIFI, networkInfo.getState(), + networkInfo = mCM.getNetworkInfo(ConnectivityManager.TYPE_WIFI); + setStateTransitionCriteria(ConnectivityManager.TYPE_WIFI, networkInfo.getState(), NetworkState.DO_NOTHING, State.DISCONNECTED); // Eanble Wifi without associating with any AP - cmActivity.enableWifi(); - sleep(2 * ConnectivityManagerTestActivity.SHORT_TIMEOUT); + enableWifi(); + sleep(2 * SHORT_TIMEOUT); // validate state and broadcast - if (!cmActivity.validateNetworkStates(ConnectivityManager.TYPE_WIFI)) { + if (!validateNetworkStates(ConnectivityManager.TYPE_WIFI)) { log("the state for WIFI is changed"); log("reason: " + - cmActivity.getTransitionFailureReason(ConnectivityManager.TYPE_WIFI)); + getTransitionFailureReason(ConnectivityManager.TYPE_WIFI)); assertTrue("state validation fail", false); } - if (!cmActivity.validateNetworkStates(ConnectivityManager.TYPE_MOBILE)) { + if (!validateNetworkStates(ConnectivityManager.TYPE_MOBILE)) { log("the state for MOBILE is changed"); log("reason: " + - cmActivity.getTransitionFailureReason(ConnectivityManager.TYPE_MOBILE)); + getTransitionFailureReason(ConnectivityManager.TYPE_MOBILE)); assertTrue("state validation fail", false); } // Verify that the device is still connected to MOBILE @@ -168,40 +154,39 @@ public class ConnectivityManagerMobileTest extends NetworkInfo networkInfo; if (!mWifiOnlyFlag) { //Prepare for connectivity verification - networkInfo = cmActivity.mCM.getNetworkInfo(ConnectivityManager.TYPE_MOBILE); - cmActivity.setStateTransitionCriteria(ConnectivityManager.TYPE_MOBILE, + networkInfo = mCM.getNetworkInfo(ConnectivityManager.TYPE_MOBILE); + setStateTransitionCriteria(ConnectivityManager.TYPE_MOBILE, networkInfo.getState(), NetworkState.TO_DISCONNECTION, State.DISCONNECTED); } - networkInfo = cmActivity.mCM.getNetworkInfo(ConnectivityManager.TYPE_WIFI); - cmActivity.setStateTransitionCriteria(ConnectivityManager.TYPE_WIFI, networkInfo.getState(), + networkInfo = mCM.getNetworkInfo(ConnectivityManager.TYPE_WIFI); + setStateTransitionCriteria(ConnectivityManager.TYPE_WIFI, networkInfo.getState(), NetworkState.TO_CONNECTION, State.CONNECTED); // Enable Wifi and connect to a test access point assertTrue("failed to connect to " + mTestAccessPoint, - cmActivity.connectToWifi(mTestAccessPoint)); + connectToWifi(mTestAccessPoint)); - assertTrue(cmActivity.waitForWifiState(WifiManager.WIFI_STATE_ENABLED, - ConnectivityManagerTestActivity.LONG_TIMEOUT)); + assertTrue(waitForWifiState(WifiManager.WIFI_STATE_ENABLED, LONG_TIMEOUT)); log("wifi state is enabled"); - assertTrue(cmActivity.waitForNetworkState(ConnectivityManager.TYPE_WIFI, State.CONNECTED, - ConnectivityManagerTestActivity.WIFI_CONNECTION_TIMEOUT)); + assertTrue(waitForNetworkState(ConnectivityManager.TYPE_WIFI, State.CONNECTED, + WIFI_CONNECTION_TIMEOUT)); if (!mWifiOnlyFlag) { - assertTrue(cmActivity.waitForNetworkState(ConnectivityManager.TYPE_MOBILE, - State.DISCONNECTED, ConnectivityManagerTestActivity.LONG_TIMEOUT)); + assertTrue(waitForNetworkState(ConnectivityManager.TYPE_MOBILE, + State.DISCONNECTED, LONG_TIMEOUT)); } // validate states - if (!cmActivity.validateNetworkStates(ConnectivityManager.TYPE_WIFI)) { + if (!validateNetworkStates(ConnectivityManager.TYPE_WIFI)) { log("Wifi state transition validation failed."); log("reason: " + - cmActivity.getTransitionFailureReason(ConnectivityManager.TYPE_WIFI)); + getTransitionFailureReason(ConnectivityManager.TYPE_WIFI)); assertTrue(false); } if (!mWifiOnlyFlag) { - if (!cmActivity.validateNetworkStates(ConnectivityManager.TYPE_MOBILE)) { + if (!validateNetworkStates(ConnectivityManager.TYPE_MOBILE)) { log("Mobile state transition validation failed."); log("reason: " + - cmActivity.getTransitionFailureReason(ConnectivityManager.TYPE_MOBILE)); + getTransitionFailureReason(ConnectivityManager.TYPE_MOBILE)); assertTrue(false); } } @@ -213,61 +198,59 @@ public class ConnectivityManagerMobileTest extends assertNotNull("SSID is null", mTestAccessPoint); // Connect to mTestAccessPoint assertTrue("failed to connect to " + mTestAccessPoint, - cmActivity.connectToWifi(mTestAccessPoint)); - assertTrue(cmActivity.waitForWifiState(WifiManager.WIFI_STATE_ENABLED, - ConnectivityManagerTestActivity.LONG_TIMEOUT)); - assertTrue(cmActivity.waitForNetworkState(ConnectivityManager.TYPE_WIFI, State.CONNECTED, - ConnectivityManagerTestActivity.WIFI_CONNECTION_TIMEOUT)); + connectToWifi(mTestAccessPoint)); + assertTrue(waitForWifiState(WifiManager.WIFI_STATE_ENABLED, LONG_TIMEOUT)); + assertTrue(waitForNetworkState(ConnectivityManager.TYPE_WIFI, State.CONNECTED, + WIFI_CONNECTION_TIMEOUT)); - sleep(ConnectivityManagerTestActivity.SHORT_TIMEOUT); + sleep(SHORT_TIMEOUT); // Disable Wifi log("Disable Wifi"); - if (!cmActivity.disableWifi()) { + if (!disableWifi()) { log("disable Wifi failed"); return; } // Wait for the Wifi state to be DISABLED - assertTrue(cmActivity.waitForWifiState(WifiManager.WIFI_STATE_DISABLED, - ConnectivityManagerTestActivity.LONG_TIMEOUT)); - assertTrue(cmActivity.waitForNetworkState(ConnectivityManager.TYPE_WIFI, - State.DISCONNECTED, ConnectivityManagerTestActivity.LONG_TIMEOUT)); + assertTrue(waitForWifiState(WifiManager.WIFI_STATE_DISABLED, LONG_TIMEOUT)); + assertTrue(waitForNetworkState(ConnectivityManager.TYPE_WIFI, + State.DISCONNECTED, LONG_TIMEOUT)); if (!mWifiOnlyFlag) { - assertTrue(cmActivity.waitForNetworkState(ConnectivityManager.TYPE_MOBILE, - State.CONNECTED, ConnectivityManagerTestActivity.LONG_TIMEOUT)); + assertTrue(waitForNetworkState(ConnectivityManager.TYPE_MOBILE, + State.CONNECTED, LONG_TIMEOUT)); } NetworkInfo networkInfo; if (!mWifiOnlyFlag) { //Prepare for connectivity state verification - networkInfo = cmActivity.mCM.getNetworkInfo(ConnectivityManager.TYPE_MOBILE); - cmActivity.setStateTransitionCriteria(ConnectivityManager.TYPE_MOBILE, + networkInfo = mCM.getNetworkInfo(ConnectivityManager.TYPE_MOBILE); + setStateTransitionCriteria(ConnectivityManager.TYPE_MOBILE, networkInfo.getState(), NetworkState.DO_NOTHING, State.DISCONNECTED); } - networkInfo = cmActivity.mCM.getNetworkInfo(ConnectivityManager.TYPE_WIFI); - cmActivity.setStateTransitionCriteria(ConnectivityManager.TYPE_WIFI, networkInfo.getState(), + networkInfo = mCM.getNetworkInfo(ConnectivityManager.TYPE_WIFI); + setStateTransitionCriteria(ConnectivityManager.TYPE_WIFI, networkInfo.getState(), NetworkState.TO_CONNECTION, State.CONNECTED); // wait for 2 minutes before restart wifi - sleep(ConnectivityManagerTestActivity.WIFI_STOP_START_INTERVAL); + sleep(WIFI_STOP_START_INTERVAL); // Enable Wifi again log("Enable Wifi again"); - cmActivity.enableWifi(); + enableWifi(); // Wait for Wifi to be connected and mobile to be disconnected - assertTrue(cmActivity.waitForNetworkState(ConnectivityManager.TYPE_WIFI, State.CONNECTED, - ConnectivityManagerTestActivity.WIFI_CONNECTION_TIMEOUT)); + assertTrue(waitForNetworkState(ConnectivityManager.TYPE_WIFI, State.CONNECTED, + WIFI_CONNECTION_TIMEOUT)); if (!mWifiOnlyFlag) { - assertTrue(cmActivity.waitForNetworkState(ConnectivityManager.TYPE_MOBILE, - State.DISCONNECTED, ConnectivityManagerTestActivity.LONG_TIMEOUT)); + assertTrue(waitForNetworkState(ConnectivityManager.TYPE_MOBILE, + State.DISCONNECTED, LONG_TIMEOUT)); } // validate wifi states - if (!cmActivity.validateNetworkStates(ConnectivityManager.TYPE_WIFI)) { + if (!validateNetworkStates(ConnectivityManager.TYPE_WIFI)) { log("Wifi state transition validation failed."); log("reason: " + - cmActivity.getTransitionFailureReason(ConnectivityManager.TYPE_WIFI)); + getTransitionFailureReason(ConnectivityManager.TYPE_WIFI)); assertTrue(false); } } @@ -279,48 +262,48 @@ public class ConnectivityManagerMobileTest extends // connect to Wifi assertTrue("failed to connect to " + mTestAccessPoint, - cmActivity.connectToWifi(mTestAccessPoint)); + connectToWifi(mTestAccessPoint)); - assertTrue(cmActivity.waitForNetworkState(ConnectivityManager.TYPE_WIFI, State.CONNECTED, - ConnectivityManagerTestActivity.WIFI_CONNECTION_TIMEOUT)); + assertTrue(waitForNetworkState(ConnectivityManager.TYPE_WIFI, State.CONNECTED, + WIFI_CONNECTION_TIMEOUT)); // Wait for a few seconds to avoid the state that both Mobile and Wifi is connected - sleep(ConnectivityManagerTestActivity.SHORT_TIMEOUT); + sleep(SHORT_TIMEOUT); NetworkInfo networkInfo; if (!mWifiOnlyFlag) { - networkInfo = cmActivity.mCM.getNetworkInfo(ConnectivityManager.TYPE_MOBILE); - cmActivity.setStateTransitionCriteria(ConnectivityManager.TYPE_MOBILE, + networkInfo = mCM.getNetworkInfo(ConnectivityManager.TYPE_MOBILE); + setStateTransitionCriteria(ConnectivityManager.TYPE_MOBILE, networkInfo.getState(), NetworkState.TO_CONNECTION, State.CONNECTED); } - networkInfo = cmActivity.mCM.getNetworkInfo(ConnectivityManager.TYPE_WIFI); - cmActivity.setStateTransitionCriteria(ConnectivityManager.TYPE_WIFI, networkInfo.getState(), + networkInfo = mCM.getNetworkInfo(ConnectivityManager.TYPE_WIFI); + setStateTransitionCriteria(ConnectivityManager.TYPE_WIFI, networkInfo.getState(), NetworkState.TO_DISCONNECTION, State.DISCONNECTED); // clear Wifi - cmActivity.removeConfiguredNetworksAndDisableWifi(); + removeConfiguredNetworksAndDisableWifi(); - assertTrue(cmActivity.waitForNetworkState(ConnectivityManager.TYPE_WIFI, State.DISCONNECTED, - ConnectivityManagerTestActivity.LONG_TIMEOUT)); + assertTrue(waitForNetworkState(ConnectivityManager.TYPE_WIFI, + State.DISCONNECTED, LONG_TIMEOUT)); if (!mWifiOnlyFlag) { - assertTrue(cmActivity.waitForNetworkState(ConnectivityManager.TYPE_MOBILE, - State.CONNECTED, ConnectivityManagerTestActivity.LONG_TIMEOUT)); + assertTrue(waitForNetworkState(ConnectivityManager.TYPE_MOBILE, + State.CONNECTED, LONG_TIMEOUT)); } // validate states - if (!cmActivity.validateNetworkStates(ConnectivityManager.TYPE_WIFI)) { + if (!validateNetworkStates(ConnectivityManager.TYPE_WIFI)) { log("Wifi state transition validation failed."); log("reason: " + - cmActivity.getTransitionFailureReason(ConnectivityManager.TYPE_WIFI)); + getTransitionFailureReason(ConnectivityManager.TYPE_WIFI)); assertTrue(false); } if (!mWifiOnlyFlag) { - if (!cmActivity.validateNetworkStates(ConnectivityManager.TYPE_MOBILE)) { + if (!validateNetworkStates(ConnectivityManager.TYPE_MOBILE)) { log("Mobile state transition validation failed."); log("reason: " + - cmActivity.getTransitionFailureReason(ConnectivityManager.TYPE_MOBILE)); + getTransitionFailureReason(ConnectivityManager.TYPE_MOBILE)); assertTrue(false); } } @@ -330,62 +313,62 @@ public class ConnectivityManagerMobileTest extends @LargeTest public void testDataConnectionWith3GToAmTo3G() { if (mWifiOnlyFlag) { - Log.v(LOG_TAG, this.getName() + " is excluded for wifi-only test"); + Log.v(TAG, this.getName() + " is excluded for wifi-only test"); return; } //Prepare for state verification - NetworkInfo networkInfo = cmActivity.mCM.getNetworkInfo(ConnectivityManager.TYPE_MOBILE); - cmActivity.setStateTransitionCriteria(ConnectivityManager.TYPE_MOBILE, + NetworkInfo networkInfo = mCM.getNetworkInfo(ConnectivityManager.TYPE_MOBILE); + setStateTransitionCriteria(ConnectivityManager.TYPE_MOBILE, networkInfo.getState(), NetworkState.TO_DISCONNECTION, State.DISCONNECTED); - networkInfo = cmActivity.mCM.getNetworkInfo(ConnectivityManager.TYPE_WIFI); + networkInfo = mCM.getNetworkInfo(ConnectivityManager.TYPE_WIFI); assertEquals(State.DISCONNECTED, networkInfo.getState()); // Enable airplane mode log("Enable airplane mode"); - cmActivity.mCM.setAirplaneMode(true); - sleep(ConnectivityManagerTestActivity.SHORT_TIMEOUT); + mCM.setAirplaneMode(true); + sleep(SHORT_TIMEOUT); - networkInfo = cmActivity.mCM.getNetworkInfo(ConnectivityManager.TYPE_WIFI); + networkInfo = mCM.getNetworkInfo(ConnectivityManager.TYPE_WIFI); assertEquals(State.DISCONNECTED, networkInfo.getState()); // wait until mobile is turn off - assertTrue(cmActivity.waitForNetworkState(ConnectivityManager.TYPE_MOBILE, - State.DISCONNECTED, ConnectivityManagerTestActivity.LONG_TIMEOUT)); - if (!cmActivity.validateNetworkStates(ConnectivityManager.TYPE_MOBILE)) { + assertTrue(waitForNetworkState(ConnectivityManager.TYPE_MOBILE, + State.DISCONNECTED, LONG_TIMEOUT)); + if (!validateNetworkStates(ConnectivityManager.TYPE_MOBILE)) { log("Mobile state transition validation failed."); log("reason: " + - cmActivity.getTransitionFailureReason(ConnectivityManager.TYPE_MOBILE)); + getTransitionFailureReason(ConnectivityManager.TYPE_MOBILE)); assertTrue(false); } // reset state recorder - networkInfo = cmActivity.mCM.getNetworkInfo(ConnectivityManager.TYPE_MOBILE); - cmActivity.setStateTransitionCriteria(ConnectivityManager.TYPE_MOBILE, + networkInfo = mCM.getNetworkInfo(ConnectivityManager.TYPE_MOBILE); + setStateTransitionCriteria(ConnectivityManager.TYPE_MOBILE, networkInfo.getState(), NetworkState.TO_CONNECTION, State.CONNECTED); - networkInfo = cmActivity.mCM.getNetworkInfo(ConnectivityManager.TYPE_WIFI); - cmActivity.setStateTransitionCriteria(ConnectivityManager.TYPE_WIFI, networkInfo.getState(), + networkInfo = mCM.getNetworkInfo(ConnectivityManager.TYPE_WIFI); + setStateTransitionCriteria(ConnectivityManager.TYPE_WIFI, networkInfo.getState(), NetworkState.DO_NOTHING, State.DISCONNECTED); // disable airplane mode - cmActivity.mCM.setAirplaneMode(false); + mCM.setAirplaneMode(false); - assertTrue(cmActivity.waitForNetworkState(ConnectivityManager.TYPE_MOBILE, State.CONNECTED, - ConnectivityManagerTestActivity.LONG_TIMEOUT)); + assertTrue(waitForNetworkState(ConnectivityManager.TYPE_MOBILE, + State.CONNECTED, LONG_TIMEOUT)); // Validate the state transition - if (!cmActivity.validateNetworkStates(ConnectivityManager.TYPE_MOBILE)) { + if (!validateNetworkStates(ConnectivityManager.TYPE_MOBILE)) { log("Mobile state transition validation failed."); log("reason: " + - cmActivity.getTransitionFailureReason(ConnectivityManager.TYPE_MOBILE)); + getTransitionFailureReason(ConnectivityManager.TYPE_MOBILE)); assertTrue(false); } - if (!cmActivity.validateNetworkStates(ConnectivityManager.TYPE_WIFI)) { + if (!validateNetworkStates(ConnectivityManager.TYPE_WIFI)) { log("Wifi state transition validation failed."); log("reason: " + - cmActivity.getTransitionFailureReason(ConnectivityManager.TYPE_WIFI)); + getTransitionFailureReason(ConnectivityManager.TYPE_WIFI)); assertTrue(false); } } @@ -394,107 +377,107 @@ public class ConnectivityManagerMobileTest extends @LargeTest public void testDataConnectionOverAMWithWifi() { if (mWifiOnlyFlag) { - Log.v(LOG_TAG, this.getName() + " is excluded for wifi-only test"); + Log.v(TAG, this.getName() + " is excluded for wifi-only test"); return; } assertNotNull("SSID is null", mTestAccessPoint); // Eanble airplane mode log("Enable airplane mode"); - cmActivity.mCM.setAirplaneMode(true); + mCM.setAirplaneMode(true); NetworkInfo networkInfo; if (!mWifiOnlyFlag) { - assertTrue(cmActivity.waitForNetworkState(ConnectivityManager.TYPE_MOBILE, - State.DISCONNECTED, ConnectivityManagerTestActivity.LONG_TIMEOUT)); - networkInfo = cmActivity.mCM.getNetworkInfo(ConnectivityManager.TYPE_MOBILE); - cmActivity.setStateTransitionCriteria(ConnectivityManager.TYPE_MOBILE, + assertTrue(waitForNetworkState(ConnectivityManager.TYPE_MOBILE, + State.DISCONNECTED, LONG_TIMEOUT)); + networkInfo = mCM.getNetworkInfo(ConnectivityManager.TYPE_MOBILE); + setStateTransitionCriteria(ConnectivityManager.TYPE_MOBILE, networkInfo.getState(), NetworkState.DO_NOTHING, State.DISCONNECTED); } - networkInfo = cmActivity.mCM.getNetworkInfo(ConnectivityManager.TYPE_WIFI); - cmActivity.setStateTransitionCriteria(ConnectivityManager.TYPE_WIFI, networkInfo.getState(), + networkInfo = mCM.getNetworkInfo(ConnectivityManager.TYPE_WIFI); + setStateTransitionCriteria(ConnectivityManager.TYPE_WIFI, networkInfo.getState(), NetworkState.TO_CONNECTION, State.CONNECTED); // Connect to Wifi assertTrue("failed to connect to " + mTestAccessPoint, - cmActivity.connectToWifi(mTestAccessPoint)); - assertTrue(cmActivity.waitForNetworkState(ConnectivityManager.TYPE_WIFI, State.CONNECTED, - ConnectivityManagerTestActivity.WIFI_CONNECTION_TIMEOUT)); + connectToWifi(mTestAccessPoint)); + assertTrue(waitForNetworkState(ConnectivityManager.TYPE_WIFI, State.CONNECTED, + WIFI_CONNECTION_TIMEOUT)); // validate state and broadcast - if (!cmActivity.validateNetworkStates(ConnectivityManager.TYPE_WIFI)) { + if (!validateNetworkStates(ConnectivityManager.TYPE_WIFI)) { log("state validate for Wifi failed"); log("reason: " + - cmActivity.getTransitionFailureReason(ConnectivityManager.TYPE_WIFI)); + getTransitionFailureReason(ConnectivityManager.TYPE_WIFI)); assertTrue("State validation failed", false); } if (!mWifiOnlyFlag) { - if (!cmActivity.validateNetworkStates(ConnectivityManager.TYPE_MOBILE)) { + if (!validateNetworkStates(ConnectivityManager.TYPE_MOBILE)) { log("state validation for Mobile failed"); log("reason: " + - cmActivity.getTransitionFailureReason(ConnectivityManager.TYPE_MOBILE)); + getTransitionFailureReason(ConnectivityManager.TYPE_MOBILE)); assertTrue("state validation failed", false); } } - cmActivity.mCM.setAirplaneMode(false); + mCM.setAirplaneMode(false); } // Test case 7: test connectivity while transit from Wifi->AM->Wifi @LargeTest public void testDataConnectionWithWifiToAMToWifi () { if (mWifiOnlyFlag) { - Log.v(LOG_TAG, this.getName() + " is excluded for wifi-only test"); + Log.v(TAG, this.getName() + " is excluded for wifi-only test"); return; } // Connect to mTestAccessPoint assertNotNull("SSID is null", mTestAccessPoint); // Connect to Wifi assertTrue("failed to connect to " + mTestAccessPoint, - cmActivity.connectToWifi(mTestAccessPoint)); + connectToWifi(mTestAccessPoint)); - assertTrue(cmActivity.waitForNetworkState(ConnectivityManager.TYPE_WIFI, State.CONNECTED, - ConnectivityManagerTestActivity.WIFI_CONNECTION_TIMEOUT)); + assertTrue(waitForNetworkState(ConnectivityManager.TYPE_WIFI, State.CONNECTED, + WIFI_CONNECTION_TIMEOUT)); try { - Thread.sleep(ConnectivityManagerTestActivity.SHORT_TIMEOUT); + Thread.sleep(SHORT_TIMEOUT); } catch (Exception e) { log("exception: " + e.toString()); } // Enable airplane mode without clearing Wifi - cmActivity.mCM.setAirplaneMode(true); + mCM.setAirplaneMode(true); - assertTrue(cmActivity.waitForNetworkState(ConnectivityManager.TYPE_WIFI, State.DISCONNECTED, - ConnectivityManagerTestActivity.LONG_TIMEOUT)); + assertTrue(waitForNetworkState(ConnectivityManager.TYPE_WIFI, + State.DISCONNECTED, LONG_TIMEOUT)); try { - Thread.sleep(ConnectivityManagerTestActivity.SHORT_TIMEOUT); + Thread.sleep(SHORT_TIMEOUT); } catch (Exception e) { log("exception: " + e.toString()); } // Prepare for state validation - NetworkInfo networkInfo = cmActivity.mCM.getNetworkInfo(ConnectivityManager.TYPE_WIFI); + NetworkInfo networkInfo = mCM.getNetworkInfo(ConnectivityManager.TYPE_WIFI); assertEquals(State.DISCONNECTED, networkInfo.getState()); - cmActivity.setStateTransitionCriteria(ConnectivityManager.TYPE_WIFI, + setStateTransitionCriteria(ConnectivityManager.TYPE_WIFI, networkInfo.getState(), NetworkState.TO_CONNECTION, State.CONNECTED); // Disable airplane mode - cmActivity.mCM.setAirplaneMode(false); + mCM.setAirplaneMode(false); - assertTrue(cmActivity.waitForNetworkState(ConnectivityManager.TYPE_WIFI, State.CONNECTED, - ConnectivityManagerTestActivity.WIFI_CONNECTION_TIMEOUT)); + assertTrue(waitForNetworkState(ConnectivityManager.TYPE_WIFI, State.CONNECTED, + WIFI_CONNECTION_TIMEOUT)); if (!mWifiOnlyFlag) { - assertTrue(cmActivity.waitForNetworkState(ConnectivityManager.TYPE_MOBILE, - State.DISCONNECTED, ConnectivityManagerTestActivity.LONG_TIMEOUT)); + assertTrue(waitForNetworkState(ConnectivityManager.TYPE_MOBILE, + State.DISCONNECTED, LONG_TIMEOUT)); } // validate the state transition - if (!cmActivity.validateNetworkStates(ConnectivityManager.TYPE_WIFI)) { + if (!validateNetworkStates(ConnectivityManager.TYPE_WIFI)) { log("Wifi state transition validation failed."); log("reason: " + - cmActivity.getTransitionFailureReason(ConnectivityManager.TYPE_WIFI)); + getTransitionFailureReason(ConnectivityManager.TYPE_WIFI)); assertTrue(false); } } @@ -505,35 +488,33 @@ public class ConnectivityManagerMobileTest extends assertNotNull("SSID is null", mTestAccessPoint); //Connect to mTestAccessPoint assertTrue("failed to connect to " + mTestAccessPoint, - cmActivity.connectToWifi(mTestAccessPoint)); - assertTrue(cmActivity.waitForWifiState(WifiManager.WIFI_STATE_ENABLED, - ConnectivityManagerTestActivity.LONG_TIMEOUT)); - assertTrue(cmActivity.waitForNetworkState(ConnectivityManager.TYPE_WIFI, State.CONNECTED, - ConnectivityManagerTestActivity.WIFI_CONNECTION_TIMEOUT)); + connectToWifi(mTestAccessPoint)); + assertTrue(waitForWifiState(WifiManager.WIFI_STATE_ENABLED, LONG_TIMEOUT)); + assertTrue(waitForNetworkState(ConnectivityManager.TYPE_WIFI, State.CONNECTED, + WIFI_CONNECTION_TIMEOUT)); assertNotNull("Not associated with any AP", - cmActivity.mWifiManager.getConnectionInfo().getBSSID()); + mWifiManager.getConnectionInfo().getBSSID()); try { - Thread.sleep(ConnectivityManagerTestActivity.SHORT_TIMEOUT); + Thread.sleep(SHORT_TIMEOUT); } catch (Exception e) { log("exception: " + e.toString()); } // Disconnect from the current AP log("disconnect from the AP"); - if (!cmActivity.disconnectAP()) { + if (!disconnectAP()) { log("failed to disconnect from " + mTestAccessPoint); } // Verify the connectivity state for Wifi is DISCONNECTED - assertTrue(cmActivity.waitForNetworkState(ConnectivityManager.TYPE_WIFI, State.DISCONNECTED, - ConnectivityManagerTestActivity.LONG_TIMEOUT)); + assertTrue(waitForNetworkState(ConnectivityManager.TYPE_WIFI, + State.DISCONNECTED, LONG_TIMEOUT)); - if (!cmActivity.disableWifi()) { + if (!disableWifi()) { log("disable Wifi failed"); return; } - assertTrue(cmActivity.waitForWifiState(WifiManager.WIFI_STATE_DISABLED, - ConnectivityManagerTestActivity.LONG_TIMEOUT)); + assertTrue(waitForWifiState(WifiManager.WIFI_STATE_DISABLED, LONG_TIMEOUT)); } } diff --git a/core/tests/ConnectivityManagerTest/src/com/android/connectivitymanagertest/functional/WifiAssociationTest.java b/core/tests/ConnectivityManagerTest/src/com/android/connectivitymanagertest/functional/WifiAssociationTest.java index f12e62e1fc4b..183f2a99e062 100644 --- a/core/tests/ConnectivityManagerTest/src/com/android/connectivitymanagertest/functional/WifiAssociationTest.java +++ b/core/tests/ConnectivityManagerTest/src/com/android/connectivitymanagertest/functional/WifiAssociationTest.java @@ -16,7 +16,7 @@ package com.android.connectivitymanagertest.functional; -import com.android.connectivitymanagertest.ConnectivityManagerTestActivity; +import com.android.connectivitymanagertest.ConnectivityManagerTestBase; import com.android.connectivitymanagertest.WifiAssociationTestRunner; import android.content.Context; @@ -32,7 +32,6 @@ import android.net.wifi.WifiManager; import android.net.ConnectivityManager; import android.net.NetworkInfo.State; import android.test.suitebuilder.annotation.LargeTest; -import android.test.ActivityInstrumentationTestCase2; import android.util.Log; /** @@ -43,30 +42,22 @@ import android.util.Log; * -w com.android.connectivitymanagertest/.WifiAssociationTestRunner" */ public class WifiAssociationTest - extends ActivityInstrumentationTestCase2<ConnectivityManagerTestActivity> { + extends ConnectivityManagerTestBase { private static final String TAG = "WifiAssociationTest"; - private ConnectivityManagerTestActivity mAct; private String mSsid = null; private String mPassword = null; private String mSecurityType = null; private String mFrequencyBand = null; private int mBand; - private WifiManager mWifiManager = null; enum SECURITY_TYPE { OPEN, WEP64, WEP128, WPA_TKIP, WPA2_AES - }; - - public WifiAssociationTest() { - super(ConnectivityManagerTestActivity.class); } @Override public void setUp() throws Exception { super.setUp(); WifiAssociationTestRunner mRunner = (WifiAssociationTestRunner)getInstrumentation(); - mWifiManager = (WifiManager) mRunner.getContext().getSystemService(Context.WIFI_SERVICE); - mAct = getActivity(); Bundle arguments = mRunner.getArguments(); mSecurityType = arguments.getString("security-type"); mSsid = arguments.getString("ssid"); @@ -77,17 +68,15 @@ public class WifiAssociationTest assertNotNull("Ssid is empty", mSsid); validateFrequencyBand(); // enable Wifi and verify wpa_supplicant is started - assertTrue("enable Wifi failed", mAct.enableWifi()); - sleep(2 * ConnectivityManagerTestActivity.SHORT_TIMEOUT, - "interrupted while waiting for WPA_SUPPLICANT to start"); - WifiInfo mConnection = mAct.mWifiManager.getConnectionInfo(); + assertTrue("enable Wifi failed", enableWifi()); + sleep(2 * SHORT_TIMEOUT, "interrupted while waiting for WPA_SUPPLICANT to start"); + WifiInfo mConnection = mWifiManager.getConnectionInfo(); assertNotNull(mConnection); - assertTrue("wpa_supplicant is not started ", mAct.mWifiManager.pingSupplicant()); + assertTrue("wpa_supplicant is not started ", mWifiManager.pingSupplicant()); } @Override public void tearDown() throws Exception { - log("tearDown()"); super.tearDown(); } @@ -107,16 +96,16 @@ public class WifiAssociationTest private void connectToWifi(WifiConfiguration config) { // step 1: connect to the test access point assertTrue("failed to associate with " + config.SSID, - mAct.connectToWifiWithConfiguration(config)); + connectToWifiWithConfiguration(config)); // step 2: verify Wifi state and network state; assertTrue("failed to connect with " + config.SSID, - mAct.waitForNetworkState(ConnectivityManager.TYPE_WIFI, - State.CONNECTED, ConnectivityManagerTestActivity.WIFI_CONNECTION_TIMEOUT)); + waitForNetworkState(ConnectivityManager.TYPE_WIFI, + State.CONNECTED, WIFI_CONNECTION_TIMEOUT)); // step 3: verify the current connected network is the given SSID - assertNotNull("Wifi connection returns null", mAct.mWifiManager.getConnectionInfo()); - assertTrue(config.SSID.contains(mAct.mWifiManager.getConnectionInfo().getSSID())); + assertNotNull("Wifi connection returns null", mWifiManager.getConnectionInfo()); + assertTrue(config.SSID.contains(mWifiManager.getConnectionInfo().getSSID())); } private void sleep(long sometime, String errorMsg) { diff --git a/core/tests/ConnectivityManagerTest/src/com/android/connectivitymanagertest/functional/WifiConnectionTest.java b/core/tests/ConnectivityManagerTest/src/com/android/connectivitymanagertest/functional/WifiConnectionTest.java index de0298e8c325..ad73ee1f4ed9 100644 --- a/core/tests/ConnectivityManagerTest/src/com/android/connectivitymanagertest/functional/WifiConnectionTest.java +++ b/core/tests/ConnectivityManagerTest/src/com/android/connectivitymanagertest/functional/WifiConnectionTest.java @@ -16,35 +16,19 @@ package com.android.connectivitymanagertest.functional; -import com.android.connectivitymanagertest.ConnectivityManagerTestActivity; +import com.android.connectivitymanagertest.ConnectivityManagerTestBase; import com.android.connectivitymanagertest.ConnectivityManagerTestRunner; -import android.R; -import android.app.Activity; -import android.content.ContentResolver; -import android.content.Intent; import android.content.Context; -import android.content.res.Resources; import android.net.wifi.WifiConfiguration; -import android.net.wifi.WifiConfiguration.KeyMgmt; -import android.net.wifi.WifiConfiguration.Status; import android.net.wifi.WifiInfo; import android.net.wifi.WifiManager; import android.net.ConnectivityManager; -import android.net.DhcpInfo; -import android.net.NetworkInfo; import android.net.NetworkInfo.State; -import android.os.Handler; -import android.os.Message; -import android.provider.Settings; import android.test.suitebuilder.annotation.LargeTest; -import android.test.ActivityInstrumentationTestCase2; import android.util.Log; -import com.android.internal.util.AsyncChannel; - import java.util.ArrayList; -import java.util.HashSet; import java.util.List; import java.util.Set; @@ -56,39 +40,25 @@ import java.util.Set; * -w com.android.connectivitymanagertest/.ConnectivityManagerTestRunner */ public class WifiConnectionTest - extends ActivityInstrumentationTestCase2<ConnectivityManagerTestActivity> { + extends ConnectivityManagerTestBase { private static final String TAG = "WifiConnectionTest"; private static final boolean DEBUG = false; private List<WifiConfiguration> networks = new ArrayList<WifiConfiguration>(); - private ConnectivityManagerTestActivity mAct; - private ConnectivityManagerTestRunner mRunner; - private WifiManager mWifiManager = null; - private Set<WifiConfiguration> enabledNetworks = null; - - public WifiConnectionTest() { - super(ConnectivityManagerTestActivity.class); - } @Override public void setUp() throws Exception { super.setUp(); - mRunner = ((ConnectivityManagerTestRunner)getInstrumentation()); - mWifiManager = (WifiManager) mRunner.getContext().getSystemService(Context.WIFI_SERVICE); - - mAct = getActivity(); - - networks = mAct.loadNetworkConfigurations(); + networks = loadNetworkConfigurations(); if (DEBUG) { printNetworkConfigurations(); } // enable Wifi and verify wpa_supplicant is started - assertTrue("enable Wifi failed", mAct.enableWifi()); - sleep(2 * ConnectivityManagerTestActivity.SHORT_TIMEOUT, - "interrupted while waiting for WPA_SUPPLICANT to start"); - WifiInfo mConnection = mAct.mWifiManager.getConnectionInfo(); + assertTrue("enable Wifi failed", enableWifi()); + sleep(2 * SHORT_TIMEOUT, "interrupted while waiting for WPA_SUPPLICANT to start"); + WifiInfo mConnection = mWifiManager.getConnectionInfo(); assertNotNull(mConnection); - assertTrue("wpa_supplicant is not started ", mAct.mWifiManager.pingSupplicant()); + assertTrue("wpa_supplicant is not started ", mWifiManager.pingSupplicant()); } private void printNetworkConfigurations() { @@ -101,8 +71,7 @@ public class WifiConnectionTest @Override public void tearDown() throws Exception { - log("tearDown()"); - mAct.removeConfiguredNetworksAndDisableWifi(); + removeConfiguredNetworksAndDisableWifi(); super.tearDown(); } @@ -114,20 +83,20 @@ public class WifiConnectionTest private void connectToWifi(WifiConfiguration config) { // step 1: connect to the test access point assertTrue("failed to connect to " + config.SSID, - mAct.connectToWifiWithConfiguration(config)); + connectToWifiWithConfiguration(config)); // step 2: verify Wifi state and network state; - assertTrue(mAct.waitForNetworkState(ConnectivityManager.TYPE_WIFI, - State.CONNECTED, ConnectivityManagerTestActivity.WIFI_CONNECTION_TIMEOUT)); + assertTrue(waitForNetworkState(ConnectivityManager.TYPE_WIFI, + State.CONNECTED, WIFI_CONNECTION_TIMEOUT)); // step 3: verify the current connected network is the given SSID - assertNotNull("Wifi connection returns null", mAct.mWifiManager.getConnectionInfo()); + assertNotNull("Wifi connection returns null", mWifiManager.getConnectionInfo()); if (DEBUG) { log("config.SSID = " + config.SSID); - log("mAct.mWifiManager.getConnectionInfo.getSSID()" + - mAct.mWifiManager.getConnectionInfo().getSSID()); + log("mWifiManager.getConnectionInfo.getSSID()" + + mWifiManager.getConnectionInfo().getSSID()); } - assertTrue(config.SSID.contains(mAct.mWifiManager.getConnectionInfo().getSSID())); + assertTrue(config.SSID.contains(mWifiManager.getConnectionInfo().getSSID())); } private void sleep(long sometime, String errorMsg) { @@ -149,8 +118,7 @@ public class WifiConnectionTest log("-- START Wi-Fi connection test to : " + ssid + " --"); connectToWifi(networks.get(i)); // wait for 2 minutes between wifi stop and start - sleep(ConnectivityManagerTestActivity.WIFI_STOP_START_INTERVAL, - "interruped while connected to wifi"); + sleep(WIFI_STOP_START_INTERVAL, "interruped while connected to wifi"); log("-- END Wi-Fi connection test to " + ssid + " -- "); } } diff --git a/core/tests/ConnectivityManagerTest/src/com/android/connectivitymanagertest/stress/WifiApStress.java b/core/tests/ConnectivityManagerTest/src/com/android/connectivitymanagertest/stress/WifiApStress.java index 60595fbf2fc1..790ca3840982 100644 --- a/core/tests/ConnectivityManagerTest/src/com/android/connectivitymanagertest/stress/WifiApStress.java +++ b/core/tests/ConnectivityManagerTest/src/com/android/connectivitymanagertest/stress/WifiApStress.java @@ -18,19 +18,13 @@ package com.android.connectivitymanagertest.stress; import com.android.connectivitymanagertest.ConnectivityManagerStressTestRunner; -import com.android.connectivitymanagertest.ConnectivityManagerTestActivity; +import com.android.connectivitymanagertest.ConnectivityManagerTestBase; -import android.content.Context; import android.net.wifi.WifiConfiguration; import android.net.wifi.WifiConfiguration.KeyMgmt; import android.net.wifi.WifiConfiguration.AuthAlgorithm; import android.net.wifi.WifiManager; import android.os.Environment; -import android.os.IPowerManager; -import android.os.PowerManager; -import android.os.ServiceManager; -import android.os.SystemClock; -import android.test.ActivityInstrumentationTestCase2; import android.test.suitebuilder.annotation.LargeTest; import android.util.Log; @@ -42,30 +36,24 @@ import java.io.FileWriter; * Stress the wifi driver as access point. */ public class WifiApStress - extends ActivityInstrumentationTestCase2<ConnectivityManagerTestActivity> { + extends ConnectivityManagerTestBase { private final static String TAG = "WifiApStress"; private static String NETWORK_ID = "AndroidAPTest"; private static String PASSWD = "androidwifi"; private final static String OUTPUT_FILE = "WifiStressTestOutput.txt"; - private ConnectivityManagerTestActivity mAct; private int iterations; private BufferedWriter mOutputWriter = null; private int mLastIteration = 0; private boolean mWifiOnlyFlag; - public WifiApStress() { - super(ConnectivityManagerTestActivity.class); - } - @Override public void setUp() throws Exception { super.setUp(); - mAct = getActivity(); ConnectivityManagerStressTestRunner mRunner = (ConnectivityManagerStressTestRunner)getInstrumentation(); iterations = mRunner.mSoftapIterations; mWifiOnlyFlag = mRunner.mWifiOnlyFlag; - mAct.turnScreenOn(); + turnScreenOn(); } @Override @@ -92,30 +80,28 @@ public class WifiApStress config.preSharedKey = PASSWD; // If Wifi is enabled, disable it - if (mAct.mWifiManager.isWifiEnabled()) { - mAct.disableWifi(); + if (mWifiManager.isWifiEnabled()) { + disableWifi(); } int i; for (i = 0; i < iterations; i++) { Log.v(TAG, "iteration: " + i); mLastIteration = i; // enable Wifi tethering - assertTrue(mAct.mWifiManager.setWifiApEnabled(config, true)); + assertTrue(mWifiManager.setWifiApEnabled(config, true)); // Wait for wifi ap state to be ENABLED - assertTrue(mAct.waitForWifiAPState(WifiManager.WIFI_AP_STATE_ENABLED, - 2 * ConnectivityManagerTestActivity.LONG_TIMEOUT)); + assertTrue(waitForWifiAPState(WifiManager.WIFI_AP_STATE_ENABLED, 2 * LONG_TIMEOUT)); // Wait for wifi tethering result - assertEquals(ConnectivityManagerTestActivity.SUCCESS, - mAct.waitForTetherStateChange(2*ConnectivityManagerTestActivity.SHORT_TIMEOUT)); + assertEquals(SUCCESS, waitForTetherStateChange(2 * SHORT_TIMEOUT)); // Allow the wifi tethering to be enabled for 10 seconds try { - Thread.sleep(2 * ConnectivityManagerTestActivity.SHORT_TIMEOUT); + Thread.sleep(2 * SHORT_TIMEOUT); } catch (Exception e) { fail("thread in sleep is interrupted"); } - assertTrue("no uplink data connection after Wi-Fi tethering", mAct.pingTest(null)); + assertTrue("no uplink data connection after Wi-Fi tethering", pingTest(null)); // Disable soft AP - assertTrue(mAct.mWifiManager.setWifiApEnabled(config, false)); + assertTrue(mWifiManager.setWifiApEnabled(config, false)); // Wait for 30 seconds until Wi-Fi tethering is stopped try { Thread.sleep(30 * 1000); @@ -123,7 +109,7 @@ public class WifiApStress } catch (Exception e) { fail("thread in sleep is interrupted"); } - assertFalse("Wi-Fi AP disable failed", mAct.mWifiManager.isWifiApEnabled()); + assertFalse("Wi-Fi AP disable failed", mWifiManager.isWifiApEnabled()); } if (i == iterations) { mLastIteration = iterations; diff --git a/core/tests/ConnectivityManagerTest/src/com/android/connectivitymanagertest/stress/WifiStressTest.java b/core/tests/ConnectivityManagerTest/src/com/android/connectivitymanagertest/stress/WifiStressTest.java index e3c7cc402a9d..04ce4b75486c 100644 --- a/core/tests/ConnectivityManagerTest/src/com/android/connectivitymanagertest/stress/WifiStressTest.java +++ b/core/tests/ConnectivityManagerTest/src/com/android/connectivitymanagertest/stress/WifiStressTest.java @@ -29,12 +29,11 @@ import android.os.Environment; import android.os.PowerManager; import android.provider.Settings; import android.view.KeyEvent; -import android.test.ActivityInstrumentationTestCase2; import android.test.suitebuilder.annotation.LargeTest; import android.util.Log; import com.android.connectivitymanagertest.ConnectivityManagerStressTestRunner; -import com.android.connectivitymanagertest.ConnectivityManagerTestActivity; +import com.android.connectivitymanagertest.ConnectivityManagerTestBase; import java.io.BufferedWriter; import java.io.File; @@ -50,7 +49,7 @@ import java.util.List; * -w com.android.connectivitymanagertest/.ConnectivityManagerStressTestRunner */ public class WifiStressTest - extends ActivityInstrumentationTestCase2<ConnectivityManagerTestActivity> { + extends ConnectivityManagerTestBase { private final static String TAG = "WifiStressTest"; /** @@ -67,7 +66,6 @@ public class WifiStressTest private final static long WIFI_SHUTDOWN_DELAY = 2 * 60 * 1000; private final static String OUTPUT_FILE = "WifiStressTestOutput.txt"; - private ConnectivityManagerTestActivity mAct; private int mReconnectIterations; private int mWifiSleepTime; private int mScanIterations; @@ -77,15 +75,10 @@ public class WifiStressTest private BufferedWriter mOutputWriter = null; private boolean mWifiOnlyFlag; - public WifiStressTest() { - super(ConnectivityManagerTestActivity.class); - } - @Override public void setUp() throws Exception { super.setUp(); - mAct = getActivity(); mRunner = (ConnectivityManagerStressTestRunner) getInstrumentation(); mReconnectIterations = mRunner.mReconnectIterations; mSsid = mRunner.mReconnectSsid; @@ -98,15 +91,14 @@ public class WifiStressTest mPassword, mScanIterations, mWifiSleepTime)); mOutputWriter = new BufferedWriter(new FileWriter(new File( Environment.getExternalStorageDirectory(), OUTPUT_FILE), true)); - mAct.turnScreenOn(); - if (!mAct.mWifiManager.isWifiEnabled()) { + turnScreenOn(); + if (!mWifiManager.isWifiEnabled()) { log("Enable wi-fi before stress tests."); - if (!mAct.enableWifi()) { + if (!enableWifi()) { tearDown(); fail("enable wifi failed."); } - sleep(ConnectivityManagerTestActivity.SHORT_TIMEOUT, - "Interruped while waiting for wifi on"); + sleep(SHORT_TIMEOUT, "Interruped while waiting for wifi on"); } } @@ -166,33 +158,32 @@ public class WifiStressTest writeOutput(String.format("ssid appear %d out of %d scan iterations", ssidAppearInScanResultsCount, i)); long startTime = System.currentTimeMillis(); - mAct.scanResultAvailable = false; - assertTrue("start scan failed", mAct.mWifiManager.startScan()); + scanResultAvailable = false; + assertTrue("start scan failed", mWifiManager.startScan()); while (true) { if ((System.currentTimeMillis() - startTime) > - ConnectivityManagerTestActivity.WIFI_SCAN_TIMEOUT) { - fail("Wifi scanning takes more than " + - ConnectivityManagerTestActivity.WIFI_SCAN_TIMEOUT + " ms"); + WIFI_SCAN_TIMEOUT) { + fail("Wifi scanning takes more than " + WIFI_SCAN_TIMEOUT + " ms"); } - synchronized(mAct) { + synchronized(this) { try { - mAct.wait(ConnectivityManagerTestActivity.WAIT_FOR_SCAN_RESULT); + wait(WAIT_FOR_SCAN_RESULT); } catch (InterruptedException e) { e.printStackTrace(); } - if (mAct.scanResultAvailable) { + if (scanResultAvailable) { long scanTime = (System.currentTimeMillis() - startTime); scanTimeSum += scanTime; break; } } } - if ((mAct.mWifiManager.getScanResults() == null) || - (mAct.mWifiManager.getScanResults().size() <= 0)) { + if ((mWifiManager.getScanResults() == null) || + (mWifiManager.getScanResults().size() <= 0)) { fail("Scan results are empty "); } - List<ScanResult> netList = mAct.mWifiManager.getScanResults(); + List<ScanResult> netList = mWifiManager.getScanResults(); if (netList != null) { log("size of scan result list: " + netList.size()); for (int s = 0; s < netList.size(); s++) { @@ -244,13 +235,13 @@ public class WifiStressTest config.proxySettings = ProxySettings.NONE; assertTrue("Failed to connect to Wi-Fi network: " + mSsid, - mAct.connectToWifiWithConfiguration(config)); - assertTrue(mAct.waitForWifiState(WifiManager.WIFI_STATE_ENABLED, - ConnectivityManagerTestActivity.SHORT_TIMEOUT)); - assertTrue(mAct.waitForNetworkState(ConnectivityManager.TYPE_WIFI, State.CONNECTED, - ConnectivityManagerTestActivity.WIFI_CONNECTION_TIMEOUT)); + connectToWifiWithConfiguration(config)); + assertTrue(waitForWifiState(WifiManager.WIFI_STATE_ENABLED, + SHORT_TIMEOUT)); + assertTrue(waitForNetworkState(ConnectivityManager.TYPE_WIFI, State.CONNECTED, + WIFI_CONNECTION_TIMEOUT)); // Run ping test to verify the data connection - assertTrue("Wi-Fi is connected, but no data connection.", mAct.pingTest(null)); + assertTrue("Wi-Fi is connected, but no data connection.", pingTest(null)); int i; long sum = 0; @@ -263,33 +254,33 @@ public class WifiStressTest writeOutput(String.format("iteration %d out of %d", i, mReconnectIterations)); log("iteration: " + i); - mAct.turnScreenOff(); + turnScreenOff(); PowerManager pm = (PowerManager)mRunner.getContext().getSystemService(Context.POWER_SERVICE); assertFalse(pm.isScreenOn()); sleep(WIFI_IDLE_MS + WIFI_SHUTDOWN_DELAY, "Interruped while wait for wifi to be idle"); assertTrue("Wait for Wi-Fi to idle timeout", - mAct.waitForNetworkState(ConnectivityManager.TYPE_WIFI, State.DISCONNECTED, - 6 * ConnectivityManagerTestActivity.SHORT_TIMEOUT)); + waitForNetworkState(ConnectivityManager.TYPE_WIFI, State.DISCONNECTED, + 6 * SHORT_TIMEOUT)); if (!mWifiOnlyFlag) { // use long timeout as the pppd startup may take several retries. assertTrue("Wait for cellular connection timeout", - mAct.waitForNetworkState(ConnectivityManager.TYPE_MOBILE, State.CONNECTED, - 2 * ConnectivityManagerTestActivity.LONG_TIMEOUT)); + waitForNetworkState(ConnectivityManager.TYPE_MOBILE, State.CONNECTED, + 2 * LONG_TIMEOUT)); } sleep(mWifiSleepTime, "Interrupted while device is in sleep mode"); // Verify the wi-fi is still off and data connection is on assertEquals("Wi-Fi is reconnected", State.DISCONNECTED, - mAct.mCM.getNetworkInfo(ConnectivityManager.TYPE_WIFI).getState()); + mCM.getNetworkInfo(ConnectivityManager.TYPE_WIFI).getState()); if (!mWifiOnlyFlag) { assertEquals("Cellular connection is down", State.CONNECTED, - mAct.mCM.getNetworkInfo(ConnectivityManager.TYPE_MOBILE).getState()); - assertTrue("Mobile is connected, but no data connection.", mAct.pingTest(null)); + mCM.getNetworkInfo(ConnectivityManager.TYPE_MOBILE).getState()); + assertTrue("Mobile is connected, but no data connection.", pingTest(null)); } // Turn screen on again - mAct.turnScreenOn(); + turnScreenOn(); // Wait for 2 seconds for the lock screen sleep(2 * 1000, "wait 2 seconds for lock screen"); // Disable lock screen by inject menu key event @@ -298,16 +289,16 @@ public class WifiStressTest // Measure the time for Wi-Fi to get connected long startTime = System.currentTimeMillis(); assertTrue("Wait for Wi-Fi enable timeout after wake up", - mAct.waitForWifiState(WifiManager.WIFI_STATE_ENABLED, - ConnectivityManagerTestActivity.SHORT_TIMEOUT)); + waitForWifiState(WifiManager.WIFI_STATE_ENABLED, + SHORT_TIMEOUT)); assertTrue("Wait for Wi-Fi connection timeout after wake up", - mAct.waitForNetworkState(ConnectivityManager.TYPE_WIFI, State.CONNECTED, - ConnectivityManagerTestActivity.WIFI_CONNECTION_TIMEOUT)); + waitForNetworkState(ConnectivityManager.TYPE_WIFI, State.CONNECTED, + WIFI_CONNECTION_TIMEOUT)); long connectionTime = System.currentTimeMillis() - startTime; sum += connectionTime; log("average reconnection time is: " + sum/(i+1)); - assertTrue("Reconnect to Wi-Fi network, but no data connection.", mAct.pingTest(null)); + assertTrue("Reconnect to Wi-Fi network, but no data connection.", pingTest(null)); } if (i == mReconnectIterations) { writeOutput(String.format("iteration %d out of %d", diff --git a/core/tests/ConnectivityManagerTest/src/com/android/connectivitymanagertest/unit/WifiClientTest.java b/core/tests/ConnectivityManagerTest/src/com/android/connectivitymanagertest/unit/WifiClientTest.java index e44023bff472..7a9bc7815d97 100644 --- a/core/tests/ConnectivityManagerTest/src/com/android/connectivitymanagertest/unit/WifiClientTest.java +++ b/core/tests/ConnectivityManagerTest/src/com/android/connectivitymanagertest/unit/WifiClientTest.java @@ -20,9 +20,6 @@ import android.content.BroadcastReceiver; import android.content.Intent; import android.content.IntentFilter; import android.content.Context; -import android.app.Instrumentation; -import android.os.Handler; -import android.os.Message; import android.net.NetworkInfo; import android.net.wifi.WifiManager; import android.net.wifi.WifiConfiguration; @@ -33,11 +30,8 @@ import android.net.wifi.SupplicantState; import android.test.suitebuilder.annotation.LargeTest; import android.test.AndroidTestCase; -import java.util.ArrayList; import java.util.List; -import android.util.Log; - /** * Test wifi client */ diff --git a/core/tests/ConnectivityManagerTest/src/com/android/connectivitymanagertest/unit/WifiSoftAPTest.java b/core/tests/ConnectivityManagerTest/src/com/android/connectivitymanagertest/unit/WifiSoftAPTest.java index 3f43e4851c4f..f202862f6dbf 100644 --- a/core/tests/ConnectivityManagerTest/src/com/android/connectivitymanagertest/unit/WifiSoftAPTest.java +++ b/core/tests/ConnectivityManagerTest/src/com/android/connectivitymanagertest/unit/WifiSoftAPTest.java @@ -16,13 +16,7 @@ package com.android.connectivitymanagertest.unit; -import android.content.BroadcastReceiver; -import android.content.Intent; import android.content.Context; -import android.app.Instrumentation; -import android.os.Handler; -import android.os.Message; -import android.net.ConnectivityManager; import android.net.wifi.WifiManager; import android.net.wifi.WifiConfiguration; import android.net.wifi.WifiConfiguration.KeyMgmt; @@ -30,8 +24,6 @@ import android.net.wifi.WifiConfiguration.KeyMgmt; import android.test.suitebuilder.annotation.LargeTest; import android.test.AndroidTestCase; -import java.util.ArrayList; - import android.util.Log; /** diff --git a/core/tests/coretests/src/android/hardware/display/VirtualDisplayTest.java b/core/tests/coretests/src/android/hardware/display/VirtualDisplayTest.java index ebecf2e38e6f..a2e9ae89e255 100644 --- a/core/tests/coretests/src/android/hardware/display/VirtualDisplayTest.java +++ b/core/tests/coretests/src/android/hardware/display/VirtualDisplayTest.java @@ -83,8 +83,8 @@ public class VirtualDisplayTest extends AndroidTestCase { mImageReaderLock.lock(); try { - mImageReader = new ImageReader(WIDTH, HEIGHT, PixelFormat.RGBA_8888, 2); - mImageReader.setImageAvailableListener(mImageListener, mHandler); + mImageReader = ImageReader.newInstance(WIDTH, HEIGHT, PixelFormat.RGBA_8888, 2); + mImageReader.setOnImageAvailableListener(mImageListener, mHandler); mSurface = mImageReader.getSurface(); } finally { mImageReaderLock.unlock(); @@ -409,19 +409,11 @@ public class VirtualDisplayTest extends AndroidTestCase { } Log.d(TAG, "New image available from virtual display."); - Image image = reader.getNextImage(); + + // Get the latest buffer. + Image image = reader.acquireLatestImage(); if (image != null) { try { - // Get the latest buffer. - for (;;) { - Image nextImage = reader.getNextImage(); - if (nextImage == null) { - break; - } - reader.releaseImage(image); - image = nextImage; - } - // Scan for colors. int color = scanImage(image); synchronized (this) { @@ -431,7 +423,7 @@ public class VirtualDisplayTest extends AndroidTestCase { } } } finally { - reader.releaseImage(image); + image.close(); } } } finally { diff --git a/docs/html/guide/topics/renderscript/compute.jd b/docs/html/guide/topics/renderscript/compute.jd index 607d16e03665..14a16822c613 100644 --- a/docs/html/guide/topics/renderscript/compute.jd +++ b/docs/html/guide/topics/renderscript/compute.jd @@ -10,6 +10,11 @@ parent.link=index.html <ol> <li><a href="#writing-an-rs-kernel">Writing a RenderScript Kernel</a></li> + <li><a href="#access-rs-apis">Accessing RenderScript Java APIs</a> + <ol> + <li><a href="#ide-setup">Setting Up Your Development Environment</a></li> + </ol> + </li> <li><a href="#using-rs-from-java">Using RenderScript from Java Code</a></li> </ol> @@ -144,9 +149,85 @@ different level of floating point precision:</p> beneficial on some architectures due to additional optimizations only available with relaxed precision (such as SIMD CPU instructions).</p> + +<h2 id="access-rs-apis">Accessing RenderScript Java APIs</h2> + +<p>When developing an Android application that uses RenderScript, you can access its Java API in + one of two ways. The APIs are available in the {@link android.renderscript} package + on devices running Android 3.0 (API level 11) and higher. These are the original APIs for + RenderScript. The APIs are also available as a Support Library in the + {@link android.support.v8.renderscript} package, which allow you to use them on devices running + Android 2.2 (API level 8) and higher.</p> + +<p>We strongly recommend using the Support Library APIs for accessing RenderScript because they + include the latest improvements to the RenderScript compute framework and provide a wider range + of device compatibility. Using the RenderScript APIs in the Support Library requires specific + setup procedures for your development environment, which is described in the next section.</p> + + +<h3 id="ide-setup">Using the RenderScript Support Library APIs</h3> + +<p>In order to use the Support Library RenderScript APIs, you must configure your development + environment to be able to access them. The following Android SDK tools are required for using + these APIs:</p> + +<ul> + <li>Android SDK Tools revision 22.2 or higher</li> + <li>Android SDK Build-tools revision 18.1.0 or higher</li> +</ul> + +<p>You can check and update the installed version of these tools in the + <a href="{@docRoot}tools/help/sdk-manager.html">Android SDK Manager</a>.</p> + +<p class="note"> + <strong>Note:</strong> Use of Support Library RenderScript APIs is not currently supported with + Android Studio or Gradle-based builds. +</p> + +<p>To use the Support Library RenderScript APIs in Eclipse:</p> + +<ol> + <li>Make sure you have the required Android SDK version and Build Tools version installed.</li> + <li>Open the {@code project.properties} file in the root folder of your application project.</li> + <li>Add the following lines to the file: +<pre> +renderscript.target=18 +renderscript.support.mode=true +sdk.buildtools=18.1.0 +</pre> + </li> + <li>In your application classes that use RenderScript, add an import for the Support Library + classes: +<pre> +import android.support.v8.renderscript.*; +</pre> + </li> +</ol> + +<p>The {@code project.properties} settings listed above control specific behavior in the Android + build process:</p> + +<ul> + <li>{@code renderscript.target} - Specifies the bytecode version to be generated. We + recommend you set this value the highest available API level and set {@code + renderscript.support.mode} to {@code true}. Valid values for this setting are any integer value + from 11 to the most recently released API level. If your minimum SDK version specified in your + application manifest is set to a higher value, this value is ignored and the target value is set + to the minimum SDK version.</li> + <li>{@code renderscript.support.mode} - Specifies that the generated bytecode should fall + back to a compatible version if the device it is running on does not support the target version. + </li> + <li>{@code sdk.buildtools} - The version of the Android SDK build tools to use. This value + should be set to 18.1.0 or higher. If this option is not specified, the highest installed build + tools version is used. You should always set this value to ensure the consistency of builds + across development machines with different configurations.</li> +</ul> + + <h2 id="using-rs-from-java">Using RenderScript from Java Code</h2> -<p>Using RenderScript from Java code relies on the {@link android.renderscript} APIs. Most +<p>Using RenderScript from Java code relies on the API classes located in the +{@link android.renderscript} or the {@link android.support.v8.renderscript} package. Most applications follow the same basic usage patterns:</p> <ol> diff --git a/docs/html/sdk/index.jd b/docs/html/sdk/index.jd index 4ea375263d7d..05814350311b 100644 --- a/docs/html/sdk/index.jd +++ b/docs/html/sdk/index.jd @@ -5,43 +5,43 @@ header.hide=1 page.metaDescription=Download the official Android SDK to develop apps for Android-powered devices. -sdk.linux32_bundle_download=adt-bundle-linux-x86-20130729.zip -sdk.linux32_bundle_bytes=457716139 -sdk.linux32_bundle_checksum=b3686d10dc1cbceba1074404d4386283 +sdk.linux32_bundle_download=adt-bundle-linux-x86-20130911.zip +sdk.linux32_bundle_bytes=474916528 +sdk.linux32_bundle_checksum=7eacc7124299ea99a8fa15c59123540f -sdk.linux64_bundle_download=adt-bundle-linux-x86_64-20130729.zip -sdk.linux64_bundle_bytes=458006784 -sdk.linux64_bundle_checksum=1fabcc3f772ba8b2fc194d6e0449da17 +sdk.linux64_bundle_download=adt-bundle-linux-x86_64-20130911.zip +sdk.linux64_bundle_bytes=475207785 +sdk.linux64_bundle_checksum=daa5794a27be7c7fa708c3d28833b0d3 -sdk.mac64_bundle_download=adt-bundle-mac-x86_64-20130729.zip -sdk.mac64_bundle_bytes=428792424 -sdk.mac64_bundle_checksum=6c42b9966abcfa8a75c0ee83d0d95882 +sdk.mac64_bundle_download=adt-bundle-mac-x86_64-20130911.zip +sdk.mac64_bundle_bytes=448575446 +sdk.mac64_bundle_checksum=a1e0cbcc820ae734cfdf439c40811b4c -sdk.win32_bundle_download=adt-bundle-windows-x86-20130729.zip -sdk.win32_bundle_bytes=463931746 -sdk.win32_bundle_checksum=51faf4e5fdf9c5b4a176179a99ce3511 +sdk.win32_bundle_download=adt-bundle-windows-x86-20130911.zip +sdk.win32_bundle_bytes=481794820 +sdk.win32_bundle_checksum=88a2f4f242aac44f4b1c53e6eccc8710 -sdk.win64_bundle_download=adt-bundle-windows-x86_64-20130729.zip -sdk.win64_bundle_bytes=464064756 -sdk.win64_bundle_checksum=e8f05c1fddb8e609e880de23113c7426 +sdk.win64_bundle_download=adt-bundle-windows-x86_64-20130911.zip +sdk.win64_bundle_bytes=481927327 +sdk.win64_bundle_checksum=e3fa9b7e38af9ed9ac0e99fce3c7026c -sdk.linux_download=android-sdk_r22.0.5-linux.tgz -sdk.linux_bytes=105641005 -sdk.linux_checksum=8201b10c21510f082c54f58a9bb082c8 +sdk.linux_download=android-sdk_r22.2-linux.tgz +sdk.linux_bytes=100909403 +sdk.linux_checksum=2a3776839e823ba9acb7a87a3fe26e02 -sdk.mac_download=android-sdk_r22.0.5-macosx.zip -sdk.mac_bytes=77225724 -sdk.mac_checksum=94f3cbe896c332b94ee0408ae610a4b8 +sdk.mac_download=android-sdk_r22.2-macosx.zip +sdk.mac_bytes=74857114 +sdk.mac_checksum=9dfef6404e2f842c433073796aed8b7d -sdk.win_download=android-sdk_r22.0.5-windows.zip -sdk.win_bytes=113510621 -sdk.win_checksum=30695dffc41e0d7cf9ff948ab0c48920 +sdk.win_download=android-sdk_r22.2-windows.zip +sdk.win_bytes=108790714 +sdk.win_checksum=1ac4c104378cd53049daa6c4458ec544 -sdk.win_installer=installer_r22.0.5-windows.exe -sdk.win_installer_bytes=93505782 -sdk.win_installer_checksum=940849be19ac6151e3e35c8706c81d86 +sdk.win_installer=installer_r22.2-windows.exe +sdk.win_installer_bytes=88788974 +sdk.win_installer_checksum=e5503fa059297d2b18475c086ac6e80c diff --git a/docs/html/sdk/installing/installing-adt.jd b/docs/html/sdk/installing/installing-adt.jd index bdc07d02283d..e038d2089e3b 100644 --- a/docs/html/sdk/installing/installing-adt.jd +++ b/docs/html/sdk/installing/installing-adt.jd @@ -1,8 +1,8 @@ page.title=Installing the Eclipse Plugin -adt.zip.version=22.0.5 -adt.zip.download=ADT-22.0.5.zip -adt.zip.bytes=16839757 -adt.zip.checksum=1097fccf32063e3638a9d27aa0f295ca +adt.zip.version=22.2.0 +adt.zip.download=ADT-22.2.0.zip +adt.zip.bytes=14474195 +adt.zip.checksum=52892c9e3b1ad2d1e6edd50e48b2a127 @jd:body diff --git a/docs/html/tools/sdk/eclipse-adt.jd b/docs/html/tools/sdk/eclipse-adt.jd index e9c514e064d3..151707a3fa84 100644 --- a/docs/html/tools/sdk/eclipse-adt.jd +++ b/docs/html/tools/sdk/eclipse-adt.jd @@ -57,6 +57,44 @@ href="http://tools.android.com/knownissues">http://tools.android.com/knownissues <div class="toggle-content opened"> <p><a href="#" onclick="return toggleContent(this)"> <img src="{@docRoot}assets/images/triangle-opened.png" class="toggle-content-img" + alt=""/>ADT 22.2</a> <em>(September 2013)</em> + </p> + + <div class="toggle-content-toggleme"> +<dl> + <dt>Dependencies:</dt> + + <dd> + <ul> + <li>Java 1.6 or higher is required.</li> + <li>Eclipse Helios (Version 3.6.2) or higher is required.</li> + <li>This version of ADT is designed for use with + <a href="{@docRoot}tools/sdk/tools-notes.html">SDK Tools r22.2</a>. + If you haven't already installed SDK Tools r22.2 into your SDK, use the + Android SDK Manager to do so.</li> + </ul> + </dd> + + <dt>General Notes:</dt> + <dd> + <ul> + <li>Updated build tools to allow use of RenderScript on older versions of Android + using new features in the + <a href="{@docRoot}tools/support-library/features.html#v8">Support Library</a>.</li> + <li>Reverted signing changes that sometimes trigger a signing verification problem on older + platforms.</li> + <li>Fixed problem with gradle export function for the Windows platform.</li> + </ul> + </dd> + +</dl> +</div> +</div> + + +<div class="toggle-content closed"> + <p><a href="#" onclick="return toggleContent(this)"> + <img src="{@docRoot}assets/images/triangle-closed.png" class="toggle-content-img" alt=""/>ADT 22.0.5</a> <em>(July 2013)</em> </p> @@ -78,7 +116,7 @@ href="http://tools.android.com/knownissues">http://tools.android.com/knownissues <dt>General Notes:</dt> <dd> <ul> - <li>Fixed Renderscript compilation issue for Windows platforms.</li> + <li>Fixed RenderScript compilation issue for Windows platforms.</li> <li>Updated <a href="{@docRoot}tools/help/systrace.html">Systrace</a> report generation in the Monitor and DDMS perspectives.</li> </ul> @@ -113,7 +151,7 @@ href="http://tools.android.com/knownissues">http://tools.android.com/knownissues <dt>General Notes:</dt> <dd> <ul> - <li>Fixed problem with compiling Renderscript code.</li> + <li>Fixed problem with compiling RenderScript code.</li> <li>Improved Gradle export with better workflow and error reporting.</li> <li>Improved Gradle multi-module export feature.</li> <li>Updated build logic to force exporting of the classpath containers unless you are using @@ -1005,7 +1043,7 @@ href="http://tools.android.com/recent/lint">more info</a>)</li> <dt>Bug fixes:</dt> <dd> <ul> - <li>Fixed build issue when using Renderscript in projects that target API levels 11-13 + <li>Fixed build issue when using RenderScript in projects that target API levels 11-13 (<a href="http://code.google.com/p/android/issues/detail?id=21006">Issue 21006</a>).</li> <li>Fixed issue when creating projects from existing source code.</li> <li>Fixed issues in the SDK Manager diff --git a/docs/html/tools/sdk/tools-notes.jd b/docs/html/tools/sdk/tools-notes.jd index 4aef8a028505..e8c471765011 100644 --- a/docs/html/tools/sdk/tools-notes.jd +++ b/docs/html/tools/sdk/tools-notes.jd @@ -30,6 +30,54 @@ href="http://tools.android.com/knownissues">http://tools.android.com/knownissues <div class="toggle-content opened"> <p><a href="#" onclick="return toggleContent(this)"> <img src="{@docRoot}assets/images/triangle-opened.png" class="toggle-content-img" + alt=""/>SDK Tools, Revision 22.2</a> <em>(September 2013)</em> + </p> + + <div class="toggle-content-toggleme"> + + <dl> + <dt>Dependencies:</dt> + <dd> + <ul> + <li>Android SDK Platform-tools revision 16 or later.</li> + <li>If you are developing in Eclipse with ADT, note that this version of SDK Tools is + designed for use with ADT 22.2 and later. If you haven't already, update your + <a href="{@docRoot}tools/sdk/eclipse-adt.html">ADT Plugin</a> to 22.2.</li> + <li>If you are developing outside Eclipse, you must have + <a href="http://ant.apache.org/">Apache Ant</a> 1.8 or later.</li> + </ul> + </dd> + + <dt>General Notes:</dt> + <dd> + <ul> + <li>Updated build tools to allow use of RenderScript on older versions of Android + using new features in the + <a href="{@docRoot}tools/support-library/features.html#v8">Support Library</a>.</li> + <li>Moved the Systrace tool to the {@code >sdk</platform-tools/} directory. </li> + <li>Modified <a href="{@docRoot}tools/help/gltracer.html">Tracer for OpenGL ES</a> to + support OpenGL ES 3.0.</li> + <li>Lint + <ul> + <li>Fixed problem with lint not detecting custom namespaces. + (<a href="http://b.android.com/55673">Issue 55673</a>)</li> + <li>Fixed problem with the XML report including invalid characters. + (<a href="http://b.android.com/56205">Issue 56205</a>)</li> + <li>Fixed command-line execution of lint to work in headless mode to support execution + by build servers. (<a href="http://b.android.com/55820">Issue 55820</a>)</li> + </ul> + </li> + <li>Improved support for path names with spaces in the Windows command-line tools.</li> + </ul> + </dd> + </dl> + </div> +</div> + + +<div class="toggle-content closed"> + <p><a href="#" onclick="return toggleContent(this)"> + <img src="{@docRoot}assets/images/triangle-closed.png" class="toggle-content-img" alt=""/>SDK Tools, Revision 22.0.5</a> <em>(July 2013)</em> </p> @@ -55,10 +103,10 @@ href="http://tools.android.com/knownissues">http://tools.android.com/knownissues <dt>General Notes:</dt> <dd> <ul> - <li>Fixed Renderscript compilation issue for Windows platforms with ant.</li> + <li>Fixed RenderScript compilation issue for Windows platforms with ant.</li> <li>Updated <a href="{@docRoot}tools/help/systrace.html">Systrace</a> to work with the Android 4.3 platform image.</li> - <li>Fixed packaging of Renderscript compiler.</li> + <li>Fixed packaging of RenderScript compiler.</li> <li>Build tools 18.0.0 is obsolete and 18.0.1 should be used instead.</li> </ul> </dd> @@ -95,7 +143,7 @@ href="http://tools.android.com/knownissues">http://tools.android.com/knownissues <dt>General Notes:</dt> <dd> <ul> - <li>Fixed problem with compiling Renderscript code.</li> + <li>Fixed problem with compiling RenderScript code.</li> </ul> </dd> </dl> @@ -274,17 +322,17 @@ href="http://tools.android.com/knownissues">http://tools.android.com/knownissues </ul> </li> - <li>Renderscript + <li>RenderScript <ul> <li>Added support for <a href="{@docRoot}guide/topics/renderscript/compute.html#filterscript">Filterscript</a> compilation.</li> - <li>Added new project setting to control the Renderscript compilation target separately + <li>Added new project setting to control the RenderScript compilation target separately from an Android project. Adding the following line to a {@code project.properties} - file causes Renderscript code to be compiled for Android API Level 17, while the + file causes RenderScript code to be compiled for Android API Level 17, while the containing application can target a different (lower) API level: <pre>renderscript.target = 17</pre> - Previously, the Renderscript compilation target was tied to the + Previously, the RenderScript compilation target was tied to the {@code android:minSdkVersion} setting in the manifest. (<a href="http://code.google.com/p/android/issues/detail?id=40487">Issue 40487</a>) </li> @@ -483,7 +531,7 @@ with GPU acceleration.</li> <li>Improved resize algorithm for better rendering on scaled emulator windows.</li> <li>Fixed a bug in the {@code lint} check for unprotected broadcast receivers to ignore unprotected receivers for default Android actions.</li> - <li>Fixed build issue for projects using Renderscript.</li> + <li>Fixed build issue for projects using RenderScript.</li> <li>Fixed memory leak in the emulator.</li> </ul> </dd> @@ -823,7 +871,7 @@ ignore attribute. (<a <li>Fixed emulator crash on Linux due to improper webcam detection (<a href="http://code.google.com/p/android/issues/detail?id=20952">Issue 20952</a>).</li> <li>Fixed emulator issue when using the <code>-wipe-data</code> argument.</li> - <li>Fixed build issue when using Renderscript in projects that target API levels 11-13 + <li>Fixed build issue when using RenderScript in projects that target API levels 11-13 (<a href="http://code.google.com/p/android/issues/detail?id=21006">Issue 21006</a>).</li> <li>Fixed issue when creating an AVD using the GoogleTV addon (<a href="http://code.google.com/p/android/issues/detail?id=20963">Issue 20963</a>).</li> diff --git a/docs/html/tools/support-library/features.jd b/docs/html/tools/support-library/features.jd index 8d25d96c9116..65148bf2d2ff 100644 --- a/docs/html/tools/support-library/features.jd +++ b/docs/html/tools/support-library/features.jd @@ -15,6 +15,7 @@ page.title=Support Library Features <li><a href="#v7-mediarouter">v7 mediarouter library</a></li> </ol> </li> + <li><a href="#v8">v8 Support Library</a></li> <li><a href="#v13">v13 Support Library</a></li> </ol> @@ -252,7 +253,7 @@ script dependency identifier <code>com.android.support:support-v7-mediarouter:&l where "18.0.0" is the minimum revision at which the library is available. For example:</p> <pre> -com.android.support:support-v7-mediarouter:18.0.0 +com.android.support:mediarouter-v7:18.0.+ </pre> <p class="caution">The v7 mediarouter library APIs introduced in Support Library @@ -262,6 +263,24 @@ href="https://developers.google.com/cast/">Google Cast developer preview</a>. </p> +<h2 id="v8">v8 Support Library</h2> + +<p>This library is designed to be used with Android (API level 8) and higher. It adds support for + the <a href="{@docRoot}guide/topics/renderscript/compute.html">RenderScript</a> computation + framework. These APIs are included in the {@link android.support.v8.renderscript} package. You + should be aware that the steps for including these APIs in your application is <em>very + different</em> from other support library APIs. For more information about using these APIs + in your application, see the + <a href="{@docRoot}guide/topics/renderscript/compute.html#access-rs-apis">RenderScript</a> + developer guide.</p> + +<p class="note"> + <strong>Note:</strong> Use of RenderScript with the support library is supported with the Android + Eclipse plugin and Ant build tools. It is <em>not currently</em> supported with Android Studio or + Gradle-based builds. +</p> + + <h2 id="v13">v13 Support Library</h2> <p>This library is designed to be used for Android 3.2 (API level 13) and higher. It adds support diff --git a/docs/html/tools/support-library/index.jd b/docs/html/tools/support-library/index.jd index 06c7a3ff638a..4ee8c1289fc5 100644 --- a/docs/html/tools/support-library/index.jd +++ b/docs/html/tools/support-library/index.jd @@ -58,6 +58,7 @@ page.title=Support Library <p>This section provides details about the Support Library package releases.</p> + <div class="toggle-content opened"> <p><a href="#" onclick="return toggleContent(this)"> <img src="{@docRoot}assets/images/triangle-opened.png" class="toggle-content-img" alt="" diff --git a/media/java/android/media/Image.java b/media/java/android/media/Image.java index 9f442f57f9b1..a346e176984e 100644 --- a/media/java/android/media/Image.java +++ b/media/java/android/media/Image.java @@ -16,20 +16,19 @@ package android.media; -import android.graphics.ImageFormat; import java.nio.ByteBuffer; import java.lang.AutoCloseable; /** * <p>A single complete image buffer to use with a media source such as a * {@link MediaCodec} or a - * {@link android.hardware.camera2.CameraDevice}.</p> + * {@link android.hardware.camera2.CameraDevice CameraDevice}.</p> * * <p>This class allows for efficient direct application access to the pixel * data of the Image through one or more * {@link java.nio.ByteBuffer ByteBuffers}. Each buffer is encapsulated in a * {@link Plane} that describes the layout of the pixel data in that plane. Due - * to this direct access, and unlike the {@link android.graphics.Bitmap} class, + * to this direct access, and unlike the {@link android.graphics.Bitmap Bitmap} class, * Images are not directly usable as as UI resources.</p> * * <p>Since Images are often directly produced or consumed by hardware @@ -40,19 +39,28 @@ import java.lang.AutoCloseable; * from various media sources, not closing old Image objects will prevent the * availability of new Images once * {@link ImageReader#getMaxImages the maximum outstanding image count} is - * reached.</p> + * reached. When this happens, the function acquiring new Images will typically + * throw an {@link IllegalStateException}.</p> * * @see ImageReader */ -public interface Image extends AutoCloseable { +public abstract class Image implements AutoCloseable { + /** + * @hide + */ + protected Image() { + } + /** * Get the format for this image. This format determines the number of * ByteBuffers needed to represent the image, and the general layout of the * pixel data in each in ByteBuffer. * + * <p> * The format is one of the values from - * {@link android.graphics.ImageFormat}. The mapping between the formats and - * the planes is as follows: + * {@link android.graphics.ImageFormat ImageFormat}. The mapping between the + * formats and the planes is as follows: + * </p> * * <table> * <tr> @@ -61,13 +69,14 @@ public interface Image extends AutoCloseable { * <th>Layout details</th> * </tr> * <tr> - * <td>{@link android.graphics.ImageFormat#JPEG}</td> + * <td>{@link android.graphics.ImageFormat#JPEG JPEG}</td> * <td>1</td> * <td>Compressed data, so row and pixel strides are 0. To uncompress, use - * {@link android.graphics.BitmapFactory#decodeByteArray}.</td> + * {@link android.graphics.BitmapFactory#decodeByteArray BitmapFactory#decodeByteArray}. + * </td> * </tr> * <tr> - * <td>{@link android.graphics.ImageFormat#YUV_420_888}</td> + * <td>{@link android.graphics.ImageFormat#YUV_420_888 YUV_420_888}</td> * <td>3</td> * <td>A luminance plane followed by the Cb and Cr chroma planes. * The chroma planes have half the width and height of the luminance @@ -75,53 +84,60 @@ public interface Image extends AutoCloseable { * Each plane has its own row stride and pixel stride.</td> * </tr> * <tr> - * <td>{@link android.graphics.ImageFormat#RAW_SENSOR}</td> + * <td>{@link android.graphics.ImageFormat#RAW_SENSOR RAW_SENSOR}</td> * <td>1</td> * <td>A single plane of raw sensor image data, with 16 bits per color * sample. The details of the layout need to be queried from the source of * the raw sensor data, such as - * {@link android.hardware.camera2.CameraDevice}. + * {@link android.hardware.camera2.CameraDevice CameraDevice}. * </td> * </tr> * </table> * * @see android.graphics.ImageFormat */ - public int getFormat(); + public abstract int getFormat(); /** * The width of the image in pixels. For formats where some color channels * are subsampled, this is the width of the largest-resolution plane. */ - public int getWidth(); + public abstract int getWidth(); /** * The height of the image in pixels. For formats where some color channels * are subsampled, this is the height of the largest-resolution plane. */ - public int getHeight(); + public abstract int getHeight(); /** - * Get the timestamp associated with this frame. The timestamp is measured - * in nanoseconds, and is monotonically increasing. However, the zero point - * and whether the timestamp can be compared against other sources of time - * or images depend on the source of this image. + * Get the timestamp associated with this frame. + * <p> + * The timestamp is measured in nanoseconds, and is monotonically + * increasing. However, the zero point and whether the timestamp can be + * compared against other sources of time or images depend on the source of + * this image. + * </p> */ - public long getTimestamp(); + public abstract long getTimestamp(); /** * Get the array of pixel planes for this Image. The number of planes is * determined by the format of the Image. */ - public Plane[] getPlanes(); + public abstract Plane[] getPlanes(); /** - * Free up this frame for reuse. After calling this method, calling any - * methods on this Image will result in an IllegalStateException, and - * attempting to read from ByteBuffers returned by an earlier - * {@code Plane#getBuffer} call will have undefined behavior. + * Free up this frame for reuse. + * <p> + * After calling this method, calling any methods on this {@code Image} will + * result in an {@link IllegalStateException}, and attempting to read from + * {@link ByteBuffer ByteBuffers} returned by an earlier + * {@link Plane#getBuffer} call will have undefined behavior. + * </p> */ - public void close(); + @Override + public abstract void close(); /** * <p>A single color plane of image data.</p> @@ -134,29 +150,41 @@ public interface Image extends AutoCloseable { * * @see #getFormat */ - public interface Plane { + public static abstract class Plane { /** - * <p>The row stride for this color plane, in bytes. + * @hide + */ + protected Plane() { + } + + /** + * <p>The row stride for this color plane, in bytes.</p> * * <p>This is the distance between the start of two consecutive rows of - * pixels in the image.</p> + * pixels in the image. The row stride is always greater than 0.</p> */ - public int getRowStride(); + public abstract int getRowStride(); /** * <p>The distance between adjacent pixel samples, in bytes.</p> * * <p>This is the distance between two consecutive pixel values in a row * of pixels. It may be larger than the size of a single pixel to - * account for interleaved image data or padded formats.</p> + * account for interleaved image data or padded formats. + * The pixel stride is always greater than 0.</p> */ - public int getPixelStride(); + public abstract int getPixelStride(); /** - * <p>Get a set of direct {@link java.nio.ByteBuffer byte buffers} + * <p>Get a direct {@link java.nio.ByteBuffer ByteBuffer} * containing the frame data.</p> * + * <p>In particular, the buffer returned will always have + * {@link java.nio.ByteBuffer#isDirect isDirect} return {@code true}, so + * the underlying data could be mapped as a pointer in JNI without doing + * any copies with {@code GetDirectBufferAddress}.</p> + * * @return the byte buffer containing the image data for this plane. */ - public ByteBuffer getBuffer(); + public abstract ByteBuffer getBuffer(); } } diff --git a/media/java/android/media/ImageReader.java b/media/java/android/media/ImageReader.java index b14a899f33fa..aee83627bb0e 100644 --- a/media/java/android/media/ImageReader.java +++ b/media/java/android/media/ImageReader.java @@ -40,41 +40,67 @@ import java.nio.ByteOrder; * <p>The image data is encapsulated in {@link Image} objects, and multiple such * objects can be accessed at the same time, up to the number specified by the * {@code maxImages} constructor parameter. New images sent to an ImageReader - * through its Surface are queued until accessed through the - * {@link #getNextImage} call. Due to memory limits, an image source will + * through its {@link Surface} are queued until accessed through the {@link #acquireLatestImage} + * or {@link #acquireNextImage} call. Due to memory limits, an image source will * eventually stall or drop Images in trying to render to the Surface if the * ImageReader does not obtain and release Images at a rate equal to the * production rate.</p> */ -public final class ImageReader implements AutoCloseable { +public class ImageReader implements AutoCloseable { + + /** + * Returned by nativeImageSetup when acquiring the image was successful. + */ + private static final int ACQUIRE_SUCCESS = 0; + /** + * Returned by nativeImageSetup when we couldn't acquire the buffer, + * because there were no buffers available to acquire. + */ + private static final int ACQUIRE_NO_BUFS = 1; + /** + * Returned by nativeImageSetup when we couldn't acquire the buffer + * because the consumer has already acquired {@maxImages} and cannot + * acquire more than that. + */ + private static final int ACQUIRE_MAX_IMAGES = 2; /** * <p>Create a new reader for images of the desired size and format.</p> * - * <p>The maxImages parameter determines the maximum number of {@link Image} - * objects that can be be acquired from the ImageReader + * <p>The {@code maxImages} parameter determines the maximum number of {@link Image} + * objects that can be be acquired from the {@code ImageReader} * simultaneously. Requesting more buffers will use up more memory, so it is * important to use only the minimum number necessary for the use case.</p> * * <p>The valid sizes and formats depend on the source of the image * data.</p> * - * @param width the width in pixels of the Images that this reader will - * produce. - * @param height the height in pixels of the Images that this reader will - * produce. - * @param format the format of the Image that this reader will produce. This - * must be one of the {@link android.graphics.ImageFormat} or - * {@link android.graphics.PixelFormat} constants. - * @param maxImages the maximum number of images the user will want to - * access simultaneously. This should be as small as possible to limit - * memory use. Once maxImages Images are obtained by the user, one of them - * has to be released before a new Image will become available for access - * through getNextImage(). Must be greater than 0. + * @param width + * The width in pixels of the Images that this reader will produce. + * @param height + * The height in pixels of the Images that this reader will produce. + * @param format + * The format of the Image that this reader will produce. This + * must be one of the {@link android.graphics.ImageFormat} or + * {@link android.graphics.PixelFormat} constants. + * @param maxImages + * The maximum number of images the user will want to + * access simultaneously. This should be as small as possible to limit + * memory use. Once maxImages Images are obtained by the user, one of them + * has to be released before a new Image will become available for access + * through {@link #acquireLatestImage()} or {@link #acquireNextImage()}. + * Must be greater than 0. * * @see Image */ - public ImageReader(int width, int height, int format, int maxImages) { + public static ImageReader newInstance(int width, int height, int format, int maxImages) { + return new ImageReader(width, height, format, maxImages); + } + + /** + * @hide + */ + protected ImageReader(int width, int height, int format, int maxImages) { mWidth = width; mHeight = height; mFormat = format; @@ -96,33 +122,79 @@ public final class ImageReader implements AutoCloseable { mSurface = nativeGetSurface(); } + /** + * The width of each {@link Image}, in pixels. + * + * <p>ImageReader guarantees that all Images acquired from ImageReader (for example, with + * {@link #acquireNextImage}) will have the same dimensions as specified in + * {@link #newInstance}.</p> + * + * @return the width of an Image + */ public int getWidth() { return mWidth; } + /** + * The height of each {@link Image}, in pixels. + * + * <p>ImageReader guarantees that all Images acquired from ImageReader (for example, with + * {@link #acquireNextImage}) will have the same dimensions as specified in + * {@link #newInstance}.</p> + * + * @return the height of an Image + */ public int getHeight() { return mHeight; } + /** + * The {@link ImageFormat image format} of each Image. + * + * <p>ImageReader guarantees that all {@link Image Images} acquired from ImageReader + * (for example, with {@link #acquireNextImage}) will have the same format as specified in + * {@link #newInstance}.</p> + * + * @return the format of an Image + * + * @see ImageFormat + */ public int getImageFormat() { return mFormat; } + /** + * Maximum number of images that can be acquired from the ImageReader by any time (for example, + * with {@link #acquireNextImage}). + * + * <p>An image is considered acquired after it's returned by a function from ImageReader, and + * until the Image is {@link Image#close closed} to release the image back to the ImageReader. + * </p> + * + * <p>Attempting to acquire more than {@code maxImages} concurrently will result in the + * acquire function throwing a {@link IllegalStateException}. Furthermore, + * while the max number of images have been acquired by the ImageReader user, the producer + * enqueueing additional images may stall until at least one image has been released. </p> + * + * @return Maximum number of images for this ImageReader. + * + * @see Image#close + */ public int getMaxImages() { return mMaxImages; } /** - * <p>Get a Surface that can be used to produce Images for this - * ImageReader.</p> + * <p>Get a {@link Surface} that can be used to produce {@link Image Images} for this + * {@code ImageReader}.</p> * - * <p>Until valid image data is rendered into this Surface, the - * {@link #getNextImage} method will return {@code null}. Only one source + * <p>Until valid image data is rendered into this {@link Surface}, the + * {@link #acquireNextImage} method will return {@code null}. Only one source * can be producing data into this Surface at the same time, although the - * same Surface can be reused with a different API once the first source is - * disconnected from the Surface.</p> + * same {@link Surface} can be reused with a different API once the first source is + * disconnected from the {@link Surface}.</p> * - * @return A Surface to use for a drawing target for various APIs. + * @return A {@link Surface} to use for a drawing target for various APIs. */ public Surface getSurface() { return mSurface; @@ -130,41 +202,154 @@ public final class ImageReader implements AutoCloseable { /** * <p> - * Get the next Image from the ImageReader's queue. Returns {@code null} if + * Acquire the latest {@link Image} from the ImageReader's queue, dropping older + * {@link Image images}. Returns {@code null} if no new image is available. + * </p> + * <p> + * This operation will acquire all the images possible from the ImageReader, + * but {@link #close} all images that aren't the latest. This function is + * recommended to use over {@link #acquireNextImage} for most use-cases, as it's + * more suited for real-time processing. + * </p> + * <p> + * Note that {@link #getMaxImages maxImages} should be at least 2 for + * {@link #acquireLatestImage} to be any different than {@link #acquireNextImage} - + * discarding all-but-the-newest {@link Image} requires temporarily acquiring two + * {@link Image Images} at once. Or more generally, calling {@link #acquireLatestImage} + * with less than two images of margin, that is + * {@code (maxImages - currentAcquiredImages < 2)} will not discard as expected. + * </p> + * <p> + * This operation will fail by throwing an {@link IllegalStateException} if + * {@code maxImages} have been acquired with {@link #acquireLatestImage} or + * {@link #acquireNextImage}. In particular a sequence of {@link #acquireLatestImage} + * calls greater than {@link #getMaxImages} without calling {@link Image#close} in-between + * will exhaust the underlying queue. At such a time, {@link IllegalStateException} + * will be thrown until more images are + * released with {@link Image#close}. + * </p> + * + * @return latest frame of image data, or {@code null} if no image data is available. + * @throws IllegalStateException if too many images are currently acquired + */ + public Image acquireLatestImage() { + Image image = acquireNextImage(); + if (image == null) { + return null; + } + try { + for (;;) { + Image next = acquireNextImageNoThrowISE(); + if (next == null) { + Image result = image; + image = null; + return result; + } + image.close(); + image = next; + } + } finally { + if (image != null) { + image.close(); + } + } + } + + /** + * Don't throw IllegalStateException if there are too many images acquired. + * + * @return Image if acquiring succeeded, or null otherwise. + * + * @hide + */ + public Image acquireNextImageNoThrowISE() { + SurfaceImage si = new SurfaceImage(); + return acquireNextSurfaceImage(si) == ACQUIRE_SUCCESS ? si : null; + } + + /** + * Attempts to acquire the next image from the underlying native implementation. + * + * <p> + * Note that unexpected failures will throw at the JNI level. + * </p> + * + * @param si A blank SurfaceImage. + * @return One of the {@code ACQUIRE_*} codes that determine success or failure. + * + * @see #ACQUIRE_MAX_IMAGES + * @see #ACQUIRE_NO_BUFS + * @see #ACQUIRE_SUCCESS + */ + private int acquireNextSurfaceImage(SurfaceImage si) { + + int status = nativeImageSetup(si); + + switch (status) { + case ACQUIRE_SUCCESS: + si.createSurfacePlanes(); + si.setImageValid(true); + case ACQUIRE_NO_BUFS: + case ACQUIRE_MAX_IMAGES: + break; + default: + throw new AssertionError("Unknown nativeImageSetup return code " + status); + } + + return status; + } + + /** + * <p> + * Acquire the next Image from the ImageReader's queue. Returns {@code null} if * no new image is available. * </p> + * + * <p><i>Warning:</i> Consider using {@link #acquireLatestImage()} instead, as it will + * automatically release older images, and allow slower-running processing routines to catch + * up to the newest frame. Usage of {@link #acquireNextImage} is recommended for + * batch/background processing. Incorrectly using this function can cause images to appear + * with an ever-increasing delay, followed by a complete stall where no new images seem to + * appear. + * </p> + * * <p> - * This operation will fail by throwing an - * {@link Surface.OutOfResourcesException OutOfResourcesException} if too - * many images have been acquired with {@link #getNextImage}. In particular - * a sequence of {@link #getNextImage} calls greater than {@link #getMaxImages} - * without calling {@link Image#close} or {@link #releaseImage} in-between - * will exhaust the underlying queue. At such a time, - * {@link Surface.OutOfResourcesException OutOfResourcesException} will be - * thrown until more images are released with {@link Image#close} or - * {@link #releaseImage}. + * This operation will fail by throwing an {@link IllegalStateException} if + * {@code maxImages} have been acquired with {@link #acquireNextImage} or + * {@link #acquireLatestImage}. In particular a sequence of {@link #acquireNextImage} or + * {@link #acquireLatestImage} calls greater than {@link #getMaxImages maxImages} without + * calling {@link Image#close} in-between will exhaust the underlying queue. At such a time, + * {@link IllegalStateException} will be thrown until more images are released with + * {@link Image#close}. * </p> * - * @return a new frame of image data, or {@code null} if no image data is - * available. - * @throws Surface.OutOfResourcesException if too many images are currently - * acquired + * @return a new frame of image data, or {@code null} if no image data is available. + * @throws IllegalStateException if {@code maxImages} images are currently acquired + * @see #acquireLatestImage */ - public Image getNextImage() { + public Image acquireNextImage() { SurfaceImage si = new SurfaceImage(); - if (nativeImageSetup(si)) { - // create SurfacePlane objects - si.createSurfacePlanes(); - si.setImageValid(true); - return si; + int status = acquireNextSurfaceImage(si); + + switch (status) { + case ACQUIRE_SUCCESS: + return si; + case ACQUIRE_NO_BUFS: + return null; + case ACQUIRE_MAX_IMAGES: + throw new IllegalStateException( + String.format( + "maxImages (%d) has already been acquired, " + + "call #close before acquiring more.", mMaxImages)); + default: + throw new AssertionError("Unknown nativeImageSetup return code " + status); } - return null; } /** * <p>Return the frame to the ImageReader for reuse.</p> */ - public void releaseImage(Image i) { + private void releaseImage(Image i) { if (! (i instanceof SurfaceImage) ) { throw new IllegalArgumentException( "This image was not produced by an ImageReader"); @@ -183,13 +368,16 @@ public final class ImageReader implements AutoCloseable { /** * Register a listener to be invoked when a new image becomes available * from the ImageReader. - * @param listener the listener that will be run - * @param handler The handler on which the listener should be invoked, or null - * if the listener should be invoked on the calling thread's looper. * - * @throws IllegalArgumentException if no handler specified and the calling thread has no looper + * @param listener + * The listener that will be run. + * @param handler + * The handler on which the listener should be invoked, or null + * if the listener should be invoked on the calling thread's looper. + * @throws IllegalArgumentException + * If no handler specified and the calling thread has no looper. */ - public void setImageAvailableListener(OnImageAvailableListener listener, Handler handler) { + public void setOnImageAvailableListener(OnImageAvailableListener listener, Handler handler) { mImageListener = listener; Looper looper; @@ -206,12 +394,16 @@ public final class ImageReader implements AutoCloseable { /** * Callback interface for being notified that a new image is available. + * + * <p> * The onImageAvailable is called per image basis, that is, callback fires for every new frame * available from ImageReader. + * </p> */ public interface OnImageAvailableListener { /** * Callback that is called when a new image is available from ImageReader. + * * @param reader the ImageReader the callback is associated with. * @see ImageReader * @see Image @@ -220,12 +412,17 @@ public final class ImageReader implements AutoCloseable { } /** - * Free up all the resources associated with this ImageReader. After - * Calling this method, this ImageReader can not be used. calling - * any methods on this ImageReader and Images previously provided by {@link #getNextImage} - * will result in an IllegalStateException, and attempting to read from - * ByteBuffers returned by an earlier {@code Plane#getBuffer} call will + * Free up all the resources associated with this ImageReader. + * + * <p> + * After calling this method, this ImageReader can not be used. Calling + * any methods on this ImageReader and Images previously provided by + * {@link #acquireNextImage} or {@link #acquireLatestImage} + * will result in an {@link IllegalStateException}, and attempting to read from + * {@link ByteBuffer ByteBuffers} returned by an earlier + * {@link Image.Plane#getBuffer Plane#getBuffer} call will * have undefined behavior. + * </p> */ @Override public void close() { @@ -242,11 +439,14 @@ public final class ImageReader implements AutoCloseable { } /** - * Only a subset of the formats defined in {@link android.graphics.ImageFormat} and - * {@link android.graphics.PixelFormat} are supported by ImageReader. When reading RGB - * data from a surface, the formats defined in {@link android.graphics.PixelFormat} - * can be used, when reading YUV, JPEG or raw sensor data ( for example, from camera - * or video decoder), formats from {@link android.graphics.ImageFormat} are used. + * Only a subset of the formats defined in + * {@link android.graphics.ImageFormat ImageFormat} and + * {@link android.graphics.PixelFormat PixelFormat} are supported by + * ImageReader. When reading RGB data from a surface, the formats defined in + * {@link android.graphics.PixelFormat PixelFormat} can be used, when + * reading YUV, JPEG or raw sensor data (for example, from camera or video + * decoder), formats from {@link android.graphics.ImageFormat ImageFormat} + * are used. */ private int getNumPlanesFromFormat() { switch (mFormat) { @@ -308,7 +508,7 @@ public final class ImageReader implements AutoCloseable { */ private long mNativeContext; - private class SurfaceImage implements android.media.Image { + private class SurfaceImage extends android.media.Image { public SurfaceImage() { mIsImageValid = false; } @@ -404,7 +604,7 @@ public final class ImageReader implements AutoCloseable { mPlanes[i] = nativeCreatePlane(i); } } - private class SurfacePlane implements android.media.Image.Plane { + private class SurfacePlane extends android.media.Image.Plane { // SurfacePlane instance is created by native code when a new SurfaceImage is created private SurfacePlane(int index, int rowStride, int pixelStride) { mIndex = index; @@ -479,9 +679,17 @@ public final class ImageReader implements AutoCloseable { private synchronized native void nativeClose(); private synchronized native void nativeReleaseImage(Image i); private synchronized native Surface nativeGetSurface(); - private synchronized native boolean nativeImageSetup(Image i); - /* + /** + * @return A return code {@code ACQUIRE_*} + * + * @see #ACQUIRE_SUCCESS + * @see #ACQUIRE_NO_BUFS + * @see #ACQUIRE_MAX_IMAGES + */ + private synchronized native int nativeImageSetup(Image i); + + /** * We use a class initializer to allow the native code to cache some * field offsets. */ diff --git a/media/java/android/media/audiofx/AudioEffect.java b/media/java/android/media/audiofx/AudioEffect.java index 52c0c2d9f5fa..12f7bd9a9956 100644 --- a/media/java/android/media/audiofx/AudioEffect.java +++ b/media/java/android/media/audiofx/AudioEffect.java @@ -120,6 +120,14 @@ public class AudioEffect { .fromString("58b4b260-8e06-11e0-aa8e-0002a5d5c51b"); /** + * @hide + * CANDIDATE FOR PUBLIC API + * UUID for Loudness Enhancer + */ + public static final UUID EFFECT_TYPE_LOUDNESS_ENHANCER = UUID + .fromString("fe3199be-aed0-413f-87bb-11260eb63cf1"); + + /** * Null effect UUID. Used when the UUID for effect type of * @hide */ diff --git a/media/java/android/media/audiofx/LoudnessEnhancer.java b/media/java/android/media/audiofx/LoudnessEnhancer.java new file mode 100644 index 000000000000..f6e3e6bf5146 --- /dev/null +++ b/media/java/android/media/audiofx/LoudnessEnhancer.java @@ -0,0 +1,302 @@ +/* + * Copyright (C) 2013 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.media.audiofx; + +import android.media.AudioTrack; +import android.media.MediaPlayer; +import android.media.audiofx.AudioEffect; +import android.util.Log; + +import java.util.StringTokenizer; + + +/** + * @hide + * CANDIDATE FOR PUBLIC API + * LoudnessEnhancer is an audio effect for increasing audio loudness. + * The processing is parametrized by a target gain value, which determines the maximum amount + * by which an audio signal will be amplified; signals amplified outside of the sample + * range supported by the platform are compressed. + * An application creates a LoudnessEnhancer object to instantiate and control a + * this audio effect in the audio framework. + * To attach the LoudnessEnhancer to a particular AudioTrack or MediaPlayer, + * specify the audio session ID of this AudioTrack or MediaPlayer when constructing the effect + * (see {@link AudioTrack#getAudioSessionId()} and {@link MediaPlayer#getAudioSessionId()}). + */ + +public class LoudnessEnhancer extends AudioEffect { + + private final static String TAG = "LoudnessEnhancer"; + + // These parameter constants must be synchronized with those in + // /system/media/audio_effects/include/audio_effects/effect_loudnessenhancer.h + /** + * @hide + * CANDIDATE FOR PUBLIC API + * The maximum gain applied applied to the signal to process. + * It is expressed in millibels (100mB = 1dB) where 0mB corresponds to no amplification. + */ + public static final int PARAM_TARGET_GAIN_MB = 0; + + /** + * Registered listener for parameter changes. + */ + private OnParameterChangeListener mParamListener = null; + + /** + * Listener used internally to to receive raw parameter change events + * from AudioEffect super class + */ + private BaseParameterListener mBaseParamListener = null; + + /** + * Lock for access to mParamListener + */ + private final Object mParamListenerLock = new Object(); + + /** + * @hide + * CANDIDATE FOR PUBLIC API + * Class constructor. + * @param audioSession system-wide unique audio session identifier. The LoudnessEnhancer + * will be attached to the MediaPlayer or AudioTrack in the same audio session. + * + * @throws java.lang.IllegalStateException + * @throws java.lang.IllegalArgumentException + * @throws java.lang.UnsupportedOperationException + * @throws java.lang.RuntimeException + */ + public LoudnessEnhancer(int audioSession) + throws IllegalStateException, IllegalArgumentException, + UnsupportedOperationException, RuntimeException { + super(EFFECT_TYPE_LOUDNESS_ENHANCER, EFFECT_TYPE_NULL, 0, audioSession); + + if (audioSession == 0) { + Log.w(TAG, "WARNING: attaching a LoudnessEnhancer to global output mix is deprecated!"); + } + } + + /** + * @hide + * Class constructor. + * @param priority the priority level requested by the application for controlling the + * LoudnessEnhancer engine. As the same engine can be shared by several applications, + * this parameter indicates how much the requesting application needs control of effect + * parameters. The normal priority is 0, above normal is a positive number, below normal a + * negative number. + * @param audioSession system-wide unique audio session identifier. The LoudnessEnhancer + * will be attached to the MediaPlayer or AudioTrack in the same audio session. + * + * @throws java.lang.IllegalStateException + * @throws java.lang.IllegalArgumentException + * @throws java.lang.UnsupportedOperationException + * @throws java.lang.RuntimeException + */ + public LoudnessEnhancer(int priority, int audioSession) + throws IllegalStateException, IllegalArgumentException, + UnsupportedOperationException, RuntimeException { + super(EFFECT_TYPE_LOUDNESS_ENHANCER, EFFECT_TYPE_NULL, priority, audioSession); + + if (audioSession == 0) { + Log.w(TAG, "WARNING: attaching a LoudnessEnhancer to global output mix is deprecated!"); + } + } + + /** + * @hide + * CANDIDATE FOR PUBLIC API + * Set the target gain for the audio effect. + * The target gain is the maximum value by which a sample value will be amplified when the + * effect is enabled. + * @param gainmB the effect target gain expressed in mB. 0mB corresponds to no amplification. + * @throws IllegalStateException + * @throws IllegalArgumentException + * @throws UnsupportedOperationException + */ + public void setTargetGain(int gainmB) + throws IllegalStateException, IllegalArgumentException, UnsupportedOperationException { + checkStatus(setParameter(PARAM_TARGET_GAIN_MB, gainmB)); + } + + /** + * @hide + * CANDIDATE FOR PUBLIC API + * Return the target gain. + * @return the effect target gain expressed in mB. + * @throws IllegalStateException + * @throws IllegalArgumentException + * @throws UnsupportedOperationException + */ + public float getTargetGain() + throws IllegalStateException, IllegalArgumentException, UnsupportedOperationException { + int[] value = new int[1]; + checkStatus(getParameter(PARAM_TARGET_GAIN_MB, value)); + return value[0]; + } + + /** + * @hide + * The OnParameterChangeListener interface defines a method called by the LoudnessEnhancer + * when a parameter value has changed. + */ + public interface OnParameterChangeListener { + /** + * @hide + * CANDIDATE FOR PUBLIC API + * Method called when a parameter value has changed. The method is called only if the + * parameter was changed by another application having the control of the same + * LoudnessEnhancer engine. + * @param effect the LoudnessEnhancer on which the interface is registered. + * @param param ID of the modified parameter. See {@link #PARAM_GENERIC_PARAM1} ... + * @param value the new parameter value. + */ + void onParameterChange(LoudnessEnhancer effect, int param, int value); + } + + /** + * Listener used internally to receive unformatted parameter change events from AudioEffect + * super class. + */ + private class BaseParameterListener implements AudioEffect.OnParameterChangeListener { + private BaseParameterListener() { + + } + public void onParameterChange(AudioEffect effect, int status, byte[] param, byte[] value) { + // only notify when the parameter was successfully change + if (status != AudioEffect.SUCCESS) { + return; + } + OnParameterChangeListener l = null; + synchronized (mParamListenerLock) { + if (mParamListener != null) { + l = mParamListener; + } + } + if (l != null) { + int p = -1; + int v = Integer.MIN_VALUE; + + if (param.length == 4) { + p = byteArrayToInt(param, 0); + } + if (value.length == 4) { + v = byteArrayToInt(value, 0); + } + if (p != -1 && v != Integer.MIN_VALUE) { + l.onParameterChange(LoudnessEnhancer.this, p, v); + } + } + } + } + + /** + * @hide + * Registers an OnParameterChangeListener interface. + * @param listener OnParameterChangeListener interface registered + */ + public void setParameterListener(OnParameterChangeListener listener) { + synchronized (mParamListenerLock) { + if (mParamListener == null) { + mBaseParamListener = new BaseParameterListener(); + super.setParameterListener(mBaseParamListener); + } + mParamListener = listener; + } + } + + /** + * @hide + * The Settings class regroups the LoudnessEnhancer parameters. It is used in + * conjunction with the getProperties() and setProperties() methods to backup and restore + * all parameters in a single call. + */ + public static class Settings { + public int targetGainmB; + + public Settings() { + } + + /** + * Settings class constructor from a key=value; pairs formatted string. The string is + * typically returned by Settings.toString() method. + * @throws IllegalArgumentException if the string is not correctly formatted. + */ + public Settings(String settings) { + StringTokenizer st = new StringTokenizer(settings, "=;"); + //int tokens = st.countTokens(); + if (st.countTokens() != 3) { + throw new IllegalArgumentException("settings: " + settings); + } + String key = st.nextToken(); + if (!key.equals("LoudnessEnhancer")) { + throw new IllegalArgumentException( + "invalid settings for LoudnessEnhancer: " + key); + } + try { + key = st.nextToken(); + if (!key.equals("targetGainmB")) { + throw new IllegalArgumentException("invalid key name: " + key); + } + targetGainmB = Integer.parseInt(st.nextToken()); + } catch (NumberFormatException nfe) { + throw new IllegalArgumentException("invalid value for key: " + key); + } + } + + @Override + public String toString() { + String str = new String ( + "LoudnessEnhancer"+ + ";targetGainmB="+Integer.toString(targetGainmB) + ); + return str; + } + }; + + + /** + * @hide + * Gets the LoudnessEnhancer properties. This method is useful when a snapshot of current + * effect settings must be saved by the application. + * @return a LoudnessEnhancer.Settings object containing all current parameters values + * @throws IllegalStateException + * @throws IllegalArgumentException + * @throws UnsupportedOperationException + */ + public LoudnessEnhancer.Settings getProperties() + throws IllegalStateException, IllegalArgumentException, UnsupportedOperationException { + Settings settings = new Settings(); + int[] value = new int[1]; + checkStatus(getParameter(PARAM_TARGET_GAIN_MB, value)); + settings.targetGainmB = value[0]; + return settings; + } + + /** + * @hide + * Sets the LoudnessEnhancer properties. This method is useful when bass boost settings + * have to be applied from a previous backup. + * @param settings a LoudnessEnhancer.Settings object containing the properties to apply + * @throws IllegalStateException + * @throws IllegalArgumentException + * @throws UnsupportedOperationException + */ + public void setProperties(LoudnessEnhancer.Settings settings) + throws IllegalStateException, IllegalArgumentException, UnsupportedOperationException { + checkStatus(setParameter(PARAM_TARGET_GAIN_MB, settings.targetGainmB)); + } +} diff --git a/media/jni/android_media_ImageReader.cpp b/media/jni/android_media_ImageReader.cpp index 94f20bcb3171..a03dbf325e50 100644 --- a/media/jni/android_media_ImageReader.cpp +++ b/media/jni/android_media_ImageReader.cpp @@ -43,13 +43,16 @@ using namespace android; -static const char* const OutOfResourcesException = - "android/view/Surface$OutOfResourcesException"; - enum { IMAGE_READER_MAX_NUM_PLANES = 3, }; +enum { + ACQUIRE_SUCCESS = 0, + ACQUIRE_NO_BUFFERS = 1, + ACQUIRE_MAX_IMAGES = 2, +}; + static struct { jfieldID mNativeContext; jmethodID postEventFromNative; @@ -685,14 +688,14 @@ static void ImageReader_imageRelease(JNIEnv* env, jobject thiz, jobject image) ctx->returnLockedBuffer(buffer); } -static jboolean ImageReader_imageSetup(JNIEnv* env, jobject thiz, +static jint ImageReader_imageSetup(JNIEnv* env, jobject thiz, jobject image) { ALOGV("%s:", __FUNCTION__); JNIImageReaderContext* ctx = ImageReader_getContext(env, thiz); if (ctx == NULL) { jniThrowRuntimeException(env, "ImageReaderContext is not initialized"); - return false; + return -1; } CpuConsumer* consumer = ctx->getCpuConsumer(); @@ -700,27 +703,22 @@ static jboolean ImageReader_imageSetup(JNIEnv* env, jobject thiz, if (buffer == NULL) { ALOGW("Unable to acquire a lockedBuffer, very likely client tries to lock more than" " maxImages buffers"); - jniThrowException(env, OutOfResourcesException, - "Too many outstanding images, close existing images" - " to be able to acquire more."); - return false; + return ACQUIRE_MAX_IMAGES; } status_t res = consumer->lockNextBuffer(buffer); if (res != NO_ERROR) { if (res != BAD_VALUE /*no buffers*/) { if (res == NOT_ENOUGH_DATA) { - jniThrowException(env, OutOfResourcesException, - "Too many outstanding images, close existing images" - " to be able to acquire more."); + return ACQUIRE_MAX_IMAGES; } else { ALOGE("%s Fail to lockNextBuffer with error: %d ", __FUNCTION__, res); - jniThrowExceptionFmt(env, "java/lang/IllegalStateException", + jniThrowExceptionFmt(env, "java/lang/AssertionError", "Unknown error (%d) when we tried to lock buffer.", res); } } - return false; + return ACQUIRE_NO_BUFFERS; } // Check if the left-top corner of the crop rect is origin, we currently assume this point is @@ -730,7 +728,7 @@ static jboolean ImageReader_imageSetup(JNIEnv* env, jobject thiz, ALOGE("crop left: %d, top = %d", lt.x, lt.y); jniThrowException(env, "java/lang/UnsupportedOperationException", "crop left top corner need to at origin"); - return false; + return -1; } // Check if the producer buffer configurations match what ImageReader configured. @@ -739,11 +737,9 @@ static jboolean ImageReader_imageSetup(JNIEnv* env, jobject thiz, int outputHeight = buffer->height; // Correct width/height when crop is set. - if (buffer->crop.getWidth() > 0) { - outputWidth = buffer->crop.getWidth() + 1; - } - if (buffer->crop.getHeight() > 0) { - outputHeight = buffer->crop.getHeight() + 1; + if (!buffer->crop.isEmpty()) { + outputWidth = buffer->crop.getWidth(); + outputHeight = buffer->crop.getHeight(); } int imageReaderWidth = ctx->getBufferWidth(); @@ -761,6 +757,7 @@ static jboolean ImageReader_imageSetup(JNIEnv* env, jobject thiz, jniThrowExceptionFmt(env, "java/lang/IllegalStateException", "Producer buffer size: %dx%d, doesn't match ImageReader configured size: %dx%d", outputWidth, outputHeight, imageReaderWidth, imageReaderHeight); + return -1; } if (ctx->getBufferFormat() != buffer->format) { @@ -777,14 +774,14 @@ static jboolean ImageReader_imageSetup(JNIEnv* env, jobject thiz, buffer->format, ctx->getBufferFormat()); jniThrowException(env, "java/lang/UnsupportedOperationException", msg.string()); - return false; + return -1; } // Set SurfaceImage instance member variables Image_setBuffer(env, image, buffer); env->SetLongField(image, gSurfaceImageClassInfo.mTimestamp, static_cast<jlong>(buffer->timestamp)); - return true; + return ACQUIRE_SUCCESS; } static jobject ImageReader_getSurface(JNIEnv* env, jobject thiz) @@ -855,7 +852,7 @@ static JNINativeMethod gImageReaderMethods[] = { {"nativeInit", "(Ljava/lang/Object;IIII)V", (void*)ImageReader_init }, {"nativeClose", "()V", (void*)ImageReader_close }, {"nativeReleaseImage", "(Landroid/media/Image;)V", (void*)ImageReader_imageRelease }, - {"nativeImageSetup", "(Landroid/media/Image;)Z", (void*)ImageReader_imageSetup }, + {"nativeImageSetup", "(Landroid/media/Image;)I", (void*)ImageReader_imageSetup }, {"nativeGetSurface", "()Landroid/view/Surface;", (void*)ImageReader_getSurface }, }; diff --git a/media/tests/MediaFrameworkTest/src/com/android/mediaframeworktest/MediaFrameworkUnitTestRunner.java b/media/tests/MediaFrameworkTest/src/com/android/mediaframeworktest/MediaFrameworkUnitTestRunner.java index ecdc287e79ea..64b12b7648ce 100644 --- a/media/tests/MediaFrameworkTest/src/com/android/mediaframeworktest/MediaFrameworkUnitTestRunner.java +++ b/media/tests/MediaFrameworkTest/src/com/android/mediaframeworktest/MediaFrameworkUnitTestRunner.java @@ -49,6 +49,7 @@ public class MediaFrameworkUnitTestRunner extends InstrumentationTestRunner { addMediaPlayerStateUnitTests(suite); addMediaScannerUnitTests(suite); addCameraUnitTests(suite); + addImageReaderTests(suite); return suite; } @@ -65,6 +66,10 @@ public class MediaFrameworkUnitTestRunner extends InstrumentationTestRunner { suite.addTestSuite(CameraMetadataTest.class); } + private void addImageReaderTests(TestSuite suite) { + suite.addTestSuite(ImageReaderTest.class); + } + // Running all unit tests checking the state machine may be time-consuming. private void addMediaMetadataRetrieverStateUnitTests(TestSuite suite) { suite.addTestSuite(MediaMetadataRetrieverTest.class); 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 2d26ac7f6c18..7b2a20ea4dbd 100644 --- a/media/tests/MediaFrameworkTest/src/com/android/mediaframeworktest/performance/MediaPlayerPerformance.java +++ b/media/tests/MediaFrameworkTest/src/com/android/mediaframeworktest/performance/MediaPlayerPerformance.java @@ -61,6 +61,7 @@ public class MediaPlayerPerformance extends ActivityInstrumentationTestCase2<Med private SurfaceHolder mSurfaceHolder = null; private static final int NUM_STRESS_LOOP = 10; private static final int NUM_PLAYBACk_IN_EACH_LOOP = 20; + private static final int SHORT_WAIT = 2 * 1000; // 2 seconds private static final long MEDIA_STRESS_WAIT_TIME = 5000; //5 seconds private static final String MEDIA_MEMORY_OUTPUT = "/sdcard/mediaMemOutput.txt"; @@ -99,16 +100,17 @@ public class MediaPlayerPerformance extends ActivityInstrumentationTestCase2<Med @Override protected void setUp() throws Exception { super.setUp(); + //Insert a 2 second before launching the test activity. This is + //the workaround for the race condition of requesting the updated surface. + Thread.sleep(SHORT_WAIT); + getActivity(); //Check if the device support the camcorder - CamcorderProfile mCamcorderProfile = CamcorderProfile.get(CAMERA_ID); + mCamcorderProfile = CamcorderProfile.get(CAMERA_ID); if (mCamcorderProfile != null) { mVideoWidth = mCamcorderProfile.videoFrameWidth; mVideoHeight = mCamcorderProfile.videoFrameHeight; + Log.v(TAG, "height = " + mVideoHeight + " width= " + mVideoWidth); } - //Insert a 2 second before launching the test activity. This is - //the workaround for the race condition of requesting the updated surface. - Thread.sleep(2000); - getActivity(); if (MediaFrameworkPerfTestRunner.mGetNativeHeapDump) MediaTestUtil.getNativeHeapDump(this.getName() + "_before"); @@ -246,6 +248,8 @@ public class MediaPlayerPerformance extends ActivityInstrumentationTestCase2<Med Thread.sleep(MEDIA_STRESS_WAIT_TIME); mRecorder.stop(); mRecorder.release(); + //Insert 2 seconds to make sure the camera released. + Thread.sleep(SHORT_WAIT); } catch (Exception e) { Log.v("record video failed ", e.toString()); mRecorder.release(); diff --git a/media/tests/MediaFrameworkTest/src/com/android/mediaframeworktest/unit/ImageReaderTest.java b/media/tests/MediaFrameworkTest/src/com/android/mediaframeworktest/unit/ImageReaderTest.java new file mode 100644 index 000000000000..f6cd9901cd39 --- /dev/null +++ b/media/tests/MediaFrameworkTest/src/com/android/mediaframeworktest/unit/ImageReaderTest.java @@ -0,0 +1,181 @@ +/* + * Copyright (C) 2013 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.mediaframeworktest.unit; + +import static org.mockito.Mockito.*; + +import android.graphics.ImageFormat; +import android.media.Image; +import android.media.Image.Plane; +import android.media.ImageReader; +import android.media.ImageReader.OnImageAvailableListener; +import android.test.AndroidTestCase; +import android.test.suitebuilder.annotation.SmallTest; + +public class ImageReaderTest extends AndroidTestCase { + + private static final String TAG = "ImageReaderTest-unit"; + + private static final int DEFAULT_WIDTH = 640; + private static final int DEFAULT_HEIGHT = 480; + private static final int DEFAULT_FORMAT = ImageFormat.YUV_420_888; + private static final int DEFAULT_MAX_IMAGES = 3; + + private ImageReader mReader; + private Image mImage1; + private Image mImage2; + private Image mImage3; + + @Override + protected void setUp() throws Exception { + super.setUp(); + + /** + * Workaround for mockito and JB-MR2 incompatibility + * + * Avoid java.lang.IllegalArgumentException: dexcache == null + * https://code.google.com/p/dexmaker/issues/detail?id=2 + */ + System.setProperty("dexmaker.dexcache", getContext().getCacheDir().toString()); + + // TODO: refactor above into one of the test runners + + mReader = spy(ImageReader.newInstance(DEFAULT_WIDTH, DEFAULT_HEIGHT, DEFAULT_FORMAT, + DEFAULT_MAX_IMAGES)); + mImage1 = mock(Image.class); + mImage2 = mock(Image.class); + mImage3 = mock(Image.class); + + /** + * Ensure rest of classes are mockable + */ + { + mock(Plane.class); + mock(OnImageAvailableListener.class); + } + + } + + @Override + protected void tearDown() throws Exception { + mReader.close(); + + super.tearDown(); + } + + /** + * Return null when there is nothing in the image queue. + */ + @SmallTest + public void testGetLatestImageEmpty() { + when(mReader.acquireNextImage()).thenReturn(null); + when(mReader.acquireNextImageNoThrowISE()).thenReturn(null); + assertEquals(null, mReader.acquireLatestImage()); + } + + /** + * Return the last image from the image queue, close up the rest. + */ + @SmallTest + public void testGetLatestImage1() { + when(mReader.acquireNextImage()).thenReturn(mImage1); + when(mReader.acquireNextImageNoThrowISE()).thenReturn(null); + assertEquals(mImage1, mReader.acquireLatestImage()); + verify(mImage1, never()).close(); + } + + /** + * Return the last image from the image queue, close up the rest. + */ + @SmallTest + public void testGetLatestImage2() { + when(mReader.acquireNextImage()).thenReturn(mImage1); + when(mReader.acquireNextImageNoThrowISE()).thenReturn(mImage2).thenReturn(null); + assertEquals(mImage2, mReader.acquireLatestImage()); + verify(mImage1, atLeastOnce()).close(); + verify(mImage2, never()).close(); + } + + /** + * Return the last image from the image queue, close up the rest. + */ + @SmallTest + public void testGetLatestImage3() { + when(mReader.acquireNextImage()).thenReturn(mImage1); + when(mReader.acquireNextImageNoThrowISE()).thenReturn(mImage2). + thenReturn(mImage3). + thenReturn(null); + assertEquals(mImage3, mReader.acquireLatestImage()); + verify(mImage1, atLeastOnce()).close(); + verify(mImage2, atLeastOnce()).close(); + verify(mImage3, never()).close(); + } + + /** + * Return null if get a IllegalStateException with no images in the queue. + */ + @SmallTest + public void testGetLatestImageTooManyBuffersAcquiredEmpty() { + when(mReader.acquireNextImage()).thenThrow(new IllegalStateException()); + try { + mReader.acquireLatestImage(); + fail("Expected IllegalStateException to be thrown"); + } catch(IllegalStateException e) { + } + } + + /** + * All images are cleaned up when we get an unexpected Error. + */ + @SmallTest + public void testGetLatestImageExceptionalError() { + when(mReader.acquireNextImage()).thenReturn(mImage1); + when(mReader.acquireNextImageNoThrowISE()).thenReturn(mImage2). + thenReturn(mImage3). + thenThrow(new OutOfMemoryError()); + try { + mReader.acquireLatestImage(); + fail("Impossible"); + } catch(OutOfMemoryError e) { + } + + verify(mImage1, atLeastOnce()).close(); + verify(mImage2, atLeastOnce()).close(); + verify(mImage3, atLeastOnce()).close(); + } + + /** + * All images are cleaned up when we get an unexpected RuntimeException. + */ + @SmallTest + public void testGetLatestImageExceptionalRuntime() { + + when(mReader.acquireNextImage()).thenReturn(mImage1); + when(mReader.acquireNextImageNoThrowISE()).thenReturn(mImage2). + thenReturn(mImage3). + thenThrow(new RuntimeException()); + try { + mReader.acquireLatestImage(); + fail("Impossible"); + } catch(RuntimeException e) { + } + + verify(mImage1, atLeastOnce()).close(); + verify(mImage2, atLeastOnce()).close(); + verify(mImage3, atLeastOnce()).close(); + } +} diff --git a/media/tests/SoundPoolTest/AndroidManifest.xml b/media/tests/SoundPoolTest/AndroidManifest.xml index 126276c04d5e..8a29052ba50b 100644 --- a/media/tests/SoundPoolTest/AndroidManifest.xml +++ b/media/tests/SoundPoolTest/AndroidManifest.xml @@ -8,4 +8,5 @@ package="com.android.soundpooltest"> </intent-filter> </activity> </application> + <uses-sdk android:minSdkVersion="8" android:targetSdkVersion="8"/> </manifest> diff --git a/media/tests/SoundPoolTest/src/com/android/SoundPoolTest.java b/media/tests/SoundPoolTest/src/com/android/SoundPoolTest.java index 33db2ddb900f..cc3306c79d3f 100644 --- a/media/tests/SoundPoolTest/src/com/android/SoundPoolTest.java +++ b/media/tests/SoundPoolTest/src/com/android/SoundPoolTest.java @@ -143,7 +143,7 @@ public class SoundPoolTest extends Activity if (DEBUG) Log.d(LOG_TAG, "Stop note " + id); sleep(50); } - if (DEBUG) Log.d(LOG_TAG, "End scale test"); + if (DEBUG) Log.d(LOG_TAG, "End sounds test"); return true; } @@ -165,7 +165,7 @@ public class SoundPoolTest extends Activity if (DEBUG) Log.d(LOG_TAG, "Stop note " + id); sleep(50); } - if (DEBUG) Log.d(LOG_TAG, "End sounds test"); + if (DEBUG) Log.d(LOG_TAG, "End scale test"); return true; } @@ -189,6 +189,7 @@ public class SoundPoolTest extends Activity if (DEBUG) Log.d(LOG_TAG, "Change rate " + mScale[step]); } mSoundPool.stop(id); + if (DEBUG) Log.d(LOG_TAG, "Stop note " + id); if (DEBUG) Log.d(LOG_TAG, "End rate test"); return true; } @@ -205,34 +206,38 @@ public class SoundPoolTest extends Activity Log.e(LOG_TAG, "Error occurred starting note"); return false; } - sleep(250); + sleep(1000); // play a low priority sound - int id = mSoundPool.play(mSounds[0], DEFAULT_VOLUME, DEFAULT_VOLUME, + int id = mSoundPool.play(mSounds[1], DEFAULT_VOLUME, DEFAULT_VOLUME, LOW_PRIORITY, DEFAULT_LOOP, 1.0f); - if (id > 0) { + if (id != 0) { Log.e(LOG_TAG, "Normal > Low priority test failed"); result = false; mSoundPool.stop(id); } else { - Log.e(LOG_TAG, "Normal > Low priority test passed"); + sleep(1000); + Log.i(LOG_TAG, "Normal > Low priority test passed"); } - sleep(250); // play a high priority sound - id = mSoundPool.play(mSounds[0], DEFAULT_VOLUME, DEFAULT_VOLUME, + id = mSoundPool.play(mSounds[2], DEFAULT_VOLUME, DEFAULT_VOLUME, HIGH_PRIORITY, DEFAULT_LOOP, 1.0f); if (id == 0) { Log.e(LOG_TAG, "High > Normal priority test failed"); result = false; } else { - Log.e(LOG_TAG, "High > Normal priority test passed"); + sleep(1000); + Log.i(LOG_TAG, "Stopping high priority"); + mSoundPool.stop(id); + sleep(1000); + Log.i(LOG_TAG, "High > Normal priority test passed"); } - sleep(250); - mSoundPool.stop(id); // stop normal note + Log.i(LOG_TAG, "Stopping normal priority"); mSoundPool.stop(normalId); + sleep(1000); if (DEBUG) Log.d(LOG_TAG, "End priority test"); return result; @@ -250,17 +255,21 @@ public class SoundPoolTest extends Activity Log.e(LOG_TAG, "Error occurred starting note"); return false; } - sleep(250); + sleep(2500); // pause and resume sound a few times for (int count = 0; count < 5; count++) { + if (DEBUG) Log.d(LOG_TAG, "Pause note " + id); mSoundPool.pause(id); - sleep(250); + sleep(1000); + if (DEBUG) Log.d(LOG_TAG, "Resume note " + id); mSoundPool.resume(id); - sleep(250); + sleep(1000); } + if (DEBUG) Log.d(LOG_TAG, "Stop note " + id); mSoundPool.stop(id); + sleep(1000); // play 5 sounds, forces one to be stolen int ids[] = new int[5]; @@ -272,18 +281,21 @@ public class SoundPoolTest extends Activity Log.e(LOG_TAG, "Error occurred starting note"); return false; } - sleep(250); + sleep(1000); } // pause and resume sound a few times for (int count = 0; count < 5; count++) { + if (DEBUG) Log.d(LOG_TAG, "autoPause"); mSoundPool.autoPause(); - sleep(250); + sleep(1000); + if (DEBUG) Log.d(LOG_TAG, "autoResume"); mSoundPool.autoResume(); - sleep(250); + sleep(1000); } for (int i = 0; i < 5; i++) { + if (DEBUG) Log.d(LOG_TAG, "Stop note " + ids[i]); mSoundPool.stop(ids[i]); } @@ -302,9 +314,9 @@ public class SoundPoolTest extends Activity return false; } - // pan from left to right + // pan from right to left for (int count = 0; count < 101; count++) { - sleep(20); + sleep(50); double radians = PI_OVER_2 * count / 100.0; float leftVolume = (float) Math.sin(radians); float rightVolume = (float) Math.cos(radians); diff --git a/media/tests/audiotests/Android.mk b/media/tests/audiotests/Android.mk new file mode 100644 index 000000000000..69f0bb5d185b --- /dev/null +++ b/media/tests/audiotests/Android.mk @@ -0,0 +1,21 @@ +ifeq ($(TARGET_ARCH),arm) + +LOCAL_PATH:= $(call my-dir) + +include $(CLEAR_VARS) + +LOCAL_MODULE:= shared_mem_test +LOCAL_SRC_FILES := \ + shared_mem_test.cpp +LOCAL_SHARED_LIBRARIES := \ + libc \ + libcutils \ + libutils \ + libbinder \ + libhardware_legacy \ + libmedia +LOCAL_MODULE_TAGS := tests + +include $(BUILD_EXECUTABLE) + +endif diff --git a/media/tests/audiotests/shared_mem_test.cpp b/media/tests/audiotests/shared_mem_test.cpp new file mode 100644 index 000000000000..992c900d31a2 --- /dev/null +++ b/media/tests/audiotests/shared_mem_test.cpp @@ -0,0 +1,216 @@ +// Copyright 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. + +// +#define LOG_NDEBUG 0 +#define LOG_TAG "shared_mem_test" + +#include <stdlib.h> +#include <stdio.h> +#include <cutils/properties.h> +#include <media/AudioSystem.h> +#include <media/AudioTrack.h> +#include <math.h> + +#include "shared_mem_test.h" +#include <binder/MemoryDealer.h> +#include <binder/MemoryHeapBase.h> +#include <binder/MemoryBase.h> +#include <binder/ProcessState.h> + + +#include <utils/Log.h> + +#include <fcntl.h> + +namespace android { + +/************************************************************ +* +* Constructor +* +************************************************************/ +AudioTrackTest::AudioTrackTest(void) { + + InitSine(); // init sine table + +} + + +/************************************************************ +* +* +************************************************************/ +void AudioTrackTest::Execute(void) { + if (Test01() == 0) { + ALOGD("01 passed\n"); + } else { + ALOGD("01 failed\n"); + } +} + +/************************************************************ +* +* Shared memory test +* +************************************************************/ +#define BUF_SZ 44100 + +int AudioTrackTest::Test01() { + + sp<MemoryDealer> heap; + sp<IMemory> iMem; + uint8_t* p; + + short smpBuf[BUF_SZ]; + long rate = 44100; + unsigned long phi; + unsigned long dPhi; + long amplitude; + long freq = 1237; + float f0; + + f0 = pow(2., 32.) * freq / (float)rate; + dPhi = (unsigned long)f0; + amplitude = 1000; + phi = 0; + Generate(smpBuf, BUF_SZ, amplitude, phi, dPhi); // fill buffer + + for (int i = 0; i < 1024; i++) { + heap = new MemoryDealer(1024*1024, "AudioTrack Heap Base"); + + iMem = heap->allocate(BUF_SZ*sizeof(short)); + + p = static_cast<uint8_t*>(iMem->pointer()); + memcpy(p, smpBuf, BUF_SZ*sizeof(short)); + + sp<AudioTrack> track = new AudioTrack(AUDIO_STREAM_MUSIC,// stream type + rate, + AUDIO_FORMAT_PCM_16_BIT,// word length, PCM + AUDIO_CHANNEL_OUT_MONO, + iMem); + + status_t status = track->initCheck(); + if(status != NO_ERROR) { + track.clear(); + ALOGD("Failed for initCheck()"); + return -1; + } + + // start play + ALOGD("start"); + track->start(); + + usleep(20000); + + ALOGD("stop"); + track->stop(); + iMem.clear(); + heap.clear(); + usleep(20000); + } + + return 0; + +} + +/************************************************************ +* +* Generate a mono buffer +* Error is less than 3lsb +* +************************************************************/ +void AudioTrackTest::Generate(short *buffer, long bufferSz, long amplitude, unsigned long &phi, long dPhi) +{ + long pi13 = 25736; // 2^13*pi + // fill buffer + for(int i0=0; i0<bufferSz; i0++) { + long sample; + long l0, l1; + + buffer[i0] = ComputeSine( amplitude, phi); + phi += dPhi; + } +} + +/************************************************************ +* +* Generate a sine +* Error is less than 3lsb +* +************************************************************/ +short AudioTrackTest::ComputeSine(long amplitude, long phi) +{ + long pi13 = 25736; // 2^13*pi + long sample; + long l0, l1; + + sample = (amplitude*sin1024[(phi>>22) & 0x3ff]) >> 15; + // correct with interpolation + l0 = (phi>>12) & 0x3ff; // 2^20 * x / (2*pi) + l1 = (amplitude*sin1024[((phi>>22) + 256) & 0x3ff]) >> 15; // 2^15*cosine + l0 = (l0 * l1) >> 10; + l0 = (l0 * pi13) >> 22; + sample = sample + l0; + + return (short)sample; +} + + +/************************************************************ +* +* init sine table +* +************************************************************/ +void AudioTrackTest::InitSine(void) { + double phi = 0; + double dPhi = 2 * M_PI / SIN_SZ; + for(int i0 = 0; i0<SIN_SZ; i0++) { + long d0; + + d0 = 32768. * sin(phi); + phi += dPhi; + if(d0 >= 32767) d0 = 32767; + if(d0 <= -32768) d0 = -32768; + sin1024[i0] = (short)d0; + } +} + +/************************************************************ +* +* main in name space +* +************************************************************/ +int main() { + ProcessState::self()->startThreadPool(); + AudioTrackTest *test; + + test = new AudioTrackTest(); + test->Execute(); + delete test; + + return 0; +} + +} + +/************************************************************ +* +* global main +* +************************************************************/ +int main(int argc, char *argv[]) { + + return android::main(); +} diff --git a/media/tests/audiotests/shared_mem_test.h b/media/tests/audiotests/shared_mem_test.h new file mode 100644 index 000000000000..f4959557b0b5 --- /dev/null +++ b/media/tests/audiotests/shared_mem_test.h @@ -0,0 +1,27 @@ +// Copyright 2008 The Android Open Source Project + +#ifndef AUDIOTRACKTEST_H_ +#define AUDIOTRACKTEST_H_ + +namespace android { + +class AudioTrackTest{ + public: + AudioTrackTest(void); + ~AudioTrackTest() {}; + + void Execute(void); + int Test01(); + + void Generate(short *buffer, long bufferSz, long amplitude, unsigned long &phi, long dPhi); + void InitSine(); + short ComputeSine(long amplitude, long phi); + + #define SIN_SZ 1024 + short sin1024[SIN_SZ]; // sine table 2*pi = 1024 +}; + +}; + + +#endif /*AUDIOTRACKTEST_H_*/ diff --git a/packages/SystemUI/AndroidManifest.xml b/packages/SystemUI/AndroidManifest.xml index 5e198a2f1639..fa5c76907767 100644 --- a/packages/SystemUI/AndroidManifest.xml +++ b/packages/SystemUI/AndroidManifest.xml @@ -189,6 +189,35 @@ android:taskAffinity="com.android.systemui.net" android:excludeFromRecents="true" /> + <!-- platform logo easter egg activity --> + <activity + android:name=".DessertCase" + android:exported="true" + android:label="@string/dessert_case" + android:theme="@android:style/Theme.Wallpaper.NoTitleBar.Fullscreen" + android:hardwareAccelerated="true" + android:launchMode="singleInstance" + android:excludeFromRecents="true"> + <intent-filter> + <action android:name="android.intent.action.MAIN" /> + <category android:name="android.intent.category.DEFAULT" /> + <category android:name="com.android.internal.category.PLATLOGO" /> + </intent-filter> + </activity> + + <!-- a gallery of delicious treats --> + <service + android:name=".DessertCaseDream" + android:exported="true" + android:label="@string/dessert_case" + android:enabled="false" + > + <intent-filter> + <action android:name="android.service.dreams.DreamService" /> + <category android:name="android.intent.category.DEFAULT" /> + </intent-filter> + </service> + <activity android:name=".Somnambulator" android:label="@string/start_dreams" android:icon="@mipmap/ic_launcher_dreams" diff --git a/packages/SystemUI/res/values/strings.xml b/packages/SystemUI/res/values/strings.xml index bbfe383d5c52..7fdd30847dda 100644 --- a/packages/SystemUI/res/values/strings.xml +++ b/packages/SystemUI/res/values/strings.xml @@ -431,8 +431,8 @@ <!-- Description of the button in the phone-style notification panel that controls auto-rotation, when auto-rotation is off. [CHAR LIMIT=NONE] --> <string name="accessibility_rotation_lock_on_portrait">Screen is locked in portrait orientation.</string> - <!-- Name of the Jelly Bean platlogo screensaver --> - <string name="jelly_bean_dream_name">BeanFlinger</string> + <!-- Name of the K-release easter egg: a display case for all our tastiest desserts. [CHAR LIMIT=30] --> + <string name="dessert_case">Dessert Case</string> <!-- Name of the launcher shortcut icon that allows dreams to be started immediately [CHAR LIMIT=20] --> <string name="start_dreams">Daydream</string> diff --git a/packages/SystemUI/src/com/android/systemui/DessertCase.java b/packages/SystemUI/src/com/android/systemui/DessertCase.java new file mode 100644 index 000000000000..b6424af03f77 --- /dev/null +++ b/packages/SystemUI/src/com/android/systemui/DessertCase.java @@ -0,0 +1,37 @@ +/* + * Copyright (C) 2013 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; + +import android.app.Activity; +import android.content.ComponentName; +import android.content.pm.PackageManager; +import android.util.Slog; + +public class DessertCase extends Activity { + + @Override + public void onStart() { + super.onStart(); + + Slog.v("DessertCase", "ACHIEVEMENT UNLOCKED"); + PackageManager pm = getPackageManager(); + pm.setComponentEnabledSetting(new ComponentName(this, DessertCaseDream.class), + PackageManager.COMPONENT_ENABLED_STATE_ENABLED, 0); + + finish(); + } +} diff --git a/packages/SystemUI/src/com/android/systemui/DessertCaseDream.java b/packages/SystemUI/src/com/android/systemui/DessertCaseDream.java new file mode 100644 index 000000000000..022e4d83a194 --- /dev/null +++ b/packages/SystemUI/src/com/android/systemui/DessertCaseDream.java @@ -0,0 +1,39 @@ +/* + * Copyright (C) 2013 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; + +import android.service.dreams.DreamService; + +public class DessertCaseDream extends DreamService { + + @Override + public void onAttachedToWindow() { + super.onAttachedToWindow(); + setInteractive(true); + setFullscreen(true); + } + + @Override + public void onDreamingStarted() { + super.onDreamingStarted(); + } + + @Override + public void onDreamingStopped() { + super.onDreamingStopped(); + } +} diff --git a/packages/SystemUI/src/com/android/systemui/recent/RecentsPanelView.java b/packages/SystemUI/src/com/android/systemui/recent/RecentsPanelView.java index ada30acc68cf..5ebd11e03160 100644 --- a/packages/SystemUI/src/com/android/systemui/recent/RecentsPanelView.java +++ b/packages/SystemUI/src/com/android/systemui/recent/RecentsPanelView.java @@ -23,6 +23,7 @@ import android.app.ActivityManager; import android.app.ActivityManagerNative; import android.app.ActivityOptions; import android.app.TaskStackBuilder; +import android.content.ActivityNotFoundException; import android.content.Context; import android.content.Intent; import android.content.res.Configuration; @@ -698,6 +699,8 @@ public class RecentsPanelView extends FrameLayout implements OnItemClickListener new UserHandle(UserHandle.USER_CURRENT)); } catch (SecurityException e) { Log.e(TAG, "Recents does not have the permission to launch " + intent, e); + } catch (ActivityNotFoundException e) { + Log.e(TAG, "Error launching activity " + intent, e); } } if (usingDrawingCache) { diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/phone/NavigationBarView.java b/packages/SystemUI/src/com/android/systemui/statusbar/phone/NavigationBarView.java index 46830308a80e..b56d7be8ac32 100644 --- a/packages/SystemUI/src/com/android/systemui/statusbar/phone/NavigationBarView.java +++ b/packages/SystemUI/src/com/android/systemui/statusbar/phone/NavigationBarView.java @@ -424,7 +424,7 @@ public class NavigationBarView extends LinearLayout { mCurrentView.getWidth(), mCurrentView.getHeight(), visibilityToString(mCurrentView.getVisibility()))); - pw.println(String.format(" disabled=0x%08x vertical=%s hidden=%s low=%s menu=%s", + pw.println(String.format(" disabled=0x%08x vertical=%s menu=%s", mDisabledFlags, mVertical ? "true" : "false", mShowMenu ? "true" : "false")); diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/phone/PhoneStatusBar.java b/packages/SystemUI/src/com/android/systemui/statusbar/phone/PhoneStatusBar.java index a3e6e3830ee3..d15626b59c32 100644 --- a/packages/SystemUI/src/com/android/systemui/statusbar/phone/PhoneStatusBar.java +++ b/packages/SystemUI/src/com/android/systemui/statusbar/phone/PhoneStatusBar.java @@ -1378,6 +1378,14 @@ public class PhoneStatusBar extends BaseStatusBar implements DemoMode { setInteracting(StatusBarManager.WINDOW_STATUS_BAR, true); } + private void releaseFocus() { + WindowManager.LayoutParams lp = + (WindowManager.LayoutParams) mStatusBarWindow.getLayoutParams(); + lp.flags |= WindowManager.LayoutParams.FLAG_NOT_FOCUSABLE; + lp.flags &= ~WindowManager.LayoutParams.FLAG_ALT_FOCUSABLE_IM; + mWindowManager.updateViewLayout(mStatusBarWindow, lp); + } + public void animateCollapsePanels() { animateCollapsePanels(CommandQueue.FLAG_EXCLUDE_NONE); } @@ -1389,6 +1397,9 @@ public class PhoneStatusBar extends BaseStatusBar implements DemoMode { + " flags=" + flags); } + // release focus immediately to kick off focus change transition + releaseFocus(); + if ((flags & CommandQueue.FLAG_EXCLUDE_RECENTS_PANEL) == 0) { mHandler.removeMessages(MSG_CLOSE_RECENTS_PANEL); mHandler.sendEmptyMessage(MSG_CLOSE_RECENTS_PANEL); diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/phone/QuickSettings.java b/packages/SystemUI/src/com/android/systemui/statusbar/phone/QuickSettings.java index ceed30ef588e..25ffbd4db698 100644 --- a/packages/SystemUI/src/com/android/systemui/statusbar/phone/QuickSettings.java +++ b/packages/SystemUI/src/com/android/systemui/statusbar/phone/QuickSettings.java @@ -671,7 +671,7 @@ class QuickSettings { alarmTile.setOnClickListener(new View.OnClickListener() { @Override public void onClick(View v) { - startSettingsActivity(AlarmClock.ACTION_SET_ALARM); + startSettingsActivity(AlarmClock.ACTION_SHOW_ALARMS); } }); mModel.addAlarmTile(alarmTile, new QuickSettingsModel.RefreshCallback() { diff --git a/policy/src/com/android/internal/policy/impl/PhoneWindowManager.java b/policy/src/com/android/internal/policy/impl/PhoneWindowManager.java index 8a285e30e7f9..0782cfbe9f7a 100644 --- a/policy/src/com/android/internal/policy/impl/PhoneWindowManager.java +++ b/policy/src/com/android/internal/policy/impl/PhoneWindowManager.java @@ -34,7 +34,6 @@ import android.content.ServiceConnection; import android.content.pm.ActivityInfo; import android.content.pm.PackageManager; import android.content.pm.ResolveInfo; -import android.content.pm.UserInfo; import android.content.res.CompatibilityInfo; import android.content.res.Configuration; import android.content.res.Resources; @@ -105,7 +104,6 @@ import java.io.File; import java.io.FileReader; import java.io.IOException; import java.io.PrintWriter; -import java.util.HashSet; import static android.view.WindowManager.LayoutParams.*; import static android.view.WindowManagerPolicy.WindowManagerFuncs.LID_ABSENT; @@ -531,7 +529,7 @@ public class PhoneWindowManager implements WindowManagerPolicy { Settings.Secure.DEFAULT_INPUT_METHOD), false, this, UserHandle.USER_ALL); resolver.registerContentObserver(Settings.System.getUriFor( - ImmersiveModeTesting.ENABLED_SETTING), false, this, + Settings.Secure.TRANSIENT_NAV_CONFIRMATIONS), false, this, UserHandle.USER_ALL); updateSettings(); } @@ -947,9 +945,7 @@ public class PhoneWindowManager implements WindowManagerPolicy { } @Override public void onDebug() { - if (ImmersiveModeTesting.enabled) { - ImmersiveModeTesting.toggleForceImmersiveMode(mFocusedWindow, mContext); - } + // no-op } }); mTransientNavigationConfirmation = new TransientNavigationConfirmation(mContext); @@ -1168,8 +1164,9 @@ public class PhoneWindowManager implements WindowManagerPolicy { mHasSoftInput = hasSoftInput; updateRotation = true; } - ImmersiveModeTesting.enabled = Settings.System.getIntForUser(resolver, - ImmersiveModeTesting.ENABLED_SETTING, 0, UserHandle.USER_CURRENT) != 0; + if (mTransientNavigationConfirmation != null) { + mTransientNavigationConfirmation.loadSetting(); + } } if (updateRotation) { updateRotation(true); @@ -2819,7 +2816,7 @@ public class PhoneWindowManager implements WindowManagerPolicy { // If the status bar is hidden, we don't want to cause // windows behind it to scroll. - if (mStatusBar.isVisibleLw() && !statusBarTransient && !statusBarTransparent) { + if (mStatusBar.isVisibleLw() && !statusBarTransient) { // Status bar may go away, so the screen area it occupies // is available to apps but just covering them when the // status bar is visible. @@ -3892,9 +3889,8 @@ public class PhoneWindowManager implements WindowManagerPolicy { case KeyEvent.KEYCODE_POWER: { result &= ~ACTION_PASS_TO_USER; if (down) { - if (isScreenOn && isTransientNavigationAllowed(mLastSystemUiFlags)) { - mTransientNavigationConfirmation.unconfirmLastPackage(); - } + mTransientNavigationConfirmation.onPowerKeyDown(isScreenOn, event.getDownTime(), + isTransientNavigationAllowed(mLastSystemUiFlags)); if (isScreenOn && !mPowerKeyTriggered && (event.getFlags() & KeyEvent.FLAG_FALLBACK) == 0) { mPowerKeyTriggered = true; @@ -4173,6 +4169,7 @@ public class PhoneWindowManager implements WindowManagerPolicy { } if (sb) mStatusBarController.showTransient(); if (nb) mNavigationBarController.showTransient(); + mTransientNavigationConfirmation.confirmCurrentPrompt(); updateSystemUiVisibilityLw(); } } @@ -5039,10 +5036,6 @@ public class PhoneWindowManager implements WindowManagerPolicy { } private int updateSystemBarsLw(int oldVis, int vis) { - if (ImmersiveModeTesting.enabled) { - vis = ImmersiveModeTesting.applyForced(mFocusedWindow, vis); - } - // prevent status bar interaction from clearing certain flags boolean statusBarHasFocus = mFocusedWindow.getAttrs().type == TYPE_STATUS_BAR; if (statusBarHasFocus) { @@ -5086,8 +5079,7 @@ public class PhoneWindowManager implements WindowManagerPolicy { boolean isTransientNav = isTransientNavigationAllowed(vis); if (mFocusedWindow != null && oldTransientNav != isTransientNav) { final String pkg = mFocusedWindow.getOwningPackage(); - mTransientNavigationConfirmation.transientNavigationChanged(mCurrentUserId, pkg, - isTransientNav); + mTransientNavigationConfirmation.transientNavigationChanged(pkg, isTransientNav); } vis = mNavigationBarController.updateVisibilityLw(isTransientNav, oldVis, vis); @@ -5104,53 +5096,6 @@ public class PhoneWindowManager implements WindowManagerPolicy { && (vis & View.SYSTEM_UI_FLAG_IMMERSIVE) != 0; } - // Temporary helper that allows testing immersive mode on existing apps - // TODO remove - private static final class ImmersiveModeTesting { - static String ENABLED_SETTING = "immersive_mode_testing_enabled"; - static boolean enabled = false; - private static final HashSet<String> sForced = new HashSet<String>(); - - private static String parseActivity(WindowState win) { - if (win != null && win.getAppToken() != null) { - String str = win.getAppToken().toString(); - int end = str.lastIndexOf(' '); - if (end > 0) { - int start = str.lastIndexOf(' ', end - 1); - if (start > -1) { - return str.substring(start + 1, end); - } - } - } - return null; - } - - public static int applyForced(WindowState focused, int vis) { - if (sForced.contains(parseActivity(focused))) { - vis |= View.SYSTEM_UI_FLAG_HIDE_NAVIGATION | - View.SYSTEM_UI_FLAG_FULLSCREEN | - View.SYSTEM_UI_FLAG_IMMERSIVE; - } - return vis; - } - - public static void toggleForceImmersiveMode(WindowState focused, Context context) { - String activity = parseActivity(focused); - if (activity != null) { - String action; - if (sForced.contains(activity)) { - sForced.remove(activity); - action = "Force immersive mode disabled"; - } else { - sForced.add(activity); - action = "Force immersive mode enabled"; - } - android.widget.Toast.makeText(context, - action + " for " + activity, android.widget.Toast.LENGTH_SHORT).show(); - } - } - } - // Use this instead of checking config_showNavigationBar so that it can be consistently // overridden by qemu.hw.mainkeys in the emulator. @Override diff --git a/policy/src/com/android/internal/policy/impl/TransientNavigationConfirmation.java b/policy/src/com/android/internal/policy/impl/TransientNavigationConfirmation.java index 3c4f0925eb7a..8613088fc09e 100644 --- a/policy/src/com/android/internal/policy/impl/TransientNavigationConfirmation.java +++ b/policy/src/com/android/internal/policy/impl/TransientNavigationConfirmation.java @@ -19,16 +19,20 @@ package com.android.internal.policy.impl; import android.content.Context; import android.os.Handler; import android.os.Message; +import android.os.UserHandle; +import android.provider.Settings; +import android.text.TextUtils; import android.util.ArraySet; import android.util.Slog; import android.view.View; import android.view.animation.Animation; -import android.view.animation.AnimationSet; import android.view.animation.AnimationUtils; import android.widget.Toast; import com.android.internal.R; +import java.util.Arrays; + /** * Helper to manage showing/hiding a confirmation prompt when the transient navigation bar * is hidden. @@ -39,16 +43,22 @@ public class TransientNavigationConfirmation { private final Context mContext; private final H mHandler; - private final ArraySet<String> mConfirmedUserPackages = new ArraySet<String>(); + private final ArraySet<String> mConfirmedPackages = new ArraySet<String>(); private final long mShowDelayMs; + private final long mPanicThresholdMs; private Toast mToast; - private String mLastUserPackage; + private String mLastPackage; + private String mPromptPackage; + private long mPanicTime; + private String mPanicPackage; public TransientNavigationConfirmation(Context context) { mContext = context; mHandler = new H(); mShowDelayMs = getNavBarExitDuration() * 3; + mPanicThresholdMs = context.getResources() + .getInteger(R.integer.config_transient_navigation_confirmation_panic); } private long getNavBarExitDuration() { @@ -56,44 +66,97 @@ public class TransientNavigationConfirmation { return exit != null ? exit.getDuration() : 0; } - public void transientNavigationChanged(int userId, String pkg, boolean isNavTransient) { + public void loadSetting() { + if (DEBUG) Slog.d(TAG, "loadSetting()"); + mConfirmedPackages.clear(); + String packages = null; + try { + packages = Settings.Secure.getStringForUser(mContext.getContentResolver(), + Settings.Secure.TRANSIENT_NAV_CONFIRMATIONS, + UserHandle.USER_CURRENT); + if (packages != null) { + mConfirmedPackages.addAll(Arrays.asList(packages.split(","))); + if (DEBUG) Slog.d(TAG, "Loaded mConfirmedPackages=" + mConfirmedPackages); + } + } catch (Throwable t) { + Slog.w(TAG, "Error loading confirmations, packages=" + packages, t); + } + } + + private void saveSetting() { + if (DEBUG) Slog.d(TAG, "saveSetting()"); + try { + final String packages = TextUtils.join(",", mConfirmedPackages); + Settings.Secure.putStringForUser(mContext.getContentResolver(), + Settings.Secure.TRANSIENT_NAV_CONFIRMATIONS, + packages, + UserHandle.USER_CURRENT); + if (DEBUG) Slog.d(TAG, "Saved packages=" + packages); + } catch (Throwable t) { + Slog.w(TAG, "Error saving confirmations, mConfirmedPackages=" + mConfirmedPackages, t); + } + } + + public void transientNavigationChanged(String pkg, boolean isNavTransient) { if (pkg == null) { return; } - String userPkg = userId + ":" + pkg; mHandler.removeMessages(H.SHOW); if (isNavTransient) { - mLastUserPackage = userPkg; - if (!mConfirmedUserPackages.contains(userPkg)) { - if (DEBUG) Slog.d(TAG, "Showing transient navigation confirmation for " + userPkg); - mHandler.sendMessageDelayed(mHandler.obtainMessage(H.SHOW, userPkg), mShowDelayMs); + mLastPackage = pkg; + if (!mConfirmedPackages.contains(pkg)) { + mHandler.sendMessageDelayed(mHandler.obtainMessage(H.SHOW, pkg), mShowDelayMs); } } else { - mLastUserPackage = null; - if (DEBUG) Slog.d(TAG, "Hiding transient navigation confirmation for " + userPkg); + mLastPackage = null; mHandler.sendEmptyMessage(H.HIDE); } } - public void unconfirmLastPackage() { - if (mLastUserPackage != null) { - if (DEBUG) Slog.d(TAG, "Unconfirming transient navigation for " + mLastUserPackage); - mConfirmedUserPackages.remove(mLastUserPackage); + public void onPowerKeyDown(boolean isScreenOn, long time, boolean transientNavigationAllowed) { + if (mPanicPackage != null && !isScreenOn && (time - mPanicTime < mPanicThresholdMs)) { + // turning the screen back on within the panic threshold + unconfirmPackage(mPanicPackage); + } + if (isScreenOn && transientNavigationAllowed) { + // turning the screen off, remember if we were hiding the transient nav + mPanicTime = time; + mPanicPackage = mLastPackage; + } else { + mPanicTime = 0; + mPanicPackage = null; + } + } + + public void confirmCurrentPrompt() { + mHandler.post(confirmAction(mPromptPackage)); + } + + private void unconfirmPackage(String pkg) { + if (pkg != null) { + if (DEBUG) Slog.d(TAG, "Unconfirming transient navigation for " + pkg); + mConfirmedPackages.remove(pkg); + saveSetting(); } } private void handleHide() { if (mToast != null) { + if (DEBUG) Slog.d(TAG, + "Hiding transient navigation confirmation for " + mPromptPackage); mToast.cancel(); mToast = null; } } - private void handleShow(String userPkg) { + private void handleShow(String pkg) { + mPromptPackage = pkg; + if (DEBUG) Slog.d(TAG, "Showing transient navigation confirmation for " + pkg); + // create the confirmation toast bar final int msg = R.string.transient_navigation_confirmation; mToast = Toast.makeBar(mContext, msg, Toast.LENGTH_INFINITE); - mToast.setAction(R.string.ok, confirmAction(userPkg)); + mToast.setAction(R.string.ok, confirmAction(pkg)); // we will be hiding the nav bar, so layout as if it's already hidden mToast.getView().setSystemUiVisibility(View.SYSTEM_UI_FLAG_LAYOUT_HIDE_NAVIGATION); @@ -102,11 +165,15 @@ public class TransientNavigationConfirmation { mToast.show(); } - private Runnable confirmAction(final String userPkg) { + private Runnable confirmAction(final String pkg) { return new Runnable() { @Override public void run() { - mConfirmedUserPackages.add(userPkg); + if (pkg != null && !mConfirmedPackages.contains(pkg)) { + if (DEBUG) Slog.d(TAG, "Confirming transient navigation for " + pkg); + mConfirmedPackages.add(pkg); + saveSetting(); + } handleHide(); } }; diff --git a/services/java/com/android/server/IdleMaintenanceService.java b/services/java/com/android/server/IdleMaintenanceService.java index 584d4bc5f4c8..b0a1aca37d7c 100644 --- a/services/java/com/android/server/IdleMaintenanceService.java +++ b/services/java/com/android/server/IdleMaintenanceService.java @@ -17,6 +17,7 @@ package com.android.server; import android.app.Activity; +import android.app.ActivityManagerNative; import android.app.AlarmManager; import android.app.PendingIntent; import android.content.BroadcastReceiver; @@ -24,12 +25,13 @@ import android.content.Context; import android.content.Intent; import android.content.IntentFilter; import android.os.Handler; -import android.os.Looper; import android.os.PowerManager; import android.os.PowerManager.WakeLock; +import android.os.RemoteException; import android.os.SystemClock; import android.os.UserHandle; import android.util.Log; +import android.util.Slog; /** * This service observes the device state and when applicable sends @@ -69,6 +71,9 @@ public class IdleMaintenanceService extends BroadcastReceiver { private static final String ACTION_UPDATE_IDLE_MAINTENANCE_STATE = "com.android.server.IdleMaintenanceService.action.UPDATE_IDLE_MAINTENANCE_STATE"; + private static final String ACTION_FORCE_IDLE_MAINTENANCE = + "com.android.server.IdleMaintenanceService.action.FORCE_IDLE_MAINTENANCE"; + private static final Intent sIdleMaintenanceStartIntent; static { sIdleMaintenanceStartIntent = new Intent(Intent.ACTION_IDLE_MAINTENANCE_START); @@ -115,10 +120,10 @@ public class IdleMaintenanceService extends BroadcastReceiver { mUpdateIdleMaintenanceStatePendingIntent = PendingIntent.getBroadcast(mContext, 0, intent, PendingIntent.FLAG_UPDATE_CURRENT); - register(mContext.getMainLooper()); + register(mHandler); } - public void register(Looper looper) { + public void register(Handler handler) { IntentFilter intentFilter = new IntentFilter(); // Alarm actions. @@ -136,7 +141,12 @@ public class IdleMaintenanceService extends BroadcastReceiver { intentFilter.addAction(Intent.ACTION_DREAMING_STOPPED); mContext.registerReceiverAsUser(this, UserHandle.ALL, - intentFilter, null, new Handler(looper)); + intentFilter, null, mHandler); + + intentFilter = new IntentFilter(); + intentFilter.addAction(ACTION_FORCE_IDLE_MAINTENANCE); + mContext.registerReceiverAsUser(this, UserHandle.ALL, + intentFilter, android.Manifest.permission.SET_ACTIVITY_WATCHER, mHandler); } private void scheduleUpdateIdleMaintenanceState(long delayMillis) { @@ -149,7 +159,7 @@ public class IdleMaintenanceService extends BroadcastReceiver { mAlarmService.cancel(mUpdateIdleMaintenanceStatePendingIntent); } - private void updateIdleMaintenanceState() { + private void updateIdleMaintenanceState(boolean noisy) { if (mIdleMaintenanceStarted) { // Idle maintenance can be interrupted by user activity, or duration // time out, or low battery. @@ -170,9 +180,9 @@ public class IdleMaintenanceService extends BroadcastReceiver { getNextIdleMaintenanceIntervalStartFromNow()); } } - } else if (deviceStatePermitsIdleMaintenanceStart() - && lastUserActivityPermitsIdleMaintenanceStart() - && lastRunPermitsIdleMaintenanceStart()) { + } else if (deviceStatePermitsIdleMaintenanceStart(noisy) + && lastUserActivityPermitsIdleMaintenanceStart(noisy) + && lastRunPermitsIdleMaintenanceStart(noisy)) { // Now that we started idle maintenance, we should schedule another // update for the moment when the idle maintenance times out. scheduleUpdateIdleMaintenanceState(MAX_IDLE_MAINTENANCE_DURATION); @@ -182,8 +192,8 @@ public class IdleMaintenanceService extends BroadcastReceiver { isBatteryCharging() ? 1 : 0); mLastIdleMaintenanceStartTimeMillis = SystemClock.elapsedRealtime(); sendIdleMaintenanceStartIntent(); - } else if (lastUserActivityPermitsIdleMaintenanceStart()) { - if (lastRunPermitsIdleMaintenanceStart()) { + } else if (lastUserActivityPermitsIdleMaintenanceStart(noisy)) { + if (lastRunPermitsIdleMaintenanceStart(noisy)) { // The user does not use the device and we did not run maintenance in more // than the min interval between runs, so schedule an update - maybe the // battery will be charged latter. @@ -204,6 +214,10 @@ public class IdleMaintenanceService extends BroadcastReceiver { private void sendIdleMaintenanceStartIntent() { mWakeLock.acquire(); + try { + ActivityManagerNative.getDefault().performIdleMaintenance(); + } catch (RemoteException e) { + } mContext.sendOrderedBroadcastAsUser(sIdleMaintenanceStartIntent, UserHandle.ALL, null, this, mHandler, Activity.RESULT_OK, null, null); } @@ -214,25 +228,37 @@ public class IdleMaintenanceService extends BroadcastReceiver { null, this, mHandler, Activity.RESULT_OK, null, null); } - private boolean deviceStatePermitsIdleMaintenanceStart() { + private boolean deviceStatePermitsIdleMaintenanceStart(boolean noisy) { final int minBatteryLevel = isBatteryCharging() ? MIN_BATTERY_LEVEL_IDLE_MAINTENANCE_START_CHARGING : MIN_BATTERY_LEVEL_IDLE_MAINTENANCE_START_NOT_CHARGING; - return (mLastUserActivityElapsedTimeMillis != LAST_USER_ACTIVITY_TIME_INVALID + boolean allowed = (mLastUserActivityElapsedTimeMillis != LAST_USER_ACTIVITY_TIME_INVALID && mBatteryService.getBatteryLevel() > minBatteryLevel); + if (!allowed && noisy) { + Slog.i("IdleMaintenance", "Idle maintenance not allowed due to power"); + } + return allowed; } - private boolean lastUserActivityPermitsIdleMaintenanceStart() { + private boolean lastUserActivityPermitsIdleMaintenanceStart(boolean noisy) { // The last time the user poked the device is above the threshold. - return (mLastUserActivityElapsedTimeMillis != LAST_USER_ACTIVITY_TIME_INVALID + boolean allowed = (mLastUserActivityElapsedTimeMillis != LAST_USER_ACTIVITY_TIME_INVALID && SystemClock.elapsedRealtime() - mLastUserActivityElapsedTimeMillis > MIN_USER_INACTIVITY_IDLE_MAINTENANCE_START); + if (!allowed && noisy) { + Slog.i("IdleMaintenance", "Idle maintenance not allowed due to last user activity"); + } + return allowed; } - private boolean lastRunPermitsIdleMaintenanceStart() { + private boolean lastRunPermitsIdleMaintenanceStart(boolean noisy) { // Enough time passed since the last maintenance run. - return SystemClock.elapsedRealtime() - mLastIdleMaintenanceStartTimeMillis + boolean allowed = SystemClock.elapsedRealtime() - mLastIdleMaintenanceStartTimeMillis > MIN_IDLE_MAINTENANCE_INTERVAL_MILLIS; + if (!allowed && noisy) { + Slog.i("IdleMaintenance", "Idle maintenance not allowed due time since last"); + } + return allowed; } private boolean lastUserActivityPermitsIdleMaintenanceRunning() { @@ -266,7 +292,7 @@ public class IdleMaintenanceService extends BroadcastReceiver { // next release. The only client for this for now is internal an holds // a wake lock correctly. if (mIdleMaintenanceStarted) { - updateIdleMaintenanceState(); + updateIdleMaintenanceState(false); } } else if (Intent.ACTION_SCREEN_ON.equals(action) || Intent.ACTION_DREAMING_STOPPED.equals(action)) { @@ -276,7 +302,7 @@ public class IdleMaintenanceService extends BroadcastReceiver { unscheduleUpdateIdleMaintenanceState(); // If the screen went on/stopped dreaming, we know the user is using the // device which means that idle maintenance should be stopped if running. - updateIdleMaintenanceState(); + updateIdleMaintenanceState(false); } else if (Intent.ACTION_SCREEN_OFF.equals(action) || Intent.ACTION_DREAMING_STARTED.equals(action)) { mLastUserActivityElapsedTimeMillis = SystemClock.elapsedRealtime(); @@ -285,7 +311,12 @@ public class IdleMaintenanceService extends BroadcastReceiver { // this timeout elapses since the device may go to sleep by then. scheduleUpdateIdleMaintenanceState(MIN_USER_INACTIVITY_IDLE_MAINTENANCE_START); } else if (ACTION_UPDATE_IDLE_MAINTENANCE_STATE.equals(action)) { - updateIdleMaintenanceState(); + updateIdleMaintenanceState(false); + } else if (ACTION_FORCE_IDLE_MAINTENANCE.equals(action)) { + long now = SystemClock.elapsedRealtime() - 1; + mLastUserActivityElapsedTimeMillis = now - MIN_USER_INACTIVITY_IDLE_MAINTENANCE_START; + mLastIdleMaintenanceStartTimeMillis = now - MIN_IDLE_MAINTENANCE_INTERVAL_MILLIS; + updateIdleMaintenanceState(true); } else if (Intent.ACTION_IDLE_MAINTENANCE_START.equals(action) || Intent.ACTION_IDLE_MAINTENANCE_END.equals(action)) { // We were holding a wake lock while broadcasting the idle maintenance diff --git a/services/java/com/android/server/SystemServer.java b/services/java/com/android/server/SystemServer.java index d38756f6fb13..ef50df722fe0 100644 --- a/services/java/com/android/server/SystemServer.java +++ b/services/java/com/android/server/SystemServer.java @@ -217,8 +217,7 @@ class ServerThread { ServiceManager.addService("telephony.registry", telephonyRegistry); Slog.i(TAG, "Scheduling Policy"); - ServiceManager.addService(Context.SCHEDULING_POLICY_SERVICE, - new SchedulingPolicyService()); + ServiceManager.addService("scheduling_policy", new SchedulingPolicyService()); AttributeCache.init(context); diff --git a/services/java/com/android/server/am/ActivityManagerService.java b/services/java/com/android/server/am/ActivityManagerService.java index e208f10f7371..2bac96ede974 100644 --- a/services/java/com/android/server/am/ActivityManagerService.java +++ b/services/java/com/android/server/am/ActivityManagerService.java @@ -172,7 +172,6 @@ import android.view.WindowManager; import java.io.BufferedInputStream; import java.io.BufferedOutputStream; -import java.io.BufferedReader; import java.io.DataInputStream; import java.io.DataOutputStream; import java.io.File; @@ -850,10 +849,39 @@ public final class ActivityManagerService extends ActivityManagerNative int mNewNumServiceProcs = 0; /** - * System monitoring: number of processes that died since the last - * N procs were started. + * Allow the current computed overall memory level of the system to go down? + * This is set to false when we are killing processes for reasons other than + * memory management, so that the now smaller process list will not be taken as + * an indication that memory is tighter. */ - int[] mProcDeaths = new int[20]; + boolean mAllowLowerMemLevel = false; + + /** + * The last computed memory level, for holding when we are in a state that + * processes are going away for other reasons. + */ + int mLastMemoryLevel = ProcessStats.ADJ_MEM_FACTOR_NORMAL; + + /** + * The last total number of process we have, to determine if changes actually look + * like a shrinking number of process due to lower RAM. + */ + int mLastNumProcesses; + + /** + * The uptime of the last time we performed idle maintenance. + */ + long mLastIdleTime = SystemClock.uptimeMillis(); + + /** + * Total time spent with RAM that has been added in the past since the last idle time. + */ + long mLowRamTimeSinceLastIdle = 0; + + /** + * If RAM is currently low, when that horrible situatin started. + */ + long mLowRamStartTime = 0; /** * This is set if we had to do a delayed dexopt of an app before launching @@ -978,17 +1006,18 @@ public final class ActivityManagerService extends ActivityManagerNative static final int CANCEL_HEAVY_NOTIFICATION_MSG = 25; static final int SHOW_STRICT_MODE_VIOLATION_MSG = 26; static final int CHECK_EXCESSIVE_WAKE_LOCKS_MSG = 27; - static final int CLEAR_DNS_CACHE = 28; - static final int UPDATE_HTTP_PROXY = 29; + static final int CLEAR_DNS_CACHE_MSG = 28; + static final int UPDATE_HTTP_PROXY_MSG = 29; static final int SHOW_COMPAT_MODE_DIALOG_MSG = 30; static final int DISPATCH_PROCESSES_CHANGED = 31; static final int DISPATCH_PROCESS_DIED = 32; - static final int REPORT_MEM_USAGE = 33; + static final int REPORT_MEM_USAGE_MSG = 33; static final int REPORT_USER_SWITCH_MSG = 34; static final int CONTINUE_USER_SWITCH_MSG = 35; static final int USER_SWITCH_TIMEOUT_MSG = 36; static final int IMMERSIVE_MODE_LOCK_MSG = 37; - static final int PERSIST_URI_GRANTS = 38; + static final int PERSIST_URI_GRANTS_MSG = 38; + static final int REQUEST_ALL_PSS_MSG = 39; static final int FIRST_ACTIVITY_STACK_MSG = 100; static final int FIRST_BROADCAST_QUEUE_MSG = 200; @@ -1169,7 +1198,7 @@ public final class ActivityManagerService extends ActivityManagerNative } } } break; - case CLEAR_DNS_CACHE: { + case CLEAR_DNS_CACHE_MSG: { synchronized (ActivityManagerService.this) { for (int i = mLruProcesses.size() - 1 ; i >= 0 ; i--) { ProcessRecord r = mLruProcesses.get(i); @@ -1183,7 +1212,7 @@ public final class ActivityManagerService extends ActivityManagerNative } } } break; - case UPDATE_HTTP_PROXY: { + case UPDATE_HTTP_PROXY_MSG: { ProxyProperties proxy = (ProxyProperties)msg.obj; String host = ""; String port = ""; @@ -1369,7 +1398,7 @@ public final class ActivityManagerService extends ActivityManagerNative dispatchProcessDied(pid, uid); break; } - case REPORT_MEM_USAGE: { + case REPORT_MEM_USAGE_MSG: { final ArrayList<ProcessMemInfo> memInfos = (ArrayList<ProcessMemInfo>)msg.obj; Thread thread = new Thread() { @Override public void run() { @@ -1583,10 +1612,14 @@ public final class ActivityManagerService extends ActivityManagerNative } break; } - case PERSIST_URI_GRANTS: { + case PERSIST_URI_GRANTS_MSG: { writeGrantedUriPermissions(); break; } + case REQUEST_ALL_PSS_MSG: { + requestPssAllProcsLocked(SystemClock.uptimeMillis(), true, false); + break; + } } } }; @@ -1630,6 +1663,10 @@ public final class ActivityManagerService extends ActivityManagerNative num++; proc.lastPssTime = SystemClock.uptimeMillis(); proc.baseProcessTracker.addPss(pss, tmp[0], true); + if (proc.initialIdlePss == 0) { + proc.initialIdlePss = pss; + } + proc.lastPss = pss; } } } @@ -2250,8 +2287,7 @@ public final class ActivityManagerService extends ActivityManagerNative } } - final void updateLruProcessLocked(ProcessRecord app, - boolean oomAdj) { + final void updateLruProcessLocked(ProcessRecord app, boolean oomAdj) { mLruSeq++; updateLruProcessInternalLocked(app, 0); @@ -2417,9 +2453,6 @@ public final class ActivityManagerService extends ActivityManagerNative updateCpuStats(); - System.arraycopy(mProcDeaths, 0, mProcDeaths, 1, mProcDeaths.length-1); - mProcDeaths[0] = 0; - try { int uid = app.uid; @@ -3428,7 +3461,7 @@ public final class ActivityManagerService extends ActivityManagerNative } } if (doReport) { - Message msg = mHandler.obtainMessage(REPORT_MEM_USAGE, memInfos); + Message msg = mHandler.obtainMessage(REPORT_MEM_USAGE_MSG, memInfos); mHandler.sendMessage(msg); } scheduleAppGcsLocked(); @@ -3438,8 +3471,6 @@ public final class ActivityManagerService extends ActivityManagerNative final void appDiedLocked(ProcessRecord app, int pid, IApplicationThread thread) { - mProcDeaths[0]++; - BatteryStatsImpl stats = mBatteryStatsService.getActiveStatistics(); synchronized (stats) { stats.noteProcessDiedLocked(app.info.uid, pid); @@ -3448,17 +3479,27 @@ public final class ActivityManagerService extends ActivityManagerNative // Clean up already done if the process has been re-started. if (app.pid == pid && app.thread != null && app.thread.asBinder() == thread.asBinder()) { - if (!app.killedBackground) { + boolean doLowMem = app.instrumentationClass == null; + boolean doOomAdj = doLowMem; + if (!app.killedByAm) { Slog.i(TAG, "Process " + app.processName + " (pid " + pid + ") has died."); + mAllowLowerMemLevel = true; + } else { + // Note that we always want to do oom adj to update our state with the + // new number of procs. + mAllowLowerMemLevel = false; + doLowMem = false; } EventLog.writeEvent(EventLogTags.AM_PROC_DIED, app.userId, app.pid, app.processName); if (DEBUG_CLEANUP) Slog.v( TAG, "Dying app: " + app + ", pid: " + pid + ", thread: " + thread.asBinder()); - boolean doLowMem = app.instrumentationClass == null; handleAppDiedLocked(app, false, true); + if (doOomAdj) { + updateOomAdjLocked(); + } if (doLowMem) { doLowMemReportIfNeededLocked(app); } @@ -3791,10 +3832,7 @@ public final class ActivityManagerService extends ActivityManagerNative synchronized (this) { if (!showBackground && !app.isInterestingToUserLocked() && app.pid != MY_PID) { - Slog.w(TAG, "Killing " + app + ": background ANR"); - EventLog.writeEvent(EventLogTags.AM_KILL, app.userId, app.pid, - app.processName, app.setAdj, "background ANR"); - Process.killProcessQuiet(app.pid); + killUnneededProcessLocked(app, "background ANR"); return; } @@ -3979,6 +4017,7 @@ public final class ActivityManagerService extends ActivityManagerNative for (int i=0; i<N; i++) { removeProcessLocked(procs.get(i), false, true, "kill all background"); } + mAllowLowerMemLevel = true; updateOomAdjLocked(); doLowMemReportIfNeededLocked(null); } @@ -4478,10 +4517,9 @@ public final class ActivityManagerService extends ActivityManagerNative mPidsSelfLocked.remove(pid); mHandler.removeMessages(PROC_START_TIMEOUT_MSG, app); } - Slog.i(TAG, "Killing proc " + app.toShortString() + ": " + reason); + killUnneededProcessLocked(app, reason); handleAppDiedLocked(app, true, allowRestart); mLruProcesses.remove(app); - Process.killProcessQuiet(pid); if (app.persistent && !app.isolated) { if (!callerWillRestart) { @@ -4523,9 +4561,7 @@ public final class ActivityManagerService extends ActivityManagerNative checkAppInLaunchingProvidersLocked(app, true); // Take care of any services that are waiting for the process. mServices.processStartTimedOutLocked(app); - EventLog.writeEvent(EventLogTags.AM_KILL, app.userId, pid, - app.processName, app.setAdj, "start timeout"); - Process.killProcessQuiet(pid); + killUnneededProcessLocked(app, "start timeout"); if (mBackupTarget != null && mBackupTarget.app.pid == pid) { Slog.w(TAG, "Unattached app died before backup, skipping"); try { @@ -5769,8 +5805,8 @@ public final class ActivityManagerService extends ActivityManagerNative pi.packageName, targetPkg, targetUid, uri); final boolean persistChanged = perm.grantModes(modeFlags, persist, owner); if (persistChanged) { - mHandler.removeMessages(PERSIST_URI_GRANTS); - mHandler.obtainMessage(PERSIST_URI_GRANTS).sendToTarget(); + mHandler.removeMessages(PERSIST_URI_GRANTS_MSG); + mHandler.obtainMessage(PERSIST_URI_GRANTS_MSG).sendToTarget(); } } @@ -5994,8 +6030,8 @@ public final class ActivityManagerService extends ActivityManagerNative } if (persistChanged) { - mHandler.removeMessages(PERSIST_URI_GRANTS); - mHandler.obtainMessage(PERSIST_URI_GRANTS).sendToTarget(); + mHandler.removeMessages(PERSIST_URI_GRANTS_MSG); + mHandler.obtainMessage(PERSIST_URI_GRANTS_MSG).sendToTarget(); } } @@ -6079,8 +6115,8 @@ public final class ActivityManagerService extends ActivityManagerNative } if (persistChanged) { - mHandler.removeMessages(PERSIST_URI_GRANTS); - mHandler.obtainMessage(PERSIST_URI_GRANTS).sendToTarget(); + mHandler.removeMessages(PERSIST_URI_GRANTS_MSG); + mHandler.obtainMessage(PERSIST_URI_GRANTS_MSG).sendToTarget(); } } @@ -6508,6 +6544,16 @@ public final class ActivityManagerService extends ActivityManagerNative } } + private void killUnneededProcessLocked(ProcessRecord pr, String reason) { + if (!pr.killedByAm) { + Slog.i(TAG, "Killing " + pr.toShortString() + " (adj " + pr.setAdj + "): " + reason); + EventLog.writeEvent(EventLogTags.AM_KILL, pr.userId, pr.pid, + pr.processName, pr.setAdj, reason); + pr.killedByAm = true; + Process.killProcessQuiet(pr.pid); + } + } + private void cleanUpRemovedTaskLocked(TaskRecord tr, int flags) { mRecentTasks.remove(tr); mStackSupervisor.removeTask(tr); @@ -6546,11 +6592,7 @@ public final class ActivityManagerService extends ActivityManagerNative for (int i=0; i<procs.size(); i++) { ProcessRecord pr = procs.get(i); if (pr.setSchedGroup == Process.THREAD_GROUP_BG_NONINTERACTIVE) { - Slog.i(TAG, "Killing " + pr.toShortString() + ": remove task"); - EventLog.writeEvent(EventLogTags.AM_KILL, pr.userId, pr.pid, - pr.processName, pr.setAdj, "remove task"); - pr.killedBackground = true; - Process.killProcessQuiet(pr.pid); + killUnneededProcessLocked(pr, "remove task"); } else { pr.waitingToKill = "remove task"; } @@ -8401,13 +8443,9 @@ public final class ActivityManagerService extends ActivityManagerNative continue; } int adj = proc.setAdj; - if (adj >= worstType && !proc.killedBackground) { - Slog.w(TAG, "Killing " + proc + " (adj " + adj + "): " + reason); - EventLog.writeEvent(EventLogTags.AM_KILL, proc.userId, proc.pid, - proc.processName, adj, reason); + if (adj >= worstType && !proc.killedByAm) { + killUnneededProcessLocked(proc, reason); killed = true; - proc.killedBackground = true; - Process.killProcessQuiet(pids[i]); } } } @@ -8449,13 +8487,9 @@ public final class ActivityManagerService extends ActivityManagerNative if (proc == null) continue; final int adj = proc.setAdj; - if (adj > belowAdj && !proc.killedBackground) { - Slog.w(TAG, "Killing " + proc + " (adj " + adj + "): " + reason); - EventLog.writeEvent(EventLogTags.AM_KILL, proc.userId, - proc.pid, proc.processName, adj, reason); + if (adj > belowAdj && !proc.killedByAm) { + killUnneededProcessLocked(proc, reason); killed = true; - proc.killedBackground = true; - Process.killProcessQuiet(pid); } } } @@ -8533,6 +8567,61 @@ public final class ActivityManagerService extends ActivityManagerNative br.onReceive(mContext, intent); } + private long getLowRamTimeSinceIdle(long now) { + return mLowRamTimeSinceLastIdle + (mLowRamStartTime > 0 ? (now-mLowRamStartTime) : 0); + } + + @Override + public void performIdleMaintenance() { + if (checkCallingPermission(android.Manifest.permission.SET_ACTIVITY_WATCHER) + != PackageManager.PERMISSION_GRANTED) { + throw new SecurityException("Requires permission " + + android.Manifest.permission.SET_ACTIVITY_WATCHER); + } + + synchronized (this) { + final long now = SystemClock.uptimeMillis(); + final long timeSinceLastIdle = now - mLastIdleTime; + final long lowRamSinceLastIdle = getLowRamTimeSinceIdle(now); + mLastIdleTime = now; + mLowRamTimeSinceLastIdle = 0; + if (mLowRamStartTime != 0) { + mLowRamStartTime = now; + } + + StringBuilder sb = new StringBuilder(128); + sb.append("Idle maintenance over "); + TimeUtils.formatDuration(timeSinceLastIdle, sb); + sb.append(" low RAM for "); + TimeUtils.formatDuration(lowRamSinceLastIdle, sb); + Slog.i(TAG, sb.toString()); + + // If at least 1/3 of our time since the last idle period has been spent + // with RAM low, then we want to kill processes. + boolean doKilling = lowRamSinceLastIdle > (timeSinceLastIdle/3); + + for (int i = mLruProcesses.size() - 1 ; i >= 0 ; i--) { + ProcessRecord proc = mLruProcesses.get(i); + if (proc.notCachedSinceIdle) { + if (proc.setProcState > ActivityManager.PROCESS_STATE_TOP + && proc.setProcState <= ActivityManager.PROCESS_STATE_SERVICE) { + if (doKilling && proc.initialIdlePss != 0 + && proc.lastPss > ((proc.initialIdlePss*3)/2)) { + killUnneededProcessLocked(proc, "idle maint (pss " + proc.lastPss + + " from " + proc.initialIdlePss + ")"); + } + } + } else if (proc.setProcState < ActivityManager.PROCESS_STATE_HOME) { + proc.notCachedSinceIdle = true; + proc.initialIdlePss = 0; + } + } + + mHandler.removeMessages(REQUEST_ALL_PSS_MSG); + mHandler.sendEmptyMessageDelayed(REQUEST_ALL_PSS_MSG, 2*60*1000); + } + } + public final void startRunning(String pkg, String cls, String action, String data) { synchronized(this) { @@ -8967,10 +9056,7 @@ public final class ActivityManagerService extends ActivityManagerNative } if (app.pid > 0 && app.pid != MY_PID) { handleAppCrashLocked(app); - Slog.i(ActivityManagerService.TAG, "Killing " + app + ": user's request"); - EventLog.writeEvent(EventLogTags.AM_KILL, app.userId, app.pid, - app.processName, app.setAdj, "user's request after error"); - Process.killProcessQuiet(app.pid); + killUnneededProcessLocked(app, "user request after error"); } } } @@ -10444,6 +10530,15 @@ public final class ActivityManagerService extends ActivityManagerNative + " mNumCachedHiddenProcs=" + mNumCachedHiddenProcs + " mNumServiceProcs=" + mNumServiceProcs + " mNewNumServiceProcs=" + mNewNumServiceProcs); + pw.println(" mAllowLowerMemLevel=" + mAllowLowerMemLevel + + " mLastMemoryLevel" + mLastMemoryLevel + + " mLastNumProcesses" + mLastNumProcesses); + long now = SystemClock.uptimeMillis(); + pw.print(" mLastIdleTime="); + TimeUtils.formatDuration(now, mLastIdleTime, pw); + pw.print(" mLowRamSinceLastIdle="); + TimeUtils.formatDuration(getLowRamTimeSinceIdle(now), pw); + pw.println(); } } @@ -11704,13 +11799,9 @@ public final class ActivityManagerService extends ActivityManagerNative if (!capp.persistent && capp.thread != null && capp.pid != 0 && capp.pid != MY_PID) { - Slog.i(TAG, "Kill " + capp.processName - + " (pid " + capp.pid + "): provider " + cpr.info.name - + " in dying process " + (proc != null ? proc.processName : "??")); - EventLog.writeEvent(EventLogTags.AM_KILL, capp.userId, capp.pid, - capp.processName, capp.setAdj, "dying provider " - + cpr.name.toShortString()); - Process.killProcessQuiet(capp.pid); + killUnneededProcessLocked(capp, "depends on provider " + + cpr.name.flattenToShortString() + + " in dying proc " + (proc != null ? proc.processName : "??")); } } else if (capp.thread != null && conn.provider.provider != null) { try { @@ -12791,12 +12882,12 @@ public final class ActivityManagerService extends ActivityManagerNative } if (intent.ACTION_CLEAR_DNS_CACHE.equals(intent.getAction())) { - mHandler.sendEmptyMessage(CLEAR_DNS_CACHE); + mHandler.sendEmptyMessage(CLEAR_DNS_CACHE_MSG); } if (Proxy.PROXY_CHANGE_ACTION.equals(intent.getAction())) { ProxyProperties proxy = intent.getParcelableExtra("proxy"); - mHandler.sendMessage(mHandler.obtainMessage(UPDATE_HTTP_PROXY, proxy)); + mHandler.sendMessage(mHandler.obtainMessage(UPDATE_HTTP_PROXY_MSG, proxy)); } // Add to the sticky list if requested. @@ -14527,26 +14618,18 @@ public final class ActivityManagerService extends ActivityManagerNative stats.reportExcessiveWakeLocked(app.info.uid, app.processName, realtimeSince, wtimeUsed); } - Slog.w(TAG, "Excessive wake lock in " + app.processName - + " (pid " + app.pid + "): held " + wtimeUsed + killUnneededProcessLocked(app, "excessive wake held " + wtimeUsed + " during " + realtimeSince); - EventLog.writeEvent(EventLogTags.AM_KILL, app.userId, app.pid, - app.processName, app.setAdj, "excessive wake lock"); app.baseProcessTracker.reportExcessiveWake(app.pkgList); - Process.killProcessQuiet(app.pid); } else if (doCpuKills && uptimeSince > 0 && ((cputimeUsed*100)/uptimeSince) >= 50) { synchronized (stats) { stats.reportExcessiveCpuLocked(app.info.uid, app.processName, uptimeSince, cputimeUsed); } - Slog.w(TAG, "Excessive CPU in " + app.processName - + " (pid " + app.pid + "): used " + cputimeUsed + killUnneededProcessLocked(app, "excessive cpu " + cputimeUsed + " during " + uptimeSince); - EventLog.writeEvent(EventLogTags.AM_KILL, app.userId, app.pid, - app.processName, app.setAdj, "excessive cpu"); app.baseProcessTracker.reportExcessiveCpu(app.pkgList); - Process.killProcessQuiet(app.pid); } else { app.lastWakeTime = wtime; app.lastCpuTime = app.curCpuTime; @@ -14593,11 +14676,7 @@ public final class ActivityManagerService extends ActivityManagerNative + " to " + app.curSchedGroup); if (app.waitingToKill != null && app.setSchedGroup == Process.THREAD_GROUP_BG_NONINTERACTIVE) { - Slog.i(TAG, "Killing " + app.toShortString() + ": " + app.waitingToKill); - EventLog.writeEvent(EventLogTags.AM_KILL, app.userId, app.pid, - app.processName, app.setAdj, app.waitingToKill); - app.killedBackground = true; - Process.killProcessQuiet(app.pid); + killUnneededProcessLocked(app, app.waitingToKill); success = false; } else { if (true) { @@ -14657,6 +14736,9 @@ public final class ActivityManagerService extends ActivityManagerNative "Proc state change of " + app.processName + " to " + app.curProcState); app.setProcState = app.curProcState; + if (app.setProcState >= ActivityManager.PROCESS_STATE_HOME) { + app.notCachedSinceIdle = false; + } if (!doingAll) { setProcessTrackerState(app, mProcessStats.getMemFactorLocked(), now); } else { @@ -14783,7 +14865,7 @@ public final class ActivityManagerService extends ActivityManagerNative int nextEmptyAdj = curEmptyAdj+2; for (int i=N-1; i>=0; i--) { ProcessRecord app = mLruProcesses.get(i); - if (!app.killedBackground && app.thread != null) { + if (!app.killedByAm && app.thread != null) { app.procStateChanged = false; final boolean wasKeeping = app.keeping; computeOomAdjLocked(app, ProcessList.UNKNOWN_ADJ, TOP_APP, true, now); @@ -14858,34 +14940,19 @@ public final class ActivityManagerService extends ActivityManagerNative mNumCachedHiddenProcs++; numCached++; if (numCached > cachedProcessLimit) { - Slog.i(TAG, "No longer want " + app.processName - + " (pid " + app.pid + "): cached #" + numCached); - EventLog.writeEvent(EventLogTags.AM_KILL, app.userId, app.pid, - app.processName, app.setAdj, "too many background"); - app.killedBackground = true; - Process.killProcessQuiet(app.pid); + killUnneededProcessLocked(app, "cached #" + numCached); } break; case ActivityManager.PROCESS_STATE_CACHED_EMPTY: if (numEmpty > ProcessList.TRIM_EMPTY_APPS && app.lastActivityTime < oldTime) { - Slog.i(TAG, "No longer want " + app.processName - + " (pid " + app.pid + "): empty for " - + ((oldTime+ProcessList.MAX_EMPTY_TIME-app.lastActivityTime) - / 1000) + "s"); - EventLog.writeEvent(EventLogTags.AM_KILL, app.userId, app.pid, - app.processName, app.setAdj, "old background process"); - app.killedBackground = true; - Process.killProcessQuiet(app.pid); + killUnneededProcessLocked(app, "empty for " + + ((oldTime + ProcessList.MAX_EMPTY_TIME - app.lastActivityTime) + / 1000) + "s"); } else { numEmpty++; if (numEmpty > emptyProcessLimit) { - Slog.i(TAG, "No longer want " + app.processName - + " (pid " + app.pid + "): empty #" + numEmpty); - EventLog.writeEvent(EventLogTags.AM_KILL, app.userId, app.pid, - app.processName, app.setAdj, "too many background"); - app.killedBackground = true; - Process.killProcessQuiet(app.pid); + killUnneededProcessLocked(app, "empty #" + numEmpty); } } break; @@ -14901,16 +14968,11 @@ public final class ActivityManagerService extends ActivityManagerNative // definition not re-use the same process again, and it is // good to avoid having whatever code was running in them // left sitting around after no longer needed. - Slog.i(TAG, "Isolated process " + app.processName - + " (pid " + app.pid + ") no longer needed"); - EventLog.writeEvent(EventLogTags.AM_KILL, app.userId, app.pid, - app.processName, app.setAdj, "isolated not needed"); - app.killedBackground = true; - Process.killProcessQuiet(app.pid); + killUnneededProcessLocked(app, "isolated not needed"); } if (app.curProcState >= ActivityManager.PROCESS_STATE_HOME - && !app.killedBackground) { + && !app.killedByAm) { numTrimming++; } } @@ -14924,31 +14986,60 @@ public final class ActivityManagerService extends ActivityManagerNative // are managing to keep around is less than half the maximum we desire; // if we are keeping a good number around, we'll let them use whatever // memory they want. - boolean allChanged; + final int numCachedAndEmpty = numCached + numEmpty; + int memFactor; if (numCached <= ProcessList.TRIM_CACHED_APPS && numEmpty <= ProcessList.TRIM_EMPTY_APPS) { - final int numCachedAndEmpty = numCached + numEmpty; - int factor = numTrimming/3; - int minFactor = 2; - if (mHomeProcess != null) minFactor++; - if (mPreviousProcess != null) minFactor++; - if (factor < minFactor) factor = minFactor; - int step = 0; - int fgTrimLevel; - int memFactor; if (numCachedAndEmpty <= ProcessList.TRIM_CRITICAL_THRESHOLD) { - fgTrimLevel = ComponentCallbacks2.TRIM_MEMORY_RUNNING_CRITICAL; memFactor = ProcessStats.ADJ_MEM_FACTOR_CRITICAL; } else if (numCachedAndEmpty <= ProcessList.TRIM_LOW_THRESHOLD) { - fgTrimLevel = ComponentCallbacks2.TRIM_MEMORY_RUNNING_LOW; memFactor = ProcessStats.ADJ_MEM_FACTOR_LOW; } else { - fgTrimLevel = ComponentCallbacks2.TRIM_MEMORY_RUNNING_MODERATE; memFactor = ProcessStats.ADJ_MEM_FACTOR_MODERATE; } + } else { + memFactor = ProcessStats.ADJ_MEM_FACTOR_NORMAL; + } + // We always allow the memory level to go up (better). We only allow it to go + // down if we are in a state where that is allowed, *and* the total number of processes + // has gone down since last time. + if (DEBUG_OOM_ADJ) Slog.d(TAG, "oom: memFactor=" + memFactor + " last=" + mLastMemoryLevel + + " allowLow=" + mAllowLowerMemLevel + " numProcs=" + mLruProcesses.size() + + " last=" + mLastNumProcesses); + if (memFactor > mLastMemoryLevel) { + if (!mAllowLowerMemLevel || mLruProcesses.size() >= mLastNumProcesses) { + memFactor = mLastMemoryLevel; + if (DEBUG_OOM_ADJ) Slog.d(TAG, "Keeping last mem factor!"); + } + } + mLastMemoryLevel = memFactor; + mLastNumProcesses = mLruProcesses.size(); + boolean allChanged = mProcessStats.setMemFactorLocked( + ProcessStats.ADJ_MEM_FACTOR_NORMAL, !mSleeping, now); + final int trackerMemFactor = mProcessStats.getMemFactorLocked(); + if (memFactor != ProcessStats.ADJ_MEM_FACTOR_NORMAL) { + if (mLowRamStartTime == 0) { + mLowRamStartTime = now; + } + int step = 0; + int fgTrimLevel; + switch (memFactor) { + case ProcessStats.ADJ_MEM_FACTOR_CRITICAL: + fgTrimLevel = ComponentCallbacks2.TRIM_MEMORY_RUNNING_CRITICAL; + break; + case ProcessStats.ADJ_MEM_FACTOR_LOW: + fgTrimLevel = ComponentCallbacks2.TRIM_MEMORY_RUNNING_LOW; + break; + default: + fgTrimLevel = ComponentCallbacks2.TRIM_MEMORY_RUNNING_MODERATE; + break; + } + int factor = numTrimming/3; + int minFactor = 2; + if (mHomeProcess != null) minFactor++; + if (mPreviousProcess != null) minFactor++; + if (factor < minFactor) factor = minFactor; int curLevel = ComponentCallbacks2.TRIM_MEMORY_COMPLETE; - allChanged = mProcessStats.setMemFactorLocked(memFactor, !mSleeping, now); - final int trackerMemFactor = mProcessStats.getMemFactorLocked(); for (int i=N-1; i>=0; i--) { ProcessRecord app = mLruProcesses.get(i); if (allChanged || app.procStateChanged) { @@ -14956,7 +15047,7 @@ public final class ActivityManagerService extends ActivityManagerNative app.procStateChanged = false; } if (app.curProcState >= ActivityManager.PROCESS_STATE_HOME - && !app.killedBackground) { + && !app.killedByAm) { if (app.trimMemoryLevel < curLevel && app.thread != null) { try { if (DEBUG_SWITCH || DEBUG_OOM_ADJ) Slog.v(TAG, @@ -15036,9 +15127,10 @@ public final class ActivityManagerService extends ActivityManagerNative } } } else { - allChanged = mProcessStats.setMemFactorLocked( - ProcessStats.ADJ_MEM_FACTOR_NORMAL, !mSleeping, now); - final int trackerMemFactor = mProcessStats.getMemFactorLocked(); + if (mLowRamStartTime != 0) { + mLowRamTimeSinceLastIdle += now - mLowRamStartTime; + mLowRamStartTime = 0; + } for (int i=N-1; i>=0; i--) { ProcessRecord app = mLruProcesses.get(i); if (allChanged || app.procStateChanged) { @@ -15107,6 +15199,7 @@ public final class ActivityManagerService extends ActivityManagerNative if (app.pid > 0 && app.pid != MY_PID) { EventLog.writeEvent(EventLogTags.AM_KILL, app.userId, app.pid, app.processName, app.setAdj, "empty"); + app.killedByAm = true; Process.killProcessQuiet(app.pid); } else { try { diff --git a/services/java/com/android/server/am/ActivityStackSupervisor.java b/services/java/com/android/server/am/ActivityStackSupervisor.java index e0eb2c431ce7..f5d45b343b3f 100644 --- a/services/java/com/android/server/am/ActivityStackSupervisor.java +++ b/services/java/com/android/server/am/ActivityStackSupervisor.java @@ -1417,7 +1417,8 @@ public final class ActivityStackSupervisor { final ActivityStack lastStack = getLastStack(); ActivityRecord curTop = lastStack == null? null : lastStack.topRunningNonDelayedActivityLocked(notTop); - if (curTop != null && curTop.task != intentActivity.task) { + if (curTop != null && (curTop.task != intentActivity.task) || + curTop.task != lastStack.topTask()) { r.intent.addFlags(Intent.FLAG_ACTIVITY_BROUGHT_TO_FRONT); if (sourceRecord == null || (sourceStack.topActivity() != null && sourceStack.topActivity().task == sourceRecord.task)) { diff --git a/services/java/com/android/server/am/ProcessRecord.java b/services/java/com/android/server/am/ProcessRecord.java index 35e06b655d98..4fdacb3733ce 100644 --- a/services/java/com/android/server/am/ProcessRecord.java +++ b/services/java/com/android/server/am/ProcessRecord.java @@ -66,6 +66,8 @@ final class ProcessRecord { long lastPssTime; // Last time we retrieved PSS data long nextPssTime; // Next time we want to request PSS data long lastStateTime; // Last time setProcState changed + long initialIdlePss; // Initial memory pss of process for idle maintenance. + long lastPss; // Last computed memory pss. int maxAdj; // Maximum OOM adjustment for this process int curRawAdj; // Current OOM unlimited adjustment for this process int setRawAdj; // Last set OOM unlimited adjustment for this process @@ -82,6 +84,7 @@ final class ProcessRecord { boolean serviceb; // Process currently is on the service B list boolean keeping; // Actively running code so don't kill due to that? boolean setIsForeground; // Running foreground UI when last set? + boolean notCachedSinceIdle; // Has this process not been in a cached state since last idle? boolean hasActivities; // Are there any activities running in this process? boolean hasClientActivities; // Are there any client services with activities? boolean hasStartedServices; // Are there any started services running in this process? @@ -92,7 +95,7 @@ final class ProcessRecord { boolean pendingUiClean; // Want to clean up resources from showing UI? boolean hasAboveClient; // Bound using BIND_ABOVE_CLIENT, so want to be lower boolean bad; // True if disabled in the bad process list - boolean killedBackground; // True when proc has been killed due to too many bg + boolean killedByAm; // True when proc has been killed by activity manager, not for RAM boolean procStateChanged; // Keep track of whether we changed 'setAdj'. String waitingToKill; // Process is waiting to be killed when in the bg; reason IBinder forcingToForeground;// Token that is forcing this process to be foreground @@ -216,6 +219,11 @@ final class ProcessRecord { pw.print(" keeping="); pw.print(keeping); pw.print(" cached="); pw.print(cached); pw.print(" empty="); pw.println(empty); + if (notCachedSinceIdle) { + pw.print(prefix); pw.print("notCachedSinceIdle="); pw.print(notCachedSinceIdle); + pw.print(" initialIdlePss="); pw.print(initialIdlePss); + pw.print(" lastPss="); pw.println(lastPss); + } pw.print(prefix); pw.print("oom: max="); pw.print(maxAdj); pw.print(" curRaw="); pw.print(curRawAdj); pw.print(" setRaw="); pw.print(setRawAdj); @@ -280,8 +288,8 @@ final class ProcessRecord { pw.print(" lastLowMemory="); TimeUtils.formatDuration(lastLowMemory, now, pw); pw.print(" reportLowMemory="); pw.println(reportLowMemory); - if (killedBackground || waitingToKill != null) { - pw.print(prefix); pw.print("killedBackground="); pw.print(killedBackground); + if (killedByAm || waitingToKill != null) { + pw.print(prefix); pw.print("killedByAm="); pw.print(killedByAm); pw.print(" waitingToKill="); pw.println(waitingToKill); } if (debugging || crashing || crashDialog != null || notResponding diff --git a/services/java/com/android/server/am/ProcessStatsService.java b/services/java/com/android/server/am/ProcessStatsService.java index c180f6e38b28..f5f43ab29548 100644 --- a/services/java/com/android/server/am/ProcessStatsService.java +++ b/services/java/com/android/server/am/ProcessStatsService.java @@ -551,17 +551,23 @@ public final class ProcessStatsService extends IProcessStats.Stub { } else if ("--current".equals(arg)) { currentOnly = true; } else if ("--commit".equals(arg)) { - mProcessStats.mFlags |= ProcessStats.FLAG_COMPLETE; - writeStateLocked(true, true); - pw.println("Process stats committed."); + synchronized (mAm) { + mProcessStats.mFlags |= ProcessStats.FLAG_COMPLETE; + writeStateLocked(true, true); + pw.println("Process stats committed."); + } return; } else if ("--write".equals(arg)) { - writeStateSyncLocked(); - pw.println("Process stats written."); + synchronized (mAm) { + writeStateSyncLocked(); + pw.println("Process stats written."); + } return; } else if ("--read".equals(arg)) { - readLocked(mProcessStats, mFile); - pw.println("Process stats read."); + synchronized (mAm) { + readLocked(mProcessStats, mFile); + pw.println("Process stats read."); + } return; } else if ("-h".equals(arg)) { dumpHelp(pw); diff --git a/services/java/com/android/server/pm/PackageManagerService.java b/services/java/com/android/server/pm/PackageManagerService.java index 7cc568e25d99..decda96060fe 100755 --- a/services/java/com/android/server/pm/PackageManagerService.java +++ b/services/java/com/android/server/pm/PackageManagerService.java @@ -5509,10 +5509,9 @@ public class PackageManagerService extends IPackageManager.Stub { // version of the one on the data partition, but which // granted a new system permission that it didn't have // before. In this case we do want to allow the app to - // now get the new permission, because it is allowed by - // the system image. - allowed = false; - if (sysPs.pkg != null) { + // now get the new permission if the new system-partition + // apk is privileged to get it. + if (sysPs.pkg != null && isPrivilegedApp(pkg)) { for (int j=0; j<sysPs.pkg.requestedPermissions.size(); j++) { if (perm.equals( diff --git a/services/java/com/android/server/pm/Settings.java b/services/java/com/android/server/pm/Settings.java index 8e0c92a2084f..377c390f7275 100644 --- a/services/java/com/android/server/pm/Settings.java +++ b/services/java/com/android/server/pm/Settings.java @@ -2853,6 +2853,7 @@ final class Settings { ApplicationInfo.FLAG_RESTORE_ANY_VERSION, "RESTORE_ANY_VERSION", ApplicationInfo.FLAG_EXTERNAL_STORAGE, "EXTERNAL_STORAGE", ApplicationInfo.FLAG_LARGE_HEAP, "LARGE_HEAP", + ApplicationInfo.FLAG_PRIVILEGED, "PRIVILEGED", ApplicationInfo.FLAG_FORWARD_LOCK, "FORWARD_LOCK", ApplicationInfo.FLAG_CANT_SAVE_STATE, "CANT_SAVE_STATE", }; diff --git a/services/java/com/android/server/wifi/WifiService.java b/services/java/com/android/server/wifi/WifiService.java index 5a24ebb4cb1f..bbcd01a87bd3 100644 --- a/services/java/com/android/server/wifi/WifiService.java +++ b/services/java/com/android/server/wifi/WifiService.java @@ -172,10 +172,16 @@ public final class WifiService extends IWifiManager.Stub { case WifiManager.CONNECT_NETWORK: case WifiManager.SAVE_NETWORK: { WifiConfiguration config = (WifiConfiguration) msg.obj; - if (config.isValid()) { + int networkId = msg.arg1; + if (config != null && config.isValid()) { + if (DBG) Slog.d(TAG, "Connect with config" + config); + mWifiStateMachine.sendMessage(Message.obtain(msg)); + } else if (config == null + && networkId != WifiConfiguration.INVALID_NETWORK_ID) { + if (DBG) Slog.d(TAG, "Connect with networkId" + networkId); mWifiStateMachine.sendMessage(Message.obtain(msg)); } else { - Slog.d(TAG, "ClientHandler.handleMessage ignoring msg=" + msg); + Slog.e(TAG, "ClientHandler.handleMessage ignoring invalid msg=" + msg); if (msg.what == WifiManager.CONNECT_NETWORK) { replyFailed(msg, WifiManager.CONNECT_NETWORK_FAILED); } else { @@ -582,7 +588,8 @@ public final class WifiService extends IWifiManager.Stub { */ public void setWifiApEnabled(WifiConfiguration wifiConfig, boolean enabled) { enforceChangePermission(); - if (wifiConfig.isValid()) { + // null wifiConfig is a meaningful input for CMD_SET_AP + if (wifiConfig == null || wifiConfig.isValid()) { mWifiController.obtainMessage(CMD_SET_AP, enabled ? 1 : 0, 0, wifiConfig).sendToTarget(); } else { Slog.e(TAG, "Invalid WifiConfiguration"); diff --git a/wifi/java/android/net/wifi/WifiConfiguration.java b/wifi/java/android/net/wifi/WifiConfiguration.java index 2ce584b2b206..2b3c9e21bed1 100644 --- a/wifi/java/android/net/wifi/WifiConfiguration.java +++ b/wifi/java/android/net/wifi/WifiConfiguration.java @@ -355,7 +355,15 @@ public class WifiConfiguration implements Parcelable { */ public boolean isValid() { if (allowedKeyManagement.cardinality() > 1) { - return false; + if (allowedKeyManagement.cardinality() != 2) { + return false; + } + if (allowedKeyManagement.get(KeyMgmt.WPA_EAP) == false) { + return false; + } + if (allowedKeyManagement.get(KeyMgmt.IEEE8021X) == false) { + return false; + } } // TODO: Add more checks |