diff options
Diffstat (limited to 'core/java')
30 files changed, 1473 insertions, 1281 deletions
diff --git a/core/java/android/app/ContextImpl.java b/core/java/android/app/ContextImpl.java index b4fe698..db6a4bf 100644 --- a/core/java/android/app/ContextImpl.java +++ b/core/java/android/app/ContextImpl.java @@ -2659,102 +2659,6 @@ class ContextImpl extends Context { return PackageManager.COMPONENT_ENABLED_STATE_DEFAULT; } - // Constants related to app heuristics - // No-installation limit for internal flash: 10% or less space available - private static final double LOW_NAND_FLASH_TRESHOLD = 0.1; - - // SD-to-internal app size threshold: currently set to 1 MB - private static final long INSTALL_ON_SD_THRESHOLD = (1024 * 1024); - - public int recommendAppInstallLocation(Package pkg) { - // Initial implementation: - // Package size = code size + cache size + data size - // If code size > 1 MB, install on SD card. - // Else install on internal NAND flash, unless space on NAND is less than 10% - - if (pkg == null) { - return INSTALL_PARSE_FAILED_NOT_APK; - } - - StatFs internalFlashStats = new StatFs(Environment.getDataDirectory().getPath()); - StatFs sdcardStats = new StatFs(Environment.getExternalStorageDirectory().getPath()); - - long totalInternalFlashSize = (long)internalFlashStats.getBlockCount() * - (long)internalFlashStats.getBlockSize(); - long availInternalFlashSize = (long)internalFlashStats.getAvailableBlocks() * - (long)internalFlashStats.getBlockSize(); - long availSDSize = (long)sdcardStats.getAvailableBlocks() * - (long)sdcardStats.getBlockSize(); - - double pctNandFree = (double)availInternalFlashSize / (double)totalInternalFlashSize; - - final String archiveFilePath = pkg.mScanPath; - File apkFile = new File(archiveFilePath); - long pkgLen = apkFile.length(); - - boolean auto = true; - // To make final copy - long reqInstallSize = pkgLen; - // For dex files - long reqInternalSize = 1 * pkgLen; - boolean intThresholdOk = (pctNandFree >= LOW_NAND_FLASH_TRESHOLD); - boolean intAvailOk = ((reqInstallSize + reqInternalSize) < availInternalFlashSize); - boolean fitsOnSd = (reqInstallSize < availSDSize) && intThresholdOk && - (reqInternalSize < availInternalFlashSize); - boolean fitsOnInt = intThresholdOk && intAvailOk; - - // Consider application flags preferences as well... - boolean installOnlyOnSd = (pkg.installLocation == - PackageInfo.INSTALL_LOCATION_PREFER_EXTERNAL); - boolean installOnlyInternal = (pkg.installLocation == - PackageInfo.INSTALL_LOCATION_INTERNAL_ONLY); - if (installOnlyInternal) { - // If set explicitly in manifest, - // let that override everything else - auto = false; - } else if (installOnlyOnSd){ - // Check if this can be accommodated on the sdcard - if (fitsOnSd) { - auto = false; - } - } else { - // Check if user option is enabled - boolean setInstallLoc = Settings.System.getInt(mContext.getContentResolver(), - Settings.System.SET_INSTALL_LOCATION, 0) != 0; - if (setInstallLoc) { - // Pick user preference - int installPreference = Settings.System.getInt(mContext.getContentResolver(), - Settings.System.DEFAULT_INSTALL_LOCATION, - PackageInfo.INSTALL_LOCATION_AUTO); - if (installPreference == 1) { - installOnlyInternal = true; - auto = false; - } else if (installPreference == 2) { - installOnlyOnSd = true; - auto = false; - } - } - } - if (!auto) { - if (installOnlyOnSd) { - return fitsOnSd ? INSTALL_ON_SDCARD : INSTALL_FAILED_INSUFFICIENT_STORAGE; - } else if (installOnlyInternal){ - // Check on internal flash - return fitsOnInt ? INSTALL_ON_INTERNAL_FLASH : INSTALL_FAILED_INSUFFICIENT_STORAGE; - } - } - // Try to install internally - if (fitsOnInt) { - return INSTALL_ON_INTERNAL_FLASH; - } - // Try the sdcard now. - if (fitsOnSd) { - return INSTALL_ON_SDCARD; - } - // Return error code - return INSTALL_FAILED_INSUFFICIENT_STORAGE; - } - private final ContextImpl mContext; private final IPackageManager mPM; diff --git a/core/java/android/content/Intent.java b/core/java/android/content/Intent.java index e36eba9..d31b25b 100644 --- a/core/java/android/content/Intent.java +++ b/core/java/android/content/Intent.java @@ -1840,7 +1840,7 @@ public class Intent implements Parcelable, Cloneable { * @hide */ public static final String ACTION_REMOTE_INTENT = - "android.intent.action.REMOTE_INTENT"; + "com.google.android.pushmessaging.intent.RECEIVE"; /** * Broadcast Action: hook for permforming cleanup after a system update. diff --git a/core/java/android/content/pm/PackageManager.java b/core/java/android/content/pm/PackageManager.java index 17bee48..ff2ed3d 100644 --- a/core/java/android/content/pm/PackageManager.java +++ b/core/java/android/content/pm/PackageManager.java @@ -258,14 +258,7 @@ public abstract class PackageManager { * package has to be installed on the sdcard. * @hide */ - public static final int INSTALL_ON_SDCARD = 0x00000008; - - /** - * Convenience flag parameter to indicate that this package has to be installed - * on internal flash. - * @hide - */ - public static final int INSTALL_ON_INTERNAL_FLASH = 0x00000000; + public static final int INSTALL_EXTERNAL = 0x00000008; /** * Flag parameter for @@ -529,6 +522,14 @@ public abstract class PackageManager { public static final int INSTALL_PARSE_FAILED_MANIFEST_EMPTY = -109; /** + * Installation failed return code: this is passed to the {@link IPackageInstallObserver} by + * {@link #installPackage(android.net.Uri, IPackageInstallObserver, int)} + * if the system failed to install the package because of system issues. + * @hide + */ + public static final int INSTALL_FAILED_INTERNAL_ERROR = -110; + + /** * Indicates the state of installation. Used by PackageManager to * figure out incomplete installations. Say a package is being installed * (the state is set to PKG_INSTALL_INCOMPLETE) and remains so till @@ -627,23 +628,6 @@ public abstract class PackageManager { */ public static final String ACTION_CLEAN_EXTERNAL_STORAGE = "android.content.pm.CLEAN_EXTERNAL_STORAGE"; - - /** - * Determines best place to install an application: either SD or internal FLASH. - * If applications explicitly set installLocation in their manifest, that - * preference takes precedence. If not a recommended location is returned - * based on current available storage on internal flash or sdcard. - * @param pkgInfo PackageParser.Package of the package that is to be installed. - * Call utility method to obtain. - * @return {@link INSTALL_ON_INTERNAL_FLASH} if it is best to install package on internal - * storage, {@link INSTALL_ON_SDCARD} if it is best to install package on SD card, - * and {@link INSTALL_FAILED_INSUFFICIENT_STORAGE} if insufficient space to safely install - * the application. {@link INSTALL_PARSE_FAILED_NOT_APK} Is returned if any input - * parameter is <code>null</code>. - * This recommendation does take into account the package's own flags. - * @hide - */ - public abstract int recommendAppInstallLocation(PackageParser.Package pkg); /** * Retrieve overall information about an application package that is diff --git a/core/java/android/net/Downloads.java b/core/java/android/net/Downloads.java index b86a0e2..72106c8 100644 --- a/core/java/android/net/Downloads.java +++ b/core/java/android/net/Downloads.java @@ -22,165 +22,197 @@ import android.content.ContentValues; import android.content.Context; import android.database.Cursor; import android.net.Uri; +import android.os.ParcelFileDescriptor; import android.os.SystemClock; import android.provider.BaseColumns; import android.util.Log; import java.io.File; +import java.io.FileNotFoundException; +import java.io.IOException; +import java.io.File; +import java.io.InputStream; /** * The Download Manager * - * @pending + * */ public final class Downloads { - public static final class ByUri { - /** @hide */ - private ByUri() {} + /** + * Download status codes + */ - /** - * Initiate a download where the download will be tracked by its URI. - * @pending - */ - public static boolean startDownloadByUri( - Context context, - String url, - boolean showDownload, - boolean allowRoaming, - String title, - String notification_package, - String notification_class) { - ContentResolver cr = context.getContentResolver(); + /** + * This download hasn't started yet + */ + public static final int STATUS_PENDING = 190; - // Tell download manager to start downloading update. - ContentValues values = new ContentValues(); - values.put(android.provider.Downloads.Impl.COLUMN_URI, url); - values.put(android.provider.Downloads.Impl.COLUMN_VISIBILITY, - showDownload ? android.provider.Downloads.Impl.VISIBILITY_VISIBLE - : android.provider.Downloads.Impl.VISIBILITY_HIDDEN); - if (title != null) { - values.put(android.provider.Downloads.Impl.COLUMN_TITLE, title); - } - values.put(android.provider.Downloads.Impl.COLUMN_APP_DATA, url); - values.put(android.provider.Downloads.Impl.COLUMN_DESTINATION, - allowRoaming ? android.provider.Downloads.Impl.DESTINATION_CACHE_PARTITION : - android.provider.Downloads.Impl.DESTINATION_CACHE_PARTITION_NOROAMING); - values.put(android.provider.Downloads.Impl.COLUMN_NO_INTEGRITY, true); // Don't check ETag - if (notification_package != null && notification_class != null) { - values.put(android.provider.Downloads.Impl.COLUMN_NOTIFICATION_PACKAGE, notification_package); - values.put(android.provider.Downloads.Impl.COLUMN_NOTIFICATION_CLASS, notification_class); - } + /** + * This download has started + */ + public static final int STATUS_RUNNING = 192; - if (cr.insert(android.provider.Downloads.Impl.CONTENT_URI, values) == null) { - return false; - } - return true; - } + /** + * This download has successfully completed. + * Warning: there might be other status values that indicate success + * in the future. + * Use isSucccess() to capture the entire category. + */ + public static final int STATUS_SUCCESS = 200; - public static final class StatusInfo { - public boolean completed = false; - /** The filename of the active download. */ - public String filename = null; - /** An opaque id for the download */ - public long id = -1; - /** An opaque status code for the download */ - public int statusCode = -1; - /** Approximate number of bytes downloaded so far, for debugging purposes. */ - public long bytesSoFar = -1; - } + /** + * This download can't be performed because the content type cannot be + * handled. + */ + public static final int STATUS_NOT_ACCEPTABLE = 406; + + /** + * This download has completed with an error. + * Warning: there will be other status values that indicate errors in + * the future. Use isStatusError() to capture the entire category. + */ + public static final int STATUS_UNKNOWN_ERROR = 491; + + /** + * This download couldn't be completed because of an HTTP + * redirect response that the download manager couldn't + * handle. + */ + public static final int STATUS_UNHANDLED_REDIRECT = 493; + + /** + * This download couldn't be completed due to insufficient storage + * space. Typically, this is because the SD card is full. + */ + public static final int STATUS_INSUFFICIENT_SPACE_ERROR = 498; + + /** + * This download couldn't be completed because no external storage + * device was found. Typically, this is because the SD card is not + * mounted. + */ + public static final int STATUS_DEVICE_NOT_FOUND_ERROR = 499; + + /** + * Returns whether the status is a success (i.e. 2xx). + */ + public static boolean isStatusSuccess(int status) { + return (status >= 200 && status < 300); + } + + /** + * Returns whether the status is an error (i.e. 4xx or 5xx). + */ + public static boolean isStatusError(int status) { + return (status >= 400 && status < 600); + } + + /** + * Download destinations + */ + + /** + * This download will be saved to the external storage. This is the + * default behavior, and should be used for any file that the user + * can freely access, copy, delete. Even with that destination, + * unencrypted DRM files are saved in secure internal storage. + * Downloads to the external destination only write files for which + * there is a registered handler. The resulting files are accessible + * by filename to all applications. + */ + public static final int DOWNLOAD_DESTINATION_EXTERNAL = 1; + + /** + * This download will be saved to the download manager's private + * partition. This is the behavior used by applications that want to + * download private files that are used and deleted soon after they + * get downloaded. All file types are allowed, and only the initiating + * application can access the file (indirectly through a content + * provider). This requires the + * android.permission.ACCESS_DOWNLOAD_MANAGER_ADVANCED permission. + */ + public static final int DOWNLOAD_DESTINATION_CACHE = 2; + + /** + * This download will be saved to the download manager's private + * partition and will be purged as necessary to make space. This is + * for private files (similar to CACHE_PARTITION) that aren't deleted + * immediately after they are used, and are kept around by the download + * manager as long as space is available. + */ + public static final int DOWNLOAD_DESTINATION_CACHE_PURGEABLE = 3; + + + /** + * An invalid download id + */ + public static final long DOWNLOAD_ID_INVALID = -1; + + + /** + * Broadcast Action: this is sent by the download manager to the app + * that had initiated a download when that download completes. The + * download's content: uri is specified in the intent's data. + */ + public static final String ACTION_DOWNLOAD_COMPLETED = + "android.intent.action.DOWNLOAD_COMPLETED"; + + /** + * If extras are specified when requesting a download they will be provided in the intent that + * is sent to the specified class and package when a download has finished. + * <P>Type: TEXT</P> + * <P>Owner can Init</P> + */ + public static final String COLUMN_NOTIFICATION_EXTRAS = "notificationextras"; - /** @hide */ - private static final int STATUS_INVALID = 0; - /** @hide */ - private static final int STATUS_DOWNLOADING_UPDATE = 3; - /** @hide */ - private static final int STATUS_DOWNLOADED_UPDATE = 4; + + /** + * Status class for a download + */ + public static final class StatusInfo { + public boolean completed = false; + /** The filename of the active download. */ + public String filename = null; + /** An opaque id for the download */ + public long id = DOWNLOAD_ID_INVALID; + /** An opaque status code for the download */ + public int statusCode = -1; + /** Approximate number of bytes downloaded so far, for debugging purposes. */ + public long bytesSoFar = -1; /** - * Column projection for the query to the download manager. This must match - * with the constants DOWNLOADS_COLUMN_*. - * @hide + * Returns whether the download is completed + * @return a boolean whether the download is complete. */ - private static final String[] DOWNLOADS_PROJECTION = { - BaseColumns._ID, - android.provider.Downloads.Impl.COLUMN_APP_DATA, - android.provider.Downloads.Impl.COLUMN_STATUS, - android.provider.Downloads.Impl._DATA, - android.provider.Downloads.Impl.COLUMN_LAST_MODIFICATION, - android.provider.Downloads.Impl.COLUMN_CURRENT_BYTES, - }; + public boolean isComplete() { + return android.provider.Downloads.Impl.isStatusCompleted(statusCode); + } /** - * The column index for the ID. - * @hide - */ - private static final int DOWNLOADS_COLUMN_ID = 0; - /** - * The column index for the URI. - * @hide - */ - private static final int DOWNLOADS_COLUMN_URI = 1; - /** - * The column index for the status code. - * @hide - */ - private static final int DOWNLOADS_COLUMN_STATUS = 2; - /** - * The column index for the filename. - * @hide - */ - private static final int DOWNLOADS_COLUMN_FILENAME = 3; - /** - * The column index for the last modification time. - * @hide - */ - private static final int DOWNLOADS_COLUMN_LAST_MODIFICATION = 4; - /** - * The column index for the number of bytes downloaded so far. - * @hide - */ - private static final int DOWNLOADS_COLUMN_CURRENT_BYTES = 5; + * Returns whether the download is successful + * @return a boolean whether the download is successful. + */ + public boolean isSuccessful() { + return android.provider.Downloads.Impl.isStatusCompleted(statusCode); + } + } + + /** + * Class to access initiate and query download by server uri + */ + public static final class ByUri extends DownloadBase { + /** @hide */ + private ByUri() {} /** - * Gets the status of a download. - * - * @param c A Cursor pointing to a download. The URL column is assumed to be valid. - * @return The status of the download. + * Query where clause by app data. * @hide */ - private static final int getStatusOfDownload( - Cursor c, - long redownload_threshold) { - int status = c.getInt(DOWNLOADS_COLUMN_STATUS); - long realtime = SystemClock.elapsedRealtime(); - - // TODO(dougz): special handling of 503, 404? (eg, special - // explanatory messages to user) - - if (!android.provider.Downloads.Impl.isStatusCompleted(status)) { - // Check if it's stuck - long modified = c.getLong(DOWNLOADS_COLUMN_LAST_MODIFICATION); - long now = System.currentTimeMillis(); - if (now < modified || now - modified > redownload_threshold) { - return STATUS_INVALID; - } - - return STATUS_DOWNLOADING_UPDATE; - } - - if (android.provider.Downloads.Impl.isStatusError(status)) { - return STATUS_INVALID; - } - - String filename = c.getString(DOWNLOADS_COLUMN_FILENAME); - if (filename == null) { - return STATUS_INVALID; - } - - return STATUS_DOWNLOADED_UPDATE; - } + private static final String QUERY_WHERE_APP_DATA_CLAUSE = + android.provider.Downloads.Impl.COLUMN_APP_DATA + "=?"; /** * Gets a Cursor pointing to the download(s) of the current system update. @@ -190,15 +222,14 @@ public final class Downloads { return context.getContentResolver().query( android.provider.Downloads.Impl.CONTENT_URI, DOWNLOADS_PROJECTION, - android.provider.Downloads.Impl.COLUMN_APP_DATA + "='" + url.replace("'", "''") + "'", - null, + QUERY_WHERE_APP_DATA_CLAUSE, + new String[] {url}, null); } /** * Returns a StatusInfo with the result of trying to download the * given URL. Returns null if no attempts have been made. - * @pending */ public static final StatusInfo getStatus( Context context, @@ -237,14 +268,15 @@ public final class Downloads { result.bytesSoFar = c.getLong(DOWNLOADS_COLUMN_CURRENT_BYTES); } } finally { - c.close(); + if (c != null) { + c.close(); + } } return result; } /** * Query where clause for general querying. - * @hide */ private static final String QUERY_WHERE_CLAUSE = android.provider.Downloads.Impl.COLUMN_NOTIFICATION_PACKAGE + "=? AND " + @@ -252,7 +284,6 @@ public final class Downloads { /** * Delete all the downloads for a package/class pair. - * @pending */ public static final void removeAllDownloadsByPackage( Context context, @@ -265,21 +296,24 @@ public final class Downloads { } /** - * @pending + * The column for the id in the Cursor returned by + * getProgressCursor() */ public static final int getProgressColumnId() { return 0; } /** - * @pending + * The column for the current byte count in the Cursor returned by + * getProgressCursor() */ public static final int getProgressColumnCurrentBytes() { return 1; } /** - * @pending + * The column for the total byte count in the Cursor returned by + * getProgressCursor() */ public static final int getProgressColumnTotalBytes() { return 2; @@ -287,19 +321,324 @@ public final class Downloads { /** @hide */ private static final String[] PROJECTION = { - BaseColumns._ID, android.provider.Downloads.Impl.COLUMN_CURRENT_BYTES, android.provider.Downloads.Impl.COLUMN_TOTAL_BYTES + BaseColumns._ID, + android.provider.Downloads.Impl.COLUMN_CURRENT_BYTES, + android.provider.Downloads.Impl.COLUMN_TOTAL_BYTES }; /** - * @pending + * Returns a Cursor representing the progress of the download identified by the ID. */ public static final Cursor getProgressCursor(Context context, long id) { - Uri downloadUri = Uri.withAppendedPath(android.provider.Downloads.Impl.CONTENT_URI, String.valueOf(id)); + Uri downloadUri = Uri.withAppendedPath(android.provider.Downloads.Impl.CONTENT_URI, + String.valueOf(id)); return context.getContentResolver().query(downloadUri, PROJECTION, null, null, null); } } /** + * Class to access downloads by opaque download id + */ + public static final class ById extends DownloadBase { + /** @hide */ + private ById() {} + + /** + * Get the mime tupe of the download specified by the download id + */ + public static String getMimeTypeForId(Context context, long downloadId) { + ContentResolver cr = context.getContentResolver(); + + String mimeType = null; + Cursor downloadCursor = null; + + try { + Uri downloadUri = getDownloadUri(downloadId); + + downloadCursor = cr.query( + downloadUri, new String[]{android.provider.Downloads.Impl.COLUMN_MIME_TYPE}, + null, null, null); + if (downloadCursor.moveToNext()) { + mimeType = downloadCursor.getString(0); + } + } finally { + if (downloadCursor != null) downloadCursor.close(); + } + return mimeType; + } + + /** + * Delete a download by Id + */ + public static void deleteDownload(Context context, long downloadId) { + ContentResolver cr = context.getContentResolver(); + + String mimeType = null; + + Uri downloadUri = getDownloadUri(downloadId); + + cr.delete(downloadUri, null, null); + } + + /** + * Open a filedescriptor to a particular download + */ + public static ParcelFileDescriptor openDownload( + Context context, long downloadId, String mode) + throws FileNotFoundException + { + ContentResolver cr = context.getContentResolver(); + + String mimeType = null; + + Uri downloadUri = getDownloadUri(downloadId); + + return cr.openFileDescriptor(downloadUri, mode); + } + + /** + * Open a stream to a particular download + */ + public static InputStream openDownloadStream(Context context, long downloadId) + throws FileNotFoundException, IOException + { + ContentResolver cr = context.getContentResolver(); + + String mimeType = null; + + Uri downloadUri = getDownloadUri(downloadId); + + return cr.openInputStream(downloadUri); + } + + private static Uri getDownloadUri(long downloadId) { + return Uri.parse(android.provider.Downloads.Impl.CONTENT_URI + "/" + downloadId); + } + + /** + * Returns a StatusInfo with the result of trying to download the + * given URL. Returns null if no attempts have been made. + */ + public static final StatusInfo getStatus( + Context context, + long downloadId) { + StatusInfo result = null; + boolean hasFailedDownload = false; + long failedDownloadModificationTime = 0; + + Uri downloadUri = getDownloadUri(downloadId); + + ContentResolver cr = context.getContentResolver(); + + Cursor c = cr.query( + downloadUri, DOWNLOADS_PROJECTION, null /* selection */, null /* selection args */, + null /* sort order */); + try { + if (!c.moveToNext()) { + return result; + } + + if (result == null) { + result = new StatusInfo(); + } + int status = getStatusOfDownload(c,0); + if (status == STATUS_DOWNLOADING_UPDATE || + status == STATUS_DOWNLOADED_UPDATE) { + result.completed = (status == STATUS_DOWNLOADED_UPDATE); + result.filename = c.getString(DOWNLOADS_COLUMN_FILENAME); + result.id = c.getLong(DOWNLOADS_COLUMN_ID); + result.statusCode = c.getInt(DOWNLOADS_COLUMN_STATUS); + result.bytesSoFar = c.getLong(DOWNLOADS_COLUMN_CURRENT_BYTES); + return result; + } + + long modTime = c.getLong(DOWNLOADS_COLUMN_LAST_MODIFICATION); + + result.statusCode = c.getInt(DOWNLOADS_COLUMN_STATUS); + result.bytesSoFar = c.getLong(DOWNLOADS_COLUMN_CURRENT_BYTES); + } finally { + if (c != null) { + c.close(); + } + } + return result; + } + } + + + /** + * Base class with common functionality for the various download classes + */ + private static class DownloadBase { + /** @hide */ + DownloadBase() {} + + /** + * Initiate a download where the download will be tracked by its URI. + */ + public static long startDownloadByUri( + Context context, + String url, + String cookieData, + boolean showDownload, + int downloadDestination, + boolean allowRoaming, + boolean skipIntegrityCheck, + String title, + String notification_package, + String notification_class, + String notification_extras) { + ContentResolver cr = context.getContentResolver(); + + // Tell download manager to start downloading update. + ContentValues values = new ContentValues(); + values.put(android.provider.Downloads.Impl.COLUMN_URI, url); + values.put(android.provider.Downloads.Impl.COLUMN_COOKIE_DATA, cookieData); + values.put(android.provider.Downloads.Impl.COLUMN_VISIBILITY, + showDownload ? android.provider.Downloads.Impl.VISIBILITY_VISIBLE + : android.provider.Downloads.Impl.VISIBILITY_HIDDEN); + if (title != null) { + values.put(android.provider.Downloads.Impl.COLUMN_TITLE, title); + } + values.put(android.provider.Downloads.Impl.COLUMN_APP_DATA, url); + + + // NOTE: destination should be seperated from whether the download + // can happen when roaming + int destination = android.provider.Downloads.Impl.DESTINATION_EXTERNAL; + switch (downloadDestination) { + case DOWNLOAD_DESTINATION_EXTERNAL: + destination = android.provider.Downloads.Impl.DESTINATION_EXTERNAL; + break; + case DOWNLOAD_DESTINATION_CACHE: + if (allowRoaming) { + destination = android.provider.Downloads.Impl.DESTINATION_CACHE_PARTITION; + } else { + destination = + android.provider.Downloads.Impl.DESTINATION_CACHE_PARTITION_NOROAMING; + } + break; + case DOWNLOAD_DESTINATION_CACHE_PURGEABLE: + destination = + android.provider.Downloads.Impl.DESTINATION_CACHE_PARTITION_PURGEABLE; + break; + } + values.put(android.provider.Downloads.Impl.COLUMN_DESTINATION, destination); + values.put(android.provider.Downloads.Impl.COLUMN_NO_INTEGRITY, + skipIntegrityCheck); // Don't check ETag + if (notification_package != null && notification_class != null) { + values.put(android.provider.Downloads.Impl.COLUMN_NOTIFICATION_PACKAGE, + notification_package); + values.put(android.provider.Downloads.Impl.COLUMN_NOTIFICATION_CLASS, + notification_class); + + if (notification_extras != null) { + values.put(android.provider.Downloads.Impl.COLUMN_NOTIFICATION_EXTRAS, + notification_extras); + } + } + + Uri downloadUri = cr.insert(android.provider.Downloads.Impl.CONTENT_URI, values); + + long downloadId = DOWNLOAD_ID_INVALID; + if (downloadUri != null) { + downloadId = Long.parseLong(downloadUri.getLastPathSegment()); + } + return downloadId; + } + } + + /** @hide */ + private static final int STATUS_INVALID = 0; + /** @hide */ + private static final int STATUS_DOWNLOADING_UPDATE = 3; + /** @hide */ + private static final int STATUS_DOWNLOADED_UPDATE = 4; + + /** + * Column projection for the query to the download manager. This must match + * with the constants DOWNLOADS_COLUMN_*. + * @hide + */ + private static final String[] DOWNLOADS_PROJECTION = { + BaseColumns._ID, + android.provider.Downloads.Impl.COLUMN_APP_DATA, + android.provider.Downloads.Impl.COLUMN_STATUS, + android.provider.Downloads.Impl._DATA, + android.provider.Downloads.Impl.COLUMN_LAST_MODIFICATION, + android.provider.Downloads.Impl.COLUMN_CURRENT_BYTES, + }; + + /** + * The column index for the ID. + * @hide + */ + private static final int DOWNLOADS_COLUMN_ID = 0; + /** + * The column index for the URI. + * @hide + */ + private static final int DOWNLOADS_COLUMN_URI = 1; + /** + * The column index for the status code. + * @hide + */ + private static final int DOWNLOADS_COLUMN_STATUS = 2; + /** + * The column index for the filename. + * @hide + */ + private static final int DOWNLOADS_COLUMN_FILENAME = 3; + /** + * The column index for the last modification time. + * @hide + */ + private static final int DOWNLOADS_COLUMN_LAST_MODIFICATION = 4; + /** + * The column index for the number of bytes downloaded so far. + * @hide + */ + private static final int DOWNLOADS_COLUMN_CURRENT_BYTES = 5; + + /** + * Gets the status of a download. + * + * @param c A Cursor pointing to a download. The URL column is assumed to be valid. + * @return The status of the download. + * @hide + */ + private static final int getStatusOfDownload( Cursor c, long redownload_threshold) { + int status = c.getInt(DOWNLOADS_COLUMN_STATUS); + long realtime = SystemClock.elapsedRealtime(); + + // TODO(dougz): special handling of 503, 404? (eg, special + // explanatory messages to user) + + if (!android.provider.Downloads.Impl.isStatusCompleted(status)) { + // Check if it's stuck + long modified = c.getLong(DOWNLOADS_COLUMN_LAST_MODIFICATION); + long now = System.currentTimeMillis(); + if (now < modified || now - modified > redownload_threshold) { + return STATUS_INVALID; + } + + return STATUS_DOWNLOADING_UPDATE; + } + + if (android.provider.Downloads.Impl.isStatusError(status)) { + return STATUS_INVALID; + } + + String filename = c.getString(DOWNLOADS_COLUMN_FILENAME); + if (filename == null) { + return STATUS_INVALID; + } + + return STATUS_DOWNLOADED_UPDATE; + } + + + /** * @hide */ private Downloads() {} diff --git a/core/java/android/net/SSLCertificateSocketFactory.java b/core/java/android/net/SSLCertificateSocketFactory.java index ed76b15..f959fee 100644 --- a/core/java/android/net/SSLCertificateSocketFactory.java +++ b/core/java/android/net/SSLCertificateSocketFactory.java @@ -50,12 +50,14 @@ import org.apache.harmony.xnet.provider.jsse.SSLParameters; * <ul> * <li>Timeout specification for SSL handshake operations * <li>Optional SSL session caching with {@link SSLSessionCache} - * <li>On development devices, "setprop socket.relaxsslcheck yes" bypasses all - * SSL certificate checks, for testing with development servers + * <li>Optionally bypass all SSL certificate checks * </ul> * Note that the handshake timeout does not apply to actual connection. * If you want a connection timeout as well, use {@link #createSocket()} and * {@link Socket#connect(SocketAddress, int)}. + * <p> + * On development devices, "setprop socket.relaxsslcheck yes" bypasses all + * SSL certificate checks, for testing with development servers. */ public class SSLCertificateSocketFactory extends SSLSocketFactory { private static final String TAG = "SSLCertificateSocketFactory"; @@ -73,41 +75,57 @@ public class SSLCertificateSocketFactory extends SSLSocketFactory { private final int mHandshakeTimeoutMillis; private final SSLClientSessionCache mSessionCache; + private final boolean mSecure; /** @deprecated Use {@link #getDefault(int)} instead. */ public SSLCertificateSocketFactory(int handshakeTimeoutMillis) { - this(handshakeTimeoutMillis, null /* cache */); + this(handshakeTimeoutMillis, null, true); } - private SSLCertificateSocketFactory(int handshakeTimeoutMillis, SSLSessionCache cache) { + private SSLCertificateSocketFactory( + int handshakeTimeoutMillis, SSLSessionCache cache, boolean secure) { mHandshakeTimeoutMillis = handshakeTimeoutMillis; mSessionCache = cache == null ? null : cache.mSessionCache; + mSecure = secure; } /** - * Returns a new instance of a socket factory using the specified socket read - * timeout while connecting with the server/negotiating an ssl session. + * Returns a new socket factory instance with an optional handshake timeout. * * @param handshakeTimeoutMillis to use for SSL connection handshake, or 0 * for none. The socket timeout is reset to 0 after the handshake. * @return a new SocketFactory with the specified parameters */ public static SocketFactory getDefault(int handshakeTimeoutMillis) { - return getDefault(handshakeTimeoutMillis, null /* cache */); + return new SSLCertificateSocketFactory(handshakeTimeoutMillis, null, true); } /** - * Returns a new instance of a socket factory using the specified socket - * read timeout while connecting with the server/negotiating an ssl session - * Persists ssl sessions using the provided {@link SSLClientSessionCache}. + * Returns a new socket factory instance with an optional handshake timeout + * and SSL session cache. * * @param handshakeTimeoutMillis to use for SSL connection handshake, or 0 * for none. The socket timeout is reset to 0 after the handshake. * @param cache The {@link SSLClientSessionCache} to use, or null for no cache. * @return a new SocketFactory with the specified parameters */ - public static SocketFactory getDefault(int handshakeTimeoutMillis, SSLSessionCache cache) { - return new SSLCertificateSocketFactory(handshakeTimeoutMillis, cache); + public static SSLSocketFactory getDefault(int handshakeTimeoutMillis, SSLSessionCache cache) { + return new SSLCertificateSocketFactory(handshakeTimeoutMillis, cache, true); + } + + /** + * Returns a new instance of a socket factory with all SSL security checks + * disabled, using an optional handshake timeout and SSL session cache. + * Sockets created using this factory are vulnerable to man-in-the-middle + * attacks! + * + * @param handshakeTimeoutMillis to use for SSL connection handshake, or 0 + * for none. The socket timeout is reset to 0 after the handshake. + * @param cache The {@link SSLClientSessionCache} to use, or null for no cache. + * @return an insecure SocketFactory with the specified parameters + */ + public static SSLSocketFactory getInsecure(int handshakeTimeoutMillis, SSLSessionCache cache) { + return new SSLCertificateSocketFactory(handshakeTimeoutMillis, cache, false); } /** @@ -123,7 +141,7 @@ public class SSLCertificateSocketFactory extends SSLSocketFactory { int handshakeTimeoutMillis, SSLSessionCache cache) { return new org.apache.http.conn.ssl.SSLSocketFactory( - new SSLCertificateSocketFactory(handshakeTimeoutMillis, cache)); + new SSLCertificateSocketFactory(handshakeTimeoutMillis, cache, true)); } private SSLSocketFactory makeSocketFactory(TrustManager[] trustManagers) { @@ -138,12 +156,15 @@ public class SSLCertificateSocketFactory extends SSLSocketFactory { } private synchronized SSLSocketFactory getDelegate() { - // only allow relaxing the ssl check on non-secure builds where the relaxation is - // specifically requested. - if ("0".equals(SystemProperties.get("ro.secure")) && - "yes".equals(SystemProperties.get("socket.relaxsslcheck"))) { + // Relax the SSL check if instructed (for this factory, or systemwide) + if (!mSecure || ("0".equals(SystemProperties.get("ro.secure")) && + "yes".equals(SystemProperties.get("socket.relaxsslcheck")))) { if (mInsecureFactory == null) { - Log.w(TAG, "*** BYPASSING SSL SECURITY CHECKS (socket.relaxsslcheck=yes) ***"); + if (mSecure) { + Log.w(TAG, "*** BYPASSING SSL SECURITY CHECKS (socket.relaxsslcheck=yes) ***"); + } else { + Log.w(TAG, "Bypassing SSL security checks at caller's request"); + } mInsecureFactory = makeSocketFactory(INSECURE_TRUST_MANAGER); } return mInsecureFactory; diff --git a/core/java/android/net/WebAddress.java b/core/java/android/net/WebAddress.java index f4ae66a..fa13894 100644 --- a/core/java/android/net/WebAddress.java +++ b/core/java/android/net/WebAddress.java @@ -16,6 +16,8 @@ package android.net; +import static com.android.common.Patterns.GOOD_IRI_CHAR; + import java.util.regex.Matcher; import java.util.regex.Pattern; @@ -54,9 +56,10 @@ public class WebAddress { static Pattern sAddressPattern = Pattern.compile( /* scheme */ "(?:(http|HTTP|https|HTTPS|file|FILE)\\:\\/\\/)?" + /* authority */ "(?:([-A-Za-z0-9$_.+!*'(),;?&=]+(?:\\:[-A-Za-z0-9$_.+!*'(),;?&=]+)?)@)?" + - /* host */ "([-A-Za-z0-9%_]+(?:\\.[-A-Za-z0-9%_]+)*|\\[[0-9a-fA-F:\\.]+\\])?" + + /* host */ "([-" + GOOD_IRI_CHAR + "%_]+(?:\\.[-" + GOOD_IRI_CHAR + "%_]+)*|\\[[0-9a-fA-F:\\.]+\\])?" + /* port */ "(?:\\:([0-9]+))?" + - /* path */ "(\\/?.*)?"); + /* path */ "(\\/?[^#]*)?" + + /* anchor */ ".*"); /** parses given uriString. */ public WebAddress(String address) throws ParseException { diff --git a/core/java/android/os/ICheckinService.aidl b/core/java/android/os/ICheckinService.aidl deleted file mode 100644 index 37af496..0000000 --- a/core/java/android/os/ICheckinService.aidl +++ /dev/null @@ -1,35 +0,0 @@ -/* - * Copyright (c) 2007 The Android Open Source Project - * - * Licensed under the Apache License, Version 2.0 (the "License"); - * you may not use this file except in compliance with the License. - * You may obtain a copy of the License at - * - * http://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, - * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - * See the License for the specific language governing permissions and - * limitations under the License. - */ - -package android.os; - -import android.os.IParentalControlCallback; - -/** - * System private API for direct access to the checkin service. - * Users should use the content provider instead. - * - * @see android.provider.Checkin - * {@hide} - */ -interface ICheckinService { - /** - * Determine if the device is under parental control. Return null if - * we are unable to check the parental control status. - */ - void getParentalControlState(IParentalControlCallback p, - String requestingApp); -} diff --git a/core/java/android/os/IParentalControlCallback.aidl b/core/java/android/os/IParentalControlCallback.aidl deleted file mode 100644 index 2f1a563..0000000 --- a/core/java/android/os/IParentalControlCallback.aidl +++ /dev/null @@ -1,27 +0,0 @@ -/* - * Copyright (c) 2008 The Android Open Source Project - * - * Licensed under the Apache License, Version 2.0 (the "License"); - * you may not use this file except in compliance with the License. - * You may obtain a copy of the License at - * - * http://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, - * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - * See the License for the specific language governing permissions and - * limitations under the License. - */ - -package android.os; - -import com.google.android.net.ParentalControlState; - -/** - * This callback interface is used to deliver the parental control state to the calling application. - * {@hide} - */ -oneway interface IParentalControlCallback { - void onResult(in ParentalControlState state); -} diff --git a/core/java/android/pim/vcard/VCardComposer.java b/core/java/android/pim/vcard/VCardComposer.java index 389c9f4..2eb25954 100644 --- a/core/java/android/pim/vcard/VCardComposer.java +++ b/core/java/android/pim/vcard/VCardComposer.java @@ -25,11 +25,13 @@ import android.database.Cursor; import android.database.sqlite.SQLiteException; import android.net.Uri; import android.os.RemoteException; +import android.pim.vcard.exception.VCardException; import android.provider.CallLog; import android.provider.CallLog.Calls; import android.provider.ContactsContract.Contacts; import android.provider.ContactsContract.Data; import android.provider.ContactsContract.RawContacts; +import android.provider.ContactsContract.RawContactsEntity; import android.provider.ContactsContract.CommonDataKinds.Email; import android.provider.ContactsContract.CommonDataKinds.Event; import android.provider.ContactsContract.CommonDataKinds.Im; @@ -54,6 +56,7 @@ import java.io.OutputStream; import java.io.OutputStreamWriter; import java.io.UnsupportedEncodingException; import java.io.Writer; +import java.lang.reflect.InvocationTargetException; import java.lang.reflect.Method; import java.nio.charset.UnsupportedCharsetException; import java.util.ArrayList; @@ -199,6 +202,10 @@ public class VCardComposer { try { // Create one empty entry. mWriter.write(createOneEntryInternal("-1", null)); + } catch (VCardException e) { + Log.e(LOG_TAG, "VCardException has been thrown during on Init(): " + + e.getMessage()); + return false; } catch (IOException e) { Log.e(LOG_TAG, "IOException occurred during exportOneContactData: " @@ -455,6 +462,9 @@ public class VCardComposer { return true; } } + } catch (VCardException e) { + Log.e(LOG_TAG, "VCardException has been thrown: " + e.getMessage()); + return false; } catch (OutOfMemoryError error) { // Maybe some data (e.g. photo) is too big to have in memory. But it // should be rare. @@ -486,36 +496,42 @@ public class VCardComposer { } private String createOneEntryInternal(final String contactId, - Method getEntityIteratorMethod) { + Method getEntityIteratorMethod) throws VCardException { final Map<String, List<ContentValues>> contentValuesListMap = new HashMap<String, List<ContentValues>>(); - // The resolver may return the entity iterator with no data. It is possiible. + // The resolver may return the entity iterator with no data. It is possible. // e.g. If all the data in the contact of the given contact id are not exportable ones, // they are hidden from the view of this method, though contact id itself exists. - boolean dataExists = false; EntityIterator entityIterator = null; try { - + final Uri uri = RawContactsEntity.CONTENT_URI.buildUpon() + .appendQueryParameter(Data.FOR_EXPORT_ONLY, "1") + .build(); + final String selection = Data.CONTACT_ID + "=?"; + final String[] selectionArgs = new String[] {contactId}; if (getEntityIteratorMethod != null) { + // Please note that this branch is executed by some tests only try { - final Uri uri = RawContacts.CONTENT_URI.buildUpon() - .appendQueryParameter(Data.FOR_EXPORT_ONLY, "1") - .build(); - final String selection = Data.CONTACT_ID + "=?"; - final String[] selectionArgs = new String[] {contactId}; entityIterator = (EntityIterator)getEntityIteratorMethod.invoke(null, mContentResolver, uri, selection, selectionArgs, null); - } catch (Exception e) { - e.printStackTrace(); + } catch (IllegalArgumentException e) { + Log.e(LOG_TAG, "IllegalArgumentException has been thrown: " + + e.getMessage()); + } catch (IllegalAccessException e) { + Log.e(LOG_TAG, "IllegalAccessException has been thrown: " + + e.getMessage()); + } catch (InvocationTargetException e) { + Log.e(LOG_TAG, "InvocationTargetException has been thrown: "); + StackTraceElement[] stackTraceElements = e.getCause().getStackTrace(); + for (StackTraceElement element : stackTraceElements) { + Log.e(LOG_TAG, " at " + element.toString()); + } + throw new VCardException("InvocationTargetException has been thrown: " + + e.getCause().getMessage()); } } else { - final Uri uri = RawContacts.CONTENT_URI.buildUpon() - .appendEncodedPath(contactId) - .appendEncodedPath(RawContacts.Entity.CONTENT_DIRECTORY) - .appendQueryParameter(Data.FOR_EXPORT_ONLY, "1") - .build(); entityIterator = RawContacts.newEntityIterator(mContentResolver.query( - uri, null, null, null, null)); + uri, null, selection, selectionArgs, null)); } if (entityIterator == null) { @@ -523,7 +539,11 @@ public class VCardComposer { return ""; } - dataExists = entityIterator.hasNext(); + if (!entityIterator.hasNext()) { + Log.w(LOG_TAG, "Data does not exist. contactId: " + contactId); + return ""; + } + while (entityIterator.hasNext()) { Entity entity = entityIterator.next(); for (NamedContentValues namedContentValues : entity.getSubValues()) { @@ -550,10 +570,6 @@ public class VCardComposer { } } - if (!dataExists) { - return ""; - } - final VCardBuilder builder = new VCardBuilder(mVCardType); builder.appendNameProperties(contentValuesListMap.get(StructuredName.CONTENT_ITEM_TYPE)) .appendNickNames(contentValuesListMap.get(Nickname.CONTENT_ITEM_TYPE)) diff --git a/core/java/android/pim/vcard/VCardEntry.java b/core/java/android/pim/vcard/VCardEntry.java index 20eee84..1cf3144 100644 --- a/core/java/android/pim/vcard/VCardEntry.java +++ b/core/java/android/pim/vcard/VCardEntry.java @@ -281,6 +281,29 @@ public class VCardEntry { isPrimary == organization.isPrimary); } + public String getFormattedString() { + final StringBuilder builder = new StringBuilder(); + if (!TextUtils.isEmpty(companyName)) { + builder.append(companyName); + } + + if (!TextUtils.isEmpty(departmentName)) { + if (builder.length() > 0) { + builder.append(", "); + } + builder.append(departmentName); + } + + if (!TextUtils.isEmpty(titleName)) { + if (builder.length() > 0) { + builder.append(", "); + } + builder.append(titleName); + } + + return builder.toString(); + } + @Override public String toString() { return String.format( @@ -1008,6 +1031,8 @@ public class VCardEntry { mDisplayName = mPhoneList.get(0).data; } else if (mPostalList != null && mPostalList.size() > 0) { mDisplayName = mPostalList.get(0).getFormattedAddress(mVCardType); + } else if (mOrganizationList != null && mOrganizationList.size() > 0) { + mDisplayName = mOrganizationList.get(0).getFormattedString(); } if (mDisplayName == null) { diff --git a/core/java/android/provider/Checkin.java b/core/java/android/provider/Checkin.java deleted file mode 100644 index 75936a1..0000000 --- a/core/java/android/provider/Checkin.java +++ /dev/null @@ -1,269 +0,0 @@ -/* - * Copyright (C) 2006 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.provider; - -import org.apache.commons.codec.binary.Base64; - -import android.content.ContentResolver; -import android.content.ContentValues; -import android.database.SQLException; -import android.net.Uri; -import android.os.SystemClock; -import android.util.Log; - -import java.io.ByteArrayOutputStream; -import java.io.DataOutputStream; - -/** - * Contract class for the checkin provider, used to store events and - * statistics that will be uploaded to a checkin server eventually. - * Describes the exposed database schema, and offers methods to add - * events and statistics to be uploaded. - * - * TODO: Move this to vendor/google when we have a home for it. - * - * @hide - */ -public final class Checkin { - public static final String AUTHORITY = "android.server.checkin"; - - /** - * The events table is a log of important timestamped occurrences. - * Each event has a type tag and an optional string value. - * If too many events are added before they can be reported, the - * content provider will erase older events to limit the table size. - */ - public interface Events extends BaseColumns { - public static final String TABLE_NAME = "events"; - public static final Uri CONTENT_URI = - Uri.parse("content://" + AUTHORITY + "/" + TABLE_NAME); - - public static final String TAG = "tag"; // TEXT - public static final String VALUE = "value"; // TEXT - public static final String DATE = "date"; // INTEGER - - /** Valid tag values. Extend as necessary for your needs. */ - public enum Tag { - APANIC_CONSOLE, - APANIC_THREADS, - AUTOTEST_FAILURE, - AUTOTEST_SEQUENCE_BEGIN, - AUTOTEST_SUITE_BEGIN, - AUTOTEST_TCPDUMP_BEGIN, - AUTOTEST_TCPDUMP_DATA, - AUTOTEST_TCPDUMP_END, - AUTOTEST_TEST_BEGIN, - AUTOTEST_TEST_FAILURE, - AUTOTEST_TEST_SUCCESS, - BROWSER_BUG_REPORT, - CARRIER_BUG_REPORT, - CHECKIN_FAILURE, - CHECKIN_SUCCESS, - FOTA_BEGIN, - FOTA_FAILURE, - FOTA_INSTALL, - FOTA_PROMPT, - FOTA_PROMPT_ACCEPT, - FOTA_PROMPT_REJECT, - FOTA_PROMPT_SKIPPED, - GSERVICES_ERROR, - GSERVICES_UPDATE, - LOGIN_SERVICE_ACCOUNT_TRIED, - LOGIN_SERVICE_ACCOUNT_SAVED, - LOGIN_SERVICE_AUTHENTICATE, - LOGIN_SERVICE_CAPTCHA_ANSWERED, - LOGIN_SERVICE_CAPTCHA_SHOWN, - LOGIN_SERVICE_PASSWORD_ENTERED, - LOGIN_SERVICE_SWITCH_GOOGLE_MAIL, - NETWORK_DOWN, - NETWORK_UP, - PHONE_UI, - RADIO_BUG_REPORT, - SETUP_COMPLETED, - SETUP_INITIATED, - SETUP_IO_ERROR, - SETUP_NETWORK_ERROR, - SETUP_REQUIRED_CAPTCHA, - SETUP_RETRIES_EXHAUSTED, - SETUP_SERVER_ERROR, - SETUP_SERVER_TIMEOUT, - SETUP_NO_DATA_NETWORK, - SYSTEM_BOOT, - SYSTEM_LAST_KMSG, - SYSTEM_RECOVERY_LOG, - SYSTEM_RESTART, - SYSTEM_SERVICE_LOOPING, - SYSTEM_TOMBSTONE, - TEST, - BATTERY_DISCHARGE_INFO, - MARKET_DOWNLOAD, - MARKET_INSTALL, - MARKET_REMOVE, - MARKET_REFUND, - MARKET_UNINSTALL, - } - } - - /** - * The stats table is a list of counter values indexed by a tag name. - * Each statistic has a count and sum fields, so it can track averages. - * When multiple statistics are inserted with the same tag, the count - * and sum fields are added together into a single entry in the database. - */ - public interface Stats extends BaseColumns { - public static final String TABLE_NAME = "stats"; - public static final Uri CONTENT_URI = - Uri.parse("content://" + AUTHORITY + "/" + TABLE_NAME); - - public static final String TAG = "tag"; // TEXT UNIQUE - public static final String COUNT = "count"; // INTEGER - public static final String SUM = "sum"; // REAL - - /** Valid tag values. Extend as necessary for your needs. */ - public enum Tag { - BROWSER_SNAP_CENTER, - BROWSER_TEXT_SIZE_CHANGE, - BROWSER_ZOOM_OVERVIEW, - - CRASHES_REPORTED, - CRASHES_TRUNCATED, - ELAPSED_REALTIME_SEC, - ELAPSED_UPTIME_SEC, - HTTP_REQUEST, - HTTP_REUSED, - HTTP_STATUS, - PHONE_GSM_REGISTERED, - PHONE_GPRS_ATTEMPTED, - PHONE_GPRS_CONNECTED, - PHONE_RADIO_RESETS, - TEST, - NETWORK_RX_MOBILE, - NETWORK_TX_MOBILE, - PHONE_CDMA_REGISTERED, - PHONE_CDMA_DATA_ATTEMPTED, - PHONE_CDMA_DATA_CONNECTED, - } - } - - /** - * The properties table is a set of tagged values sent with every checkin. - * Unlike statistics or events, they are not cleared after being uploaded. - * Multiple properties inserted with the same tag overwrite each other. - */ - public interface Properties extends BaseColumns { - public static final String TABLE_NAME = "properties"; - public static final Uri CONTENT_URI = - Uri.parse("content://" + AUTHORITY + "/" + TABLE_NAME); - - public static final String TAG = "tag"; // TEXT UNIQUE - public static final String VALUE = "value"; // TEXT - - /** Valid tag values, to be extended as necessary. */ - public enum Tag { - DESIRED_BUILD, - MARKET_CHECKIN, - } - } - - /** - * The crashes table is a log of crash reports, kept separate from the - * general event log because crashes are large, important, and bursty. - * Like the events table, the crashes table is pruned on insert. - */ - public interface Crashes extends BaseColumns { - public static final String TABLE_NAME = "crashes"; - public static final Uri CONTENT_URI = - Uri.parse("content://" + AUTHORITY + "/" + TABLE_NAME); - - // TODO: one or both of these should be a file attachment, not a column - public static final String DATA = "data"; // TEXT - public static final String LOGS = "logs"; // TEXT - } - - /** - * Intents with this action cause a checkin attempt. Normally triggered by - * a periodic alarm, these may be sent directly to force immediate checkin. - */ - public interface TriggerIntent { - public static final String ACTION = "android.server.checkin.CHECKIN"; - - // The category is used for GTalk service messages - public static final String CATEGORY = "android.server.checkin.CHECKIN"; - - // If true indicates that the checkin should only transfer market related data - public static final String EXTRA_MARKET_ONLY = "market_only"; - } - - private static final String TAG = "Checkin"; - - /** - * Helper function to log an event to the database. - * - * @param resolver from {@link android.content.Context#getContentResolver} - * @param tag identifying the type of event being recorded - * @param value associated with event, if any - * @return URI of the event that was added - */ - static public Uri logEvent(ContentResolver resolver, - Events.Tag tag, String value) { - try { - // Don't specify the date column; the content provider will add that. - ContentValues values = new ContentValues(); - values.put(Events.TAG, tag.toString()); - if (value != null) values.put(Events.VALUE, value); - return resolver.insert(Events.CONTENT_URI, values); - } catch (IllegalArgumentException e) { // thrown when provider is unavailable. - Log.w(TAG, "Can't log event " + tag + ": " + e); - return null; - } catch (SQLException e) { - Log.e(TAG, "Can't log event " + tag, e); // Database errors are not fatal. - return null; - } - } - - /** - * Helper function to update statistics in the database. - * Note that multiple updates to the same tag will be combined. - * - * @param tag identifying what is being observed - * @param count of occurrences - * @param sum of some value over these occurrences - * @return URI of the statistic that was returned - */ - static public Uri updateStats(ContentResolver resolver, - Stats.Tag tag, int count, double sum) { - try { - ContentValues values = new ContentValues(); - values.put(Stats.TAG, tag.toString()); - if (count != 0) values.put(Stats.COUNT, count); - if (sum != 0.0) values.put(Stats.SUM, sum); - return resolver.insert(Stats.CONTENT_URI, values); - } catch (IllegalArgumentException e) { // thrown when provider is unavailable. - Log.w(TAG, "Can't update stat " + tag + ": " + e); - return null; - } catch (SQLException e) { - Log.e(TAG, "Can't update stat " + tag, e); // Database errors are not fatal. - return null; - } - } - - /** Minimum time to wait after a crash failure before trying again. */ - static private final long MIN_CRASH_FAILURE_RETRY = 10000; // 10 seconds - - /** {@link SystemClock#elapsedRealtime} of the last time a crash report failed. */ - static private volatile long sLastCrashFailureRealtime = -MIN_CRASH_FAILURE_RETRY; -} diff --git a/core/java/android/provider/Settings.java b/core/java/android/provider/Settings.java index bfab30a..7b52f7f 100644 --- a/core/java/android/provider/Settings.java +++ b/core/java/android/provider/Settings.java @@ -1497,6 +1497,66 @@ public final class Settings { public static final String POINTER_LOCATION = "pointer_location"; /** + * Whether to play a sound for low-battery alerts. + * @hide + */ + public static final String POWER_SOUNDS_ENABLED = "power_sounds_enabled"; + + /** + * Whether to play a sound for dock events. + * @hide + */ + public static final String DOCK_SOUNDS_ENABLED = "dock_sounds_enabled"; + + /** + * Whether to play sounds when the keyguard is shown and dismissed. + * @hide + */ + public static final String LOCKSCREEN_SOUNDS_ENABLED = "lockscreen_sounds_enabled"; + + /** + * URI for the low battery sound file. + * @hide + */ + public static final String LOW_BATTERY_SOUND = "low_battery_sound"; + + /** + * URI for the desk dock "in" event sound. + * @hide + */ + public static final String DESK_DOCK_SOUND = "desk_dock_sound"; + + /** + * URI for the desk dock "out" event sound. + * @hide + */ + public static final String DESK_UNDOCK_SOUND = "desk_undock_sound"; + + /** + * URI for the car dock "in" event sound. + * @hide + */ + public static final String CAR_DOCK_SOUND = "car_dock_sound"; + + /** + * URI for the car dock "out" event sound. + * @hide + */ + public static final String CAR_UNDOCK_SOUND = "car_undock_sound"; + + /** + * URI for the "device locked" (keyguard shown) sound. + * @hide + */ + public static final String LOCK_SOUND = "lock_sound"; + + /** + * URI for the "device unlocked" (keyguard dismissed) sound. + * @hide + */ + public static final String UNLOCK_SOUND = "unlock_sound"; + + /** * Settings to backup. This is here so that it's in the same place as the settings * keys and easy to update. * @hide @@ -2150,14 +2210,17 @@ public final class Settings { public static final String NETWORK_PREFERENCE = "network_preference"; /** + * No longer supported. */ public static final String PARENTAL_CONTROL_ENABLED = "parental_control_enabled"; /** + * No longer supported. */ public static final String PARENTAL_CONTROL_LAST_UPDATE = "parental_control_last_update"; /** + * No longer supported. */ public static final String PARENTAL_CONTROL_REDIRECT_URL = "parental_control_redirect_url"; diff --git a/core/java/android/text/AndroidCharacter.java b/core/java/android/text/AndroidCharacter.java index 6dfd64d..af93b5d 100644 --- a/core/java/android/text/AndroidCharacter.java +++ b/core/java/android/text/AndroidCharacter.java @@ -22,6 +22,13 @@ package android.text; */ public class AndroidCharacter { + public static final int EAST_ASIAN_WIDTH_NEUTRAL = 0; + public static final int EAST_ASIAN_WIDTH_AMBIGUOUS = 1; + public static final int EAST_ASIAN_WIDTH_HALF_WIDTH = 2; + public static final int EAST_ASIAN_WIDTH_FULL_WIDTH = 3; + public static final int EAST_ASIAN_WIDTH_NARROW = 4; + public static final int EAST_ASIAN_WIDTH_WIDE = 5; + /** * Fill in the first <code>count</code> bytes of <code>dest</code> with the * directionalities from the first <code>count</code> chars of <code>src</code>. @@ -30,6 +37,38 @@ public class AndroidCharacter */ public native static void getDirectionalities(char[] src, byte[] dest, int count); + + /** + * Calculate the East Asian Width of a character according to + * <a href="http://unicode.org/reports/tr11/">Unicode TR#11</a>. The return + * will be one of {@link #EAST_ASIAN_WIDTH_NEUTRAL}, + * {@link #EAST_ASIAN_WIDTH_AMBIGUOUS}, {@link #EAST_ASIAN_WIDTH_HALF_WIDTH}, + * {@link #EAST_ASIAN_WIDTH_FULL_WIDTH}, {@link #EAST_ASIAN_WIDTH_NARROW}, + * or {@link #EAST_ASIAN_WIDTH_WIDE}. + * + * @param input the character to measure + * @return the East Asian Width for input + */ + public native static int getEastAsianWidth(char input); + + /** + * Fill the first <code>count</code> bytes of <code>dest</code> with the + * East Asian Width from the first <code>count</code> chars of + * <code>src</code>. East Asian Width is calculated based on + * <a href="http://unicode.org/reports/tr11/">Unicode TR#11</a>. Each entry + * in <code>dest> will be one of {@link #EAST_ASIAN_WIDTH_NEUTRAL}, + * {@link #EAST_ASIAN_WIDTH_AMBIGUOUS}, {@link #EAST_ASIAN_WIDTH_HALF_WIDTH}, + * {@link #EAST_ASIAN_WIDTH_FULL_WIDTH}, {@link #EAST_ASIAN_WIDTH_NARROW}, + * or {@link #EAST_ASIAN_WIDTH_WIDE}. + * + * @param src character array of input to measure + * @param start first character in array to measure + * @param count maximum number of characters to measure + * @param dest byte array of results for each character in src + */ + public native static void getEastAsianWidths(char[] src, int start, + int count, byte[] dest); + /** * Replace the specified slice of <code>text</code> with the chars' * right-to-left mirrors (if any), returning true if any diff --git a/core/java/android/text/util/Rfc822Tokenizer.java b/core/java/android/text/util/Rfc822Tokenizer.java index 9d8bfd9..69d745d 100644 --- a/core/java/android/text/util/Rfc822Tokenizer.java +++ b/core/java/android/text/util/Rfc822Tokenizer.java @@ -84,7 +84,7 @@ public class Rfc822Tokenizer implements MultiAutoCompleteTextView.Tokenizer { if (c == '"') { i++; break; - } else if (c == '\\') { + } else if (c == '\\' && i + 1 < cursor) { name.append(text.charAt(i + 1)); i += 2; } else { @@ -110,7 +110,7 @@ public class Rfc822Tokenizer implements MultiAutoCompleteTextView.Tokenizer { comment.append(c); level++; i++; - } else if (c == '\\') { + } else if (c == '\\' && i + 1 < cursor) { comment.append(text.charAt(i + 1)); i += 2; } else { diff --git a/core/java/android/util/base64/Base64.java b/core/java/android/util/base64/Base64.java index 088de9f..f6d3905 100644 --- a/core/java/android/util/base64/Base64.java +++ b/core/java/android/util/base64/Base64.java @@ -16,9 +16,13 @@ package android.util.base64; +import java.io.UnsupportedEncodingException; + /** - * Utilities for encoding and decoding the Base64 encoding. See RFCs - * 2045 and 3548. + * Utilities for encoding and decoding the Base64 representation of + * binary data. See RFCs <a + * href="http://www.ietf.org/rfc/rfc2045.txt">2045</a> and <a + * href="http://www.ietf.org/rfc/rfc3548.txt">3548</a>. */ public class Base64 { /** @@ -27,100 +31,82 @@ public class Base64 { public static final int DEFAULT = 0; /** - * Encoder flag bit to indicate you want the padding '=' - * characters at the end (if any) to be omitted. + * Encoder flag bit to omit the padding '=' characters at the end + * of the output (if any). */ public static final int NO_PADDING = 1; /** - * Encoder flag bit to indicate you want all line terminators to - * be omitted (ie, the output will be on one long line). + * Encoder flag bit to omit all line terminators (i.e., the output + * will be on one long line). */ public static final int NO_WRAP = 2; /** - * Encoder flag bit to indicate you want lines to be ended with - * CRLF instead of just LF. Has no effect if {@code NO_WRAP} is - * specified as well. + * Encoder flag bit to indicate lines should be terminated with a + * CRLF pair instead of just an LF. Has no effect if {@code + * NO_WRAP} is specified as well. */ public static final int CRLF = 4; /** - * Encoder/decoder flag bit to indicate using the "web safe" - * variant of Base64 (see RFC 3548 section 4) where '-' and '_' - * are used in place of '+' and '/'. + * Encoder/decoder flag bit to indicate using the "URL and + * filename safe" variant of Base64 (see RFC 3548 section 4) where + * {@code -} and {@code _} are used in place of {@code +} and + * {@code /}. */ - public static final int WEB_SAFE = 8; + public static final int URL_SAFE = 8; /** - * Flag to pass to Base64OutputStream to indicate that it should - * not close the output stream it is wrapping when it itself is - * closed. + * Flag to pass to {@link Base64OutputStream} to indicate that it + * should not close the output stream it is wrapping when it + * itself is closed. */ public static final int NO_CLOSE = 16; // -------------------------------------------------------- - // decoding + // shared code // -------------------------------------------------------- - /** - * Lookup table for turning bytes into their position in the - * Base64 alphabet. - */ - private static final int DECODE[] = { - -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, - -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, - -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, 62, -1, -1, -1, 63, - 52, 53, 54, 55, 56, 57, 58, 59, 60, 61, -1, -1, -1, -2, -1, -1, - -1, 0, 1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11, 12, 13, 14, - 15, 16, 17, 18, 19, 20, 21, 22, 23, 24, 25, -1, -1, -1, -1, -1, - -1, 26, 27, 28, 29, 30, 31, 32, 33, 34, 35, 36, 37, 38, 39, 40, - 41, 42, 43, 44, 45, 46, 47, 48, 49, 50, 51, -1, -1, -1, -1, -1, - -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, - -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, - -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, - -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, - -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, - -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, - -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, - -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, - }; + /* package */ static abstract class Coder { + public byte[] output; + public int op; - /** - * Decode lookup table for the "web safe" variant (RFC 3548 - * sec. 4) where - and _ replace + and /. - */ - private static final int DECODE_WEBSAFE[] = { - -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, - -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, - -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, 62, -1, -1, - 52, 53, 54, 55, 56, 57, 58, 59, 60, 61, -1, -1, -1, -2, -1, -1, - -1, 0, 1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11, 12, 13, 14, - 15, 16, 17, 18, 19, 20, 21, 22, 23, 24, 25, -1, -1, -1, -1, 63, - -1, 26, 27, 28, 29, 30, 31, 32, 33, 34, 35, 36, 37, 38, 39, 40, - 41, 42, 43, 44, 45, 46, 47, 48, 49, 50, 51, -1, -1, -1, -1, -1, - -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, - -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, - -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, - -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, - -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, - -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, - -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, - -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, - }; - - /** Non-data values in the DECODE arrays. */ - private static final int SKIP = -1; - private static final int EQUALS = -2; + /** + * Encode/decode another block of input data. this.output is + * provided by the caller, and must be big enough to hold all + * the coded data. On exit, this.opwill be set to the length + * of the coded data. + * + * @param finish true if this is the final call to process for + * this object. Will finalize the coder state and + * include any final bytes in the output. + * + * @return true if the input so far is good; false if some + * error has been detected in the input stream.. + */ + public abstract boolean process(byte[] input, int offset, int len, boolean finish); + + /** + * @return the maximum number of bytes a call to process() + * could produce for the given number of input bytes. This may + * be an overestimate. + */ + public abstract int maxOutputSize(int len); + } + + // -------------------------------------------------------- + // decoding + // -------------------------------------------------------- /** * Decode the Base64-encoded data in input and return the data in * a new byte array. * - * The padding '=' characters at the end are considered optional, but + * <p>The padding '=' characters at the end are considered optional, but * if any are present, there must be the correct number of them. * - * @param input the input String to decode, which is converted to + * @param str the input String to decode, which is converted to * bytes using the default charset * @param flags controls certain features of the decoded output. * Pass {@code DEFAULT} to decode standard Base64. @@ -136,7 +122,7 @@ public class Base64 { * Decode the Base64-encoded data in input and return the data in * a new byte array. * - * The padding '=' characters at the end are considered optional, but + * <p>The padding '=' characters at the end are considered optional, but * if any are present, there must be the correct number of them. * * @param input the input array to decode @@ -154,7 +140,7 @@ public class Base64 { * Decode the Base64-encoded data in input and return the data in * a new byte array. * - * The padding '=' characters at the end are considered optional, but + * <p>The padding '=' characters at the end are considered optional, but * if any are present, there must be the correct number of them. * * @param input the data to decode @@ -169,121 +155,172 @@ public class Base64 { public static byte[] decode(byte[] input, int offset, int len, int flags) { // Allocate space for the most data the input could represent. // (It could contain less if it contains whitespace, etc.) - DecoderState state = new DecoderState(flags, new byte[len*3/4]); + Decoder decoder = new Decoder(flags, new byte[len*3/4]); - if (!decodeInternal(input, offset, len, state, true)) { + if (!decoder.process(input, offset, len, true)) { throw new IllegalArgumentException("bad base-64"); } // Maybe we got lucky and allocated exactly enough output space. - if (state.op == state.output.length) { - return state.output; + if (decoder.op == decoder.output.length) { + return decoder.output; } // Need to shorten the array, so allocate a new one of the // right size and copy. - byte[] temp = new byte[state.op]; - System.arraycopy(state.output, 0, temp, 0, state.op); + byte[] temp = new byte[decoder.op]; + System.arraycopy(decoder.output, 0, temp, 0, decoder.op); return temp; } - /* package */ static class DecoderState { - public byte[] output; - public int op; - - public int state; // state number (0 to 6) - public int value; - - final public int[] alphabet; - - public DecoderState(int flags, byte[] output) { + /* package */ static class Decoder extends Coder { + /** + * Lookup table for turning bytes into their position in the + * Base64 alphabet. + */ + private static final int DECODE[] = { + -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, + -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, + -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, 62, -1, -1, -1, 63, + 52, 53, 54, 55, 56, 57, 58, 59, 60, 61, -1, -1, -1, -2, -1, -1, + -1, 0, 1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11, 12, 13, 14, + 15, 16, 17, 18, 19, 20, 21, 22, 23, 24, 25, -1, -1, -1, -1, -1, + -1, 26, 27, 28, 29, 30, 31, 32, 33, 34, 35, 36, 37, 38, 39, 40, + 41, 42, 43, 44, 45, 46, 47, 48, 49, 50, 51, -1, -1, -1, -1, -1, + -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, + -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, + -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, + -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, + -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, + -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, + -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, + -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, + }; + + /** + * Decode lookup table for the "web safe" variant (RFC 3548 + * sec. 4) where - and _ replace + and /. + */ + private static final int DECODE_WEBSAFE[] = { + -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, + -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, + -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, 62, -1, -1, + 52, 53, 54, 55, 56, 57, 58, 59, 60, 61, -1, -1, -1, -2, -1, -1, + -1, 0, 1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11, 12, 13, 14, + 15, 16, 17, 18, 19, 20, 21, 22, 23, 24, 25, -1, -1, -1, -1, 63, + -1, 26, 27, 28, 29, 30, 31, 32, 33, 34, 35, 36, 37, 38, 39, 40, + 41, 42, 43, 44, 45, 46, 47, 48, 49, 50, 51, -1, -1, -1, -1, -1, + -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, + -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, + -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, + -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, + -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, + -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, + -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, + -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, + }; + + /** Non-data values in the DECODE arrays. */ + private static final int SKIP = -1; + private static final int EQUALS = -2; + + /** + * States 0-3 are reading through the next input tuple. + * State 4 is having read one '=' and expecting exactly + * one more. + * State 5 is expecting no more data or padding characters + * in the input. + * State 6 is the error state; an error has been detected + * in the input and no future input can "fix" it. + */ + private int state; // state number (0 to 6) + private int value; + + final private int[] alphabet; + + public Decoder(int flags, byte[] output) { this.output = output; - alphabet = ((flags & WEB_SAFE) == 0) ? DECODE : DECODE_WEBSAFE; + alphabet = ((flags & URL_SAFE) == 0) ? DECODE : DECODE_WEBSAFE; state = 0; value = 0; } - } - /** - * Decode another block of input data. - * - * @param dstate a DecoderState object whose (caller-provided) - * output array is big enough to hold all the decoded data. - * On return, dstate.op will be set to the length of the - * decoded data. - * @param finish true if this is the final call to decodeInternal - * with the given DecoderState object. Will finalize the - * decoder state and include any final bytes in the output. - * - * @return true if the state machine is still healthy. false if - * bad base-64 data has been detected in the input stream. - */ + /** + * @return an overestimate for the number of bytes {@code + * len} bytes could decode to. + */ + public int maxOutputSize(int len) { + return len * 3/4 + 10; + } - /* package */ static boolean decodeInternal( - byte[] input, int offset, int len, final DecoderState dstate, boolean finish) { - if (dstate.state == 6) return false; - - int state = dstate.state; - int value = dstate.value; - final int[] decode = dstate.alphabet; - final byte[] output = dstate.output; - int op = 0; - - int p = offset; - len += offset; - - while (p < len) { - - // Try the fast path: we're starting a new tuple and the - // next four bytes of the input stream are all data - // bytes. This corresponds to going through states - // 0-1-2-3-0. We expect to use this method for most of - // the data. - // - // If any of the next four bytes of input are non-data - // (whitespace, etc.), value will end up negative. (All - // the non-data values in decode are small negative - // numbers, so shifting any of them up and or'ing them - // together will result in a value with its top bit set.) - // - // You can remove this whole block and the output should - // be the same, just slower. - if (state == 0 && p+4 <= len && - (value = ((decode[input[p] & 0xff] << 18) | - (decode[input[p+1] & 0xff] << 12) | - (decode[input[p+2] & 0xff] << 6) | - (decode[input[p+3] & 0xff]))) >= 0) { - output[op+2] = (byte) value; - output[op+1] = (byte) (value >> 8); - output[op] = (byte) (value >> 16); - op += 3; - p += 4; - continue; - } + /** + * Decode another block of input data. + * + * @return true if the state machine is still healthy. false if + * bad base-64 data has been detected in the input stream. + */ + public boolean process(byte[] input, int offset, int len, boolean finish) { + if (this.state == 6) return false; + + int p = offset; + len += offset; + + // Using local variables makes the decoder about 12% + // faster than if we manipulate the member variables in + // the loop. (Even alphabet makes a measurable + // difference, which is somewhat surprising to me since + // the member variable is final.) + int state = this.state; + int value = this.value; + int op = 0; + final byte[] output = this.output; + final int[] alphabet = this.alphabet; + + while (p < len) { + // Try the fast path: we're starting a new tuple and the + // next four bytes of the input stream are all data + // bytes. This corresponds to going through states + // 0-1-2-3-0. We expect to use this method for most of + // the data. + // + // If any of the next four bytes of input are non-data + // (whitespace, etc.), value will end up negative. (All + // the non-data values in decode are small negative + // numbers, so shifting any of them up and or'ing them + // together will result in a value with its top bit set.) + // + // You can remove this whole block and the output should + // be the same, just slower. + if (state == 0) { + while (p+4 <= len && + (value = ((alphabet[input[p] & 0xff] << 18) | + (alphabet[input[p+1] & 0xff] << 12) | + (alphabet[input[p+2] & 0xff] << 6) | + (alphabet[input[p+3] & 0xff]))) >= 0) { + output[op+2] = (byte) value; + output[op+1] = (byte) (value >> 8); + output[op] = (byte) (value >> 16); + op += 3; + p += 4; + } + if (p >= len) break; + } - // The fast path isn't available -- either we've read a - // partial tuple, or the next four input bytes aren't all - // data, or whatever. Fall back to the slower state - // machine implementation. - // - // States 0-3 are reading through the next input tuple. - // State 4 is having read one '=' and expecting exactly - // one more. - // State 5 is expecting no more data or padding characters - // in the input. - // State 6 is the error state; an error has been detected - // in the input and no future input can "fix" it. - - int d = decode[input[p++] & 0xff]; + // The fast path isn't available -- either we've read a + // partial tuple, or the next four input bytes aren't all + // data, or whatever. Fall back to the slower state + // machine implementation. - switch (state) { + int d = alphabet[input[p++] & 0xff]; + + switch (state) { case 0: if (d >= 0) { value = d; ++state; } else if (d != SKIP) { - dstate.state = 6; + this.state = 6; return false; } break; @@ -293,7 +330,7 @@ public class Base64 { value = (value << 6) | d; ++state; } else if (d != SKIP) { - dstate.state = 6; + this.state = 6; return false; } break; @@ -308,7 +345,7 @@ public class Base64 { output[op++] = (byte) (value >> 4); state = 4; } else if (d != SKIP) { - dstate.state = 6; + this.state = 6; return false; } break; @@ -330,7 +367,7 @@ public class Base64 { op += 2; state = 5; } else if (d != SKIP) { - dstate.state = 6; + this.state = 6; return false; } break; @@ -339,41 +376,40 @@ public class Base64 { if (d == EQUALS) { ++state; } else if (d != SKIP) { - dstate.state = 6; + this.state = 6; return false; } break; case 5: if (d != SKIP) { - dstate.state = 6; + this.state = 6; return false; } break; + } } - } - if (!finish) { - // We're out of input, but a future call could provide - // more. Return the output we've produced on this call - // and save the current state of the state machine. - dstate.state = state; - dstate.value = value; - dstate.op = op; - return true; - } + if (!finish) { + // We're out of input, but a future call could provide + // more. + this.state = state; + this.value = value; + this.op = op; + return true; + } - // Done reading input. Now figure out where we are left in - // the state machine and finish up. + // Done reading input. Now figure out where we are left in + // the state machine and finish up. - switch (state) { + switch (state) { case 0: // Output length is a multiple of three. Fine. break; case 1: // Read one extra input byte, which isn't enough to // make another output byte. Illegal. - dstate.state = 6; + this.state = 6; return false; case 2: // Read two extra input bytes, enough to emit 1 more @@ -383,22 +419,23 @@ public class Base64 { case 3: // Read three extra input bytes, enough to emit 2 more // output bytes. Fine. - output[op+1] = (byte) (value >> 2); - output[op] = (byte) (value >> 10); - op += 2; + output[op++] = (byte) (value >> 10); + output[op++] = (byte) (value >> 2); break; case 4: // Read one padding '=' when we expected 2. Illegal. - dstate.state = 6; + this.state = 6; return false; case 5: // Read all the padding '='s we expected and no more. // Fine. break; - } + } - dstate.op = op; - return true; + this.state = state; + this.op = op; + return true; + } } // -------------------------------------------------------- @@ -406,43 +443,6 @@ public class Base64 { // -------------------------------------------------------- /** - * Emit a new line every this many output tuples. Corresponds to - * a 76-character line length (the maximum allowable according to - * RFC 2045). - */ - private static final int LINE_GROUPS = 19; - - /** - * Lookup table for turning Base64 alphabet positions (6 bits) - * into output bytes. - */ - private static final byte ENCODE[] = { - 'A', 'B', 'C', 'D', 'E', 'F', 'G', 'H', - 'I', 'J', 'K', 'L', 'M', 'N', 'O', 'P', - 'Q', 'R', 'S', 'T', 'U', 'V', 'W', 'X', - 'Y', 'Z', 'a', 'b', 'c', 'd', 'e', 'f', - 'g', 'h', 'i', 'j', 'k', 'l', 'm', 'n', - 'o', 'p', 'q', 'r', 's', 't', 'u', 'v', - 'w', 'x', 'y', 'z', '0', '1', '2', '3', - '4', '5', '6', '7', '8', '9', '+', '/', - }; - - /** - * Lookup table for turning Base64 alphabet positions (6 bits) - * into output bytes. - */ - private static final byte ENCODE_WEBSAFE[] = { - 'A', 'B', 'C', 'D', 'E', 'F', 'G', 'H', - 'I', 'J', 'K', 'L', 'M', 'N', 'O', 'P', - 'Q', 'R', 'S', 'T', 'U', 'V', 'W', 'X', - 'Y', 'Z', 'a', 'b', 'c', 'd', 'e', 'f', - 'g', 'h', 'i', 'j', 'k', 'l', 'm', 'n', - 'o', 'p', 'q', 'r', 's', 't', 'u', 'v', - 'w', 'x', 'y', 'z', '0', '1', '2', '3', - '4', '5', '6', '7', '8', '9', '-', '_', - }; - - /** * Base64-encode the given data and return a newly allocated * String with the result. * @@ -452,7 +452,12 @@ public class Base64 { * adheres to RFC 2045. */ public static String encodeToString(byte[] input, int flags) { - return new String(encode(input, flags)); + try { + return new String(encode(input, flags), "US-ASCII"); + } catch (UnsupportedEncodingException e) { + // US-ASCII is guaranteed to be available. + throw new AssertionError(e); + } } /** @@ -468,7 +473,12 @@ public class Base64 { * adheres to RFC 2045. */ public static String encodeToString(byte[] input, int offset, int len, int flags) { - return new String(encode(input, offset, len, flags)); + try { + return new String(encode(input, offset, len, flags), "US-ASCII"); + } catch (UnsupportedEncodingException e) { + // US-ASCII is guaranteed to be available. + throw new AssertionError(e); + } } /** @@ -497,13 +507,13 @@ public class Base64 { * adheres to RFC 2045. */ public static byte[] encode(byte[] input, int offset, int len, int flags) { - EncoderState state = new EncoderState(flags, null); + Encoder encoder = new Encoder(flags, null); // Compute the exact length of the array we will produce. int output_len = len / 3 * 4; // Account for the tail of the data and the padding bytes, if any. - if (state.do_padding) { + if (encoder.do_padding) { if (len % 3 > 0) { output_len += 4; } @@ -516,190 +526,215 @@ public class Base64 { } // Account for the newlines, if any. - if (state.do_newline && len > 0) { - output_len += (((len-1) / (3 * LINE_GROUPS)) + 1) * (state.do_cr ? 2 : 1); + if (encoder.do_newline && len > 0) { + output_len += (((len-1) / (3 * Encoder.LINE_GROUPS)) + 1) * + (encoder.do_cr ? 2 : 1); } - state.output = new byte[output_len]; - encodeInternal(input, offset, len, state, true); + encoder.output = new byte[output_len]; + encoder.process(input, offset, len, true); - assert state.op == output_len; + assert encoder.op == output_len; - return state.output; + return encoder.output; } - /* package */ static class EncoderState { - public byte[] output; - public int op; - - final public byte[] tail; - public int tailLen; - public int count; + /* package */ static class Encoder extends Coder { + /** + * Emit a new line every this many output tuples. Corresponds to + * a 76-character line length (the maximum allowable according to + * <a href="http://www.ietf.org/rfc/rfc2045.txt">RFC 2045</a>). + */ + public static final int LINE_GROUPS = 19; + + /** + * Lookup table for turning Base64 alphabet positions (6 bits) + * into output bytes. + */ + private static final byte ENCODE[] = { + 'A', 'B', 'C', 'D', 'E', 'F', 'G', 'H', 'I', 'J', 'K', 'L', 'M', 'N', 'O', 'P', + 'Q', 'R', 'S', 'T', 'U', 'V', 'W', 'X', 'Y', 'Z', 'a', 'b', 'c', 'd', 'e', 'f', + 'g', 'h', 'i', 'j', 'k', 'l', 'm', 'n', 'o', 'p', 'q', 'r', 's', 't', 'u', 'v', + 'w', 'x', 'y', 'z', '0', '1', '2', '3', '4', '5', '6', '7', '8', '9', '+', '/', + }; + + /** + * Lookup table for turning Base64 alphabet positions (6 bits) + * into output bytes. + */ + private static final byte ENCODE_WEBSAFE[] = { + 'A', 'B', 'C', 'D', 'E', 'F', 'G', 'H', 'I', 'J', 'K', 'L', 'M', 'N', 'O', 'P', + 'Q', 'R', 'S', 'T', 'U', 'V', 'W', 'X', 'Y', 'Z', 'a', 'b', 'c', 'd', 'e', 'f', + 'g', 'h', 'i', 'j', 'k', 'l', 'm', 'n', 'o', 'p', 'q', 'r', 's', 't', 'u', 'v', + 'w', 'x', 'y', 'z', '0', '1', '2', '3', '4', '5', '6', '7', '8', '9', '-', '_', + }; + + final private byte[] tail; + /* package */ int tailLen; + private int count; final public boolean do_padding; final public boolean do_newline; final public boolean do_cr; - final public byte[] alphabet; + final private byte[] alphabet; - public EncoderState(int flags, byte[] output) { + public Encoder(int flags, byte[] output) { this.output = output; do_padding = (flags & NO_PADDING) == 0; do_newline = (flags & NO_WRAP) == 0; do_cr = (flags & CRLF) != 0; - alphabet = ((flags & WEB_SAFE) == 0) ? ENCODE : ENCODE_WEBSAFE; + alphabet = ((flags & URL_SAFE) == 0) ? ENCODE : ENCODE_WEBSAFE; tail = new byte[2]; tailLen = 0; count = do_newline ? LINE_GROUPS : -1; } - } - - /** - * Encode another block of input data. - * - * @param estate an EncoderState object whose (caller-provided) - * output array is big enough to hold all the encoded data. - * On return, estate.op will be set to the length of the - * encoded data. - * @param finish true if this is the final call to encodeInternal - * with the given EncoderState object. Will finalize the - * encoder state and include any final bytes in the output. - */ - static void encodeInternal(byte[] input, int offset, int len, - final EncoderState estate, boolean finish) { - final boolean do_cr = estate.do_cr; - final boolean do_newline = estate.do_newline; - final boolean do_padding = estate.do_padding; - final byte[] output = estate.output; - int op = 0; + /** + * @return an overestimate for the number of bytes {@code + * len} bytes could encode to. + */ + public int maxOutputSize(int len) { + return len * 8/5 + 10; + } - int p = offset; - len += offset; - int v = -1; - int count = estate.count; + public boolean process(byte[] input, int offset, int len, boolean finish) { + // Using local variables makes the encoder about 9% faster. + final byte[] alphabet = this.alphabet; + final byte[] output = this.output; + int op = 0; + int count = this.count; - // First we need to concatenate the tail of the previous call - // with any input bytes available now and see if we can empty - // the tail. + int p = offset; + len += offset; + int v = -1; - switch (estate.tailLen) { - case 0: - // There was no tail. - break; + // First we need to concatenate the tail of the previous call + // with any input bytes available now and see if we can empty + // the tail. - case 1: - if (p+2 <= len) { - // A 1-byte tail with at least 2 bytes of - // input available now. - v = ((estate.tail[0] & 0xff) << 16) | - ((input[p++] & 0xff) << 8) | - (input[p++] & 0xff); - estate.tailLen = 0; - }; - break; + switch (tailLen) { + case 0: + // There was no tail. + break; - case 2: - if (p+1 <= len) { - // A 2-byte tail with at least 1 byte of input. - v = ((estate.tail[0] & 0xff) << 16) | - ((estate.tail[1] & 0xff) << 8) | - (input[p++] & 0xff); - estate.tailLen = 0; - } - break; - } + case 1: + if (p+2 <= len) { + // A 1-byte tail with at least 2 bytes of + // input available now. + v = ((tail[0] & 0xff) << 16) | + ((input[p++] & 0xff) << 8) | + (input[p++] & 0xff); + tailLen = 0; + }; + break; - if (v != -1) { - output[op++] = estate.alphabet[(v >> 18) & 0x3f]; - output[op++] = estate.alphabet[(v >> 12) & 0x3f]; - output[op++] = estate.alphabet[(v >> 6) & 0x3f]; - output[op++] = estate.alphabet[v & 0x3f]; - if (--count == 0) { - if (do_cr) output[op++] = '\r'; - output[op++] = '\n'; - count = LINE_GROUPS; + case 2: + if (p+1 <= len) { + // A 2-byte tail with at least 1 byte of input. + v = ((tail[0] & 0xff) << 16) | + ((tail[1] & 0xff) << 8) | + (input[p++] & 0xff); + tailLen = 0; + } + break; } - } - // At this point either there is no tail, or there are fewer - // than 3 bytes of input available. - - // The main loop, turning 3 input bytes into 4 output bytes on - // each iteration. - while (p+3 <= len) { - v = ((input[p++] & 0xff) << 16) | - ((input[p++] & 0xff) << 8) | - (input[p++] & 0xff); - output[op++] = estate.alphabet[(v >> 18) & 0x3f]; - output[op++] = estate.alphabet[(v >> 12) & 0x3f]; - output[op++] = estate.alphabet[(v >> 6) & 0x3f]; - output[op++] = estate.alphabet[v & 0x3f]; - if (--count == 0) { - if (do_cr) output[op++] = '\r'; - output[op++] = '\n'; - count = LINE_GROUPS; + if (v != -1) { + output[op++] = alphabet[(v >> 18) & 0x3f]; + output[op++] = alphabet[(v >> 12) & 0x3f]; + output[op++] = alphabet[(v >> 6) & 0x3f]; + output[op++] = alphabet[v & 0x3f]; + if (--count == 0) { + if (do_cr) output[op++] = '\r'; + output[op++] = '\n'; + count = LINE_GROUPS; + } } - } - if (finish) { - // Finish up the tail of the input. Note that we need to - // consume any bytes in estate.tail before any bytes - // remaining in input; there should be at most two bytes - // total. - - if (p-estate.tailLen == len-1) { - int t = 0; - v = ((estate.tailLen > 0 ? estate.tail[t++] : input[p++]) & 0xff) << 4; - estate.tailLen -= t; - output[op++] = estate.alphabet[(v >> 6) & 0x3f]; - output[op++] = estate.alphabet[v & 0x3f]; - if (do_padding) { - output[op++] = '='; - output[op++] = '='; - } - if (do_newline) { + // At this point either there is no tail, or there are fewer + // than 3 bytes of input available. + + // The main loop, turning 3 input bytes into 4 output bytes on + // each iteration. + while (p+3 <= len) { + v = ((input[p] & 0xff) << 16) | + ((input[p+1] & 0xff) << 8) | + (input[p+2] & 0xff); + output[op] = alphabet[(v >> 18) & 0x3f]; + output[op+1] = alphabet[(v >> 12) & 0x3f]; + output[op+2] = alphabet[(v >> 6) & 0x3f]; + output[op+3] = alphabet[v & 0x3f]; + p += 3; + op += 4; + if (--count == 0) { if (do_cr) output[op++] = '\r'; output[op++] = '\n'; + count = LINE_GROUPS; } - } else if (p-estate.tailLen == len-2) { - int t = 0; - v = (((estate.tailLen > 1 ? estate.tail[t++] : input[p++]) & 0xff) << 10) | - (((estate.tailLen > 0 ? estate.tail[t++] : input[p++]) & 0xff) << 2); - estate.tailLen -= t; - output[op++] = estate.alphabet[(v >> 12) & 0x3f]; - output[op++] = estate.alphabet[(v >> 6) & 0x3f]; - output[op++] = estate.alphabet[v & 0x3f]; - if (do_padding) { - output[op++] = '='; - } - if (do_newline) { + } + + if (finish) { + // Finish up the tail of the input. Note that we need to + // consume any bytes in tail before any bytes + // remaining in input; there should be at most two bytes + // total. + + if (p-tailLen == len-1) { + int t = 0; + v = ((tailLen > 0 ? tail[t++] : input[p++]) & 0xff) << 4; + tailLen -= t; + output[op++] = alphabet[(v >> 6) & 0x3f]; + output[op++] = alphabet[v & 0x3f]; + if (do_padding) { + output[op++] = '='; + output[op++] = '='; + } + if (do_newline) { + if (do_cr) output[op++] = '\r'; + output[op++] = '\n'; + } + } else if (p-tailLen == len-2) { + int t = 0; + v = (((tailLen > 1 ? tail[t++] : input[p++]) & 0xff) << 10) | + (((tailLen > 0 ? tail[t++] : input[p++]) & 0xff) << 2); + tailLen -= t; + output[op++] = alphabet[(v >> 12) & 0x3f]; + output[op++] = alphabet[(v >> 6) & 0x3f]; + output[op++] = alphabet[v & 0x3f]; + if (do_padding) { + output[op++] = '='; + } + if (do_newline) { + if (do_cr) output[op++] = '\r'; + output[op++] = '\n'; + } + } else if (do_newline && op > 0 && count != LINE_GROUPS) { if (do_cr) output[op++] = '\r'; output[op++] = '\n'; } - } else if (do_newline && op > 0 && count != LINE_GROUPS) { - if (do_cr) output[op++] = '\r'; - output[op++] = '\n'; - } - assert estate.tailLen == 0; - assert p == len; - } else { - // Save the leftovers in tail to be consumed on the next - // call to encodeInternal. - - if (p == len-1) { - estate.tail[estate.tailLen++] = input[p]; - } else if (p == len-2) { - estate.tail[estate.tailLen++] = input[p]; - estate.tail[estate.tailLen++] = input[p+1]; + assert tailLen == 0; + assert p == len; + } else { + // Save the leftovers in tail to be consumed on the next + // call to encodeInternal. + + if (p == len-1) { + tail[tailLen++] = input[p]; + } else if (p == len-2) { + tail[tailLen++] = input[p]; + tail[tailLen++] = input[p+1]; + } } - } - estate.op = op; - estate.count = count; + this.op = op; + this.count = count; + + return true; + } } private Base64() { } // don't instantiate diff --git a/core/java/android/util/base64/Base64InputStream.java b/core/java/android/util/base64/Base64InputStream.java index 646bbdb..935939e 100644 --- a/core/java/android/util/base64/Base64InputStream.java +++ b/core/java/android/util/base64/Base64InputStream.java @@ -25,16 +25,13 @@ import java.io.InputStream; * it. */ public class Base64InputStream extends FilterInputStream { - private final boolean encode; - private final Base64.EncoderState estate; - private final Base64.DecoderState dstate; + private final Base64.Coder coder; private static byte[] EMPTY = new byte[0]; private static final int BUFFER_SIZE = 2048; private boolean eof; private byte[] inputBuffer; - private byte[] outputBuffer; private int outputStart; private int outputEnd; @@ -63,22 +60,14 @@ public class Base64InputStream extends FilterInputStream { */ public Base64InputStream(InputStream in, int flags, boolean encode) { super(in); - this.encode = encode; eof = false; inputBuffer = new byte[BUFFER_SIZE]; if (encode) { - // len*8/5+10 is an overestimate of the most bytes the - // encoder can produce for len bytes of input. - outputBuffer = new byte[BUFFER_SIZE * 8/5 + 10]; - estate = new Base64.EncoderState(flags, outputBuffer); - dstate = null; + coder = new Base64.Encoder(flags, null); } else { - // len*3/4+10 is an overestimate of the most bytes the - // decoder can produce for len bytes of input. - outputBuffer = new byte[BUFFER_SIZE * 3/4 + 10]; - estate = null; - dstate = new Base64.DecoderState(flags, outputBuffer); + coder = new Base64.Decoder(flags, null); } + coder.output = new byte[coder.maxOutputSize(BUFFER_SIZE)]; outputStart = 0; outputEnd = 0; } @@ -123,7 +112,7 @@ public class Base64InputStream extends FilterInputStream { if (outputStart >= outputEnd) { return -1; } else { - return outputBuffer[outputStart++]; + return coder.output[outputStart++]; } } @@ -135,36 +124,30 @@ public class Base64InputStream extends FilterInputStream { return -1; } int bytes = Math.min(len, outputEnd-outputStart); - System.arraycopy(outputBuffer, outputStart, b, off, bytes); + System.arraycopy(coder.output, outputStart, b, off, bytes); outputStart += bytes; return bytes; } /** * Read data from the input stream into inputBuffer, then - * decode/encode it into the empty outputBuffer, and reset the + * decode/encode it into the empty coder.output, and reset the * outputStart and outputEnd pointers. */ private void refill() throws IOException { if (eof) return; int bytesRead = in.read(inputBuffer); - if (encode) { - if (bytesRead == -1) { - eof = true; - Base64.encodeInternal(EMPTY, 0, 0, estate, true); - } else { - Base64.encodeInternal(inputBuffer, 0, bytesRead, estate, false); - } - outputEnd = estate.op; + boolean success; + if (bytesRead == -1) { + eof = true; + success = coder.process(EMPTY, 0, 0, true); } else { - if (bytesRead == -1) { - eof = true; - Base64.decodeInternal(EMPTY, 0, 0, dstate, true); - } else { - Base64.decodeInternal(inputBuffer, 0, bytesRead, dstate, false); - } - outputEnd = dstate.op; + success = coder.process(inputBuffer, 0, bytesRead, false); + } + if (!success) { + throw new IOException("bad base-64"); } + outputEnd = coder.op; outputStart = 0; } } diff --git a/core/java/android/util/base64/Base64OutputStream.java b/core/java/android/util/base64/Base64OutputStream.java index 8edb511..35e9a2b 100644 --- a/core/java/android/util/base64/Base64OutputStream.java +++ b/core/java/android/util/base64/Base64OutputStream.java @@ -25,9 +25,7 @@ import java.io.OutputStream; * it, writing the resulting data to another OutputStream. */ public class Base64OutputStream extends FilterOutputStream { - private final boolean encode; - private final Base64.EncoderState estate; - private final Base64.DecoderState dstate; + private final Base64.Coder coder; private final int flags; private byte[] buffer = null; @@ -62,13 +60,10 @@ public class Base64OutputStream extends FilterOutputStream { public Base64OutputStream(OutputStream out, int flags, boolean encode) { super(out); this.flags = flags; - this.encode = encode; if (encode) { - estate = new Base64.EncoderState(flags, null); - dstate = null; + coder = new Base64.Encoder(flags, null); } else { - estate = null; - dstate = new Base64.DecoderState(flags, null); + coder = new Base64.Decoder(flags, null); } } @@ -107,12 +102,28 @@ public class Base64OutputStream extends FilterOutputStream { } public void close() throws IOException { - flushBuffer(); - internalWrite(EMPTY, 0, 0, true); - if ((flags & Base64.NO_CLOSE) == 0) { - out.close(); - } else { - out.flush(); + IOException thrown = null; + try { + flushBuffer(); + internalWrite(EMPTY, 0, 0, true); + } catch (IOException e) { + thrown = e; + } + + try { + if ((flags & Base64.NO_CLOSE) == 0) { + out.close(); + } else { + out.flush(); + } + } catch (IOException e) { + if (thrown != null) { + thrown = e; + } + } + + if (thrown != null) { + throw thrown; } } @@ -123,21 +134,11 @@ public class Base64OutputStream extends FilterOutputStream { * encoder/decoder state to be finalized. */ private void internalWrite(byte[] b, int off, int len, boolean finish) throws IOException { - if (encode) { - // len*8/5+10 is an overestimate of the most bytes the - // encoder can produce for len bytes of input. - estate.output = embiggen(estate.output, len*8/5+10); - Base64.encodeInternal(b, off, len, estate, finish); - out.write(estate.output, 0, estate.op); - } else { - // len*3/4+10 is an overestimate of the most bytes the - // decoder can produce for len bytes of input. - dstate.output = embiggen(dstate.output, len*3/4+10); - if (!Base64.decodeInternal(b, off, len, dstate, finish)) { - throw new IOException("bad base-64"); - } - out.write(dstate.output, 0, dstate.op); + coder.output = embiggen(coder.output, coder.maxOutputSize(len)); + if (!coder.process(b, off, len, finish)) { + throw new IOException("bad base-64"); } + out.write(coder.output, 0, coder.op); } /** diff --git a/core/java/android/view/GestureDetector.java b/core/java/android/view/GestureDetector.java index 3c79200..58f998e 100644 --- a/core/java/android/view/GestureDetector.java +++ b/core/java/android/view/GestureDetector.java @@ -465,10 +465,10 @@ public class GestureDetector { case MotionEvent.ACTION_POINTER_UP: // Ending a multitouch gesture and going back to 1 finger if (mIgnoreMultitouch && ev.getPointerCount() == 2) { - int id = (((action & MotionEvent.ACTION_POINTER_ID_MASK) - >> MotionEvent.ACTION_POINTER_ID_SHIFT) == 0) ? 1 : 0; - mLastMotionX = ev.getX(id); - mLastMotionY = ev.getY(id); + int index = (((action & MotionEvent.ACTION_POINTER_INDEX_MASK) + >> MotionEvent.ACTION_POINTER_INDEX_SHIFT) == 0) ? 1 : 0; + mLastMotionX = ev.getX(index); + mLastMotionY = ev.getY(index); mVelocityTracker.recycle(); mVelocityTracker = VelocityTracker.obtain(); } diff --git a/core/java/android/view/MotionEvent.java b/core/java/android/view/MotionEvent.java index ca907af..d648e96 100644 --- a/core/java/android/view/MotionEvent.java +++ b/core/java/android/view/MotionEvent.java @@ -76,61 +76,81 @@ public final class MotionEvent implements Parcelable { public static final int ACTION_POINTER_DOWN = 5; /** - * Synonym for {@link #ACTION_POINTER_DOWN} with - * {@link #ACTION_POINTER_ID_MASK} of 0: the primary pointer has gone done. + * A non-primary pointer has gone up. The bits in + * {@link #ACTION_POINTER_ID_MASK} indicate which pointer changed. */ - public static final int ACTION_POINTER_1_DOWN = ACTION_POINTER_DOWN | 0x0000; + public static final int ACTION_POINTER_UP = 6; /** - * Synonym for {@link #ACTION_POINTER_DOWN} with - * {@link #ACTION_POINTER_ID_MASK} of 1: the secondary pointer has gone done. + * Bits in the action code that represent a pointer index, used with + * {@link #ACTION_POINTER_DOWN} and {@link #ACTION_POINTER_UP}. Shifting + * down by {@link #ACTION_POINTER_INDEX_SHIFT} provides the actual pointer + * index where the data for the pointer going up or down can be found; you can + * get its identifier with {@link #getPointerId(int)} and the actual + * data with {@link #getX(int)} etc. */ - public static final int ACTION_POINTER_2_DOWN = ACTION_POINTER_DOWN | 0x0100; + public static final int ACTION_POINTER_INDEX_MASK = 0xff00; /** - * Synonym for {@link #ACTION_POINTER_DOWN} with - * {@link #ACTION_POINTER_ID_MASK} of 2: the tertiary pointer has gone done. + * Bit shift for the action bits holding the pointer index as + * defined by {@link #ACTION_POINTER_INDEX_MASK}. */ - public static final int ACTION_POINTER_3_DOWN = ACTION_POINTER_DOWN | 0x0200; + public static final int ACTION_POINTER_INDEX_SHIFT = 8; /** - * A non-primary pointer has gone up. The bits in - * {@link #ACTION_POINTER_ID_MASK} indicate which pointer changed. + * @deprecated Use {@link #ACTION_POINTER_INDEX_MASK} to retrieve the + * data index associated with {@link #ACTION_POINTER_DOWN}. */ - public static final int ACTION_POINTER_UP = 6; + @Deprecated + public static final int ACTION_POINTER_1_DOWN = ACTION_POINTER_DOWN | 0x0000; + + /** + * @deprecated Use {@link #ACTION_POINTER_INDEX_MASK} to retrieve the + * data index associated with {@link #ACTION_POINTER_DOWN}. + */ + @Deprecated + public static final int ACTION_POINTER_2_DOWN = ACTION_POINTER_DOWN | 0x0100; /** - * Synonym for {@link #ACTION_POINTER_UP} with - * {@link #ACTION_POINTER_ID_MASK} of 0: the primary pointer has gone up. + * @deprecated Use {@link #ACTION_POINTER_INDEX_MASK} to retrieve the + * data index associated with {@link #ACTION_POINTER_DOWN}. */ + @Deprecated + public static final int ACTION_POINTER_3_DOWN = ACTION_POINTER_DOWN | 0x0200; + + /** + * @deprecated Use {@link #ACTION_POINTER_INDEX_MASK} to retrieve the + * data index associated with {@link #ACTION_POINTER_UP}. + */ + @Deprecated public static final int ACTION_POINTER_1_UP = ACTION_POINTER_UP | 0x0000; /** - * Synonym for {@link #ACTION_POINTER_UP} with - * {@link #ACTION_POINTER_ID_MASK} of 1: the secondary pointer has gone up. + * @deprecated Use {@link #ACTION_POINTER_INDEX_MASK} to retrieve the + * data index associated with {@link #ACTION_POINTER_UP}. */ + @Deprecated public static final int ACTION_POINTER_2_UP = ACTION_POINTER_UP | 0x0100; /** - * Synonym for {@link #ACTION_POINTER_UP} with - * {@link #ACTION_POINTER_ID_MASK} of 2: the tertiary pointer has gone up. + * @deprecated Use {@link #ACTION_POINTER_INDEX_MASK} to retrieve the + * data index associated with {@link #ACTION_POINTER_UP}. */ + @Deprecated public static final int ACTION_POINTER_3_UP = ACTION_POINTER_UP | 0x0200; /** - * Bits in the action code that represent a pointer ID, used with - * {@link #ACTION_POINTER_DOWN} and {@link #ACTION_POINTER_UP}. Pointer IDs - * start at 0, with 0 being the primary (first) pointer in the motion. Note - * that this not <em>not</em> an index into the array of pointer values, - * which is compacted to only contain pointers that are down; the pointer - * ID for a particular index can be found with {@link #findPointerIndex}. + * @deprecated Renamed to {@link #ACTION_POINTER_INDEX_MASK} to match + * the actual data contained in these bits. */ + @Deprecated public static final int ACTION_POINTER_ID_MASK = 0xff00; /** - * Bit shift for the action bits holding the pointer identifier as - * defined by {@link #ACTION_POINTER_ID_MASK}. + * @deprecated Renamed to {@link #ACTION_POINTER_INDEX_SHIFT} to match + * the actual data contained in these bits. */ + @Deprecated public static final int ACTION_POINTER_ID_SHIFT = 8; private static final boolean TRACK_RECYCLED_LOCATION = false; @@ -618,13 +638,39 @@ public final class MotionEvent implements Parcelable { /** * Return the kind of action being performed -- one of either * {@link #ACTION_DOWN}, {@link #ACTION_MOVE}, {@link #ACTION_UP}, or - * {@link #ACTION_CANCEL}. + * {@link #ACTION_CANCEL}. Consider using {@link #getActionMasked} + * and {@link #getActionIndex} to retrieve the separate masked action + * and pointer index. */ public final int getAction() { return mAction; } /** + * Return the masked action being performed, without pointer index + * information. May be any of the actions: {@link #ACTION_DOWN}, + * {@link #ACTION_MOVE}, {@link #ACTION_UP}, {@link #ACTION_CANCEL}, + * {@link #ACTION_POINTER_DOWN}, or {@link #ACTION_POINTER_UP}. + * Use {@link #getActionIndex} to return the index associated with + * pointer actions. + */ + public final int getActionMasked() { + return mAction & ACTION_MASK; + } + + /** + * For {@link #ACTION_POINTER_DOWN} or {@link #ACTION_POINTER_UP} + * as returned by {@link #getActionMasked}, this returns the associated + * pointer index. The index may be used with {@link #getPointerId(int)}, + * {@link #getX(int)}, {@link #getY(int)}, {@link #getPressure(int)}, + * and {@link #getSize(int)} to get information about the pointer that has + * gone down or up. + */ + public final int getActionIndex() { + return (mAction & ACTION_POINTER_INDEX_MASK) >> ACTION_POINTER_INDEX_SHIFT; + } + + /** * Returns the time (in ms) when the user originally pressed down to start * a stream of position events. */ diff --git a/core/java/android/view/VelocityTracker.java b/core/java/android/view/VelocityTracker.java index 9a8ee02..91fd6f1 100644 --- a/core/java/android/view/VelocityTracker.java +++ b/core/java/android/view/VelocityTracker.java @@ -124,24 +124,37 @@ public final class VelocityTracker implements Poolable<VelocityTracker> { * @param ev The MotionEvent you received and would like to track. */ public void addMovement(MotionEvent ev) { - long time = ev.getEventTime(); final int N = ev.getHistorySize(); final int pointerCount = ev.getPointerCount(); - for (int p = 0; p < pointerCount; p++) { - for (int i=0; i<N; i++) { - addPoint(p, ev.getHistoricalX(p, i), ev.getHistoricalY(p, i), - ev.getHistoricalEventTime(i)); + int touchIndex = (mLastTouch + 1) % NUM_PAST; + for (int i=0; i<N; i++) { + for (int id = 0; id < MotionEvent.BASE_AVAIL_POINTERS; id++) { + mPastTime[id][touchIndex] = 0; + } + for (int p = 0; p < pointerCount; p++) { + int id = ev.getPointerId(p); + mPastX[id][touchIndex] = ev.getHistoricalX(p, i); + mPastY[id][touchIndex] = ev.getHistoricalY(p, i); + mPastTime[id][touchIndex] = ev.getHistoricalEventTime(i); } - addPoint(p, ev.getX(p), ev.getY(p), time); + + touchIndex = (touchIndex + 1) % NUM_PAST; + } + + // During calculation any pointer values with a time of 0 are treated + // as a break in input. Initialize all to 0 for each new touch index. + for (int id = 0; id < MotionEvent.BASE_AVAIL_POINTERS; id++) { + mPastTime[id][touchIndex] = 0; + } + final long time = ev.getEventTime(); + for (int p = 0; p < pointerCount; p++) { + int id = ev.getPointerId(p); + mPastX[id][touchIndex] = ev.getX(p); + mPastY[id][touchIndex] = ev.getY(p); + mPastTime[id][touchIndex] = time; } - } - private void addPoint(int pos, float x, float y, long time) { - final int lastTouch = (mLastTouch + 1) % NUM_PAST; - mPastX[pos][lastTouch] = x; - mPastY[pos][lastTouch] = y; - mPastTime[pos][lastTouch] = time; - mLastTouch = lastTouch; + mLastTouch = touchIndex; } /** @@ -177,10 +190,12 @@ public final class VelocityTracker implements Poolable<VelocityTracker> { // find oldest acceptable time int oldestTouch = lastTouch; if (pastTime[lastTouch] > 0) { // cleared ? - oldestTouch = (lastTouch + 1) % NUM_PAST; final float acceptableTime = pastTime[lastTouch] - LONGEST_PAST_TIME; - while (pastTime[oldestTouch] < acceptableTime) { - oldestTouch = (oldestTouch + 1) % NUM_PAST; + int nextOldestTouch = (NUM_PAST + oldestTouch - 1) % NUM_PAST; + while (pastTime[nextOldestTouch] >= acceptableTime && + nextOldestTouch != lastTouch) { + oldestTouch = nextOldestTouch; + nextOldestTouch = (NUM_PAST + oldestTouch - 1) % NUM_PAST; } } @@ -241,25 +256,25 @@ public final class VelocityTracker implements Poolable<VelocityTracker> { * Retrieve the last computed X velocity. You must first call * {@link #computeCurrentVelocity(int)} before calling this function. * - * @param pos Which pointer's velocity to return. + * @param id Which pointer's velocity to return. * @return The previously computed X velocity. * * @hide Pending API approval */ - public float getXVelocity(int pos) { - return mXVelocity[pos]; + public float getXVelocity(int id) { + return mXVelocity[id]; } /** * Retrieve the last computed Y velocity. You must first call * {@link #computeCurrentVelocity(int)} before calling this function. * - * @param pos Which pointer's velocity to return. + * @param id Which pointer's velocity to return. * @return The previously computed Y velocity. * * @hide Pending API approval */ - public float getYVelocity(int pos) { - return mYVelocity[pos]; + public float getYVelocity(int id) { + return mYVelocity[id]; } } diff --git a/core/java/android/webkit/WebView.java b/core/java/android/webkit/WebView.java index 9148a18..ae6c666 100644 --- a/core/java/android/webkit/WebView.java +++ b/core/java/android/webkit/WebView.java @@ -5893,6 +5893,10 @@ public class WebView extends AbsoluteLayout } break; case UPDATE_TEXT_SELECTION_MSG_ID: + // If no textfield was in focus, and the user touched one, + // causing it to send this message, then WebTextView has not + // been set up yet. Rebuild it so it can set its selection. + rebuildWebTextView(); if (inEditingMode() && mWebTextView.isSameTextField(msg.arg1) && msg.arg2 == mTextGeneration) { diff --git a/core/java/android/widget/GridView.java b/core/java/android/widget/GridView.java index 30a38df..b9acf5e 100644 --- a/core/java/android/widget/GridView.java +++ b/core/java/android/widget/GridView.java @@ -1856,8 +1856,11 @@ public class GridView extends AbsListView { final int top = view.getTop(); int height = view.getHeight(); if (height > 0) { - final int whichRow = mFirstPosition / mNumColumns; - return Math.max(whichRow * 100 - (top * 100) / height, 0); + final int numColumns = mNumColumns; + final int whichRow = mFirstPosition / numColumns; + final int rowCount = (mItemCount + numColumns - 1) / numColumns; + return Math.max(whichRow * 100 - (top * 100) / height + + (int) ((float) mScrollY / getHeight() * rowCount * 100), 0); } } return 0; @@ -1868,7 +1871,12 @@ public class GridView extends AbsListView { // TODO: Account for vertical spacing too final int numColumns = mNumColumns; final int rowCount = (mItemCount + numColumns - 1) / numColumns; - return Math.max(rowCount * 100, 0); + int result = Math.max(rowCount * 100, 0); + if (mScrollY != 0) { + // Compensate for overscroll + result += Math.abs((int) ((float) mScrollY / getHeight() * rowCount * 100)); + } + return result; } } diff --git a/core/java/android/widget/HorizontalScrollView.java b/core/java/android/widget/HorizontalScrollView.java index c62724c..a7b819a 100644 --- a/core/java/android/widget/HorizontalScrollView.java +++ b/core/java/android/widget/HorizontalScrollView.java @@ -890,14 +890,15 @@ public class HorizontalScrollView extends FrameLayout { */ @Override protected int computeHorizontalScrollRange() { - int count = getChildCount(); + final int count = getChildCount(); + final int contentWidth = getWidth() - mPaddingLeft - mPaddingRight; if (count == 0) { - return getWidth(); + return contentWidth; } int scrollRange = getChildAt(0).getRight(); - int scrollX = mScrollX; - int overscrollRight = scrollRange - getWidth() - mPaddingLeft - mPaddingRight; + final int scrollX = mScrollX; + final int overscrollRight = Math.max(0, scrollRange - contentWidth); if (scrollX < 0) { scrollRange -= scrollX; } else if (scrollX > overscrollRight) { diff --git a/core/java/android/widget/ScrollView.java b/core/java/android/widget/ScrollView.java index 2ee7ad5..52ed11d 100644 --- a/core/java/android/widget/ScrollView.java +++ b/core/java/android/widget/ScrollView.java @@ -892,14 +892,15 @@ public class ScrollView extends FrameLayout { */ @Override protected int computeVerticalScrollRange() { - int count = getChildCount(); + final int count = getChildCount(); + final int contentHeight = getHeight() - mPaddingBottom - mPaddingTop; if (count == 0) { - return getHeight(); + return contentHeight; } int scrollRange = getChildAt(0).getBottom(); - int scrollY = mScrollY; - int overscrollBottom = scrollRange - getHeight() - mPaddingBottom - mPaddingTop; + final int scrollY = mScrollY; + final int overscrollBottom = Math.max(0, scrollRange - contentHeight); if (scrollY < 0) { scrollRange -= scrollY; } else if (scrollY > overscrollBottom) { diff --git a/core/java/com/android/internal/app/IMediaContainerService.aidl b/core/java/com/android/internal/app/IMediaContainerService.aidl index 726e28f..c0e9587 100755 --- a/core/java/com/android/internal/app/IMediaContainerService.aidl +++ b/core/java/com/android/internal/app/IMediaContainerService.aidl @@ -25,4 +25,5 @@ interface IMediaContainerService { String key, String resFileName); boolean copyResource(in Uri packageURI, in ParcelFileDescriptor outStream); + int getRecommendedInstallLocation(in Uri fileUri); }
\ No newline at end of file diff --git a/core/java/com/android/internal/content/PackageHelper.java b/core/java/com/android/internal/content/PackageHelper.java new file mode 100644 index 0000000..c5b869b --- /dev/null +++ b/core/java/com/android/internal/content/PackageHelper.java @@ -0,0 +1,182 @@ +/* + * Copyright (C) 2009 The Android Open Source Project + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +package com.android.internal.content; + +import android.os.storage.IMountService; + +import android.os.IBinder; +import android.os.RemoteException; +import android.os.ServiceManager; +import android.os.storage.StorageResultCode; +import android.util.Log; + +import java.io.File; + +/** + * Constants used internally between the PackageManager + * and media container service transports. + * Some utility methods to invoke MountService api. + */ +public class PackageHelper { + public static final int RECOMMEND_INSTALL_INTERNAL = 1; + public static final int RECOMMEND_INSTALL_EXTERNAL = 2; + public static final int RECOMMEND_FAILED_INSUFFICIENT_STORAGE = -1; + public static final int RECOMMEND_FAILED_INVALID_APK = -2; + private static final boolean DEBUG_SD_INSTALL = true; + private static final String TAG = "PackageHelper"; + + public static IMountService getMountService() { + IBinder service = ServiceManager.getService("mount"); + if (service != null) { + return IMountService.Stub.asInterface(service); + } else { + Log.e(TAG, "Can't get mount service"); + } + return null; + } + + public static String createSdDir(File tmpPackageFile, String cid, + String sdEncKey, int uid) { + // Create mount point via MountService + IMountService mountService = getMountService(); + long len = tmpPackageFile.length(); + int mbLen = (int) (len/(1024*1024)); + if ((len - (mbLen * 1024 * 1024)) > 0) { + mbLen++; + } + if (DEBUG_SD_INSTALL) Log.i(TAG, "Size of resource " + mbLen); + + try { + int rc = mountService.createSecureContainer( + cid, mbLen, "vfat", sdEncKey, uid); + if (rc != StorageResultCode.OperationSucceeded) { + Log.e(TAG, "Failed to create secure container " + cid); + return null; + } + String cachePath = mountService.getSecureContainerPath(cid); + if (DEBUG_SD_INSTALL) Log.i(TAG, "Created secure container " + cid + + " at " + cachePath); + return cachePath; + } catch (RemoteException e) { + Log.e(TAG, "MountService running?"); + } + return null; + } + + public static String mountSdDir(String cid, String key, int ownerUid) { + try { + int rc = getMountService().mountSecureContainer(cid, key, ownerUid); + if (rc != StorageResultCode.OperationSucceeded) { + Log.i(TAG, "Failed to mount container " + cid + " rc : " + rc); + return null; + } + return getMountService().getSecureContainerPath(cid); + } catch (RemoteException e) { + Log.e(TAG, "MountService running?"); + } + return null; + } + + public static boolean unMountSdDir(String cid) { + try { + int rc = getMountService().unmountSecureContainer(cid); + if (rc != StorageResultCode.OperationSucceeded) { + Log.e(TAG, "Failed to unmount " + cid + " with rc " + rc); + return false; + } + return true; + } catch (RemoteException e) { + Log.e(TAG, "MountService running?"); + } + return false; + } + + public static boolean renameSdDir(String oldId, String newId) { + try { + int rc = getMountService().renameSecureContainer(oldId, newId); + if (rc != StorageResultCode.OperationSucceeded) { + Log.e(TAG, "Failed to rename " + oldId + " to " + + newId + "with rc " + rc); + return false; + } + return true; + } catch (RemoteException e) { + Log.i(TAG, "Failed ot rename " + oldId + " to " + newId + + " with exception : " + e); + } + return false; + } + + public static String getSdDir(String cid) { + try { + return getMountService().getSecureContainerPath(cid); + } catch (RemoteException e) { + Log.e(TAG, "Failed to get container path for " + cid + + " with exception " + e); + } + return null; + } + + public static boolean finalizeSdDir(String cid) { + try { + int rc = getMountService().finalizeSecureContainer(cid); + if (rc != StorageResultCode.OperationSucceeded) { + Log.i(TAG, "Failed to finalize container " + cid); + return false; + } + return true; + } catch (RemoteException e) { + Log.e(TAG, "Failed to finalize container " + cid + + " with exception " + e); + } + return false; + } + + public static boolean destroySdDir(String cid) { + try { + int rc = getMountService().destroySecureContainer(cid); + if (rc != StorageResultCode.OperationSucceeded) { + Log.i(TAG, "Failed to destroy container " + cid); + return false; + } + return true; + } catch (RemoteException e) { + Log.e(TAG, "Failed to destroy container " + cid + + " with exception " + e); + } + return false; + } + + public static String[] getSecureContainerList() { + try { + return getMountService().getSecureContainerList(); + } catch (RemoteException e) { + Log.e(TAG, "Failed to get secure container list with exception" + + e); + } + return null; + } + + public static boolean isContainerMounted(String cid) { + try { + return getMountService().isSecureContainerMounted(cid); + } catch (RemoteException e) { + Log.e(TAG, "Failed to find out if container " + cid + " mounted"); + } + return false; + } +} diff --git a/core/java/com/android/internal/os/RuntimeInit.java b/core/java/com/android/internal/os/RuntimeInit.java index 57a28e6..c134d88 100644 --- a/core/java/com/android/internal/os/RuntimeInit.java +++ b/core/java/com/android/internal/os/RuntimeInit.java @@ -22,7 +22,6 @@ import android.app.IActivityManager; import android.os.Build; import android.os.Debug; import android.os.IBinder; -import android.os.ICheckinService; import android.os.Process; import android.os.RemoteException; import android.os.ServiceManager; diff --git a/core/java/com/google/android/net/ParentalControl.java b/core/java/com/google/android/net/ParentalControl.java deleted file mode 100644 index 71a3958..0000000 --- a/core/java/com/google/android/net/ParentalControl.java +++ /dev/null @@ -1,73 +0,0 @@ -/* - * Copyright (C) 2008 The Android Open Source Project - * - * Licensed under the Apache License, Version 2.0 (the "License"); - * you may not use this file except in compliance with the License. - * You may obtain a copy of the License at - * - * http://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, - * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - * See the License for the specific language governing permissions and - * limitations under the License. - */ - -package com.google.android.net; - -import android.os.ICheckinService; -import android.os.IParentalControlCallback; -import android.os.RemoteException; -import android.os.ServiceManager; -import android.util.Log; - -public class ParentalControl { - /** - * Strings to identify your app. To enable parental control checking for - * new apps, please add it here, and configure GServices accordingly. - */ - public static final String VENDING = "vending"; - public static final String YOUTUBE = "youtube"; - - /** - * This interface is supplied to getParentalControlState and is callback upon with - * the state of parental control. - */ - public interface Callback { - /** - * This method will be called when the state of parental control is known. If state is - * null, then the state of parental control is unknown. - * @param state The state of parental control. - */ - void onResult(ParentalControlState state); - } - - private static class RemoteCallback extends IParentalControlCallback.Stub { - private Callback mCallback; - - public RemoteCallback(Callback callback) { - mCallback = callback; - } - - public void onResult(ParentalControlState state) { - if (mCallback != null) { - mCallback.onResult(state); - } - } - }; - - public static void getParentalControlState(Callback callback, - String requestingApp) { - ICheckinService service = - ICheckinService.Stub.asInterface(ServiceManager.getService("checkin")); - - RemoteCallback remoteCallback = new RemoteCallback(callback); - try { - service.getParentalControlState(remoteCallback, requestingApp); - } catch (RemoteException e) { - // This should never happen. - Log.e("ParentalControl", "Failed to talk to the checkin service."); - } - } -} diff --git a/core/java/com/google/android/net/ParentalControlState.aidl b/core/java/com/google/android/net/ParentalControlState.aidl deleted file mode 100644 index ed1326a..0000000 --- a/core/java/com/google/android/net/ParentalControlState.aidl +++ /dev/null @@ -1,18 +0,0 @@ -/** - * Copyright (c) 2008, The Android Open Source Project - * - * Licensed under the Apache License, Version 2.0 (the "License"); - * you may not use this file except in compliance with the License. - * You may obtain a copy of the License at - * - * http://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, - * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - * See the License for the specific language governing permissions and - * limitations under the License. - */ - -package com.google.android.net; -parcelable ParentalControlState; diff --git a/core/java/com/google/android/net/ParentalControlState.java b/core/java/com/google/android/net/ParentalControlState.java deleted file mode 100644 index 162a1f6..0000000 --- a/core/java/com/google/android/net/ParentalControlState.java +++ /dev/null @@ -1,56 +0,0 @@ -/* - * Copyright (C) 2008 The Android Open Source Project - * - * Licensed under the Apache License, Version 2.0 (the "License"); - * you may not use this file except in compliance with the License. - * You may obtain a copy of the License at - * - * http://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, - * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - * See the License for the specific language governing permissions and - * limitations under the License. - */ - -package com.google.android.net; - -import android.os.Parcel; -import android.os.Parcelable; - -public class ParentalControlState implements Parcelable { - public boolean isEnabled; - public String redirectUrl; - - /** - * Used to read a ParentalControlStatus from a Parcel. - */ - public static final Parcelable.Creator<ParentalControlState> CREATOR = - new Parcelable.Creator<ParentalControlState>() { - public ParentalControlState createFromParcel(Parcel source) { - ParentalControlState status = new ParentalControlState(); - status.isEnabled = (source.readInt() == 1); - status.redirectUrl = source.readString(); - return status; - } - - public ParentalControlState[] newArray(int size) { - return new ParentalControlState[size]; - } - }; - - public int describeContents() { - return 0; - } - - public void writeToParcel(Parcel dest, int flags) { - dest.writeInt(isEnabled ? 1 : 0); - dest.writeString(redirectUrl); - } - - @Override - public String toString() { - return isEnabled + ", " + redirectUrl; - } -}; |