Merge "Offer to enable a print service after it is installed." into klp-dev
diff --git a/api/current.txt b/api/current.txt
index c7e0a93..1bfa49e 100644
--- a/api/current.txt
+++ b/api/current.txt
@@ -10840,22 +10840,20 @@
ctor public CameraAccessException(int, java.lang.String, java.lang.Throwable);
ctor public CameraAccessException(int, java.lang.Throwable);
method public final int getReason();
- field public static final int CAMERA_DISABLED = 3; // 0x3
- field public static final int CAMERA_DISCONNECTED = 4; // 0x4
- field public static final int CAMERA_IN_USE = 1; // 0x1
- field public static final int MAX_CAMERAS_IN_USE = 2; // 0x2
+ field public static final int CAMERA_DISABLED = 1; // 0x1
+ field public static final int CAMERA_DISCONNECTED = 2; // 0x2
+ field public static final int CAMERA_ERROR = 3; // 0x3
}
public abstract interface CameraDevice implements java.lang.AutoCloseable {
method public abstract void capture(android.hardware.camera2.CaptureRequest, android.hardware.camera2.CameraDevice.CaptureListener, android.os.Handler) throws android.hardware.camera2.CameraAccessException;
method public abstract void captureBurst(java.util.List<android.hardware.camera2.CaptureRequest>, android.hardware.camera2.CameraDevice.CaptureListener, android.os.Handler) throws android.hardware.camera2.CameraAccessException;
- method public abstract void close() throws java.lang.Exception;
+ method public abstract void close();
method public abstract void configureOutputs(java.util.List<android.view.Surface>) throws android.hardware.camera2.CameraAccessException;
method public abstract android.hardware.camera2.CaptureRequest.Builder createCaptureRequest(int) throws android.hardware.camera2.CameraAccessException;
method public abstract void flush() throws android.hardware.camera2.CameraAccessException;
method public abstract java.lang.String getId();
method public abstract android.hardware.camera2.CameraProperties getProperties() throws android.hardware.camera2.CameraAccessException;
- method public abstract void setDeviceListener(android.hardware.camera2.CameraDevice.CameraDeviceListener, android.os.Handler);
method public abstract void setRepeatingBurst(java.util.List<android.hardware.camera2.CaptureRequest>, android.hardware.camera2.CameraDevice.CaptureListener, android.os.Handler) throws android.hardware.camera2.CameraAccessException;
method public abstract void setRepeatingRequest(android.hardware.camera2.CaptureRequest, android.hardware.camera2.CameraDevice.CaptureListener, android.os.Handler) throws android.hardware.camera2.CameraAccessException;
method public abstract void stopRepeating() throws android.hardware.camera2.CameraAccessException;
@@ -10866,15 +10864,6 @@
field public static final int TEMPLATE_VIDEO_SNAPSHOT = 4; // 0x4
}
- public static abstract class CameraDevice.CameraDeviceListener {
- ctor public CameraDevice.CameraDeviceListener();
- method public void onCameraDisconnected(android.hardware.camera2.CameraDevice);
- method public void onCameraError(android.hardware.camera2.CameraDevice, int);
- method public void onCameraIdle(android.hardware.camera2.CameraDevice);
- field public static final int ERROR_CAMERA_DEVICE = 1; // 0x1
- field public static final int ERROR_CAMERA_SERVICE = 2; // 0x2
- }
-
public static abstract class CameraDevice.CaptureListener {
ctor public CameraDevice.CaptureListener();
method public void onCaptureCompleted(android.hardware.camera2.CameraDevice, android.hardware.camera2.CaptureRequest, android.hardware.camera2.CaptureResult);
@@ -10882,11 +10871,28 @@
method public void onCaptureStarted(android.hardware.camera2.CameraDevice, android.hardware.camera2.CaptureRequest, long);
}
+ public static abstract class CameraDevice.StateListener {
+ ctor public CameraDevice.StateListener();
+ method public void onActive(android.hardware.camera2.CameraDevice);
+ method public void onBusy(android.hardware.camera2.CameraDevice);
+ method public void onClosed(android.hardware.camera2.CameraDevice);
+ method public abstract void onDisconnected(android.hardware.camera2.CameraDevice);
+ method public abstract void onError(android.hardware.camera2.CameraDevice, int);
+ method public void onIdle(android.hardware.camera2.CameraDevice);
+ method public abstract void onOpened(android.hardware.camera2.CameraDevice);
+ method public void onUnconfigured(android.hardware.camera2.CameraDevice);
+ field public static final int ERROR_CAMERA_DEVICE = 4; // 0x4
+ field public static final int ERROR_CAMERA_DISABLED = 3; // 0x3
+ field public static final int ERROR_CAMERA_IN_USE = 1; // 0x1
+ field public static final int ERROR_CAMERA_SERVICE = 5; // 0x5
+ field public static final int ERROR_MAX_CAMERAS_IN_USE = 2; // 0x2
+ }
+
public final class CameraManager {
method public void addAvailabilityListener(android.hardware.camera2.CameraManager.AvailabilityListener, android.os.Handler);
method public java.lang.String[] getCameraIdList() throws android.hardware.camera2.CameraAccessException;
method public android.hardware.camera2.CameraProperties getCameraProperties(java.lang.String) throws android.hardware.camera2.CameraAccessException;
- method public android.hardware.camera2.CameraDevice openCamera(java.lang.String) throws android.hardware.camera2.CameraAccessException;
+ method public void openCamera(java.lang.String, android.hardware.camera2.CameraDevice.StateListener, android.os.Handler) throws android.hardware.camera2.CameraAccessException;
method public void removeAvailabilityListener(android.hardware.camera2.CameraManager.AvailabilityListener);
}
@@ -18412,6 +18418,7 @@
method public long getStatSize();
method public static android.os.ParcelFileDescriptor open(java.io.File, int) throws java.io.FileNotFoundException;
method public static android.os.ParcelFileDescriptor open(java.io.File, int, android.os.Handler, android.os.ParcelFileDescriptor.OnCloseListener) throws java.io.IOException;
+ method public static int parseMode(java.lang.String);
method public void writeToParcel(android.os.Parcel, int);
field public static final android.os.Parcelable.Creator CREATOR;
field public static final int MODE_APPEND = 33554432; // 0x2000000
diff --git a/core/java/android/app/ContextImpl.java b/core/java/android/app/ContextImpl.java
index 8e9f3bb..0ba2ac5 100644
--- a/core/java/android/app/ContextImpl.java
+++ b/core/java/android/app/ContextImpl.java
@@ -92,6 +92,7 @@
import android.os.UserHandle;
import android.os.SystemVibrator;
import android.os.UserManager;
+import android.os.storage.IMountService;
import android.os.storage.StorageManager;
import android.print.IPrintManager;
import android.print.PrintManager;
@@ -864,7 +865,9 @@
if (mExternalObbDirs == null) {
mExternalObbDirs = Environment.buildExternalStorageAppObbDirs(getPackageName());
}
- return mExternalObbDirs;
+
+ // Create dirs if needed
+ return ensureDirsExistOrFilter(mExternalObbDirs);
}
}
@@ -2127,14 +2130,25 @@
* Ensure that given directories exist, trying to create them if missing. If
* unable to create, they are filtered by replacing with {@code null}.
*/
- private static File[] ensureDirsExistOrFilter(File[] dirs) {
+ private File[] ensureDirsExistOrFilter(File[] dirs) {
File[] result = new File[dirs.length];
for (int i = 0; i < dirs.length; i++) {
File dir = dirs[i];
if (!dir.exists()) {
if (!dir.mkdirs()) {
- Log.w(TAG, "Failed to ensure directory: " + dir);
- dir = null;
+ // Failing to mkdir() may be okay, since we might not have
+ // enough permissions; ask vold to create on our behalf.
+ final IMountService mount = IMountService.Stub.asInterface(
+ ServiceManager.getService("mount"));
+ int res = -1;
+ try {
+ res = mount.mkdirs(getPackageName(), dir.getAbsolutePath());
+ } catch (RemoteException e) {
+ }
+ if (res != 0) {
+ Log.w(TAG, "Failed to ensure directory: " + dir);
+ dir = null;
+ }
}
}
result[i] = dir;
diff --git a/core/java/android/content/ContentProvider.java b/core/java/android/content/ContentProvider.java
index 9a258dc..461dc1d 100644
--- a/core/java/android/content/ContentProvider.java
+++ b/core/java/android/content/ContentProvider.java
@@ -1310,7 +1310,7 @@
throw new FileNotFoundException("Column _data not found.");
}
- int modeBits = ContentResolver.modeToMode(uri, mode);
+ int modeBits = ParcelFileDescriptor.parseMode(mode);
return ParcelFileDescriptor.open(new File(path), modeBits);
}
diff --git a/core/java/android/content/ContentResolver.java b/core/java/android/content/ContentResolver.java
index 5618cab..2750d68 100644
--- a/core/java/android/content/ContentResolver.java
+++ b/core/java/android/content/ContentResolver.java
@@ -886,7 +886,7 @@
}
} else if (SCHEME_FILE.equals(scheme)) {
ParcelFileDescriptor pfd = ParcelFileDescriptor.open(
- new File(uri.getPath()), modeToMode(uri, mode));
+ new File(uri.getPath()), ParcelFileDescriptor.parseMode(mode));
return new AssetFileDescriptor(pfd, 0, -1);
} else {
if ("r".equals(mode)) {
@@ -1158,33 +1158,6 @@
return res;
}
- /** @hide */
- static public int modeToMode(Uri uri, String mode) throws FileNotFoundException {
- int modeBits;
- if ("r".equals(mode)) {
- modeBits = ParcelFileDescriptor.MODE_READ_ONLY;
- } else if ("w".equals(mode) || "wt".equals(mode)) {
- modeBits = ParcelFileDescriptor.MODE_WRITE_ONLY
- | ParcelFileDescriptor.MODE_CREATE
- | ParcelFileDescriptor.MODE_TRUNCATE;
- } else if ("wa".equals(mode)) {
- modeBits = ParcelFileDescriptor.MODE_WRITE_ONLY
- | ParcelFileDescriptor.MODE_CREATE
- | ParcelFileDescriptor.MODE_APPEND;
- } else if ("rw".equals(mode)) {
- modeBits = ParcelFileDescriptor.MODE_READ_WRITE
- | ParcelFileDescriptor.MODE_CREATE;
- } else if ("rwt".equals(mode)) {
- modeBits = ParcelFileDescriptor.MODE_READ_WRITE
- | ParcelFileDescriptor.MODE_CREATE
- | ParcelFileDescriptor.MODE_TRUNCATE;
- } else {
- throw new FileNotFoundException("Bad mode for " + uri + ": "
- + mode);
- }
- return modeBits;
- }
-
/**
* Inserts a row into a table at the given URL.
*
diff --git a/core/java/android/hardware/camera2/CameraAccessException.java b/core/java/android/hardware/camera2/CameraAccessException.java
index e08d1e6..1af575f 100644
--- a/core/java/android/hardware/camera2/CameraAccessException.java
+++ b/core/java/android/hardware/camera2/CameraAccessException.java
@@ -29,22 +29,24 @@
public class CameraAccessException extends AndroidException {
/**
* The camera device is in use already
+ * @hide
*/
- public static final int CAMERA_IN_USE = 1;
+ public static final int CAMERA_IN_USE = 4;
/**
* The system-wide limit for number of open cameras has been reached,
* and more camera devices cannot be opened until previous instances are
* closed.
+ * @hide
*/
- public static final int MAX_CAMERAS_IN_USE = 2;
+ public static final int MAX_CAMERAS_IN_USE = 5;
/**
* The camera is disabled due to a device policy, and cannot be opened.
*
* @see android.app.admin.DevicePolicyManager#setCameraDisabled(android.content.ComponentName, boolean)
*/
- public static final int CAMERA_DISABLED = 3;
+ public static final int CAMERA_DISABLED = 1;
/**
* The camera device is removable and has been disconnected from the Android
@@ -52,7 +54,23 @@
* is no longer valid, or the camera service has shut down the connection due to a
* higher-priority access request for the camera device.
*/
- public static final int CAMERA_DISCONNECTED = 4;
+ public static final int CAMERA_DISCONNECTED = 2;
+
+ /**
+ * The camera device is currently in the error state.
+ *
+ * <p>The camera has failed to open or has failed at a later time
+ * as a result of some non-user interaction. Refer to
+ * {@link CameraDevice.StateListener#onError} for the exact
+ * nature of the error.</p>
+ *
+ * <p>No further calls to the camera will succeed. Clean up
+ * the camera with {@link CameraDevice#close} and try
+ * handling the error in order to successfully re-open the camera.
+ * </p>
+ *
+ */
+ public static final int CAMERA_ERROR = 3;
/**
* A deprecated HAL version is in use.
@@ -68,10 +86,9 @@
/**
* The reason for the failure to access the camera.
*
- * @see #CAMERA_IN_USE
- * @see #MAX_CAMERAS_IN_USE
* @see #CAMERA_DISABLED
* @see #CAMERA_DISCONNECTED
+ * @see #CAMERA_ERROR
*/
public final int getReason() {
return mReason;
@@ -105,12 +122,15 @@
return "The system-wide limit for number of open cameras has been reached, " +
"and more camera devices cannot be opened until previous instances " +
"are closed.";
+ case CAMERA_DISCONNECTED:
+ return "The camera device is removable and has been disconnected from the " +
+ "Android device, or the camera service has shut down the connection due " +
+ "to a higher-priority access request for the camera device.";
case CAMERA_DISABLED:
return "The camera is disabled due to a device policy, and cannot be opened.";
- case CAMERA_DISCONNECTED:
- return "The camera device is removable and has been disconnected from the Android" +
- " device, or the camera service has shut down the connection due to a " +
- "higher-priority access request for the camera device.";
+ case CAMERA_ERROR:
+ return "The camera device is currently in the error state; " +
+ "no further calls to it will succeed.";
}
return null;
}
diff --git a/core/java/android/hardware/camera2/CameraDevice.java b/core/java/android/hardware/camera2/CameraDevice.java
index 0c13b0f..f047b0d 100644
--- a/core/java/android/hardware/camera2/CameraDevice.java
+++ b/core/java/android/hardware/camera2/CameraDevice.java
@@ -137,7 +137,9 @@
*
* @return the static properties of the camera
*
- * @throws CameraAccessException if the camera device is no longer connected
+ * @throws CameraAccessException if the camera device is no longer connected or has
+ * encountered a fatal error
+ * @throws IllegalStateException if the camera device has been closed
*
* @see CameraManager#getCameraProperties
*/
@@ -218,23 +220,31 @@
*
* <p>To reach an idle state without cancelling any submitted captures, first
* stop any repeating request/burst with {@link #stopRepeating}, and then
- * wait for the {@link CameraDeviceListener#onCameraIdle} callback to be
+ * wait for the {@link StateListener#onIdle} callback to be
* called. To idle as fast as possible, use {@link #flush} and wait for the
* idle callback.</p>
*
* <p>Using larger resolution outputs, or more outputs, can result in slower
* output rate from the device.</p>
*
+ * <p>Configuring the outputs with an empty or null list will transition
+ * the camera into an {@link StateListener#onUnconfigured unconfigured state}.
+ * </p>
+ *
+ * <p>Calling configureOutputs with the same arguments as the last call to
+ * configureOutputs has no effect.</p>
+ *
* @param outputs The new set of Surfaces that should be made available as
* targets for captured image data.
*
* @throws IllegalArgumentException if the set of output Surfaces do not
* meet the requirements
- * @throws CameraAccessException if the camera device is no longer connected
- * @throws IllegalStateException if the camera device is not idle, or has
- * encountered a fatal error
+ * @throws CameraAccessException if the camera device is no longer connected or has
+ * encountered a fatal error
+ * @throws IllegalStateException if the camera device is not idle, or
+ * if the camera device has been closed
*
- * @see CameraDeviceListener#onCameraIdle
+ * @see StateListener#onIdle
* @see #stopRepeating
* @see #flush
*/
@@ -255,9 +265,9 @@
*
* @throws IllegalArgumentException if the templateType is not in the list
* of supported templates.
- * @throws CameraAccessException if the camera device is no longer connected
- * @throws IllegalStateException if the camera device has been closed or the
- * device has encountered a fatal error.
+ * @throws CameraAccessException if the camera device is no longer connected or has
+ * encountered a fatal error
+ * @throws IllegalStateException if the camera device has been closed
*
* @see #TEMPLATE_PREVIEW
* @see #TEMPLATE_RECORD
@@ -295,9 +305,10 @@
* {@code null} to use the current thread's {@link android.os.Looper
* looper}.
*
- * @throws CameraAccessException if the camera device is no longer connected
- * @throws IllegalStateException if the camera device has been closed or the
- * device has encountered a fatal error.
+ * @throws CameraAccessException if the camera device is no longer connected or has
+ * encountered a fatal error
+ * @throws IllegalStateException if the camera is currently busy or unconfigured,
+ * or the camera device has been closed.
* @throws IllegalArgumentException If the request targets Surfaces not
* currently configured as outputs. Or if the handler is null, the listener
* is not null, and the calling thread has no looper.
@@ -335,9 +346,10 @@
* {@code null} to use the current thread's {@link android.os.Looper
* looper}.
*
- * @throws CameraAccessException if the camera device is no longer connected
- * @throws IllegalStateException if the camera device has been closed or the
- * device has encountered a fatal error.
+ * @throws CameraAccessException if the camera device is no longer connected or has
+ * encountered a fatal error
+ * @throws IllegalStateException if the camera is currently busy or unconfigured,
+ * or the camera device has been closed.
* @throws IllegalArgumentException If the requests target Surfaces not
* currently configured as outputs. Or if the handler is null, the listener
* is not null, and the calling thread has no looper.
@@ -387,10 +399,10 @@
* {@code null} to use the current thread's {@link android.os.Looper
* looper}.
*
- * @throws CameraAccessException if the camera device is no longer
- * connected
- * @throws IllegalStateException if the camera device has been closed or the
- * device has encountered a fatal error.
+ * @throws CameraAccessException if the camera device is no longer connected or has
+ * encountered a fatal error
+ * @throws IllegalStateException if the camera is currently busy or unconfigured,
+ * or the camera device has been closed.
* @throws IllegalArgumentException If the requests reference Surfaces not
* currently configured as outputs. Or if the handler is null, the listener
* is not null, and the calling thread has no looper.
@@ -442,9 +454,10 @@
* {@code null} to use the current thread's {@link android.os.Looper
* looper}.
*
- * @throws CameraAccessException if the camera device is no longer connected
- * @throws IllegalStateException if the camera device has been closed or the
- * device has encountered a fatal error.
+ * @throws CameraAccessException if the camera device is no longer connected or has
+ * encountered a fatal error
+ * @throws IllegalStateException if the camera is currently busy or unconfigured,
+ * or the camera device has been closed.
* @throws IllegalArgumentException If the requests reference Surfaces not
* currently configured as outputs. Or if the handler is null, the listener
* is not null, and the calling thread has no looper.
@@ -467,21 +480,17 @@
* <p>Any currently in-flight captures will still complete, as will any
* burst that is mid-capture. To ensure that the device has finished
* processing all of its capture requests and is in idle state, wait for the
- * {@link CameraDeviceListener#onCameraIdle} callback after calling this
+ * {@link StateListener#onIdle} callback after calling this
* method..</p>
*
- * @throws CameraAccessException if the camera device is no longer connected
- * @throws IllegalStateException if the camera device has been closed or the
- * device has encountered a fatal error.
+ * @throws CameraAccessException if the camera device is no longer connected or has
+ * encountered a fatal error
+ * @throws IllegalStateException if the camera is currently busy or unconfigured,
+ * or the camera device has been closed.
*
* @see #setRepeatingRequest
* @see #setRepeatingBurst
- * @see CameraDeviceListener#onCameraIdle
- *
- * @throws CameraAccessException if the camera device is no longer connected
- * @throws IllegalStateException if the camera device has been closed, the
- * device has encountered a fatal error, or if there is an active repeating
- * request or burst.
+ * @see StateListener#onIdle
*/
public void stopRepeating() throws CameraAccessException;
@@ -519,7 +528,7 @@
* {@link CaptureListener}.</p>
*
* <p>If the camera device is idle when the listener is set, then the
- * {@link CameraDeviceListener#onCameraIdle} method will be immediately called,
+ * {@link StateListener#onIdle} method will be immediately called,
* even if the device has never been active before.
* </p>
*
@@ -530,8 +539,10 @@
*
* @throws IllegalArgumentException if handler is null, the listener is
* not null, and the calling thread has no looper
+ *
+ * @hide
*/
- public void setDeviceListener(CameraDeviceListener listener, Handler handler);
+ public void setDeviceListener(StateListener listener, Handler handler);
/**
* Flush all captures currently pending and in-progress as fast as
@@ -558,7 +569,11 @@
* configurations, or for cancelling long in-progress requests (such as a
* multi-second capture).</p>
*
- * @throws CameraAccessException if the camera device is no longer connected
+ * @throws CameraAccessException if the camera device is no longer connected or has
+ * encountered a fatal error
+ * @throws IllegalStateException if the camera is not idle/active,
+ * or the camera device has been closed.
+ *
* @see #setRepeatingRequest
* @see #setRepeatingBurst
* @see #configureOutputs
@@ -569,10 +584,9 @@
* Close the connection to this camera device. After this call, all calls to
* the camera device interface will throw a {@link IllegalStateException},
* except for calls to close().
- * @throws Exception
*/
@Override
- public void close() throws Exception;
+ public void close();
// TODO: We should decide on the behavior of in-flight requests should be on close.
/**
@@ -687,37 +701,190 @@
*
* @see #setDeviceListener
*/
- public static abstract class CameraDeviceListener {
+ public static abstract class StateListener {
+ /**
+ * An error code that can be reported by {@link #onError}
+ * indicating that the camera device is in use already.
+ *
+ * <p>
+ * This error can be produced when opening the camera fails.
+ * </p>
+ *
+ * @see #onError
+ */
+ public static final int ERROR_CAMERA_IN_USE = 1;
/**
- * An error code that can be reported by {@link #onCameraError}
+ * An error code that can be reported by {@link #onError}
+ * indicating that the camera device could not be opened
+ * because there are too many other open camera devices.
+ *
+ * <p>
+ * The system-wide limit for number of open cameras has been reached,
+ * and more camera devices cannot be opened until previous instances are
+ * closed.
+ * </p>
+ *
+ * <p>
+ * This error can be produced when opening the camera fails.
+ * </p>
+ *
+ * @see #onError
+ */
+ public static final int ERROR_MAX_CAMERAS_IN_USE = 2;
+
+ /**
+ * An error code that can be reported by {@link #onError}
+ * indicating that the camera device could not be opened due to a device
+ * policy.
+ *
+ * @see android.app.admin.DevicePolicyManager#setCameraDisabled(android.content.ComponentName, boolean)
+ * @see #onError
+ */
+ public static final int ERROR_CAMERA_DISABLED = 3;
+
+ /**
+ * An error code that can be reported by {@link #onError}
* indicating that the camera device has encountered a fatal error.
*
* <p>The camera device needs to be re-opened to be used again.</p>
*
- * @see #onCameraDeviceError
+ * @see #onError
*/
- public static final int ERROR_CAMERA_DEVICE = 1;
+ public static final int ERROR_CAMERA_DEVICE = 4;
/**
- * An error code that can be reported by {@link #onCameraError}
+ * An error code that can be reported by {@link #onError}
* indicating that the camera service has encountered a fatal error.
*
* <p>The Android device may need to be shut down and restarted to restore
* camera function, or there may be a persistent hardware problem.</p>
*
- * @see #onCameraDeviceError
+ * <p>An attempt at recovery <i>may</i> be possible by closing the
+ * CameraDevice and the CameraManager, and trying to acquire all resources
+ * again from scratch.</p>
+ *
+ * @see #onError
*/
- public static final int ERROR_CAMERA_SERVICE = 2;
+ public static final int ERROR_CAMERA_SERVICE = 5;
+
+ /**
+ * The method called when a camera device has finished opening.
+ *
+ * <p>An opened camera will immediately afterwards transition into
+ * {@link #onUnconfigured}.</p>
+ *
+ * @param camera the camera device that has become opened
+ */
+ public abstract void onOpened(CameraDevice camera); // Must implement
+
+ /**
+ * The method called when a camera device has no outputs configured.
+ *
+ * <p>An unconfigured camera device needs to be configured with
+ * {@link CameraDevice#configureOutputs} before being able to
+ * submit any capture request.</p>
+ *
+ * <p>This state may be entered by a newly opened camera or by
+ * calling {@link CameraDevice#configureOutputs} with a null/empty
+ * list of Surfaces when idle.</p>
+ *
+ * <p>Any attempts to submit a capture request while in this state
+ * will result in an {@link IllegalStateException} being thrown.</p>
+ *
+ * <p>The default implementation of this method does nothing.</p>
+ *
+ * @param camera the camera device has that become unconfigured
+ */
+ public void onUnconfigured(CameraDevice camera) {
+ // Default empty implementation
+ }
+
+ /**
+ * The method called when a camera device begins processing
+ * {@link CaptureRequest capture requests}.
+ *
+ * <p>A camera may not be re-configured while in this state. The camera
+ * will transition to the idle state once all pending captures have
+ * completed. If a repeating request is set, the camera will remain active
+ * until it is cleared and the remaining requests finish processing. To
+ * transition to the idle state as quickly as possible, call {@link #flush()},
+ * which will idle the camera device as quickly as possible, likely canceling
+ * most in-progress captures.</p>
+ *
+ * <p>All calls except for {@link CameraDevice#configureOutputs} are
+ * legal while in this state.
+ * </p>
+ *
+ * <p>The default implementation of this method does nothing.</p>
+ *
+ * @param camera the camera device that has become active
+ *
+ * @see CameraDevice#capture
+ * @see CameraDevice#captureBurst
+ * @see CameraDevice#setRepeatingBurst
+ * @see CameraDevice#setRepeatingRequest
+ */
+ public void onActive(CameraDevice camera) {
+ // Default empty implementation
+ }
+
+ /**
+ * The method called when a camera device is busy.
+ *
+ * <p>A camera becomes busy while it's outputs are being configured
+ * (after a call to {@link CameraDevice#configureOutputs} or while it's
+ * being flushed (after a call to {@link CameraDevice#flush}.</p>
+ *
+ * <p>Once the on-going operations are complete, the camera will automatically
+ * transition into {@link #onIdle} if there is at least one configured output,
+ * or {@link #onUnconfigured} otherwise.</p>
+ *
+ * <p>Any attempts to manipulate the camera while its is busy
+ * will result in an {@link IllegalStateException} being thrown.</p>
+ *
+ * <p>Only the following methods are valid to call while in this state:
+ * <ul>
+ * <li>{@link CameraDevice#getId}</li>
+ * <li>{@link CameraDevice#createCaptureRequest}</li>
+ * <li>{@link CameraDevice#close}</li>
+ * </ul>
+ * </p>
+ *
+ * <p>The default implementation of this method does nothing.</p>
+ *
+ * @param camera the camera device that has become busy
+ *
+ * @see CameraDevice#configureOutputs
+ * @see CameraDevice#flush
+ */
+ public void onBusy(CameraDevice camera) {
+ // Default empty implementation
+ }
+
+ /**
+ * The method called when a camera device has been closed with
+ * {@link CameraDevice#close}.
+ *
+ * <p>Any attempt to call methods on this CameraDevice in the
+ * future will throw a {@link IllegalStateException}.</p>
+ *
+ * <p>The default implementation of this method does nothing.</p>
+ *
+ * @param camera the camera device that has become closed
+ */
+ public void onClosed(CameraDevice camera) {
+ // Default empty implementation
+ }
/**
* The method called when a camera device has finished processing all
* submitted capture requests and has reached an idle state.
*
- * <p>An idle camera device can have its outputs changed by calling
- * {@link CameraDevice#configureOutputs}.</p>
+ * <p>An idle camera device can have its outputs changed by calling {@link
+ * CameraDevice#configureOutputs}, which will transition it into the busy state.</p>
*
- * <p>To idle and reconfigure outputs without cancelling any submitted
+ * <p>To idle and reconfigure outputs without canceling any submitted
* capture requests, the application needs to clear its repeating
* request/burst, if set, with {@link CameraDevice#stopRepeating}, and
* then wait for this callback to be called before calling {@link
@@ -725,7 +892,7 @@
*
* <p>To idle and reconfigure a camera device as fast as possible, the
* {@link CameraDevice#flush} method can be used, which will discard all
- * pending and in-progess capture requests. Once the {@link
+ * pending and in-progress capture requests. Once the {@link
* CameraDevice#flush} method is called, the application must wait for
* this callback to fire before calling {@link
* CameraDevice#configureOutputs}.</p>
@@ -738,7 +905,7 @@
* @see CameraDevice#stopRepeating
* @see CameraDevice#flush
*/
- public void onCameraIdle(CameraDevice camera) {
+ public void onIdle(CameraDevice camera) {
// Default empty implementation
}
@@ -746,6 +913,9 @@
* The method called when a camera device is no longer available for
* use.
*
+ * <p>This callback may be called instead of {@link #onOpened}
+ * if opening the camera fails.</p>
+ *
* <p>Any attempt to call methods on this CameraDevice will throw a
* {@link CameraAccessException}. The disconnection could be due to a
* change in security policy or permissions; the physical disconnection
@@ -759,25 +929,32 @@
* <p>The default implementation logs a notice to the system log
* about the disconnection.</p>
*
+ * <p>You should clean up the camera with {@link CameraDevice#close} after
+ * this happens, as it is not recoverable until opening the camera again
+ * after it becomes {@link CameraManager.AvailabilityListener#onCameraAvailable available}.
+ * </p>
+ *
* @param camera the device that has been disconnected
*/
- public void onCameraDisconnected(CameraDevice camera) {
- Log.i("CameraListener",
- String.format("Camera device %s disconnected", camera.getId()));
- }
+ public abstract void onDisconnected(CameraDevice camera); // Must implement
/**
* The method called when a camera device has encountered a serious error.
*
+ * <p>This callback may be called instead of {@link #onOpened}
+ * if opening the camera fails.</p>
+ *
* <p>This indicates a failure of the camera device or camera service in
* some way. Any attempt to call methods on this CameraDevice in the
- * future will throw a {@link java.lang.IllegalStateException}.</p>
+ * future will throw a {@link CameraAccessException} with the
+ * {@link CameraAccessException#CAMERA_ERROR CAMERA_ERROR} reason.
+ * </p>
*
* <p>There may still be capture completion or camera stream listeners
* that will be called after this error is received.</p>
*
- * <p>The default implementation logs an error to the system log about
- * the camera failure.</p>
+ * <p>You should clean up the camera with {@link CameraDevice#close} after
+ * this happens. Further attempts at recovery are error-code specific.</p>
*
* @param camera The device reporting the error
* @param error The error code, one of the
@@ -785,11 +962,9 @@
*
* @see #ERROR_CAMERA_DEVICE
* @see #ERROR_CAMERA_SERVICE
+ * @see #ERROR_CAMERA_DISABLED
+ * @see #ERROR_CAMERA_IN_USE
*/
- public void onCameraError(CameraDevice camera, int error) {
- Log.e("CameraListener",
- String.format("Camera device %s has encountered an error: %d",
- camera.getId(), error));
- }
+ public abstract void onError(CameraDevice camera, int error); // Must implement
}
}
diff --git a/core/java/android/hardware/camera2/CameraManager.java b/core/java/android/hardware/camera2/CameraManager.java
index 4ad9259..29895ef 100644
--- a/core/java/android/hardware/camera2/CameraManager.java
+++ b/core/java/android/hardware/camera2/CameraManager.java
@@ -58,7 +58,7 @@
private final ICameraService mCameraService;
private ArrayList<String> mDeviceIdList;
- private ArrayMap<AvailabilityListener, Handler> mListenerMap =
+ private final ArrayMap<AvailabilityListener, Handler> mListenerMap =
new ArrayMap<AvailabilityListener, Handler>();
private final Context mContext;
@@ -201,8 +201,7 @@
* @see #getCameraIdList
* @see android.app.admin.DevicePolicyManager#setCameraDisabled
*/
- public CameraDevice openCamera(String cameraId) throws CameraAccessException {
-
+ private CameraDevice openCamera(String cameraId) throws CameraAccessException {
try {
synchronized (mLock) {
@@ -237,6 +236,79 @@
}
/**
+ * Open a connection to a camera with the given ID.
+ *
+ * <p>Use {@link #getCameraIdList} to get the list of available camera
+ * devices. Note that even if an id is listed, open may fail if the device
+ * is disconnected between the calls to {@link #getCameraIdList} and
+ * {@link #openCamera}.</p>
+ *
+ * <p>If the camera successfully opens after this function call returns,
+ * {@link CameraDevice.StateListener#onOpened} will be invoked with the
+ * newly opened {@link CameraDevice} in the unconfigured state.</p>
+ *
+ * <p>If the camera becomes disconnected during initialization
+ * after this function call returns,
+ * {@link CameraDevice.StateListener#onDisconnected} with a
+ * {@link CameraDevice} in the disconnected state (and
+ * {@link CameraDevice.StateListener#onOpened} will be skipped).</p>
+ *
+ * <p>If the camera fails to initialize after this function call returns,
+ * {@link CameraDevice.StateListener#onError} will be invoked with a
+ * {@link CameraDevice} in the error state (and
+ * {@link CameraDevice.StateListener#onOpened} will be skipped).</p>
+ *
+ * @param cameraId
+ * The unique identifier of the camera device to open
+ * @param listener
+ * The listener which is invoked once the camera is opened
+ * @param handler
+ * The handler on which the listener should be invoked, or
+ * {@code null} to use the current thread's {@link android.os.Looper looper}.
+ *
+ * @throws CameraAccessException if the camera is disabled by device policy,
+ * or the camera has become or was disconnected.
+ *
+ * @throws IllegalArgumentException if cameraId or the listener was null,
+ * or the cameraId does not match any currently or previously available
+ * camera device.
+ *
+ * @throws SecurityException if the application does not have permission to
+ * access the camera
+ *
+ * @see #getCameraIdList
+ * @see android.app.admin.DevicePolicyManager#setCameraDisabled
+ */
+ public void openCamera(String cameraId, final CameraDevice.StateListener listener,
+ Handler handler)
+ throws CameraAccessException {
+
+ if (cameraId == null) {
+ throw new IllegalArgumentException("cameraId was null");
+ } else if (listener == null) {
+ throw new IllegalArgumentException("listener was null");
+ } else if (handler == null) {
+ if (Looper.myLooper() != null) {
+ handler = new Handler();
+ } else {
+ throw new IllegalArgumentException(
+ "Looper doesn't exist in the calling thread");
+ }
+ }
+
+ final CameraDevice camera = openCamera(cameraId);
+ camera.setDeviceListener(listener, handler);
+
+ // TODO: make truly async in the camera service
+ handler.post(new Runnable() {
+ @Override
+ public void run() {
+ listener.onOpened(camera);
+ }
+ });
+ }
+
+ /**
* Interface for listening to camera devices becoming available or
* unavailable.
*
@@ -265,7 +337,7 @@
*
* <p>If an application had an active CameraDevice instance for the
* now-disconnected camera, that application will receive a
- * {@link CameraDevice.CameraDeviceListener#onCameraDisconnected disconnection error}.</p>
+ * {@link CameraDevice.StateListener#onDisconnected disconnection error}.</p>
*
* <p>The default implementation of this method does nothing.</p>
*
@@ -403,6 +475,7 @@
if (isAvailable(status)) {
handler.post(
new Runnable() {
+ @Override
public void run() {
listener.onCameraAvailable(id);
}
@@ -410,6 +483,7 @@
} else {
handler.post(
new Runnable() {
+ @Override
public void run() {
listener.onCameraUnavailable(id);
}
diff --git a/core/java/android/hardware/camera2/impl/CameraDevice.java b/core/java/android/hardware/camera2/impl/CameraDevice.java
index 995555a..efbd769 100644
--- a/core/java/android/hardware/camera2/impl/CameraDevice.java
+++ b/core/java/android/hardware/camera2/impl/CameraDevice.java
@@ -55,7 +55,7 @@
private final Object mLock = new Object();
private final CameraDeviceCallbacks mCallbacks = new CameraDeviceCallbacks();
- private CameraDeviceListener mDeviceListener;
+ private StateListener mDeviceListener;
private Handler mDeviceHandler;
private final SparseArray<CaptureListenerHolder> mCaptureListenerMap =
@@ -292,7 +292,7 @@
}
@Override
- public void setDeviceListener(CameraDeviceListener listener, Handler handler) {
+ public void setDeviceListener(StateListener listener, Handler handler) {
synchronized (mLock) {
mDeviceListener = listener;
mDeviceHandler = handler;
@@ -314,7 +314,7 @@
}
@Override
- public void close() throws Exception {
+ public void close() {
// TODO: every method should throw IllegalStateException after close has been called
@@ -325,7 +325,7 @@
mRemoteDevice.disconnect();
}
} catch (CameraRuntimeException e) {
- throw e.asChecked();
+ Log.e(TAG, "Exception while closing: ", e.asChecked());
} catch (RemoteException e) {
// impossible
}
@@ -339,8 +339,6 @@
protected void finalize() throws Throwable {
try {
close();
- } catch (CameraRuntimeException e) {
- Log.e(TAG, "Got error while trying to finalize, ignoring: " + e.getMessage());
}
finally {
super.finalize();
diff --git a/core/java/android/hardware/location/GeofenceHardwareImpl.java b/core/java/android/hardware/location/GeofenceHardwareImpl.java
index eac6620..6b61690 100644
--- a/core/java/android/hardware/location/GeofenceHardwareImpl.java
+++ b/core/java/android/hardware/location/GeofenceHardwareImpl.java
@@ -18,17 +18,14 @@
import android.content.Context;
import android.content.pm.PackageManager;
-import android.location.FusedBatchOptions;
import android.location.IFusedGeofenceHardware;
import android.location.IGpsGeofenceHardware;
import android.location.Location;
-import android.location.LocationManager;
import android.os.Handler;
import android.os.IBinder;
import android.os.Message;
import android.os.PowerManager;
import android.os.RemoteException;
-import android.os.SystemClock;
import android.util.Log;
import android.util.SparseArray;
@@ -601,12 +598,13 @@
GeofenceTransition geofenceTransition = (GeofenceTransition)(msg.obj);
synchronized (mGeofences) {
callback = mGeofences.get(geofenceTransition.mGeofenceId);
- }
- if (DEBUG) Log.d(TAG, "GeofenceTransistionCallback: GPS : GeofenceId: " +
- geofenceTransition.mGeofenceId +
- " Transition: " + geofenceTransition.mTransition +
- " Location: " + geofenceTransition.mLocation + ":" + mGeofences);
+ // need to keep access to mGeofences synchronized at all times
+ if (DEBUG) Log.d(TAG, "GeofenceTransistionCallback: GPS : GeofenceId: " +
+ geofenceTransition.mGeofenceId +
+ " Transition: " + geofenceTransition.mTransition +
+ " Location: " + geofenceTransition.mLocation + ":" + mGeofences);
+ }
if (callback != null) {
try {
diff --git a/core/java/android/os/Environment.java b/core/java/android/os/Environment.java
index 5b36bca..fc53580 100644
--- a/core/java/android/os/Environment.java
+++ b/core/java/android/os/Environment.java
@@ -109,29 +109,36 @@
// TODO: generalize further to create package-specific environment
// TODO: add support for secondary external storage
- private final File[] mExternalDirs;
- private final File mMediaDir;
+ /** External storage dirs, as visible to vold */
+ private final File[] mExternalDirsForVold;
+ /** External storage dirs, as visible to apps */
+ private final File[] mExternalDirsForApp;
+ /** Primary emulated storage dir for direct access */
+ private final File mEmulatedDirForDirect;
public UserEnvironment(int userId) {
// See storage config details at http://source.android.com/tech/storage/
String rawExternalStorage = System.getenv(ENV_EXTERNAL_STORAGE);
- String rawEmulatedStorageTarget = System.getenv(ENV_EMULATED_STORAGE_TARGET);
+ String rawEmulatedSource = System.getenv(ENV_EMULATED_STORAGE_SOURCE);
+ String rawEmulatedTarget = System.getenv(ENV_EMULATED_STORAGE_TARGET);
String rawMediaStorage = System.getenv(ENV_MEDIA_STORAGE);
if (TextUtils.isEmpty(rawMediaStorage)) {
rawMediaStorage = "/data/media";
}
- if (!TextUtils.isEmpty(rawEmulatedStorageTarget)) {
+ if (!TextUtils.isEmpty(rawEmulatedTarget)) {
// Device has emulated storage; external storage paths should have
// userId burned into them.
final String rawUserId = Integer.toString(userId);
- final File emulatedBase = new File(rawEmulatedStorageTarget);
+ final File emulatedSourceBase = new File(rawEmulatedSource);
+ final File emulatedTargetBase = new File(rawEmulatedTarget);
final File mediaBase = new File(rawMediaStorage);
// /storage/emulated/0
- mExternalDirs = new File[] { buildPath(emulatedBase, rawUserId) };
+ mExternalDirsForVold = new File[] { buildPath(emulatedSourceBase, rawUserId) };
+ mExternalDirsForApp = new File[] { buildPath(emulatedTargetBase, rawUserId) };
// /data/media/0
- mMediaDir = buildPath(mediaBase, rawUserId);
+ mEmulatedDirForDirect = buildPath(mediaBase, rawUserId);
} else {
// Device has physical external storage; use plain paths.
@@ -141,15 +148,16 @@
}
// /storage/sdcard0
- mExternalDirs = new File[] { new File(rawExternalStorage) };
+ mExternalDirsForVold = new File[] { new File(rawExternalStorage) };
+ mExternalDirsForApp = new File[] { new File(rawExternalStorage) };
// /data/media
- mMediaDir = new File(rawMediaStorage);
+ mEmulatedDirForDirect = new File(rawMediaStorage);
}
}
@Deprecated
public File getExternalStorageDirectory() {
- return mExternalDirs[0];
+ return mExternalDirsForApp[0];
}
@Deprecated
@@ -157,44 +165,56 @@
return buildExternalStoragePublicDirs(type)[0];
}
- public File[] getExternalDirs() {
- return mExternalDirs;
+ public File[] getExternalDirsForVold() {
+ return mExternalDirsForVold;
+ }
+
+ public File[] getExternalDirsForApp() {
+ return mExternalDirsForApp;
}
public File getMediaDir() {
- return mMediaDir;
+ return mEmulatedDirForDirect;
}
public File[] buildExternalStoragePublicDirs(String type) {
- return buildPaths(mExternalDirs, type);
+ return buildPaths(mExternalDirsForApp, type);
}
public File[] buildExternalStorageAndroidDataDirs() {
- return buildPaths(mExternalDirs, DIR_ANDROID, DIR_DATA);
+ return buildPaths(mExternalDirsForApp, DIR_ANDROID, DIR_DATA);
}
public File[] buildExternalStorageAndroidObbDirs() {
- return buildPaths(mExternalDirs, DIR_ANDROID, DIR_OBB);
+ return buildPaths(mExternalDirsForApp, DIR_ANDROID, DIR_OBB);
}
public File[] buildExternalStorageAppDataDirs(String packageName) {
- return buildPaths(mExternalDirs, DIR_ANDROID, DIR_DATA, packageName);
+ return buildPaths(mExternalDirsForApp, DIR_ANDROID, DIR_DATA, packageName);
+ }
+
+ public File[] buildExternalStorageAppDataDirsForVold(String packageName) {
+ return buildPaths(mExternalDirsForVold, DIR_ANDROID, DIR_DATA, packageName);
}
public File[] buildExternalStorageAppMediaDirs(String packageName) {
- return buildPaths(mExternalDirs, DIR_ANDROID, DIR_MEDIA, packageName);
+ return buildPaths(mExternalDirsForApp, DIR_ANDROID, DIR_MEDIA, packageName);
}
public File[] buildExternalStorageAppObbDirs(String packageName) {
- return buildPaths(mExternalDirs, DIR_ANDROID, DIR_OBB, packageName);
+ return buildPaths(mExternalDirsForApp, DIR_ANDROID, DIR_OBB, packageName);
+ }
+
+ public File[] buildExternalStorageAppObbDirsForVold(String packageName) {
+ return buildPaths(mExternalDirsForVold, DIR_ANDROID, DIR_OBB, packageName);
}
public File[] buildExternalStorageAppFilesDirs(String packageName) {
- return buildPaths(mExternalDirs, DIR_ANDROID, DIR_DATA, packageName, DIR_FILES);
+ return buildPaths(mExternalDirsForApp, DIR_ANDROID, DIR_DATA, packageName, DIR_FILES);
}
public File[] buildExternalStorageAppCacheDirs(String packageName) {
- return buildPaths(mExternalDirs, DIR_ANDROID, DIR_DATA, packageName, DIR_CACHE);
+ return buildPaths(mExternalDirsForApp, DIR_ANDROID, DIR_DATA, packageName, DIR_CACHE);
}
}
@@ -344,7 +364,7 @@
*/
public static File getExternalStorageDirectory() {
throwIfUserRequired();
- return sCurrentUser.getExternalDirs()[0];
+ return sCurrentUser.getExternalDirsForApp()[0];
}
/** {@hide} */
diff --git a/core/java/android/os/ParcelFileDescriptor.java b/core/java/android/os/ParcelFileDescriptor.java
index e436241..55c083b 100644
--- a/core/java/android/os/ParcelFileDescriptor.java
+++ b/core/java/android/os/ParcelFileDescriptor.java
@@ -462,6 +462,39 @@
}
/**
+ * Converts a string representing a file mode, such as "rw", into a bitmask suitable for use
+ * with {@link #open}.
+ * <p>
+ * @param mode The string representation of the file mode.
+ * @return A bitmask representing the given file mode.
+ * @throws IllegalArgumentException if the given string does not match a known file mode.
+ */
+ public static int parseMode(String mode) {
+ final int modeBits;
+ if ("r".equals(mode)) {
+ modeBits = ParcelFileDescriptor.MODE_READ_ONLY;
+ } else if ("w".equals(mode) || "wt".equals(mode)) {
+ modeBits = ParcelFileDescriptor.MODE_WRITE_ONLY
+ | ParcelFileDescriptor.MODE_CREATE
+ | ParcelFileDescriptor.MODE_TRUNCATE;
+ } else if ("wa".equals(mode)) {
+ modeBits = ParcelFileDescriptor.MODE_WRITE_ONLY
+ | ParcelFileDescriptor.MODE_CREATE
+ | ParcelFileDescriptor.MODE_APPEND;
+ } else if ("rw".equals(mode)) {
+ modeBits = ParcelFileDescriptor.MODE_READ_WRITE
+ | ParcelFileDescriptor.MODE_CREATE;
+ } else if ("rwt".equals(mode)) {
+ modeBits = ParcelFileDescriptor.MODE_READ_WRITE
+ | ParcelFileDescriptor.MODE_CREATE
+ | ParcelFileDescriptor.MODE_TRUNCATE;
+ } else {
+ throw new IllegalArgumentException("Bad mode '" + mode + "'");
+ }
+ return modeBits;
+ }
+
+ /**
* Retrieve the actual FileDescriptor associated with this object.
*
* @return Returns the FileDescriptor associated with this object.
diff --git a/core/java/android/os/WorkSource.java b/core/java/android/os/WorkSource.java
index 30d535b..e66df04 100644
--- a/core/java/android/os/WorkSource.java
+++ b/core/java/android/os/WorkSource.java
@@ -103,7 +103,21 @@
* @hide
*/
public void clearNames() {
- mNames = null;
+ if (mNames != null) {
+ mNames = null;
+ // Clear out any duplicate uids now that we don't have names to disambiguate them.
+ int destIndex = 1;
+ int newNum = mNum;
+ for (int sourceIndex = 1; sourceIndex < mNum; sourceIndex++) {
+ if (mUids[sourceIndex] == mUids[sourceIndex - 1]) {
+ newNum--;
+ } else {
+ mUids[destIndex] = mUids[sourceIndex];
+ destIndex++;
+ }
+ }
+ mNum = newNum;
+ }
}
/**
diff --git a/core/java/android/os/storage/IMountService.java b/core/java/android/os/storage/IMountService.java
index fc18617..51ba2f6 100644
--- a/core/java/android/os/storage/IMountService.java
+++ b/core/java/android/os/storage/IMountService.java
@@ -20,9 +20,7 @@
import android.os.IBinder;
import android.os.IInterface;
import android.os.Parcel;
-import android.os.Parcelable;
import android.os.RemoteException;
-import android.os.storage.StorageVolume;
/**
* WARNING! Update IMountService.h and IMountService.cpp if you change this
@@ -737,7 +735,25 @@
_data.recycle();
}
return _result;
+ }
+ @Override
+ public int mkdirs(String callingPkg, String path) throws RemoteException {
+ Parcel _data = Parcel.obtain();
+ Parcel _reply = Parcel.obtain();
+ int _result;
+ try {
+ _data.writeInterfaceToken(DESCRIPTOR);
+ _data.writeString(callingPkg);
+ _data.writeString(path);
+ mRemote.transact(Stub.TRANSACTION_mkdirs, _data, _reply, 0);
+ _reply.readException();
+ _result = _reply.readInt();
+ } finally {
+ _reply.recycle();
+ _data.recycle();
+ }
+ return _result;
}
}
@@ -811,6 +827,8 @@
static final int TRANSACTION_fixPermissionsSecureContainer = IBinder.FIRST_CALL_TRANSACTION + 33;
+ static final int TRANSACTION_mkdirs = IBinder.FIRST_CALL_TRANSACTION + 34;
+
/**
* Cast an IBinder object into an IMountService interface, generating a
* proxy if needed.
@@ -1154,6 +1172,15 @@
reply.writeInt(resultCode);
return true;
}
+ case TRANSACTION_mkdirs: {
+ data.enforceInterface(DESCRIPTOR);
+ String callingPkg = data.readString();
+ String path = data.readString();
+ int result = mkdirs(callingPkg, path);
+ reply.writeNoException();
+ reply.writeInt(result);
+ return true;
+ }
}
return super.onTransact(code, data, reply, flags);
}
@@ -1376,4 +1403,13 @@
*/
public int fixPermissionsSecureContainer(String id, int gid, String filename)
throws RemoteException;
+
+ /**
+ * Ensure that all directories along given path exist, creating parent
+ * directories as needed. Validates that given path is absolute and that it
+ * contains no relative "." or ".." paths or symlinks. Also ensures that
+ * path belongs to a volume managed by vold, and that path is either
+ * external storage data or OBB directory belonging to calling app.
+ */
+ public int mkdirs(String callingPkg, String path) throws RemoteException;
}
diff --git a/core/java/android/text/Layout.java b/core/java/android/text/Layout.java
index a6e8c70..9dfd383 100644
--- a/core/java/android/text/Layout.java
+++ b/core/java/android/text/Layout.java
@@ -1115,7 +1115,7 @@
float dist = Math.abs(getPrimaryHorizontal(max) - horiz);
- if (dist < bestdist) {
+ if (dist <= bestdist) {
bestdist = dist;
best = max;
}
diff --git a/core/jni/android_media_AudioRecord.cpp b/core/jni/android_media_AudioRecord.cpp
index 3994047..1c43cc5 100644
--- a/core/jni/android_media_AudioRecord.cpp
+++ b/core/jni/android_media_AudioRecord.cpp
@@ -310,18 +310,6 @@
// ----------------------------------------------------------------------------
-// This class is used to destroy a RefBase asynchronously
-class AsyncDestructThread : public Thread
-{
-public:
- AsyncDestructThread(sp<RefBase> refBase) : mRefBase(refBase) { }
-protected:
- virtual ~AsyncDestructThread() { }
-private:
- virtual bool threadLoop() { return false; }
- const sp<RefBase> mRefBase;
-};
-
#define CALLBACK_COND_WAIT_TIMEOUT_MS 1000
static void android_media_AudioRecord_release(JNIEnv *env, jobject thiz) {
sp<AudioRecord> lpRecorder = setAudioRecord(env, thiz, 0);
@@ -354,17 +342,6 @@
env->DeleteGlobalRef(lpCookie->audioRecord_ref);
delete lpCookie;
}
- // FIXME AudioRecord destruction should not be slow
- if (lpRecorder != 0) {
- // must be a raw reference to avoid a race after run()
- AsyncDestructThread *adt = new AsyncDestructThread(lpRecorder);
- // guaranteed to not run destructor
- lpRecorder.clear();
- // after the run(), adt thread will hold a strong reference to lpRecorder,
- // and the only strong reference to itself
- adt->run("AsyncDestruct");
- // do not delete adt here: adt thread destroys itself, and lpRecorder if needed
- }
}
diff --git a/libs/hwui/DisplayListOp.h b/libs/hwui/DisplayListOp.h
index b052461..326805a 100644
--- a/libs/hwui/DisplayListOp.h
+++ b/libs/hwui/DisplayListOp.h
@@ -772,7 +772,7 @@
TextureVertex* vertex = &vertices[0];
const bool hasLayer = renderer.hasLayer();
- bool transformed = false;
+ bool pureTranslate = true;
// TODO: manually handle rect clip for bitmaps by adjusting texCoords per op,
// and allowing them to be merged in getBatchId()
@@ -782,7 +782,7 @@
// When we reach multiDraw(), the matrix can be either
// pureTranslate or simple (translate and/or scale).
// If the matrix is not pureTranslate, then we have a scale
- if (state.mMatrix.isPureTranslate()) transformed = true;
+ pureTranslate &= state.mMatrix.isPureTranslate();
Rect texCoords(0, 0, 1, 1);
((DrawBitmapOp*) ops[i].op)->mUvMapper.map(texCoords);
@@ -801,7 +801,7 @@
}
return renderer.drawBitmaps(mBitmap, mEntry, ops.size(), &vertices[0],
- transformed, bounds, mPaint);
+ pureTranslate, bounds, mPaint);
}
virtual void output(int level, uint32_t logFlags) const {
diff --git a/libs/hwui/Matrix.cpp b/libs/hwui/Matrix.cpp
index 1948778..ba22071 100644
--- a/libs/hwui/Matrix.cpp
+++ b/libs/hwui/Matrix.cpp
@@ -110,10 +110,6 @@
mType |= kTypeRectToRect;
}
}
-
- if (m00 > 0.0f && m11 > 0.0f) {
- mType |= kTypePositiveScale;
- }
}
return mType;
}
@@ -127,7 +123,7 @@
}
bool Matrix4::positiveScale() const {
- return getType() & kTypePositiveScale;
+ return (data[kScaleX] > 0.0f && data[kScaleY] > 0.0f);
}
bool Matrix4::changesBounds() const {
diff --git a/libs/hwui/Matrix.h b/libs/hwui/Matrix.h
index e2c5b20..b861ba4 100644
--- a/libs/hwui/Matrix.h
+++ b/libs/hwui/Matrix.h
@@ -64,8 +64,7 @@
kTypeAffine = 0x4,
kTypePerspective = 0x8,
kTypeRectToRect = 0x10,
- kTypePositiveScale = 0x20,
- kTypeUnknown = 0x40,
+ kTypeUnknown = 0x20,
};
static const int sGeometryMask = 0xf;
diff --git a/libs/hwui/OpenGLRenderer.cpp b/libs/hwui/OpenGLRenderer.cpp
index 89a82fd..35fc804 100644
--- a/libs/hwui/OpenGLRenderer.cpp
+++ b/libs/hwui/OpenGLRenderer.cpp
@@ -2096,7 +2096,7 @@
* The caller is responsible for properly dirtying the current layer.
*/
status_t OpenGLRenderer::drawBitmaps(SkBitmap* bitmap, AssetAtlas::Entry* entry, int bitmapCount,
- TextureVertex* vertices, bool transformed, const Rect& bounds, SkPaint* paint) {
+ TextureVertex* vertices, bool pureTranslate, const Rect& bounds, SkPaint* paint) {
mCaches.activeTexture(0);
Texture* texture = entry ? entry->texture : mCaches.textureCache.get(bitmap);
if (!texture) return DrawGlInfo::kStatusDone;
@@ -2108,7 +2108,7 @@
getAlphaAndMode(paint, &alpha, &mode);
texture->setWrap(GL_CLAMP_TO_EDGE, true);
- texture->setFilter(transformed ? FILTER(paint) : GL_NEAREST, true);
+ texture->setFilter(pureTranslate ? GL_NEAREST : FILTER(paint), true);
const float x = (int) floorf(bounds.left + 0.5f);
const float y = (int) floorf(bounds.top + 0.5f);
diff --git a/libs/hwui/OpenGLRenderer.h b/libs/hwui/OpenGLRenderer.h
index f74df97..9afb7ad 100644
--- a/libs/hwui/OpenGLRenderer.h
+++ b/libs/hwui/OpenGLRenderer.h
@@ -285,7 +285,7 @@
virtual status_t drawLayer(Layer* layer, float x, float y);
virtual status_t drawBitmap(SkBitmap* bitmap, float left, float top, SkPaint* paint);
status_t drawBitmaps(SkBitmap* bitmap, AssetAtlas::Entry* entry, int bitmapCount,
- TextureVertex* vertices, bool transformed, const Rect& bounds, SkPaint* paint);
+ TextureVertex* vertices, bool pureTranslate, const Rect& bounds, SkPaint* paint);
virtual status_t drawBitmap(SkBitmap* bitmap, SkMatrix* matrix, SkPaint* paint);
virtual status_t drawBitmap(SkBitmap* bitmap, float srcLeft, float srcTop,
float srcRight, float srcBottom, float dstLeft, float dstTop,
diff --git a/packages/ExternalStorageProvider/src/com/android/externalstorage/ExternalStorageProvider.java b/packages/ExternalStorageProvider/src/com/android/externalstorage/ExternalStorageProvider.java
index 3e2cd15..f468abc 100644
--- a/packages/ExternalStorageProvider/src/com/android/externalstorage/ExternalStorageProvider.java
+++ b/packages/ExternalStorageProvider/src/com/android/externalstorage/ExternalStorageProvider.java
@@ -310,7 +310,7 @@
String documentId, String mode, CancellationSignal signal)
throws FileNotFoundException {
final File file = getFileForDocId(documentId);
- return ParcelFileDescriptor.open(file, ContentResolver.modeToMode(null, mode));
+ return ParcelFileDescriptor.open(file, ParcelFileDescriptor.parseMode(mode));
}
@Override
diff --git a/packages/PrintSpooler/src/com/android/printspooler/PrintJobConfigActivity.java b/packages/PrintSpooler/src/com/android/printspooler/PrintJobConfigActivity.java
index af1c60e..bcef79c 100644
--- a/packages/PrintSpooler/src/com/android/printspooler/PrintJobConfigActivity.java
+++ b/packages/PrintSpooler/src/com/android/printspooler/PrintJobConfigActivity.java
@@ -29,7 +29,6 @@
import android.graphics.drawable.Drawable;
import android.net.Uri;
import android.os.AsyncTask;
-import android.os.Build;
import android.os.Bundle;
import android.os.Handler;
import android.os.IBinder;
@@ -102,7 +101,7 @@
private static final String LOG_TAG = "PrintJobConfigActivity";
- private static final boolean DEBUG = true && Build.IS_DEBUGGABLE;
+ private static final boolean DEBUG = false;
public static final String EXTRA_PRINT_DOCUMENT_ADAPTER = "printDocumentAdapter";
public static final String EXTRA_PRINT_JOB = "printJob";
@@ -344,7 +343,9 @@
if (!mController.hasStarted()) {
mController.start();
}
- if (!printAttributesChanged()) {
+ // If print is confirmed we always do a layout since the previous
+ // ones were for preview and this one is for printing.
+ if (!printAttributesChanged() && !mEditor.isPrintConfirmed()) {
if (mDocument.info == null) {
// We are waiting for the result of a layout, so do nothing.
return;
@@ -391,8 +392,12 @@
mControllerState = CONTROLLER_STATE_LAYOUT_COMPLETED;
+ // For layout purposes we care only whether the type or the page
+ // count changed. We still do not have the size since we did not
+ // call write. We use "layoutChanged" set by the application to
+ // know whether something else changed about the document.
+ final boolean infoChanged = !equalsIgnoreSize(info, mDocument.info);
// If the info changed, we update the document and the print job.
- final boolean infoChanged = !info.equals(mDocument.info);
if (infoChanged) {
mDocument.info = info;
// Set the info.
@@ -482,6 +487,10 @@
.generateFileForPrintJob(mPrintJobId);
mDocument.info.setDataSize(file.length());
+ // Update the print job with the updated info.
+ PrintSpoolerService.peekInstance().setPrintJobPrintDocumentInfoNoPersistence(
+ mPrintJobId, mDocument.info);
+
// Update which pages we have fetched.
mDocument.pages = PageRangeUtils.normalize(pages);
@@ -556,6 +565,26 @@
PrintJobConfigActivity.this.finish();
}
+ private boolean equalsIgnoreSize(PrintDocumentInfo lhs, PrintDocumentInfo rhs) {
+ if (lhs == rhs) {
+ return true;
+ }
+ if (lhs == null) {
+ if (rhs != null) {
+ return false;
+ }
+ } else {
+ if (rhs == null) {
+ return false;
+ }
+ if (lhs.getContentType() != rhs.getContentType()
+ || lhs.getPageCount() != rhs.getPageCount()) {
+ return false;
+ }
+ }
+ return true;
+ }
+
private final class ControllerHandler extends Handler {
public static final int MSG_ON_LAYOUT_FINISHED = 1;
public static final int MSG_ON_LAYOUT_FAILED = 2;
diff --git a/services/java/com/android/server/MountService.java b/services/java/com/android/server/MountService.java
index 1facb80..6ab86f5 100644
--- a/services/java/com/android/server/MountService.java
+++ b/services/java/com/android/server/MountService.java
@@ -19,6 +19,7 @@
import static android.content.pm.PackageManager.PERMISSION_GRANTED;
import android.Manifest;
+import android.app.AppOpsManager;
import android.content.BroadcastReceiver;
import android.content.ComponentName;
import android.content.Context;
@@ -2128,6 +2129,85 @@
}
@Override
+ public int mkdirs(String callingPkg, String appPath) {
+ final int userId = UserHandle.getUserId(Binder.getCallingUid());
+ final UserEnvironment userEnv = new UserEnvironment(userId);
+
+ // Validate that reported package name belongs to caller
+ final AppOpsManager appOps = (AppOpsManager) mContext.getSystemService(
+ Context.APP_OPS_SERVICE);
+ appOps.checkPackage(Binder.getCallingUid(), callingPkg);
+
+ try {
+ appPath = new File(appPath).getCanonicalPath();
+ } catch (IOException e) {
+ Slog.e(TAG, "Failed to resolve " + appPath + ": " + e);
+ return -1;
+ }
+
+ // Try translating the app path into a vold path, but require that it
+ // belong to the calling package.
+ String voldPath = maybeTranslatePathForVold(appPath,
+ userEnv.buildExternalStorageAppDataDirs(callingPkg),
+ userEnv.buildExternalStorageAppDataDirsForVold(callingPkg));
+ if (voldPath != null) {
+ try {
+ mConnector.execute("volume", "mkdirs", voldPath);
+ return 0;
+ } catch (NativeDaemonConnectorException e) {
+ return e.getCode();
+ }
+ }
+
+ voldPath = maybeTranslatePathForVold(appPath,
+ userEnv.buildExternalStorageAppObbDirs(callingPkg),
+ userEnv.buildExternalStorageAppObbDirsForVold(callingPkg));
+ if (voldPath != null) {
+ try {
+ mConnector.execute("volume", "mkdirs", voldPath);
+ return 0;
+ } catch (NativeDaemonConnectorException e) {
+ return e.getCode();
+ }
+ }
+
+ throw new SecurityException("Invalid mkdirs path: " + appPath);
+ }
+
+ /**
+ * Translate the given path from an app-visible path to a vold-visible path,
+ * but only if it's under the given whitelisted paths.
+ *
+ * @param path a canonicalized app-visible path.
+ * @param appPaths list of app-visible paths that are allowed.
+ * @param voldPaths list of vold-visible paths directly corresponding to the
+ * allowed app-visible paths argument.
+ * @return a vold-visible path representing the original path, or
+ * {@code null} if the given path didn't have an app-to-vold
+ * mapping.
+ */
+ @VisibleForTesting
+ public static String maybeTranslatePathForVold(
+ String path, File[] appPaths, File[] voldPaths) {
+ if (appPaths.length != voldPaths.length) {
+ throw new IllegalStateException("Paths must be 1:1 mapping");
+ }
+
+ for (int i = 0; i < appPaths.length; i++) {
+ final String appPath = appPaths[i].getAbsolutePath();
+ if (path.startsWith(appPath)) {
+ path = new File(voldPaths[i], path.substring(appPath.length() + 1))
+ .getAbsolutePath();
+ if (!path.endsWith("/")) {
+ path = path + "/";
+ }
+ return path;
+ }
+ }
+ return null;
+ }
+
+ @Override
public StorageVolume[] getVolumeList() {
final int callingUserId = UserHandle.getCallingUserId();
final boolean accessAll = (mContext.checkPermission(
@@ -2651,7 +2731,7 @@
if (forVold) {
return new File(Environment.getEmulatedStorageSource(userId), path).getAbsolutePath();
} else {
- return new File(userEnv.getExternalDirs()[0], path).getAbsolutePath();
+ return new File(userEnv.getExternalDirsForApp()[0], path).getAbsolutePath();
}
}
diff --git a/services/java/com/android/server/wm/WindowManagerService.java b/services/java/com/android/server/wm/WindowManagerService.java
index 29a5d5f..d063db5 100644
--- a/services/java/com/android/server/wm/WindowManagerService.java
+++ b/services/java/com/android/server/wm/WindowManagerService.java
@@ -4800,7 +4800,6 @@
}
final TaskStack stack = task.mStack;
stack.moveTaskToBottom(task);
- task.getDisplayContent().moveStack(stack, false);
moveStackWindowsLocked(stack);
}
} finally {
diff --git a/services/jni/com_android_server_location_FlpHardwareProvider.cpp b/services/jni/com_android_server_location_FlpHardwareProvider.cpp
index ac269eb..6c14887 100644
--- a/services/jni/com_android_server_location_FlpHardwareProvider.cpp
+++ b/services/jni/com_android_server_location_FlpHardwareProvider.cpp
@@ -938,6 +938,7 @@
}
sFlpGeofencingInterface->remove_geofences(geofenceIdsCount, geofenceIds);
+ env->ReleaseIntArrayElements(geofenceIdsArray, geofenceIds, 0 /*mode*/);
}
static JNINativeMethod sMethods[] = {
diff --git a/wifi/java/android/net/wifi/WifiConfiguration.java b/wifi/java/android/net/wifi/WifiConfiguration.java
index 2b3c9e2..4c84f17 100644
--- a/wifi/java/android/net/wifi/WifiConfiguration.java
+++ b/wifi/java/android/net/wifi/WifiConfiguration.java
@@ -554,8 +554,8 @@
/** @hide */
public int getAuthType() {
- if (allowedKeyManagement.cardinality() > 1) {
- throw new IllegalStateException("More than one auth type set");
+ if (isValid() == false) {
+ throw new IllegalStateException("Invalid configuration");
}
if (allowedKeyManagement.get(KeyMgmt.WPA_PSK)) {
return KeyMgmt.WPA_PSK;
diff --git a/wifi/java/android/net/wifi/WifiManager.java b/wifi/java/android/net/wifi/WifiManager.java
index 95ee85b..b7594ee1 100644
--- a/wifi/java/android/net/wifi/WifiManager.java
+++ b/wifi/java/android/net/wifi/WifiManager.java
@@ -1867,13 +1867,16 @@
boolean changed = true;
if (ws == null) {
mWorkSource = null;
- } else if (mWorkSource == null) {
- changed = mWorkSource != null;
- mWorkSource = new WorkSource(ws);
} else {
- changed = mWorkSource.diff(ws);
- if (changed) {
- mWorkSource.set(ws);
+ ws.clearNames();
+ if (mWorkSource == null) {
+ changed = mWorkSource != null;
+ mWorkSource = new WorkSource(ws);
+ } else {
+ changed = mWorkSource.diff(ws);
+ if (changed) {
+ mWorkSource.set(ws);
+ }
}
}
if (changed && mHeld) {