diff options
Diffstat (limited to 'core/java/android')
42 files changed, 1529 insertions, 383 deletions
diff --git a/core/java/android/app/ActivityManagerNative.java b/core/java/android/app/ActivityManagerNative.java index a727b07..7d4d57c 100644 --- a/core/java/android/app/ActivityManagerNative.java +++ b/core/java/android/app/ActivityManagerNative.java @@ -152,7 +152,7 @@ public abstract class ActivityManagerNative extends Binder implements IActivityM int startFlags = data.readInt(); String profileFile = data.readString(); ParcelFileDescriptor profileFd = data.readInt() != 0 - ? data.readFileDescriptor() : null; + ? ParcelFileDescriptor.CREATOR.createFromParcel(data) : null; Bundle options = data.readInt() != 0 ? Bundle.CREATOR.createFromParcel(data) : null; int userId = data.readInt(); @@ -178,7 +178,7 @@ public abstract class ActivityManagerNative extends Binder implements IActivityM int startFlags = data.readInt(); String profileFile = data.readString(); ParcelFileDescriptor profileFd = data.readInt() != 0 - ? data.readFileDescriptor() : null; + ? ParcelFileDescriptor.CREATOR.createFromParcel(data) : null; Bundle options = data.readInt() != 0 ? Bundle.CREATOR.createFromParcel(data) : null; int userId = data.readInt(); @@ -1354,7 +1354,7 @@ public abstract class ActivityManagerNative extends Binder implements IActivityM int profileType = data.readInt(); String path = data.readString(); ParcelFileDescriptor fd = data.readInt() != 0 - ? data.readFileDescriptor() : null; + ? ParcelFileDescriptor.CREATOR.createFromParcel(data) : null; boolean res = profileControl(process, userId, start, path, fd, profileType); reply.writeNoException(); reply.writeInt(res ? 1 : 0); @@ -1608,7 +1608,7 @@ public abstract class ActivityManagerNative extends Binder implements IActivityM boolean managed = data.readInt() != 0; String path = data.readString(); ParcelFileDescriptor fd = data.readInt() != 0 - ? data.readFileDescriptor() : null; + ? ParcelFileDescriptor.CREATOR.createFromParcel(data) : null; boolean res = dumpHeap(process, userId, managed, path, fd); reply.writeNoException(); reply.writeInt(res ? 1 : 0); diff --git a/core/java/android/app/AppOpsManager.java b/core/java/android/app/AppOpsManager.java index bf2a1e4..055044b 100644 --- a/core/java/android/app/AppOpsManager.java +++ b/core/java/android/app/AppOpsManager.java @@ -23,6 +23,7 @@ import com.android.internal.app.IAppOpsService; import com.android.internal.app.IAppOpsCallback; import java.util.ArrayList; +import java.util.HashMap; import java.util.List; import android.content.Context; @@ -32,50 +33,76 @@ import android.os.Process; import android.os.RemoteException; /** - * API for interacting with "application operation" tracking. Allows you to: + * API for interacting with "application operation" tracking. * - * <ul> - * <li> Note when operations are happening, and find out if they are allowed for the current - * caller.</li> - * <li> Disallow specific apps from doing specific operations.</li> - * <li> Collect all of the current information about operations that have been executed or are not - * being allowed.</li> - * <li> Monitor for changes in whether an operation is allowed.</li> - * </ul> - * - * <p>Each operation is identified by a single integer; these integers are a fixed set of - * operations, enumerated by the OP_* constants. - * - * <p></p>When checking operations, the result is a "mode" integer indicating the current setting - * for the operation under that caller: MODE_ALLOWED, MODE_IGNORED (don't execute the operation but - * fake its behavior enough so that the caller doesn't crash), MODE_ERRORED (through a - * SecurityException back to the caller; the normal operation calls will do this for you). + * <p>This API is not generally intended for third party application developers; most + * features are only available to system applicatins. Obtain an instance of it through + * {@link Context#getSystemService(String) Context.getSystemService} with + * {@link Context#APP_OPS_SERVICE Context.APP_OPS_SERVICE}.</p> */ public class AppOpsManager { + /** + * <p>App ops allows callers to:</p> + * + * <ul> + * <li> Note when operations are happening, and find out if they are allowed for the current + * caller.</li> + * <li> Disallow specific apps from doing specific operations.</li> + * <li> Collect all of the current information about operations that have been executed or + * are not being allowed.</li> + * <li> Monitor for changes in whether an operation is allowed.</li> + * </ul> + * + * <p>Each operation is identified by a single integer; these integers are a fixed set of + * operations, enumerated by the OP_* constants. + * + * <p></p>When checking operations, the result is a "mode" integer indicating the current + * setting for the operation under that caller: MODE_ALLOWED, MODE_IGNORED (don't execute + * the operation but fake its behavior enough so that the caller doesn't crash), + * MODE_ERRORED (throw a SecurityException back to the caller; the normal operation calls + * will do this for you). + */ + final Context mContext; final IAppOpsService mService; - final ArrayMap<Callback, IAppOpsCallback> mModeWatchers - = new ArrayMap<Callback, IAppOpsCallback>(); + final ArrayMap<OnOpChangedListener, IAppOpsCallback> mModeWatchers + = new ArrayMap<OnOpChangedListener, IAppOpsCallback>(); static IBinder sToken; + /** + * Result from {@link #checkOp}, {@link #noteOp}, {@link #startOp}: the given caller is + * allowed to perform the given operation. + */ public static final int MODE_ALLOWED = 0; + + /** + * Result from {@link #checkOp}, {@link #noteOp}, {@link #startOp}: the given caller is + * not allowed to perform the given operation, and this attempt should + * <em>silently fail</em> (it should not cause the app to crash). + */ public static final int MODE_IGNORED = 1; + + /** + * Result from {@link #checkOpNoThrow}, {@link #noteOpNoThrow}, {@link #startOpNoThrow}: the + * given caller is not allowed to perform the given operation, and this attempt should + * cause it to have a fatal error, typically a {@link SecurityException}. + */ public static final int MODE_ERRORED = 2; // when adding one of these: // - increment _NUM_OP - // - add rows to sOpToSwitch, sOpNames, sOpPerms, sOpDefaultMode + // - add rows to sOpToSwitch, sOpToString, sOpNames, sOpPerms, sOpDefaultMode // - add descriptive strings to Settings/res/values/arrays.xml // - add the op to the appropriate template in AppOpsState.OpsTemplate (settings app) - /** No operation specified. */ + /** @hide No operation specified. */ public static final int OP_NONE = -1; - /** Access to coarse location information. */ + /** @hide Access to coarse location information. */ public static final int OP_COARSE_LOCATION = 0; - /** Access to fine location information. */ + /** @hide Access to fine location information. */ public static final int OP_FINE_LOCATION = 1; - /** Causing GPS to run. */ + /** @hide Causing GPS to run. */ public static final int OP_GPS = 2; /** @hide */ public static final int OP_VIBRATE = 3; @@ -153,13 +180,26 @@ public class AppOpsManager { public static final int OP_AUDIO_BLUETOOTH_VOLUME = 39; /** @hide */ public static final int OP_WAKE_LOCK = 40; - /** Continually monitoring location data. */ + /** @hide Continually monitoring location data. */ public static final int OP_MONITOR_LOCATION = 41; - /** Continually monitoring location data with a relatively high power request. */ + /** @hide Continually monitoring location data with a relatively high power request. */ public static final int OP_MONITOR_HIGH_POWER_LOCATION = 42; /** @hide */ public static final int _NUM_OP = 43; + /** Access to coarse location information. */ + public static final String OPSTR_COARSE_LOCATION = + "android:coarse_location"; + /** Access to fine location information. */ + public static final String OPSTR_FINE_LOCATION = + "android:fine_location"; + /** Continually monitoring location data. */ + public static final String OPSTR_MONITOR_LOCATION + = "android:monitor_location"; + /** Continually monitoring location data with a relatively high power request. */ + public static final String OPSTR_MONITOR_HIGH_POWER_LOCATION + = "android:monitor_location_high_power"; + /** * This maps each operation to the operation that serves as the * switch to determine whether it is allowed. Generally this is @@ -215,6 +255,56 @@ public class AppOpsManager { }; /** + * This maps each operation to the public string constant for it. + * If it doesn't have a public string constant, it maps to null. + */ + private static String[] sOpToString = new String[] { + OPSTR_COARSE_LOCATION, + OPSTR_FINE_LOCATION, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + OPSTR_MONITOR_LOCATION, + OPSTR_MONITOR_HIGH_POWER_LOCATION, + }; + + /** * This provides a simple name for each operation to be used * in debug output. */ @@ -363,6 +453,36 @@ public class AppOpsManager { AppOpsManager.MODE_ALLOWED, }; + private static HashMap<String, Integer> sOpStrToOp = new HashMap<String, Integer>(); + + static { + if (sOpToSwitch.length != _NUM_OP) { + throw new IllegalStateException("sOpStringLength " + sOpToSwitch.length + + " should be " + _NUM_OP); + } + if (sOpToString.length != _NUM_OP) { + throw new IllegalStateException("sOpStringLength " + sOpToString.length + + " should be " + _NUM_OP); + } + if (sOpNames.length != _NUM_OP) { + throw new IllegalStateException("sOpStringLength " + sOpNames.length + + " should be " + _NUM_OP); + } + if (sOpPerms.length != _NUM_OP) { + throw new IllegalStateException("sOpStringLength " + sOpPerms.length + + " should be " + _NUM_OP); + } + if (sOpDefaultMode.length != _NUM_OP) { + throw new IllegalStateException("sOpStringLength " + sOpDefaultMode.length + + " should be " + _NUM_OP); + } + for (int i=0; i<_NUM_OP; i++) { + if (sOpToString[i] != null) { + sOpStrToOp.put(sOpToString[i], i); + } + } + } + /** * Retrieve the op switch that controls the given operation. * @hide @@ -373,6 +493,7 @@ public class AppOpsManager { /** * Retrieve a non-localized name for the operation, for debugging output. + * @hide */ public static String opToName(int op) { if (op == OP_NONE) return "NONE"; @@ -537,8 +658,18 @@ public class AppOpsManager { /** * Callback for notification of changes to operation state. */ - public interface Callback { - public void opChanged(int op, String packageName); + public interface OnOpChangedListener { + public void onOpChanged(String op, String packageName); + } + + /** + * Callback for notification of changes to operation state. + * This allows you to see the raw op codes instead of strings. + * @hide + */ + public static class OnOpChangedInternalListener implements OnOpChangedListener { + public void onOpChanged(String op, String packageName) { } + public void onOpChanged(int op, String packageName) { } } AppOpsManager(Context context, IAppOpsService service) { @@ -598,13 +729,18 @@ public class AppOpsManager { * @param packageName The name of the application to monitor. * @param callback Where to report changes. */ - public void startWatchingMode(int op, String packageName, final Callback callback) { + public void startWatchingMode(int op, String packageName, final OnOpChangedListener callback) { synchronized (mModeWatchers) { IAppOpsCallback cb = mModeWatchers.get(callback); if (cb == null) { cb = new IAppOpsCallback.Stub() { public void opChanged(int op, String packageName) { - callback.opChanged(op, packageName); + if (callback instanceof OnOpChangedInternalListener) { + ((OnOpChangedInternalListener)callback).onOpChanged(op, packageName); + } + if (sOpToString[op] != null) { + callback.onOpChanged(sOpToString[op], packageName); + } } }; mModeWatchers.put(callback, cb); @@ -620,7 +756,7 @@ public class AppOpsManager { * Stop monitoring that was previously started with {@link #startWatchingMode}. All * monitoring associated with this callback will be removed. */ - public void stopWatchingMode(Callback callback) { + public void stopWatchingMode(OnOpChangedListener callback) { synchronized (mModeWatchers) { IAppOpsCallback cb = mModeWatchers.get(callback); if (cb != null) { @@ -636,6 +772,106 @@ public class AppOpsManager { return packageName + " from uid " + uid + " not allowed to perform " + sOpNames[op]; } + private int strOpToOp(String op) { + Integer val = sOpStrToOp.get(op); + if (val == null) { + throw new IllegalArgumentException("Unknown operation string: " + op); + } + return val; + } + + /** + * Do a quick check for whether an application might be able to perform an operation. + * This is <em>not</em> a security check; you must use {@link #noteOp(String, int, String)} + * or {@link #startOp(String, int, String)} for your actual security checks, which also + * ensure that the given uid and package name are consistent. This function can just be + * used for a quick check to see if an operation has been disabled for the application, + * as an early reject of some work. This does not modify the time stamp or other data + * about the operation. + * @param op The operation to check. One of the OPSTR_* constants. + * @param uid The user id of the application attempting to perform the operation. + * @param packageName The name of the application attempting to perform the operation. + * @return Returns {@link #MODE_ALLOWED} if the operation is allowed, or + * {@link #MODE_IGNORED} if it is not allowed and should be silently ignored (without + * causing the app to crash). + * @throws SecurityException If the app has been configured to crash on this op. + */ + public int checkOp(String op, int uid, String packageName) { + return checkOp(strOpToOp(op), uid, packageName); + } + + /** + * Like {@link #checkOp but instead of throwing a {@link SecurityException} it + * returns {@link #MODE_ERRORED}. + */ + public int checkOpNoThrow(String op, int uid, String packageName) { + return checkOpNoThrow(strOpToOp(op), uid, packageName); + } + + /** + * Make note of an application performing an operation. Note that you must pass + * in both the uid and name of the application to be checked; this function will verify + * that these two match, and if not, return {@link #MODE_IGNORED}. If this call + * succeeds, the last execution time of the operation for this app will be updated to + * the current time. + * @param op The operation to note. One of the OPSTR_* constants. + * @param uid The user id of the application attempting to perform the operation. + * @param packageName The name of the application attempting to perform the operation. + * @return Returns {@link #MODE_ALLOWED} if the operation is allowed, or + * {@link #MODE_IGNORED} if it is not allowed and should be silently ignored (without + * causing the app to crash). + * @throws SecurityException If the app has been configured to crash on this op. + */ + public int noteOp(String op, int uid, String packageName) { + return noteOp(strOpToOp(op), uid, packageName); + } + + /** + * Like {@link #noteOp} but instead of throwing a {@link SecurityException} it + * returns {@link #MODE_ERRORED}. + */ + public int noteOpNoThrow(String op, int uid, String packageName) { + return noteOpNoThrow(strOpToOp(op), uid, packageName); + } + + /** + * Report that an application has started executing a long-running operation. Note that you + * must pass in both the uid and name of the application to be checked; this function will + * verify that these two match, and if not, return {@link #MODE_IGNORED}. If this call + * succeeds, the last execution time of the operation for this app will be updated to + * the current time and the operation will be marked as "running". In this case you must + * later call {@link #finishOp(String, int, String)} to report when the application is no + * longer performing the operation. + * @param op The operation to start. One of the OPSTR_* constants. + * @param uid The user id of the application attempting to perform the operation. + * @param packageName The name of the application attempting to perform the operation. + * @return Returns {@link #MODE_ALLOWED} if the operation is allowed, or + * {@link #MODE_IGNORED} if it is not allowed and should be silently ignored (without + * causing the app to crash). + * @throws SecurityException If the app has been configured to crash on this op. + */ + public int startOp(String op, int uid, String packageName) { + return startOp(strOpToOp(op), uid, packageName); + } + + /** + * Like {@link #startOp} but instead of throwing a {@link SecurityException} it + * returns {@link #MODE_ERRORED}. + */ + public int startOpNoThrow(String op, int uid, String packageName) { + return startOpNoThrow(strOpToOp(op), uid, packageName); + } + + /** + * Report that an application is no longer performing an operation that had previously + * been started with {@link #startOp(String, int, String)}. There is no validation of input + * or result; the parameters supplied here must be the exact same ones previously passed + * in when starting the operation. + */ + public void finishOp(String op, int uid, String packageName) { + finishOp(strOpToOp(op), uid, packageName); + } + /** * Do a quick check for whether an application might be able to perform an operation. * This is <em>not</em> a security check; you must use {@link #noteOp(int, int, String)} @@ -651,6 +887,7 @@ public class AppOpsManager { * {@link #MODE_IGNORED} if it is not allowed and should be silently ignored (without * causing the app to crash). * @throws SecurityException If the app has been configured to crash on this op. + * @hide */ public int checkOp(int op, int uid, String packageName) { try { @@ -667,6 +904,7 @@ public class AppOpsManager { /** * Like {@link #checkOp} but instead of throwing a {@link SecurityException} it * returns {@link #MODE_ERRORED}. + * @hide */ public int checkOpNoThrow(int op, int uid, String packageName) { try { @@ -706,6 +944,7 @@ public class AppOpsManager { * {@link #MODE_IGNORED} if it is not allowed and should be silently ignored (without * causing the app to crash). * @throws SecurityException If the app has been configured to crash on this op. + * @hide */ public int noteOp(int op, int uid, String packageName) { try { @@ -722,6 +961,7 @@ public class AppOpsManager { /** * Like {@link #noteOp} but instead of throwing a {@link SecurityException} it * returns {@link #MODE_ERRORED}. + * @hide */ public int noteOpNoThrow(int op, int uid, String packageName) { try { @@ -766,6 +1006,7 @@ public class AppOpsManager { * {@link #MODE_IGNORED} if it is not allowed and should be silently ignored (without * causing the app to crash). * @throws SecurityException If the app has been configured to crash on this op. + * @hide */ public int startOp(int op, int uid, String packageName) { try { @@ -782,6 +1023,7 @@ public class AppOpsManager { /** * Like {@link #startOp} but instead of throwing a {@link SecurityException} it * returns {@link #MODE_ERRORED}. + * @hide */ public int startOpNoThrow(int op, int uid, String packageName) { try { @@ -801,6 +1043,7 @@ public class AppOpsManager { * been started with {@link #startOp(int, int, String)}. There is no validation of input * or result; the parameters supplied here must be the exact same ones previously passed * in when starting the operation. + * @hide */ public void finishOp(int op, int uid, String packageName) { try { @@ -809,6 +1052,7 @@ public class AppOpsManager { } } + /** @hide */ public void finishOp(int op) { finishOp(op, Process.myUid(), mContext.getOpPackageName()); } diff --git a/core/java/android/app/ApplicationThreadNative.java b/core/java/android/app/ApplicationThreadNative.java index 876bf78..e40a04b 100644 --- a/core/java/android/app/ApplicationThreadNative.java +++ b/core/java/android/app/ApplicationThreadNative.java @@ -143,7 +143,7 @@ public abstract class ApplicationThreadNative extends Binder boolean isForward = data.readInt() != 0; String profileName = data.readString(); ParcelFileDescriptor profileFd = data.readInt() != 0 - ? data.readFileDescriptor() : null; + ? ParcelFileDescriptor.CREATOR.createFromParcel(data) : null; boolean autoStopProfiler = data.readInt() != 0; scheduleLaunchActivity(intent, b, ident, info, curConfig, compatInfo, procState, state, ri, pi, notResumed, isForward, profileName, profileFd, autoStopProfiler); @@ -267,7 +267,7 @@ public abstract class ApplicationThreadNative extends Binder ? new ComponentName(data) : null; String profileName = data.readString(); ParcelFileDescriptor profileFd = data.readInt() != 0 - ? data.readFileDescriptor() : null; + ? ParcelFileDescriptor.CREATOR.createFromParcel(data) : null; boolean autoStopProfiler = data.readInt() != 0; Bundle testArgs = data.readBundle(); IBinder binder = data.readStrongBinder(); @@ -418,7 +418,7 @@ public abstract class ApplicationThreadNative extends Binder int profileType = data.readInt(); String path = data.readString(); ParcelFileDescriptor fd = data.readInt() != 0 - ? data.readFileDescriptor() : null; + ? ParcelFileDescriptor.CREATOR.createFromParcel(data) : null; profilerControl(start, path, fd, profileType); return true; } @@ -473,7 +473,7 @@ public abstract class ApplicationThreadNative extends Binder boolean managed = data.readInt() != 0; String path = data.readString(); ParcelFileDescriptor fd = data.readInt() != 0 - ? data.readFileDescriptor() : null; + ? ParcelFileDescriptor.CREATOR.createFromParcel(data) : null; dumpHeap(managed, path, fd); return true; } 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.ServiceManager; 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 @@ class ContextImpl extends Context { if (mExternalObbDirs == null) { mExternalObbDirs = Environment.buildExternalStorageAppObbDirs(getPackageName()); } - return mExternalObbDirs; + + // Create dirs if needed + return ensureDirsExistOrFilter(mExternalObbDirs); } } @@ -2127,14 +2130,25 @@ class ContextImpl extends Context { * 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/app/DownloadManager.java b/core/java/android/app/DownloadManager.java index 165c3db..800ead9 100644 --- a/core/java/android/app/DownloadManager.java +++ b/core/java/android/app/DownloadManager.java @@ -150,6 +150,11 @@ public class DownloadManager { public static final String COLUMN_MEDIAPROVIDER_URI = Downloads.Impl.COLUMN_MEDIAPROVIDER_URI; /** + * @hide + */ + public final static String COLUMN_ALLOW_WRITE = Downloads.Impl.COLUMN_ALLOW_WRITE; + + /** * Value of {@link #COLUMN_STATUS} when the download is waiting to start. */ public final static int STATUS_PENDING = 1 << 0; @@ -315,6 +320,7 @@ public class DownloadManager { Downloads.Impl.COLUMN_TOTAL_BYTES + " AS " + COLUMN_TOTAL_SIZE_BYTES, Downloads.Impl.COLUMN_LAST_MODIFICATION + " AS " + COLUMN_LAST_MODIFIED_TIMESTAMP, Downloads.Impl.COLUMN_CURRENT_BYTES + " AS " + COLUMN_BYTES_DOWNLOADED_SO_FAR, + Downloads.Impl.COLUMN_ALLOW_WRITE, /* add the following 'computed' columns to the cursor. * they are not 'returned' by the database, but their inclusion * eliminates need to have lot of methods in CursorTranslator @@ -1185,6 +1191,14 @@ public class DownloadManager { public long addCompletedDownload(String title, String description, boolean isMediaScannerScannable, String mimeType, String path, long length, boolean showNotification) { + return addCompletedDownload(title, description, isMediaScannerScannable, mimeType, path, + length, showNotification, false); + } + + /** {@hide} */ + public long addCompletedDownload(String title, String description, + boolean isMediaScannerScannable, String mimeType, String path, long length, + boolean showNotification, boolean allowWrite) { // make sure the input args are non-null/non-zero validateArgumentIsNonEmpty("title", title); validateArgumentIsNonEmpty("description", description); @@ -1210,12 +1224,14 @@ public class DownloadManager { Request.SCANNABLE_VALUE_NO); values.put(Downloads.Impl.COLUMN_VISIBILITY, (showNotification) ? Request.VISIBILITY_VISIBLE_NOTIFY_ONLY_COMPLETION : Request.VISIBILITY_HIDDEN); + values.put(Downloads.Impl.COLUMN_ALLOW_WRITE, allowWrite ? 1 : 0); Uri downloadUri = mResolver.insert(Downloads.Impl.CONTENT_URI, values); if (downloadUri == null) { return -1; } return Long.parseLong(downloadUri.getLastPathSegment()); } + private static final String NON_DOWNLOADMANAGER_DOWNLOAD = "non-dwnldmngr-download-dont-retry2download"; @@ -1227,8 +1243,10 @@ public class DownloadManager { /** * Get the DownloadProvider URI for the download with the given ID. + * + * @hide */ - Uri getDownloadUri(long id) { + public Uri getDownloadUri(long id) { return ContentUris.withAppendedId(mBaseUri, id); } 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 @@ public abstract class ContentProvider implements ComponentCallbacks2 { 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/ContentProviderClient.java b/core/java/android/content/ContentProviderClient.java index 39b453d..0650798 100644 --- a/core/java/android/content/ContentProviderClient.java +++ b/core/java/android/content/ContentProviderClient.java @@ -26,6 +26,8 @@ import android.os.RemoteException; import android.os.ParcelFileDescriptor; import android.content.res.AssetFileDescriptor; +import dalvik.system.CloseGuard; + import java.io.FileNotFoundException; import java.util.ArrayList; @@ -49,6 +51,8 @@ public class ContentProviderClient { private final boolean mStable; private boolean mReleased; + private final CloseGuard mGuard = CloseGuard.get(); + /** * @hide */ @@ -58,6 +62,7 @@ public class ContentProviderClient { mContentResolver = contentResolver; mPackageName = contentResolver.mPackageName; mStable = stable; + mGuard.open("release"); } /** See {@link ContentProvider#query ContentProvider.query} */ @@ -324,6 +329,7 @@ public class ContentProviderClient { throw new IllegalStateException("Already released"); } mReleased = true; + mGuard.close(); if (mStable) { return mContentResolver.releaseProvider(mContentProvider); } else { @@ -332,6 +338,13 @@ public class ContentProviderClient { } } + @Override + protected void finalize() throws Throwable { + if (mGuard != null) { + mGuard.warnIfOpen(); + } + } + /** * Get a reference to the {@link ContentProvider} that is associated with this * client. If the {@link ContentProvider} is running in a different process then 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 @@ public abstract class ContentResolver { } } 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 @@ public abstract class ContentResolver { 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/content/res/Resources.java b/core/java/android/content/res/Resources.java index 6483cd9..cd5b5d2 100644 --- a/core/java/android/content/res/Resources.java +++ b/core/java/android/content/res/Resources.java @@ -195,7 +195,9 @@ public class Resources { CompatibilityInfo compatInfo, IBinder token) { mAssets = assets; mMetrics.setToDefaults(); - mCompatibilityInfo = compatInfo; + if (compatInfo != null) { + mCompatibilityInfo = compatInfo; + } mToken = new WeakReference<IBinder>(token); updateConfiguration(config, metrics); assets.ensureStringBlocks(); @@ -1536,9 +1538,8 @@ public class Resources { // it would be cleaner and more maintainble to just be // consistently dealing with a compatible display everywhere in // the framework. - if (mCompatibilityInfo != null) { - mCompatibilityInfo.applyToDisplayMetrics(mMetrics); - } + mCompatibilityInfo.applyToDisplayMetrics(mMetrics); + int configChanges = 0xfffffff; if (config != null) { mTmpConfig.setTo(config); @@ -1546,9 +1547,9 @@ public class Resources { if (density == Configuration.DENSITY_DPI_UNDEFINED) { density = mMetrics.noncompatDensityDpi; } - if (mCompatibilityInfo != null) { - mCompatibilityInfo.applyToConfiguration(density, mTmpConfig); - } + + mCompatibilityInfo.applyToConfiguration(density, mTmpConfig); + if (mTmpConfig.locale == null) { mTmpConfig.locale = Locale.getDefault(); mTmpConfig.setLayoutDirection(mTmpConfig.locale); @@ -1697,8 +1698,7 @@ public class Resources { * @hide */ public CompatibilityInfo getCompatibilityInfo() { - return mCompatibilityInfo != null ? mCompatibilityInfo - : CompatibilityInfo.DEFAULT_COMPATIBILITY_INFO; + return mCompatibilityInfo; } /** @@ -1706,8 +1706,10 @@ public class Resources { * @hide */ public void setCompatibilityInfo(CompatibilityInfo ci) { - mCompatibilityInfo = ci; - updateConfiguration(mConfiguration, mMetrics); + if (ci != null) { + mCompatibilityInfo = ci; + updateConfiguration(mConfiguration, mMetrics); + } } /** @@ -2407,6 +2409,5 @@ public class Resources { mMetrics.setToDefaults(); updateConfiguration(null, null); mAssets.ensureStringBlocks(); - mCompatibilityInfo = CompatibilityInfo.DEFAULT_COMPATIBILITY_INFO; } } 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 @@ import android.util.AndroidException; 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 @@ public class CameraAccessException extends AndroidException { * 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 @@ public class CameraAccessException extends AndroidException { /** * 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 @@ public class CameraAccessException extends AndroidException { 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/CameraProperties.java b/core/java/android/hardware/camera2/CameraCharacteristics.java index a2faf09..85fa7d6 100644 --- a/core/java/android/hardware/camera2/CameraProperties.java +++ b/core/java/android/hardware/camera2/CameraCharacteristics.java @@ -32,7 +32,7 @@ import java.util.List; * @see CameraDevice * @see CameraManager */ -public final class CameraProperties extends CameraMetadata { +public final class CameraCharacteristics extends CameraMetadata { private final CameraMetadataNative mProperties; private List<Key<?>> mAvailableRequestKeys; @@ -42,7 +42,7 @@ public final class CameraProperties extends CameraMetadata { * Takes ownership of the passed-in properties object * @hide */ - public CameraProperties(CameraMetadataNative properties) { + public CameraCharacteristics(CameraMetadataNative properties) { mProperties = properties; } @@ -60,7 +60,7 @@ public final class CameraProperties extends CameraMetadata { * * <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 + * <p>Note that there is no {@code getAvailableCameraCharacteristicsKeys()} -- use * {@link #getKeys()} instead.</p> * * @return List of keys supported by this CameraDevice for CaptureRequests. @@ -81,7 +81,7 @@ public final class CameraProperties extends CameraMetadata { * * <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 + * <p>Note that there is no {@code getAvailableCameraCharacteristicsKeys()} -- use * {@link #getKeys()} instead.</p> * * @return List of keys supported by this CameraDevice for CaptureResults. diff --git a/core/java/android/hardware/camera2/CameraDevice.java b/core/java/android/hardware/camera2/CameraDevice.java index 0c13b0f..ec89041 100644 --- a/core/java/android/hardware/camera2/CameraDevice.java +++ b/core/java/android/hardware/camera2/CameraDevice.java @@ -118,31 +118,32 @@ public interface CameraDevice extends AutoCloseable { * this camera device.</p> * * <p>This ID can be used to query the camera device's {@link - * CameraProperties fixed properties} with {@link - * CameraManager#getCameraProperties}.</p> + * CameraCharacteristics fixed properties} with {@link + * CameraManager#getCameraCharacteristics}.</p> * * <p>This method can be called even if the device has been closed or has encountered * a serious error.</p> * * @return the ID for this camera device * - * @see CameraManager#getCameraProperties + * @see CameraManager#getCameraCharacteristics * @see CameraManager#getDeviceIdList */ public String getId(); /** * Get the static properties for this camera. These are identical to the - * properties returned by {@link CameraManager#getCameraProperties}. + * properties returned by {@link CameraManager#getCameraCharacteristics}. * * @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 + * @see CameraManager#getCameraCharacteristics */ - public CameraProperties getProperties() throws CameraAccessException; - + public CameraCharacteristics getProperties() throws CameraAccessException; /** * <p>Set up a new output set of Surfaces for the camera device.</p> * @@ -163,7 +164,7 @@ public interface CameraDevice extends AutoCloseable { * the size of the Surface with * {@link android.view.SurfaceHolder#setFixedSize} to be one of the * supported - * {@link CameraProperties#SCALER_AVAILABLE_PROCESSED_SIZES processed sizes} + * {@link CameraCharacteristics#SCALER_AVAILABLE_PROCESSED_SIZES processed sizes} * before calling {@link android.view.SurfaceHolder#getSurface}.</li> * * <li>For accessing through an OpenGL texture via a @@ -171,14 +172,14 @@ public interface CameraDevice extends AutoCloseable { * the SurfaceTexture with * {@link android.graphics.SurfaceTexture#setDefaultBufferSize} to be one * of the supported - * {@link CameraProperties#SCALER_AVAILABLE_PROCESSED_SIZES processed sizes} + * {@link CameraCharacteristics#SCALER_AVAILABLE_PROCESSED_SIZES processed sizes} * before creating a Surface from the SurfaceTexture with * {@link Surface#Surface}.</li> * * <li>For recording with {@link android.media.MediaCodec}: Call * {@link android.media.MediaCodec#createInputSurface} after configuring * the media codec to use one of the - * {@link CameraProperties#SCALER_AVAILABLE_PROCESSED_SIZES processed sizes} + * {@link CameraCharacteristics#SCALER_AVAILABLE_PROCESSED_SIZES processed sizes} * </li> * * <li>For recording with {@link android.media.MediaRecorder}: TODO</li> @@ -187,16 +188,16 @@ public interface CameraDevice extends AutoCloseable { * Create a RenderScript * {@link android.renderscript.Allocation Allocation} with a supported YUV * type, the IO_INPUT flag, and one of the supported - * {@link CameraProperties#SCALER_AVAILABLE_PROCESSED_SIZES processed sizes}. Then + * {@link CameraCharacteristics#SCALER_AVAILABLE_PROCESSED_SIZES processed sizes}. Then * obtain the Surface with * {@link android.renderscript.Allocation#getSurface}.</li> * * <li>For access to uncompressed or JPEG data in the application: Create a * {@link android.media.ImageReader} object with the desired - * {@link CameraProperties#SCALER_AVAILABLE_FORMATS image format}, and a + * {@link CameraCharacteristics#SCALER_AVAILABLE_FORMATS image format}, and a * size from the matching - * {@link CameraProperties#SCALER_AVAILABLE_PROCESSED_SIZES processed}, - * {@link CameraProperties#SCALER_AVAILABLE_JPEG_SIZES jpeg}. Then obtain + * {@link CameraCharacteristics#SCALER_AVAILABLE_PROCESSED_SIZES processed}, + * {@link CameraCharacteristics#SCALER_AVAILABLE_JPEG_SIZES jpeg}. Then obtain * a Surface from it.</li> * * </ul> @@ -218,23 +219,31 @@ public interface CameraDevice extends AutoCloseable { * * <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 +264,9 @@ public interface CameraDevice extends AutoCloseable { * * @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 +304,13 @@ public interface CameraDevice extends AutoCloseable { * {@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. + * @return int A unique capture sequence ID used by + * {@link CaptureListener#onCaptureSequenceCompleted}. + * + * @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. @@ -306,7 +319,7 @@ public interface CameraDevice extends AutoCloseable { * @see #setRepeatingRequest * @see #setRepeatingBurst */ - public void capture(CaptureRequest request, CaptureListener listener, Handler handler) + public int capture(CaptureRequest request, CaptureListener listener, Handler handler) throws CameraAccessException; /** @@ -335,9 +348,13 @@ public interface CameraDevice extends AutoCloseable { * {@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. + * @return int A unique capture sequence ID used by + * {@link CaptureListener#onCaptureSequenceCompleted}. + * + * @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. @@ -346,7 +363,7 @@ public interface CameraDevice extends AutoCloseable { * @see #setRepeatingRequest * @see #setRepeatingBurst */ - public void captureBurst(List<CaptureRequest> requests, CaptureListener listener, + public int captureBurst(List<CaptureRequest> requests, CaptureListener listener, Handler handler) throws CameraAccessException; /** @@ -387,10 +404,13 @@ public interface CameraDevice extends AutoCloseable { * {@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. + * @return int A unique capture sequence ID used by + * {@link CaptureListener#onCaptureSequenceCompleted}. + * + * @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. @@ -401,7 +421,7 @@ public interface CameraDevice extends AutoCloseable { * @see #stopRepeating * @see #flush */ - public void setRepeatingRequest(CaptureRequest request, CaptureListener listener, + public int setRepeatingRequest(CaptureRequest request, CaptureListener listener, Handler handler) throws CameraAccessException; /** @@ -442,9 +462,13 @@ public interface CameraDevice extends AutoCloseable { * {@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. + * @return int A unique capture sequence ID used by + * {@link CaptureListener#onCaptureSequenceCompleted}. + * + * @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. @@ -455,7 +479,7 @@ public interface CameraDevice extends AutoCloseable { * @see #stopRepeating * @see #flush */ - public void setRepeatingBurst(List<CaptureRequest> requests, CaptureListener listener, + public int setRepeatingBurst(List<CaptureRequest> requests, CaptureListener listener, Handler handler) throws CameraAccessException; /** @@ -467,21 +491,17 @@ public interface CameraDevice extends AutoCloseable { * <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 +539,7 @@ public interface CameraDevice extends AutoCloseable { * {@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 +550,10 @@ public interface CameraDevice extends AutoCloseable { * * @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 +580,11 @@ public interface CameraDevice extends AutoCloseable { * 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 +595,9 @@ public interface CameraDevice extends AutoCloseable { * 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. /** @@ -587,7 +612,6 @@ public interface CameraDevice extends AutoCloseable { * @see #captureBurst * @see #setRepeatingRequest * @see #setRepeatingBurst - * */ public static abstract class CaptureListener { @@ -658,8 +682,13 @@ public interface CameraDevice extends AutoCloseable { * * <p>The default implementation of this method does nothing.</p> * - * @param camera The CameraDevice sending the callback. - * @param request The request that was given to the CameraDevice + * @param camera + * The CameraDevice sending the callback. + * @param request + * The request that was given to the CameraDevice + * @param failure + * The output failure from the capture, including the failure reason + * and the frame number. * * @see #capture * @see #captureBurst @@ -667,7 +696,30 @@ public interface CameraDevice extends AutoCloseable { * @see #setRepeatingBurst */ public void onCaptureFailed(CameraDevice camera, - CaptureRequest request) { + CaptureRequest request, CaptureFailure failure) { + // default empty implementation + } + + /** + * This method is called independently of the others in CaptureListener, + * when a capture sequence finishes and all {@link CaptureResult} + * or {@link CaptureFailure} for it have been returned via this listener. + * + * @param camera + * The CameraDevice sending the callback. + * @param sequenceId + * A sequence ID returned by the {@link #capture} family of functions. + * @param frameNumber + * The last frame number (returned by {@link CaptureResult#getFrameNumber} + * or {@link CaptureFailure#getFrameNumber}) in the capture sequence. + * + * @see CaptureResult#getFrameNumber() + * @see CaptureFailure#getFrameNumber() + * @see CaptureResult#getSequenceId() + * @see CaptureFailure#getSequenceId() + */ + public void onCaptureSequenceCompleted(CameraDevice camera, + int sequenceId, int frameNumber) { // default empty implementation } } @@ -687,37 +739,190 @@ public interface CameraDevice extends AutoCloseable { * * @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 +930,7 @@ public interface CameraDevice extends AutoCloseable { * * <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 +943,7 @@ public interface CameraDevice extends AutoCloseable { * @see CameraDevice#stopRepeating * @see CameraDevice#flush */ - public void onCameraIdle(CameraDevice camera) { + public void onIdle(CameraDevice camera) { // Default empty implementation } @@ -746,6 +951,9 @@ public interface CameraDevice extends AutoCloseable { * 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 +967,32 @@ public interface CameraDevice extends AutoCloseable { * <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 +1000,9 @@ public interface CameraDevice extends AutoCloseable { * * @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..af0512e 100644 --- a/core/java/android/hardware/camera2/CameraManager.java +++ b/core/java/android/hardware/camera2/CameraManager.java @@ -58,7 +58,7 @@ public final class CameraManager { 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; @@ -166,7 +166,7 @@ public final class CameraManager { * @see #getCameraIdList * @see android.app.admin.DevicePolicyManager#setCameraDisabled */ - public CameraProperties getCameraProperties(String cameraId) + public CameraCharacteristics getCameraCharacteristics(String cameraId) throws CameraAccessException { synchronized (mLock) { @@ -179,7 +179,7 @@ public final class CameraManager { // TODO: implement and call a service function to get the capabilities on C++ side // TODO: get properties from service - return new CameraProperties(new CameraMetadataNative()); + return new CameraCharacteristics(new CameraMetadataNative()); } /** @@ -201,8 +201,7 @@ public final class CameraManager { * @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 @@ public final class CameraManager { } /** + * 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 @@ public final class CameraManager { * * <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 @@ public final class CameraManager { if (isAvailable(status)) { handler.post( new Runnable() { + @Override public void run() { listener.onCameraAvailable(id); } @@ -410,6 +483,7 @@ public final class CameraManager { } else { handler.post( new Runnable() { + @Override public void run() { listener.onCameraUnavailable(id); } diff --git a/core/java/android/hardware/camera2/CameraMetadata.java b/core/java/android/hardware/camera2/CameraMetadata.java index 7f4ba4f..b447494 100644 --- a/core/java/android/hardware/camera2/CameraMetadata.java +++ b/core/java/android/hardware/camera2/CameraMetadata.java @@ -40,11 +40,19 @@ import java.util.List; * * @see CameraDevice * @see CameraManager - * @see CameraProperties + * @see CameraCharacteristics **/ public abstract class CameraMetadata { /** + * Set a camera metadata field to a value. The field definitions can be + * found in {@link CameraCharacteristics}, {@link CaptureResult}, and + * {@link CaptureRequest}. + * + * @param key The metadata field to write. + * @param value The value to set the field to, which must be of a matching + * type to the key. + * * @hide */ protected CameraMetadata() { @@ -54,7 +62,7 @@ public abstract class CameraMetadata { * Get a camera metadata field value. * * <p>The field definitions can be - * found in {@link CameraProperties}, {@link CaptureResult}, and + * found in {@link CameraCharacteristics}, {@link CaptureResult}, and * {@link CaptureRequest}.</p> * * <p>Querying the value for the same key more than once will return a value @@ -195,43 +203,43 @@ public abstract class CameraMetadata { *~@~@~@~@~@~@~@~@~@~@~@~@~@~@~@~@~@~@~@~@~@~@~@~@~@~@~@~@~@~@~@~@~@~@~*/ // - // Enumeration values for CameraProperties#LENS_FACING + // Enumeration values for CameraCharacteristics#LENS_FACING // /** - * @see CameraProperties#LENS_FACING + * @see CameraCharacteristics#LENS_FACING */ public static final int LENS_FACING_FRONT = 0; /** - * @see CameraProperties#LENS_FACING + * @see CameraCharacteristics#LENS_FACING */ public static final int LENS_FACING_BACK = 1; // - // Enumeration values for CameraProperties#LED_AVAILABLE_LEDS + // Enumeration values for CameraCharacteristics#LED_AVAILABLE_LEDS // /** * <p> * android.led.transmit control is used * </p> - * @see CameraProperties#LED_AVAILABLE_LEDS + * @see CameraCharacteristics#LED_AVAILABLE_LEDS * @hide */ public static final int LED_AVAILABLE_LEDS_TRANSMIT = 0; // - // Enumeration values for CameraProperties#INFO_SUPPORTED_HARDWARE_LEVEL + // Enumeration values for CameraCharacteristics#INFO_SUPPORTED_HARDWARE_LEVEL // /** - * @see CameraProperties#INFO_SUPPORTED_HARDWARE_LEVEL + * @see CameraCharacteristics#INFO_SUPPORTED_HARDWARE_LEVEL */ public static final int INFO_SUPPORTED_HARDWARE_LEVEL_LIMITED = 0; /** - * @see CameraProperties#INFO_SUPPORTED_HARDWARE_LEVEL + * @see CameraCharacteristics#INFO_SUPPORTED_HARDWARE_LEVEL */ public static final int INFO_SUPPORTED_HARDWARE_LEVEL_FULL = 1; diff --git a/core/java/android/hardware/camera2/CaptureFailure.java b/core/java/android/hardware/camera2/CaptureFailure.java new file mode 100644 index 0000000..3b408cf --- /dev/null +++ b/core/java/android/hardware/camera2/CaptureFailure.java @@ -0,0 +1,144 @@ +/* + * 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.hardware.camera2.CameraDevice.CaptureListener; + +/** + * A report of failed capture for a single image capture from the image sensor. + * + * <p>CaptureFailures are produced by a {@link CameraDevice} if processing a + * {@link CaptureRequest} fails, either partially or fully. Use {@link #getReason} + * to determine the specific nature of the failed capture.</p> + * + * <p>Receiving a CaptureFailure means that the metadata associated with that frame number + * has been dropped -- no {@link CaptureResult} with the same frame number will be + * produced.</p> + */ +public class CaptureFailure { + /** + * The {@link CaptureResult} has been dropped this frame only due to an error + * in the framework. + * + * @see #getReason() + */ + public static final int REASON_ERROR = 0; + + /** + * The capture has failed due to a {@link CameraDevice#flush} call from the application. + * + * @see #getReason() + */ + public static final int REASON_FLUSHED = 1; + + private final CaptureRequest mRequest; + private final int mReason; + private final boolean mDropped; + private final int mSequenceId; + private final int mFrameNumber; + + /** + * @hide + */ + public CaptureFailure(CaptureRequest request, int reason, boolean dropped, int sequenceId, + int frameNumber) { + mRequest = request; + mReason = reason; + mDropped = dropped; + mSequenceId = sequenceId; + mFrameNumber = frameNumber; + } + + /** + * Get the request associated with this failed capture. + * + * <p>Whenever a request is unsuccessfully captured, with + * {@link CameraDevice.CaptureListener#onCaptureFailed}, + * the {@code failed capture}'s {@code getRequest()} will return that {@code request}. + * </p> + * + * <p>In particular, + * <code><pre>cameraDevice.capture(someRequest, new CaptureListener() { + * {@literal @}Override + * void onCaptureFailed(CaptureRequest myRequest, CaptureFailure myFailure) { + * assert(myFailure.getRequest.equals(myRequest) == true); + * } + * }; + * </code></pre> + * </p> + * + * @return The request associated with this failed capture. Never {@code null}. + */ + public CaptureRequest getRequest() { + return mRequest; + } + + /** + * Get the frame number associated with this failed capture. + * + * <p>Whenever a request has been processed, regardless of failed capture or success, + * it gets a unique frame number assigned to its future result/failed capture.</p> + * + * <p>This value monotonically increments, starting with 0, + * for every new result or failure; and the scope is the lifetime of the + * {@link CameraDevice}.</p> + * + * @return int frame number + */ + public int getFrameNumber() { + return mFrameNumber; + } + + /** + * Determine why the request was dropped, whether due to an error or to a user + * action. + * + * @return int One of {@code REASON_*} integer constants. + * + * @see #REASON_ERROR + * @see #REASON_FLUSHED + */ + public int getReason() { + return mReason; + } + + /** + * Determine if the image was captured from the camera. + * + * <p>If the image was not captured, no image buffers will be available. + * If the image was captured, then image buffers may be available.</p> + * + * @return boolean True if the image was captured, false otherwise. + */ + public boolean wasImageCaptured() { + return !mDropped; + } + + /** + * The sequence ID for this failed capture that was returned by the + * {@link CameraDevice#capture} family of functions. + * + * <p>The sequence ID is a unique monotonically increasing value starting from 0, + * incremented every time a new group of requests is submitted to the CameraDevice.</p> + * + * @return int The ID for the sequence of requests that this capture failure is the result of + * + * @see CameraDevice.CaptureListener#onCaptureSequenceCompleted + */ + public int getSequenceId() { + return mSequenceId; + } +} diff --git a/core/java/android/hardware/camera2/CaptureRequest.java b/core/java/android/hardware/camera2/CaptureRequest.java index 3ec5ca0..f30bcc5 100644 --- a/core/java/android/hardware/camera2/CaptureRequest.java +++ b/core/java/android/hardware/camera2/CaptureRequest.java @@ -17,11 +17,13 @@ package android.hardware.camera2; import android.hardware.camera2.impl.CameraMetadataNative; +import android.hardware.camera2.CameraDevice.CaptureListener; import android.os.Parcel; import android.os.Parcelable; import android.view.Surface; import java.util.HashSet; +import java.util.Objects; /** @@ -62,30 +64,37 @@ public final class CaptureRequest extends CameraMetadata implements Parcelable { private Object mUserTag; /** - * Construct empty request - * @hide + * Construct empty request. + * + * Used by Binder to unparcel this object only. */ - public CaptureRequest() { + private CaptureRequest() { mSettings = new CameraMetadataNative(); mSurfaceSet = new HashSet<Surface>(); } /** - * Clone from source capture request + * Clone from source capture request. + * + * Used by the Builder to create an immutable copy. */ + @SuppressWarnings("unchecked") private CaptureRequest(CaptureRequest source) { mSettings = new CameraMetadataNative(source.mSettings); mSurfaceSet = (HashSet<Surface>) source.mSurfaceSet.clone(); } /** - * Take ownership of passed-in settings + * Take ownership of passed-in settings. + * + * Used by the Builder to create a mutable CaptureRequest. */ private CaptureRequest(CameraMetadataNative settings) { mSettings = settings; mSurfaceSet = new HashSet<Surface>(); } + @SuppressWarnings("unchecked") @Override public <T> T get(Key<T> key) { return mSettings.get(key); @@ -108,6 +117,34 @@ public final class CaptureRequest extends CameraMetadata implements Parcelable { return mUserTag; } + /** + * Determine whether this CaptureRequest is equal to another CaptureRequest. + * + * <p>A request is considered equal to another is if it's set of key/values is equal, it's + * list of output surfaces is equal, and the user tag is equal.</p> + * + * @param other Another instance of CaptureRequest. + * + * @return True if the requests are the same, false otherwise. + */ + @Override + public boolean equals(Object other) { + return other instanceof CaptureRequest + && equals((CaptureRequest)other); + } + + private boolean equals(CaptureRequest other) { + return other != null + && Objects.equals(mUserTag, other.mUserTag) + && mSurfaceSet.equals(other.mSurfaceSet) + && mSettings.equals(other.mSettings); + } + + @Override + public int hashCode() { + return mSettings.hashCode(); + } + public static final Parcelable.Creator<CaptureRequest> CREATOR = new Parcelable.Creator<CaptureRequest>() { @Override diff --git a/core/java/android/hardware/camera2/CaptureResult.java b/core/java/android/hardware/camera2/CaptureResult.java index 377e78a..b82104d 100644 --- a/core/java/android/hardware/camera2/CaptureResult.java +++ b/core/java/android/hardware/camera2/CaptureResult.java @@ -37,13 +37,25 @@ import android.hardware.camera2.impl.CameraMetadataNative; public final class CaptureResult extends CameraMetadata { private final CameraMetadataNative mResults; + private final CaptureRequest mRequest; + private final int mSequenceId; /** * Takes ownership of the passed-in properties object * @hide */ - public CaptureResult(CameraMetadataNative results) { + public CaptureResult(CameraMetadataNative results, CaptureRequest parent, int sequenceId) { + if (results == null) { + throw new IllegalArgumentException("results was null"); + } + + if (parent == null) { + throw new IllegalArgumentException("parent was null"); + } + mResults = results; + mRequest = parent; + mSequenceId = sequenceId; } @Override @@ -51,6 +63,61 @@ public final class CaptureResult extends CameraMetadata { return mResults.get(key); } + /** + * Get the request associated with this result. + * + * <p>Whenever a request is successfully captured, with + * {@link CameraDevice.CaptureListener#onCaptureCompleted}, + * the {@code result}'s {@code getRequest()} will return that {@code request}. + * </p> + * + * <p>In particular, + * <code><pre>cameraDevice.capture(someRequest, new CaptureListener() { + * {@literal @}Override + * void onCaptureCompleted(CaptureRequest myRequest, CaptureResult myResult) { + * assert(myResult.getRequest.equals(myRequest) == true); + * } + * }; + * </code></pre> + * </p> + * + * @return The request associated with this result. Never {@code null}. + */ + public CaptureRequest getRequest() { + return mRequest; + } + + /** + * Get the frame number associated with this result. + * + * <p>Whenever a request has been processed, regardless of failure or success, + * it gets a unique frame number assigned to its future result/failure.</p> + * + * <p>This value monotonically increments, starting with 0, + * for every new result or failure; and the scope is the lifetime of the + * {@link CameraDevice}.</p> + * + * @return int frame number + */ + public int getFrameNumber() { + return get(REQUEST_FRAME_COUNT); + } + + /** + * The sequence ID for this failure that was returned by the + * {@link CameraDevice#capture} family of functions. + * + * <p>The sequence ID is a unique monotonically increasing value starting from 0, + * incremented every time a new group of requests is submitted to the CameraDevice.</p> + * + * @return int The ID for the sequence of requests that this capture result is a part of + * + * @see CameraDevice.CaptureListener#onCaptureSequenceCompleted + */ + public int getSequenceId() { + return mSequenceId; + } + /*@O~@~@~@~@~@~@~@~@~@~@~@~@~@~@~@~@~@~@~@~@~@~@~@~@~@~@~@~@~@~@~@~@~@~ * The key entries below this point are generated from metadata * definitions in /system/media/camera/docs. Do not modify by hand or @@ -523,8 +590,9 @@ public final class CaptureResult extends CameraMetadata { /** * <p> - * Number of frames captured since - * open() + * A frame counter set by the framework. This value monotonically + * increases with every new result (that is, each new result has a unique + * frameCount value). * </p> * <p> * Reset on release() diff --git a/core/java/android/hardware/camera2/Face.java b/core/java/android/hardware/camera2/Face.java index 6bfc535..ef068ca 100644 --- a/core/java/android/hardware/camera2/Face.java +++ b/core/java/android/hardware/camera2/Face.java @@ -128,7 +128,7 @@ public final class Face { * Bounds of the face. * * <p>A rectangle relative to the sensor's - * {@link CameraProperties#SENSOR_INFO_ACTIVE_ARRAY_SIZE}, with (0,0) + * {@link CameraCharacteristics#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 diff --git a/core/java/android/hardware/camera2/impl/CameraDevice.java b/core/java/android/hardware/camera2/impl/CameraDevice.java index 995555a..f126472 100644 --- a/core/java/android/hardware/camera2/impl/CameraDevice.java +++ b/core/java/android/hardware/camera2/impl/CameraDevice.java @@ -20,7 +20,7 @@ import static android.hardware.camera2.CameraAccessException.CAMERA_IN_USE; import android.hardware.camera2.CameraAccessException; import android.hardware.camera2.CameraMetadata; -import android.hardware.camera2.CameraProperties; +import android.hardware.camera2.CameraCharacteristics; import android.hardware.camera2.CaptureRequest; import android.hardware.camera2.CaptureResult; import android.hardware.camera2.ICameraDeviceCallbacks; @@ -55,7 +55,7 @@ public class CameraDevice implements android.hardware.camera2.CameraDevice { 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 = @@ -88,7 +88,7 @@ public class CameraDevice implements android.hardware.camera2.CameraDevice { } @Override - public CameraProperties getProperties() throws CameraAccessException { + public CameraCharacteristics getProperties() throws CameraAccessException { CameraMetadataNative info = new CameraMetadataNative(); @@ -101,7 +101,7 @@ public class CameraDevice implements android.hardware.camera2.CameraDevice { return null; } - CameraProperties properties = new CameraProperties(info); + CameraCharacteristics properties = new CameraCharacteristics(info); return properties; } @@ -179,24 +179,24 @@ public class CameraDevice implements android.hardware.camera2.CameraDevice { } @Override - public void capture(CaptureRequest request, CaptureListener listener, Handler handler) + public int capture(CaptureRequest request, CaptureListener listener, Handler handler) throws CameraAccessException { - submitCaptureRequest(request, listener, handler, /*streaming*/false); + return submitCaptureRequest(request, listener, handler, /*streaming*/false); } @Override - public void captureBurst(List<CaptureRequest> requests, CaptureListener listener, + public int captureBurst(List<CaptureRequest> requests, CaptureListener listener, Handler handler) throws CameraAccessException { if (requests.isEmpty()) { Log.w(TAG, "Capture burst request list is empty, do nothing!"); - return; + return -1; } // TODO throw new UnsupportedOperationException("Burst capture implemented yet"); } - private void submitCaptureRequest(CaptureRequest request, CaptureListener listener, + private int submitCaptureRequest(CaptureRequest request, CaptureListener listener, Handler handler, boolean repeating) throws CameraAccessException { // Need a valid handler, or current thread needs to have a looper, if @@ -220,7 +220,7 @@ public class CameraDevice implements android.hardware.camera2.CameraDevice { throw e.asChecked(); } catch (RemoteException e) { // impossible - return; + return -1; } if (listener != null) { mCaptureListenerMap.put(requestId, new CaptureListenerHolder(listener, request, @@ -231,21 +231,22 @@ public class CameraDevice implements android.hardware.camera2.CameraDevice { mRepeatingRequestIdStack.add(requestId); } + return requestId; } } @Override - public void setRepeatingRequest(CaptureRequest request, CaptureListener listener, + public int setRepeatingRequest(CaptureRequest request, CaptureListener listener, Handler handler) throws CameraAccessException { - submitCaptureRequest(request, listener, handler, /*streaming*/true); + return submitCaptureRequest(request, listener, handler, /*streaming*/true); } @Override - public void setRepeatingBurst(List<CaptureRequest> requests, CaptureListener listener, + public int setRepeatingBurst(List<CaptureRequest> requests, CaptureListener listener, Handler handler) throws CameraAccessException { if (requests.isEmpty()) { Log.w(TAG, "Set Repeating burst request list is empty, do nothing!"); - return; + return -1; } // TODO throw new UnsupportedOperationException("Burst capture implemented yet"); @@ -292,7 +293,7 @@ public class CameraDevice implements android.hardware.camera2.CameraDevice { } @Override - public void setDeviceListener(CameraDeviceListener listener, Handler handler) { + public void setDeviceListener(StateListener listener, Handler handler) { synchronized (mLock) { mDeviceListener = listener; mDeviceHandler = handler; @@ -314,7 +315,7 @@ public class CameraDevice implements android.hardware.camera2.CameraDevice { } @Override - public void close() throws Exception { + public void close() { // TODO: every method should throw IllegalStateException after close has been called @@ -325,7 +326,7 @@ public class CameraDevice implements android.hardware.camera2.CameraDevice { mRemoteDevice.disconnect(); } } catch (CameraRuntimeException e) { - throw e.asChecked(); + Log.e(TAG, "Exception while closing: ", e.asChecked()); } catch (RemoteException e) { // impossible } @@ -339,8 +340,6 @@ public class CameraDevice implements android.hardware.camera2.CameraDevice { 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(); @@ -431,14 +430,16 @@ public class CameraDevice implements android.hardware.camera2.CameraDevice { return; } - final CaptureResult resultAsCapture = new CaptureResult(result); + final CaptureRequest request = holder.getRequest(); + final CaptureResult resultAsCapture = new CaptureResult(result, request, requestId); holder.getHandler().post( new Runnable() { + @Override public void run() { holder.getListener().onCaptureCompleted( CameraDevice.this, - holder.getRequest(), + request, resultAsCapture); } }); diff --git a/core/java/android/hardware/camera2/impl/CameraMetadataNative.java b/core/java/android/hardware/camera2/impl/CameraMetadataNative.java index c13438a..6d7b03e 100644 --- a/core/java/android/hardware/camera2/impl/CameraMetadataNative.java +++ b/core/java/android/hardware/camera2/impl/CameraMetadataNative.java @@ -101,7 +101,7 @@ public class CameraMetadataNative extends CameraMetadata implements Parcelable { /** * Set a camera metadata field to a value. The field definitions can be - * found in {@link CameraProperties}, {@link CaptureResult}, and + * found in {@link CameraCharacteristics}, {@link CaptureResult}, and * {@link CaptureRequest}. * * @param key The metadata field to write. diff --git a/core/java/android/hardware/camera2/package.html b/core/java/android/hardware/camera2/package.html index e9d9cea..9f6c2a9 100644 --- a/core/java/android/hardware/camera2/package.html +++ b/core/java/android/hardware/camera2/package.html @@ -34,7 +34,7 @@ framerate on most Android devices.</p> CameraDevices} provide a set of static property information that describes the hardware device and the available settings and output parameters for the device. This information is provided through the -{@link android.hardware.camera2.CameraProperties} object.</p> +{@link android.hardware.camera2.CameraCharacteristics} object.</p> <p>To capture or stream images from a camera device, the application must first configure a set of output Surfaces for use with the camera diff --git a/core/java/android/hardware/camera2/utils/CameraBinderDecorator.java b/core/java/android/hardware/camera2/utils/CameraBinderDecorator.java index d0b3ec4..e535e00 100644 --- a/core/java/android/hardware/camera2/utils/CameraBinderDecorator.java +++ b/core/java/android/hardware/camera2/utils/CameraBinderDecorator.java @@ -51,7 +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; + public static final int EUSERS = -87; private static class CameraBinderDecoratorListener implements Decorator.DecoratorListener { @@ -85,7 +85,7 @@ public class CameraBinderDecorator { case EBUSY: UncheckedThrow.throwAnyException(new CameraRuntimeException( CAMERA_IN_USE)); - case EDQUOT: + case EUSERS: UncheckedThrow.throwAnyException(new CameraRuntimeException( MAX_CAMERAS_IN_USE)); case ENODEV: 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 @@ package android.hardware.location; 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 @@ public final class GeofenceHardwareImpl { 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/net/MobileDataStateTracker.java b/core/java/android/net/MobileDataStateTracker.java index b914940..c106514 100644 --- a/core/java/android/net/MobileDataStateTracker.java +++ b/core/java/android/net/MobileDataStateTracker.java @@ -211,8 +211,6 @@ public class MobileDataStateTracker extends BaseNetworkStateTracker { private class MobileDataStateReceiver extends BroadcastReceiver { @Override public void onReceive(Context context, Intent intent) { - // Assume this isn't a provisioning network. - mNetworkInfo.setIsConnectedToProvisioningNetwork(false); if (intent.getAction().equals(TelephonyIntents. ACTION_DATA_CONNECTION_CONNECTED_TO_PROVISIONING_APN)) { String apnName = intent.getStringExtra(PhoneConstants.DATA_APN_KEY); @@ -244,6 +242,11 @@ public class MobileDataStateTracker extends BaseNetworkStateTracker { if (!TextUtils.equals(apnType, mApnType)) { return; } + // Assume this isn't a provisioning network. + mNetworkInfo.setIsConnectedToProvisioningNetwork(false); + if (DBG) { + log("Broadcast received: " + intent.getAction() + " apnType=" + apnType); + } int oldSubtype = mNetworkInfo.getSubtype(); int newSubType = TelephonyManager.getDefault().getNetworkType(); @@ -351,6 +354,8 @@ public class MobileDataStateTracker extends BaseNetworkStateTracker { } return; } + // Assume this isn't a provisioning network. + mNetworkInfo.setIsConnectedToProvisioningNetwork(false); String reason = intent.getStringExtra(PhoneConstants.FAILURE_REASON_KEY); String apnName = intent.getStringExtra(PhoneConstants.DATA_APN_KEY); if (DBG) { diff --git a/core/java/android/os/Binder.java b/core/java/android/os/Binder.java index 26fc769..f4a8391 100644 --- a/core/java/android/os/Binder.java +++ b/core/java/android/os/Binder.java @@ -299,7 +299,6 @@ public class Binder implements IBinder { try { dump(fd, pw, args); } catch (SecurityException e) { - pw.println(); pw.println("Security exception: " + e.getMessage()); throw e; } catch (Throwable e) { diff --git a/core/java/android/os/Bundle.java b/core/java/android/os/Bundle.java index 32b1b60..f47ac4e 100644 --- a/core/java/android/os/Bundle.java +++ b/core/java/android/os/Bundle.java @@ -33,6 +33,8 @@ public final class Bundle implements Parcelable, Cloneable { private static final String LOG_TAG = "Bundle"; public static final Bundle EMPTY; + static final int BUNDLE_MAGIC = 0x4C444E42; // 'B' 'N' 'D' 'L' + static { EMPTY = new Bundle(); EMPTY.mMap = ArrayMap.EMPTY; @@ -1643,11 +1645,11 @@ public final class Bundle implements Parcelable, Cloneable { if (mParcelledData != null) { int length = mParcelledData.dataSize(); parcel.writeInt(length); - parcel.writeInt(0x4C444E42); // 'B' 'N' 'D' 'L' + parcel.writeInt(BUNDLE_MAGIC); parcel.appendFrom(mParcelledData, 0, length); } else { parcel.writeInt(-1); // dummy, will hold length - parcel.writeInt(0x4C444E42); // 'B' 'N' 'D' 'L' + parcel.writeInt(BUNDLE_MAGIC); int oldPos = parcel.dataPosition(); parcel.writeArrayMapInternal(mMap); @@ -1679,11 +1681,10 @@ public final class Bundle implements Parcelable, Cloneable { void readFromParcelInner(Parcel parcel, int length) { int magic = parcel.readInt(); - if (magic != 0x4C444E42) { + if (magic != BUNDLE_MAGIC) { //noinspection ThrowableInstanceNeverThrown - String st = Log.getStackTraceString(new RuntimeException()); - Log.e("Bundle", "readBundle: bad magic number"); - Log.e("Bundle", "readBundle: trace = " + st); + throw new IllegalStateException("Bad magic number for Bundle: 0x" + + Integer.toHexString(magic)); } // Advance within this Parcel @@ -1694,10 +1695,23 @@ public final class Bundle implements Parcelable, Cloneable { p.setDataPosition(0); p.appendFrom(parcel, offset, length); p.setDataPosition(0); - - mParcelledData = p; - mHasFds = p.hasFileDescriptors(); - mFdsKnown = true; + + if (mMap != null) { + // It is not allowed to have a Bundle with both a map and a parcel, so if we + // already have a map then we need to immediately unparcel into it. This also + // lets us know we need to go through the slow path of unparceling, since the + // map may already contains some data so the two need to be merged. + if (mFdsKnown) { + mHasFds |= p.hasFileDescriptors(); + } + int N = p.readInt(); + p.readArrayMapSafelyInternal(mMap, N, mClassLoader); + p.recycle(); + } else { + mParcelledData = p; + mHasFds = p.hasFileDescriptors(); + mFdsKnown = true; + } } @Override 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 @@ public class Environment { // 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 @@ public class Environment { } // /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 @@ public class Environment { 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 class Environment { */ public static File getExternalStorageDirectory() { throwIfUserRequired(); - return sCurrentUser.getExternalDirs()[0]; + return sCurrentUser.getExternalDirsForApp()[0]; } /** {@hide} */ diff --git a/core/java/android/os/Parcel.java b/core/java/android/os/Parcel.java index fec2a3e..5f3a81c 100644 --- a/core/java/android/os/Parcel.java +++ b/core/java/android/os/Parcel.java @@ -178,6 +178,7 @@ import java.util.Set; */ public final class Parcel { private static final boolean DEBUG_RECYCLE = false; + private static final boolean DEBUG_ARRAY_MAP = false; private static final String TAG = "Parcel"; @SuppressWarnings({"UnusedDeclaration"}) @@ -605,7 +606,14 @@ public final class Parcel { } final int N = val.size(); writeInt(N); + if (DEBUG_ARRAY_MAP) { + RuntimeException here = new RuntimeException("here"); + here.fillInStackTrace(); + Log.d(TAG, "Writing " + N + " ArrayMap entries", here); + } for (int i=0; i<N; i++) { + if (DEBUG_ARRAY_MAP) Log.d(TAG, " Write #" + i + ": key=0x" + + (val.keyAt(i) != null ? val.keyAt(i).hashCode() : 0) + " " + val.keyAt(i)); writeValue(val.keyAt(i)); writeValue(val.valueAt(i)); } @@ -2289,14 +2297,38 @@ public final class Parcel { /* package */ void readArrayMapInternal(ArrayMap outVal, int N, ClassLoader loader) { + if (DEBUG_ARRAY_MAP) { + RuntimeException here = new RuntimeException("here"); + here.fillInStackTrace(); + Log.d(TAG, "Reading " + N + " ArrayMap entries", here); + } while (N > 0) { Object key = readValue(loader); + if (DEBUG_ARRAY_MAP) Log.d(TAG, " Read #" + (N-1) + ": key=0x" + + (key != null ? key.hashCode() : 0) + " " + key); Object value = readValue(loader); outVal.append(key, value); N--; } } + /* package */ void readArrayMapSafelyInternal(ArrayMap outVal, int N, + ClassLoader loader) { + if (DEBUG_ARRAY_MAP) { + RuntimeException here = new RuntimeException("here"); + here.fillInStackTrace(); + Log.d(TAG, "Reading safely " + N + " ArrayMap entries", here); + } + while (N > 0) { + Object key = readValue(loader); + if (DEBUG_ARRAY_MAP) Log.d(TAG, " Read safe #" + (N-1) + ": key=0x" + + (key != null ? key.hashCode() : 0) + " " + key); + Object value = readValue(loader); + outVal.put(key, value); + N--; + } + } + private void readListInternal(List outVal, int N, ClassLoader loader) { while (N > 0) { 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 @@ public class ParcelFileDescriptor implements Parcelable, Closeable { } /** + * 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..f8da87a 100644 --- a/core/java/android/os/WorkSource.java +++ b/core/java/android/os/WorkSource.java @@ -103,7 +103,21 @@ public class WorkSource implements Parcelable { * @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; + } } /** @@ -209,7 +223,6 @@ public class WorkSource implements Parcelable { } mUids[0] = uid; mNames[0] = name; - mNames = null; } /** @hide */ 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.Binder; 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 @@ public interface IMountService extends IInterface { _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 @@ public interface IMountService extends IInterface { 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 @@ public interface IMountService extends IInterface { 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 interface IMountService extends IInterface { */ 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/preference/PreferenceActivity.java b/core/java/android/preference/PreferenceActivity.java index a99705b..2ab5a91 100644 --- a/core/java/android/preference/PreferenceActivity.java +++ b/core/java/android/preference/PreferenceActivity.java @@ -887,8 +887,9 @@ public abstract class PreferenceActivity extends ListActivity implements /** * Subclasses should override this method and verify that the given fragment is a valid type - * to be attached to this activity. The default implementation returns <code>true</code> prior - * to Key Lime Pie, <code>false</code> otherwise. + * to be attached to this activity. The default implementation returns <code>true</code> for + * apps built for <code>android:targetSdkVersion</code> older than + * {@link android.os.Build.VERSION_CODES#KITKAT}. For later versions, it will throw an exception. * @param fragmentName the class name of the Fragment about to be attached to this activity. * @return true if the fragment class name is valid for this Activity and false otherwise. */ diff --git a/core/java/android/print/PrintAttributes.java b/core/java/android/print/PrintAttributes.java index 94bbb08..959380d 100644 --- a/core/java/android/print/PrintAttributes.java +++ b/core/java/android/print/PrintAttributes.java @@ -30,10 +30,9 @@ import com.android.internal.R; * This class represents the attributes of a print job. */ public final class PrintAttributes implements Parcelable { - - /** Color mode: Monochrome color scheme, e.g. one color is used. */ + /** Color mode: Monochrome color scheme, for example one color is used. */ public static final int COLOR_MODE_MONOCHROME = 1 << 0; - /** Color mode: Color color scheme, e.g. many colors are used. */ + /** Color mode: Color color scheme, for example many colors are used. */ public static final int COLOR_MODE_COLOR = 1 << 1; private static final int VALID_COLOR_MODES = @@ -260,321 +259,344 @@ public final class PrintAttributes implements Parcelable { } /** - * This class specifies a supported media size. + * This class specifies a supported media size. Media size is the + * dimension of the media on which the content is printed. For + * example, the {@link #NA_LETTER} media size designates a page + * with size 8.5" x 11". */ public static final class MediaSize { private static final String LOG_TAG = "MediaSize"; - // TODO: Verify media sizes and add more standard ones. + /** + * Unknown media size in portrait mode. + * <p> + * <strong>Note: </strong>This is for specifying orientation without media + * size. You should not use the dimensions reported by this class. + * </p> + */ + public static final MediaSize UNKNOWN_PORTRAIT = + new MediaSize("UNKNOWN_PORTRAIT", "android", + R.string.mediasize_unknown_portrait, Integer.MAX_VALUE, 1); + + /** + * Unknown media size in landscape mode. + * <p> + * <strong>Note: </strong>This is for specifying orientation without media + * size. You should not use the dimensions reported by this class. + * </p> + */ + public static final MediaSize UNKNOWN_LANDSCAPE = + new MediaSize("UNKNOWN_LANDSCAPE", "android", + R.string.mediasize_unknown_landscape, 1, Integer.MAX_VALUE); // ISO sizes /** ISO A0 media size: 841mm x 1189mm (33.11" x 46.81") */ public static final MediaSize ISO_A0 = - new MediaSize("ISO_A0", "android", R.string.mediaSize_iso_a0, 33110, 46810); + new MediaSize("ISO_A0", "android", R.string.mediasize_iso_a0, 33110, 46810); /** ISO A1 media size: 594mm x 841mm (23.39" x 33.11") */ public static final MediaSize ISO_A1 = - new MediaSize("ISO_A1", "android", R.string.mediaSize_iso_a1, 23390, 33110); + new MediaSize("ISO_A1", "android", R.string.mediasize_iso_a1, 23390, 33110); /** ISO A2 media size: 420mm x 594mm (16.54" x 23.39") */ public static final MediaSize ISO_A2 = - new MediaSize("ISO_A2", "android", R.string.mediaSize_iso_a2, 16540, 23390); + new MediaSize("ISO_A2", "android", R.string.mediasize_iso_a2, 16540, 23390); /** ISO A3 media size: 297mm x 420mm (11.69" x 16.54") */ public static final MediaSize ISO_A3 = - new MediaSize("ISO_A3", "android", R.string.mediaSize_iso_a3, 11690, 16540); + new MediaSize("ISO_A3", "android", R.string.mediasize_iso_a3, 11690, 16540); /** ISO A4 media size: 210mm x 297mm (8.27" x 11.69") */ public static final MediaSize ISO_A4 = - new MediaSize("ISO_A4", "android", R.string.mediaSize_iso_a4, 8270, 11690); + new MediaSize("ISO_A4", "android", R.string.mediasize_iso_a4, 8270, 11690); /** ISO A5 media size: 148mm x 210mm (5.83" x 8.27") */ public static final MediaSize ISO_A5 = - new MediaSize("ISO_A5", "android", R.string.mediaSize_iso_a5, 5830, 8270); + new MediaSize("ISO_A5", "android", R.string.mediasize_iso_a5, 5830, 8270); /** ISO A6 media size: 105mm x 148mm (4.13" x 5.83") */ public static final MediaSize ISO_A6 = - new MediaSize("ISO_A6", "android", R.string.mediaSize_iso_a6, 4130, 5830); + new MediaSize("ISO_A6", "android", R.string.mediasize_iso_a6, 4130, 5830); /** ISO A7 media size: 74mm x 105mm (2.91" x 4.13") */ public static final MediaSize ISO_A7 = - new MediaSize("ISO_A7", "android", R.string.mediaSize_iso_a7, 2910, 4130); + new MediaSize("ISO_A7", "android", R.string.mediasize_iso_a7, 2910, 4130); /** ISO A8 media size: 52mm x 74mm (2.05" x 2.91") */ public static final MediaSize ISO_A8 = - new MediaSize("ISO_A8", "android", R.string.mediaSize_iso_a8, 2050, 2910); + new MediaSize("ISO_A8", "android", R.string.mediasize_iso_a8, 2050, 2910); /** ISO A9 media size: 37mm x 52mm (1.46" x 2.05") */ public static final MediaSize ISO_A9 = - new MediaSize("ISO_A9", "android", R.string.mediaSize_iso_a9, 1460, 2050); + new MediaSize("ISO_A9", "android", R.string.mediasize_iso_a9, 1460, 2050); /** ISO A10 media size: 26mm x 37mm (1.02" x 1.46") */ public static final MediaSize ISO_A10 = - new MediaSize("ISO_A10", "android", R.string.mediaSize_iso_a10, 1020, 1460); + new MediaSize("ISO_A10", "android", R.string.mediasize_iso_a10, 1020, 1460); /** ISO B0 media size: 1000mm x 1414mm (39.37" x 55.67") */ public static final MediaSize ISO_B0 = - new MediaSize("ISO_B0", "android", R.string.mediaSize_iso_b0, 39370, 55670); + new MediaSize("ISO_B0", "android", R.string.mediasize_iso_b0, 39370, 55670); /** ISO B1 media size: 707mm x 1000mm (27.83" x 39.37") */ public static final MediaSize ISO_B1 = - new MediaSize("ISO_B1", "android", R.string.mediaSize_iso_b1, 27830, 39370); + new MediaSize("ISO_B1", "android", R.string.mediasize_iso_b1, 27830, 39370); /** ISO B2 media size: 500mm x 707mm (19.69" x 27.83") */ public static final MediaSize ISO_B2 = - new MediaSize("ISO_B2", "android", R.string.mediaSize_iso_b2, 19690, 27830); + new MediaSize("ISO_B2", "android", R.string.mediasize_iso_b2, 19690, 27830); /** ISO B3 media size: 353mm x 500mm (13.90" x 19.69") */ public static final MediaSize ISO_B3 = - new MediaSize("ISO_B3", "android", R.string.mediaSize_iso_b3, 13900, 19690); + new MediaSize("ISO_B3", "android", R.string.mediasize_iso_b3, 13900, 19690); /** ISO B4 media size: 250mm x 353mm (9.84" x 13.90") */ public static final MediaSize ISO_B4 = - new MediaSize("ISO_B4", "android", R.string.mediaSize_iso_b4, 9840, 13900); + new MediaSize("ISO_B4", "android", R.string.mediasize_iso_b4, 9840, 13900); /** ISO B5 media size: 176mm x 250mm (6.93" x 9.84") */ public static final MediaSize ISO_B5 = - new MediaSize("ISO_B5", "android", R.string.mediaSize_iso_b5, 6930, 9840); + new MediaSize("ISO_B5", "android", R.string.mediasize_iso_b5, 6930, 9840); /** ISO B6 media size: 125mm x 176mm (4.92" x 6.93") */ public static final MediaSize ISO_B6 = - new MediaSize("ISO_B6", "android", R.string.mediaSize_iso_b6, 4920, 6930); + new MediaSize("ISO_B6", "android", R.string.mediasize_iso_b6, 4920, 6930); /** ISO B7 media size: 88mm x 125mm (3.46" x 4.92") */ public static final MediaSize ISO_B7 = - new MediaSize("ISO_B7", "android", R.string.mediaSize_iso_b7, 3460, 4920); + new MediaSize("ISO_B7", "android", R.string.mediasize_iso_b7, 3460, 4920); /** ISO B8 media size: 62mm x 88mm (2.44" x 3.46") */ public static final MediaSize ISO_B8 = - new MediaSize("ISO_B8", "android", R.string.mediaSize_iso_b8, 2440, 3460); + new MediaSize("ISO_B8", "android", R.string.mediasize_iso_b8, 2440, 3460); /** ISO B9 media size: 44mm x 62mm (1.73" x 2.44") */ public static final MediaSize ISO_B9 = - new MediaSize("ISO_B9", "android", R.string.mediaSize_iso_b9, 1730, 2440); + new MediaSize("ISO_B9", "android", R.string.mediasize_iso_b9, 1730, 2440); /** ISO B10 media size: 31mm x 44mm (1.22" x 1.73") */ public static final MediaSize ISO_B10 = - new MediaSize("ISO_B10", "android", R.string.mediaSize_iso_b10, 1220, 1730); + new MediaSize("ISO_B10", "android", R.string.mediasize_iso_b10, 1220, 1730); /** ISO C0 media size: 917mm x 1297mm (36.10" x 51.06") */ public static final MediaSize ISO_C0 = - new MediaSize("ISO_C0", "android", R.string.mediaSize_iso_c0, 36100, 51060); + new MediaSize("ISO_C0", "android", R.string.mediasize_iso_c0, 36100, 51060); /** ISO C1 media size: 648mm x 917mm (25.51" x 36.10") */ public static final MediaSize ISO_C1 = - new MediaSize("ISO_C1", "android", R.string.mediaSize_iso_c1, 25510, 36100); + new MediaSize("ISO_C1", "android", R.string.mediasize_iso_c1, 25510, 36100); /** ISO C2 media size: 458mm x 648mm (18.03" x 25.51") */ public static final MediaSize ISO_C2 = - new MediaSize("ISO_C2", "android", R.string.mediaSize_iso_c2, 18030, 25510); + new MediaSize("ISO_C2", "android", R.string.mediasize_iso_c2, 18030, 25510); /** ISO C3 media size: 324mm x 458mm (12.76" x 18.03") */ public static final MediaSize ISO_C3 = - new MediaSize("ISO_C3", "android", R.string.mediaSize_iso_c3, 12760, 18030); + new MediaSize("ISO_C3", "android", R.string.mediasize_iso_c3, 12760, 18030); /** ISO C4 media size: 229mm x 324mm (9.02" x 12.76") */ public static final MediaSize ISO_C4 = - new MediaSize("ISO_C4", "android", R.string.mediaSize_iso_c4, 9020, 12760); + new MediaSize("ISO_C4", "android", R.string.mediasize_iso_c4, 9020, 12760); /** ISO C5 media size: 162mm x 229mm (6.38" x 9.02") */ public static final MediaSize ISO_C5 = - new MediaSize("ISO_C5", "android", R.string.mediaSize_iso_c5, 6380, 9020); + new MediaSize("ISO_C5", "android", R.string.mediasize_iso_c5, 6380, 9020); /** ISO C6 media size: 114mm x 162mm (4.49" x 6.38") */ public static final MediaSize ISO_C6 = - new MediaSize("ISO_C6", "android", R.string.mediaSize_iso_c6, 4490, 6380); + new MediaSize("ISO_C6", "android", R.string.mediasize_iso_c6, 4490, 6380); /** ISO C7 media size: 81mm x 114mm (3.19" x 4.49") */ public static final MediaSize ISO_C7 = - new MediaSize("ISO_C7", "android", R.string.mediaSize_iso_c7, 3190, 4490); + new MediaSize("ISO_C7", "android", R.string.mediasize_iso_c7, 3190, 4490); /** ISO C8 media size: 57mm x 81mm (2.24" x 3.19") */ public static final MediaSize ISO_C8 = - new MediaSize("ISO_C8", "android", R.string.mediaSize_iso_c8, 2240, 3190); + new MediaSize("ISO_C8", "android", R.string.mediasize_iso_c8, 2240, 3190); /** ISO C9 media size: 40mm x 57mm (1.57" x 2.24") */ public static final MediaSize ISO_C9 = - new MediaSize("ISO_C9", "android", R.string.mediaSize_iso_c9, 1570, 2240); + new MediaSize("ISO_C9", "android", R.string.mediasize_iso_c9, 1570, 2240); /** ISO C10 media size: 28mm x 40mm (1.10" x 1.57") */ public static final MediaSize ISO_C10 = - new MediaSize("ISO_C10", "android", R.string.mediaSize_iso_c10, 1100, 1570); + new MediaSize("ISO_C10", "android", R.string.mediasize_iso_c10, 1100, 1570); // North America /** North America Letter media size: 8.5" x 11" (279mm x 216mm) */ public static final MediaSize NA_LETTER = - new MediaSize("NA_LETTER", "android", R.string.mediaSize_na_letter, 8500, 11000); + new MediaSize("NA_LETTER", "android", R.string.mediasize_na_letter, 8500, 11000); /** North America Government-Letter media size: 8.0" x 10.5" (203mm x 267mm) */ public static final MediaSize NA_GOVT_LETTER = new MediaSize("NA_GOVT_LETTER", "android", - R.string.mediaSize_na_gvrnmt_letter, 8000, 10500); + R.string.mediasize_na_gvrnmt_letter, 8000, 10500); /** North America Legal media size: 8.5" x 14" (216mm x 356mm) */ public static final MediaSize NA_LEGAL = - new MediaSize("NA_LEGAL", "android", R.string.mediaSize_na_legal, 8500, 14000); + new MediaSize("NA_LEGAL", "android", R.string.mediasize_na_legal, 8500, 14000); /** North America Junior Legal media size: 8.0" x 5.0" (203mm × 127mm) */ public static final MediaSize NA_JUNIOR_LEGAL = new MediaSize("NA_JUNIOR_LEGAL", "android", - R.string.mediaSize_na_junior_legal, 8000, 5000); + R.string.mediasize_na_junior_legal, 8000, 5000); /** North America Ledger media size: 17" x 11" (432mm × 279mm) */ public static final MediaSize NA_LEDGER = - new MediaSize("NA_LEDGER", "android", R.string.mediaSize_na_ledger, 17000, 11000); + new MediaSize("NA_LEDGER", "android", R.string.mediasize_na_ledger, 17000, 11000); /** North America Tabloid media size: 11" x 17" (279mm × 432mm) */ public static final MediaSize NA_TABLOID = new MediaSize("NA_TABLOID", "android", - R.string.mediaSize_na_tabloid, 11000, 17000); + R.string.mediasize_na_tabloid, 11000, 17000); /** North America Index Card 3x5 media size: 3" x 5" (76mm x 127mm) */ public static final MediaSize NA_INDEX_3X5 = new MediaSize("NA_INDEX_3X5", "android", - R.string.mediaSize_na_index_3x5, 3000, 5000); + R.string.mediasize_na_index_3x5, 3000, 5000); /** North America Index Card 4x6 media size: 4" x 6" (102mm x 152mm) */ public static final MediaSize NA_INDEX_4X6 = new MediaSize("NA_INDEX_4X6", "android", - R.string.mediaSize_na_index_4x6, 4000, 6000); + R.string.mediasize_na_index_4x6, 4000, 6000); /** North America Index Card 5x8 media size: 5" x 8" (127mm x 203mm) */ public static final MediaSize NA_INDEX_5X8 = new MediaSize("NA_INDEX_5X8", "android", - R.string.mediaSize_na_index_5x8, 5000, 8000); + R.string.mediasize_na_index_5x8, 5000, 8000); /** North America Monarch media size: 7.25" x 10.5" (184mm x 267mm) */ public static final MediaSize NA_MONARCH = new MediaSize("NA_MONARCH", "android", - R.string.mediaSize_na_monarch, 7250, 10500); + R.string.mediasize_na_monarch, 7250, 10500); /** North America Quarto media size: 8" x 10" (203mm x 254mm) */ public static final MediaSize NA_QUARTO = new MediaSize("NA_QUARTO", "android", - R.string.mediaSize_na_quarto, 8000, 10000); + R.string.mediasize_na_quarto, 8000, 10000); /** North America Foolscap media size: 8" x 13" (203mm x 330mm) */ public static final MediaSize NA_FOOLSCAP = new MediaSize("NA_FOOLSCAP", "android", - R.string.mediaSize_na_foolscap, 8000, 13000); + R.string.mediasize_na_foolscap, 8000, 13000); // Chinese /** Chinese ROC 8K media size: 270mm x 390mm (10.629" x 15.3543") */ public static final MediaSize ROC_8K = new MediaSize("ROC_8K", "android", - R.string.mediaSize_chinese_roc_8k, 10629, 15354); + R.string.mediasize_chinese_roc_8k, 10629, 15354); /** Chinese ROC 16K media size: 195mm x 270mm (7.677" x 10.629") */ public static final MediaSize ROC_16K = new MediaSize("ROC_16K", "android", - R.string.mediaSize_chinese_roc_16k, 7677, 10629); + R.string.mediasize_chinese_roc_16k, 7677, 10629); /** Chinese PRC 1 media size: 102mm x 165mm (4.015" x 6.496") */ public static final MediaSize PRC_1 = new MediaSize("PRC_1", "android", - R.string.mediaSize_chinese_prc_1, 4015, 6496); + R.string.mediasize_chinese_prc_1, 4015, 6496); /** Chinese PRC 2 media size: 102mm x 176mm (4.015" x 6.929") */ public static final MediaSize PRC_2 = new MediaSize("PRC_2", "android", - R.string.mediaSize_chinese_prc_2, 4015, 6929); + R.string.mediasize_chinese_prc_2, 4015, 6929); /** Chinese PRC 3 media size: 125mm x 176mm (4.921" x 6.929") */ public static final MediaSize PRC_3 = new MediaSize("PRC_3", "android", - R.string.mediaSize_chinese_prc_3, 4921, 6929); + R.string.mediasize_chinese_prc_3, 4921, 6929); /** Chinese PRC 4 media size: 110mm x 208mm (4.330" x 8.189") */ public static final MediaSize PRC_4 = new MediaSize("PRC_4", "android", - R.string.mediaSize_chinese_prc_4, 4330, 8189); + R.string.mediasize_chinese_prc_4, 4330, 8189); /** Chinese PRC 5 media size: 110mm x 220mm (4.330" x 8.661") */ public static final MediaSize PRC_5 = new MediaSize("PRC_5", "android", - R.string.mediaSize_chinese_prc_5, 4330, 8661); + R.string.mediasize_chinese_prc_5, 4330, 8661); /** Chinese PRC 6 media size: 120mm x 320mm (4.724" x 12.599") */ public static final MediaSize PRC_6 = new MediaSize("PRC_6", "android", - R.string.mediaSize_chinese_prc_6, 4724, 12599); + R.string.mediasize_chinese_prc_6, 4724, 12599); /** Chinese PRC 7 media size: 160mm x 230mm (6.299" x 9.055") */ public static final MediaSize PRC_7 = new MediaSize("PRC_7", "android", - R.string.mediaSize_chinese_prc_7, 6299, 9055); + R.string.mediasize_chinese_prc_7, 6299, 9055); /** Chinese PRC 8 media size: 120mm x 309mm (4.724" x 12.165") */ public static final MediaSize PRC_8 = new MediaSize("PRC_8", "android", - R.string.mediaSize_chinese_prc_8, 4724, 12165); + R.string.mediasize_chinese_prc_8, 4724, 12165); /** Chinese PRC 9 media size: 229mm x 324mm (9.016" x 12.756") */ public static final MediaSize PRC_9 = new MediaSize("PRC_9", "android", - R.string.mediaSize_chinese_prc_9, 9016, 12756); + R.string.mediasize_chinese_prc_9, 9016, 12756); /** Chinese PRC 10 media size: 324mm x 458mm (12.756" x 18.032") */ public static final MediaSize PRC_10 = new MediaSize("PRC_10", "android", - R.string.mediaSize_chinese_prc_10, 12756, 18032); + R.string.mediasize_chinese_prc_10, 12756, 18032); /** Chinese PRC 16k media size: 146mm x 215mm (5.749" x 8.465") */ public static final MediaSize PRC_16k = new MediaSize("PRC_16k", "android", - R.string.mediaSize_chinese_prc_16k, 5749, 8465); + R.string.mediasize_chinese_prc_16k, 5749, 8465); /** Chinese Pa Kai media size: 267mm x 389mm (10.512" x 15.315") */ public static final MediaSize OM_PA_KAI = new MediaSize("OM_PA_KAI", "android", - R.string.mediaSize_chinese_om_pa_kai, 10512, 15315); + R.string.mediasize_chinese_om_pa_kai, 10512, 15315); /** Chinese Dai Pa Kai media size: 275mm x 395mm (10.827" x 15.551") */ public static final MediaSize OM_DAI_PA_KAI = new MediaSize("OM_DAI_PA_KAI", "android", - R.string.mediaSize_chinese_om_dai_pa_kai, 10827, 15551); + R.string.mediasize_chinese_om_dai_pa_kai, 10827, 15551); /** Chinese Jurro Ku Kai media size: 198mm x 275mm (7.796" x 10.827") */ public static final MediaSize OM_JUURO_KU_KAI = new MediaSize("OM_JUURO_KU_KAI", "android", - R.string.mediaSize_chinese_om_jurro_ku_kai, 7796, 10827); + R.string.mediasize_chinese_om_jurro_ku_kai, 7796, 10827); // Japanese /** Japanese JIS B10 media size: 32mm x 45mm (1.259" x 1.772") */ public static final MediaSize JIS_B10 = new MediaSize("JIS_B10", "android", - R.string.mediaSize_japanese_jis_b10, 1259, 1772); + R.string.mediasize_japanese_jis_b10, 1259, 1772); /** Japanese JIS B9 media size: 45mm x 64mm (1.772" x 2.52") */ public static final MediaSize JIS_B9 = new MediaSize("JIS_B9", "android", - R.string.mediaSize_japanese_jis_b9, 1772, 2520); + R.string.mediasize_japanese_jis_b9, 1772, 2520); /** Japanese JIS B8 media size: 64mm x 91mm (2.52" x 3.583") */ public static final MediaSize JIS_B8 = new MediaSize("JIS_B8", "android", - R.string.mediaSize_japanese_jis_b8, 2520, 3583); + R.string.mediasize_japanese_jis_b8, 2520, 3583); /** Japanese JIS B7 media size: 91mm x 128mm (3.583" x 5.049") */ public static final MediaSize JIS_B7 = new MediaSize("JIS_B7", "android", - R.string.mediaSize_japanese_jis_b7, 3583, 5049); + R.string.mediasize_japanese_jis_b7, 3583, 5049); /** Japanese JIS B6 media size: 128mm x 182mm (5.049" x 7.165") */ public static final MediaSize JIS_B6 = new MediaSize("JIS_B6", "android", - R.string.mediaSize_japanese_jis_b6, 5049, 7165); + R.string.mediasize_japanese_jis_b6, 5049, 7165); /** Japanese JIS B5 media size: 182mm x 257mm (7.165" x 10.118") */ public static final MediaSize JIS_B5 = new MediaSize("JIS_B5", "android", - R.string.mediaSize_japanese_jis_b5, 7165, 10118); + R.string.mediasize_japanese_jis_b5, 7165, 10118); /** Japanese JIS B4 media size: 257mm x 364mm (10.118" x 14.331") */ public static final MediaSize JIS_B4 = new MediaSize("JIS_B4", "android", - R.string.mediaSize_japanese_jis_b4, 10118, 14331); + R.string.mediasize_japanese_jis_b4, 10118, 14331); /** Japanese JIS B3 media size: 364mm x 515mm (14.331" x 20.276") */ public static final MediaSize JIS_B3 = new MediaSize("JIS_B3", "android", - R.string.mediaSize_japanese_jis_b3, 14331, 20276); + R.string.mediasize_japanese_jis_b3, 14331, 20276); /** Japanese JIS B2 media size: 515mm x 728mm (20.276" x 28.661") */ public static final MediaSize JIS_B2 = new MediaSize("JIS_B2", "android", - R.string.mediaSize_japanese_jis_b2, 20276, 28661); + R.string.mediasize_japanese_jis_b2, 20276, 28661); /** Japanese JIS B1 media size: 728mm x 1030mm (28.661" x 40.551") */ public static final MediaSize JIS_B1 = new MediaSize("JIS_B1", "android", - R.string.mediaSize_japanese_jis_b1, 28661, 40551); + R.string.mediasize_japanese_jis_b1, 28661, 40551); /** Japanese JIS B0 media size: 1030mm x 1456mm (40.551" x 57.323") */ public static final MediaSize JIS_B0 = new MediaSize("JIS_B0", "android", - R.string.mediaSize_japanese_jis_b0, 40551, 57323); + R.string.mediasize_japanese_jis_b0, 40551, 57323); /** Japanese JIS Exec media size: 216mm x 330mm (8.504" x 12.992") */ public static final MediaSize JIS_EXEC = new MediaSize("JIS_EXEC", "android", - R.string.mediaSize_japanese_jis_exec, 8504, 12992); + R.string.mediasize_japanese_jis_exec, 8504, 12992); /** Japanese Chou4 media size: 90mm x 205mm (3.543" x 8.071") */ public static final MediaSize JPN_CHOU4 = new MediaSize("JPN_CHOU4", "android", - R.string.mediaSize_japanese_chou4, 3543, 8071); + R.string.mediasize_japanese_chou4, 3543, 8071); /** Japanese Chou3 media size: 120mm x 235mm (4.724" x 9.252") */ public static final MediaSize JPN_CHOU3 = new MediaSize("JPN_CHOU3", "android", - R.string.mediaSize_japanese_chou3, 4724, 9252); + R.string.mediasize_japanese_chou3, 4724, 9252); /** Japanese Chou2 media size: 111.1mm x 146mm (4.374" x 5.748") */ public static final MediaSize JPN_CHOU2 = new MediaSize("JPN_CHOU2", "android", - R.string.mediaSize_japanese_chou2, 4374, 5748); + R.string.mediasize_japanese_chou2, 4374, 5748); /** Japanese Hagaki media size: 100mm x 148mm (3.937" x 5.827") */ public static final MediaSize JPN_HAGAKI = new MediaSize("JPN_HAGAKI", "android", - R.string.mediaSize_japanese_hagaki, 3937, 5827); + R.string.mediasize_japanese_hagaki, 3937, 5827); /** Japanese Oufuku media size: 148mm x 200mm (5.827" x 7.874") */ public static final MediaSize JPN_OUFUKU = new MediaSize("JPN_OUFUKU", "android", - R.string.mediaSize_japanese_oufuku, 5827, 7874); + R.string.mediasize_japanese_oufuku, 5827, 7874); /** Japanese Kahu media size: 240mm x 322.1mm (9.449" x 12.681") */ public static final MediaSize JPN_KAHU = new MediaSize("JPN_KAHU", "android", - R.string.mediaSize_japanese_kahu, 9449, 12681); + R.string.mediasize_japanese_kahu, 9449, 12681); /** Japanese Kaku2 media size: 240mm x 332mm (9.449" x 13.071") */ public static final MediaSize JPN_KAKU2 = new MediaSize("JPN_KAKU2", "android", - R.string.mediaSize_japanese_kaku2, 9449, 13071); + R.string.mediasize_japanese_kaku2, 9449, 13071); /** Japanese You4 media size: 105mm x 235mm (4.134" x 9.252") */ public static final MediaSize JPN_YOU4 = new MediaSize("JPN_YOU4", "android", - R.string.mediaSize_japanese_you4, 4134, 9252); + R.string.mediasize_japanese_you4, 4134, 9252); private final String mId; /**@hide */ @@ -634,7 +656,8 @@ public final class PrintAttributes implements Parcelable { /** * Creates a new instance. * - * @param id The unique media size id. + * @param id The unique media size id. It is unique amongst other media sizes + * supported by the printer. * @param label The <strong>internationalized</strong> human readable label. * @param widthMils The width in mils (thousands of an inch). * @param heightMils The height in mils (thousands of an inch). @@ -679,7 +702,12 @@ public final class PrintAttributes implements Parcelable { } /** - * Gets the unique media size id. + * Gets the unique media size id. It is unique amongst other media sizes + * supported by the printer. + * <p> + * This id is defined by the client that generated the media size + * instance and should not be interpreted by other parties. + * </p> * * @return The unique media size id. */ @@ -829,7 +857,11 @@ public final class PrintAttributes implements Parcelable { } /** - * This class specifies a supported resolution in dpi (dots per inch). + * This class specifies a supported resolution in DPI (dots per inch). + * Resolution defines how many points with different color can be placed + * on one inch in horizontal or vertical direction of the target media. + * For example, a printer with 600DIP can produce higher quality images + * the one with 300DPI resolution. */ public static final class Resolution { private final String mId; @@ -840,10 +872,11 @@ public final class PrintAttributes implements Parcelable { /** * Creates a new instance. * - * @param id The unique resolution id. + * @param id The unique resolution id. It is unique amongst other resolutions + * supported by the printer. * @param label The <strong>internationalized</strong> human readable label. - * @param horizontalDpi The horizontal resolution in dpi. - * @param verticalDpi The vertical resolution in dpi. + * @param horizontalDpi The horizontal resolution in DPI (dots per inch). + * @param verticalDpi The vertical resolution in DPI (dots per inch). * * @throws IllegalArgumentException If the id is empty. * @throws IllegalArgumentException If the label is empty. @@ -872,7 +905,12 @@ public final class PrintAttributes implements Parcelable { } /** - * Gets the unique resolution id. + * Gets the unique resolution id. It is unique amongst other resolutions + * supported by the printer. + * <p> + * This id is defined by the client that generated the resolution + * instance and should not be interpreted by other parties. + * </p> * * @return The unique resolution id. */ @@ -890,7 +928,7 @@ public final class PrintAttributes implements Parcelable { } /** - * Gets the vertical resolution in dpi. + * Gets the horizontal resolution in DPI (dots per inch). * * @return The horizontal resolution. */ @@ -899,7 +937,7 @@ public final class PrintAttributes implements Parcelable { } /** - * Gets the vertical resolution in dpi. + * Gets the vertical resolution in DPI (dots per inch). * * @return The vertical resolution. */ @@ -966,7 +1004,9 @@ public final class PrintAttributes implements Parcelable { } /** - * This class specifies content margins. + * This class specifies content margins. Margins define the white space + * around the content where the left margin defines the amount of white + * space on the left of the content and so on. */ public static final class Margins { public static final Margins NO_MARGINS = new Margins(0, 0, 0, 0); diff --git a/core/java/android/provider/Downloads.java b/core/java/android/provider/Downloads.java index 9999760..b2d9b93 100644 --- a/core/java/android/provider/Downloads.java +++ b/core/java/android/provider/Downloads.java @@ -410,6 +410,8 @@ public final class Downloads { /** The column that is used to count retries */ public static final String COLUMN_FAILED_CONNECTIONS = "numfailed"; + public static final String COLUMN_ALLOW_WRITE = "allow_write"; + /** * default value for {@link #COLUMN_LAST_UPDATESRC}. * This value is used when this column's value is not relevant. 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 @@ public abstract class Layout { float dist = Math.abs(getPrimaryHorizontal(max) - horiz); - if (dist < bestdist) { + if (dist <= bestdist) { bestdist = dist; best = max; } diff --git a/core/java/android/text/SpannableStringBuilder.java b/core/java/android/text/SpannableStringBuilder.java index 8929930..6efde05 100644 --- a/core/java/android/text/SpannableStringBuilder.java +++ b/core/java/android/text/SpannableStringBuilder.java @@ -1288,6 +1288,47 @@ public class SpannableStringBuilder implements CharSequence, GetChars, Spannable return mFilters; } + // Same as SpannableStringInternal + @Override + public boolean equals(Object o) { + if (o instanceof Spanned && + toString().equals(o.toString())) { + // Check span data + Object[] otherSpans = ((Spanned) o).getSpans(0, + ((Spanned) o).length(), Object.class); + if (mSpanCount == otherSpans.length) { + for (int i = 0; i < mSpanCount; ++i) { + Object thisSpan = mSpans[i]; + Object otherSpan = otherSpans[i]; + if (!thisSpan.equals(otherSpan) || + getSpanStart(thisSpan) != getSpanStart(otherSpan) || + getSpanEnd(thisSpan) != getSpanEnd(otherSpan) || + getSpanFlags(thisSpan) != getSpanFlags(otherSpan)) { + return false; + } + } + return true; + } + + } + return false; + } + + // Same as SpannableStringInternal + @Override + public int hashCode() { + int hash = toString().hashCode(); + hash = hash * 31 + mSpanCount; + for (int i = 0; i < mSpanCount; ++i) { + Object span = mSpans[i]; + hash = hash * 31 + span.hashCode(); + hash = hash * 31 + getSpanStart(span); + hash = hash * 31 + getSpanEnd(span); + hash = hash * 31 + getSpanFlags(span); + } + return hash; + } + private static final InputFilter[] NO_FILTERS = new InputFilter[0]; private InputFilter[] mFilters = NO_FILTERS; diff --git a/core/java/android/text/SpannableStringInternal.java b/core/java/android/text/SpannableStringInternal.java index 0825bf3..9b24a2e 100644 --- a/core/java/android/text/SpannableStringInternal.java +++ b/core/java/android/text/SpannableStringInternal.java @@ -358,6 +358,47 @@ import java.lang.reflect.Array; } } + // Same as SpannableStringBuilder + @Override + public boolean equals(Object o) { + if (o instanceof Spanned && + toString().equals(o.toString())) { + // Check span data + Object[] otherSpans = ((Spanned) o).getSpans(0, + ((Spanned) o).length(), Object.class); + if (mSpanCount == otherSpans.length) { + for (int i = 0; i < mSpanCount; ++i) { + Object thisSpan = mSpans[i]; + Object otherSpan = otherSpans[i]; + if (!thisSpan.equals(otherSpan) || + getSpanStart(thisSpan) != getSpanStart(otherSpan) || + getSpanEnd(thisSpan) != getSpanEnd(otherSpan) || + getSpanFlags(thisSpan) != getSpanFlags(otherSpan)) { + return false; + } + } + return true; + } + + } + return false; + } + + // Same as SpannableStringBuilder + @Override + public int hashCode() { + int hash = toString().hashCode(); + hash = hash * 31 + mSpanCount; + for (int i = 0; i < mSpanCount; ++i) { + Object span = mSpans[i]; + hash = hash * 31 + span.hashCode(); + hash = hash * 31 + getSpanStart(span); + hash = hash * 31 + getSpanEnd(span); + hash = hash * 31 + getSpanFlags(span); + } + return hash; + } + private String mText; private Object[] mSpans; private int[] mSpanData; diff --git a/core/java/android/transition/TextChange.java b/core/java/android/transition/TextChange.java index 0b1e4e1..fa2f548 100644 --- a/core/java/android/transition/TextChange.java +++ b/core/java/android/transition/TextChange.java @@ -21,6 +21,7 @@ import android.animation.AnimatorListenerAdapter; import android.animation.AnimatorSet; import android.animation.ValueAnimator; import android.graphics.Color; +import android.util.Log; import android.view.ViewGroup; import android.widget.TextView; @@ -36,6 +37,9 @@ import java.util.Map; * @hide */ public class TextChange extends Transition { + + private static final String LOG_TAG = "TextChange"; + private static final String PROPNAME_TEXT = "android:textchange:text"; private static final String PROPNAME_TEXT_COLOR = "android:textchange:textColor"; @@ -224,6 +228,9 @@ public class TextChange extends Transition { } }; addListener(transitionListener); + if (DBG) { + Log.d(LOG_TAG, "createAnimator returning " + anim); + } return anim; } return null; diff --git a/core/java/android/webkit/WebView.java b/core/java/android/webkit/WebView.java index 20fb1a7..90cc72e 100644 --- a/core/java/android/webkit/WebView.java +++ b/core/java/android/webkit/WebView.java @@ -58,9 +58,6 @@ import java.util.Map; * It uses the WebKit rendering engine to display * web pages and includes methods to navigate forward and backward * through a history, zoom in and out, perform text searches and more.</p> - * <p>To enable the built-in zoom, set - * {@link #getSettings() WebSettings}.{@link WebSettings#setBuiltInZoomControls(boolean)} - * (introduced in API level {@link android.os.Build.VERSION_CODES#CUPCAKE}). * <p>Note that, in order for your Activity to access the Internet and load web pages * in a WebView, you must add the {@code INTERNET} permissions to your * Android Manifest file:</p> @@ -161,9 +158,18 @@ import java.util.Map; * } * }); * - * webview.loadUrl("http://slashdot.org/"); + * webview.loadUrl("http://developer.android.com/"); * </pre> * + * <h3>Zoom</h3> + * + * <p>To enable the built-in zoom, set + * {@link #getSettings() WebSettings}.{@link WebSettings#setBuiltInZoomControls(boolean)} + * (introduced in API level {@link android.os.Build.VERSION_CODES#CUPCAKE}).</p> + * <p>NOTE: Using zoom if either the height or width is set to + * {@link android.view.ViewGroup.LayoutParams#WRAP_CONTENT} may lead to undefined behavior + * and should be avoided.</p> + * * <h3>Cookie and window management</h3> * * <p>For obvious security reasons, your application has its own @@ -222,6 +228,7 @@ import java.util.Map; * <p>The {@code hdpi.css} stylesheet is only used for devices with a screen pixel ration of 1.5, * which is the high density pixel ratio.</p> * </li> + * </ul> * * <h3>HTML5 Video support</h3> * @@ -1673,6 +1680,22 @@ public class WebView extends AbsoluteLayout } /** + * Enables debugging of web contents (HTML / CSS / JavaScript) + * loaded into any WebViews of this application. This flag can be enabled + * in order to facilitate debugging of web layouts and JavaScript + * code running inside WebViews. Please refer to WebView documentation + * for the debugging guide. + * + * The default is false. + * + * @param enabled whether to enable web contents debugging + */ + public static void setWebContentsDebuggingEnabled(boolean enabled) { + checkThread(); + getFactory().getStatics().setWebContentsDebuggingEnabled(enabled); + } + + /** * Gets the list of currently loaded plugins. * * @return the list of currently loaded plugins diff --git a/core/java/android/webkit/WebViewClassic.java b/core/java/android/webkit/WebViewClassic.java index e82ce30..3c377d3 100644 --- a/core/java/android/webkit/WebViewClassic.java +++ b/core/java/android/webkit/WebViewClassic.java @@ -1361,6 +1361,11 @@ public final class WebViewClassic implements WebViewProvider, WebViewProvider.Sc return WebSettingsClassic.getDefaultUserAgentForLocale(context, Locale.getDefault()); } + + @Override + public void setWebContentsDebuggingEnabled(boolean enable) { + // no-op for WebViewClassic. + } } private void onHandleUiEvent(MotionEvent event, int eventType, int flags) { diff --git a/core/java/android/webkit/WebViewFactoryProvider.java b/core/java/android/webkit/WebViewFactoryProvider.java index 934ef83..9d9d882 100644 --- a/core/java/android/webkit/WebViewFactoryProvider.java +++ b/core/java/android/webkit/WebViewFactoryProvider.java @@ -48,6 +48,12 @@ public interface WebViewFactoryProvider { * {@link android.webkit.WebSettings#getDefaultUserAgent(Context) } */ String getDefaultUserAgent(Context context); + + /** + * Implements the API method: + * {@link android.webkit.WebView#setWebContentsDebuggingEnabled(boolean) } + */ + void setWebContentsDebuggingEnabled(boolean enable); } Statics getStatics(); diff --git a/core/java/android/widget/AdapterView.java b/core/java/android/widget/AdapterView.java index 31ab0af..a5fad60 100644 --- a/core/java/android/widget/AdapterView.java +++ b/core/java/android/widget/AdapterView.java @@ -280,7 +280,9 @@ public abstract class AdapterView<T extends Adapter> extends ViewGroup { } /** - * Call the OnItemClickListener, if it is defined. + * Call the OnItemClickListener, if it is defined. Performs all normal + * actions associated with clicking: reporting accessibility event, playing + * a sound, etc. * * @param view The view within the AdapterView that was clicked. * @param position The position of the view in the adapter. |
