diff options
38 files changed, 939 insertions, 459 deletions
diff --git a/cmds/am/src/com/android/commands/am/Am.java b/cmds/am/src/com/android/commands/am/Am.java index d640de1..b6c9de4 100644 --- a/cmds/am/src/com/android/commands/am/Am.java +++ b/cmds/am/src/com/android/commands/am/Am.java @@ -88,6 +88,8 @@ public class Am { if (op.equals("start")) { runStart(); + } else if (op.equals("startservice")) { + runStartService(); } else if (op.equals("instrument")) { runInstrument(); } else if (op.equals("broadcast")) { @@ -183,6 +185,15 @@ public class Am { return intent; } + private void runStartService() throws Exception { + Intent intent = makeIntent(); + System.out.println("Starting service: " + intent); + ComponentName cn = mAm.startService(null, intent, intent.getType()); + if (cn == null) { + System.err.println("Error: Not found; no service started."); + } + } + private void runStart() throws Exception { Intent intent = makeIntent(); System.out.println("Starting: " + intent); @@ -496,6 +507,8 @@ public class Am { " start an Activity: am start [-D] <INTENT>\n" + " -D: enable debugging\n" + "\n" + + " start a Service: am startservice <INTENT>\n" + + "\n" + " send a broadcast Intent: am broadcast <INTENT>\n" + "\n" + " start an Instrumentation: am instrument [flags] <COMPONENT>\n" + diff --git a/cmds/installd/installd.h b/cmds/installd/installd.h index 92ae310..66f3676 100644 --- a/cmds/installd/installd.h +++ b/cmds/installd/installd.h @@ -68,7 +68,7 @@ /* other handy constants */ #define PROTECTED_DIR_PREFIX "/data/app-private/" -#define SDCARD_DIR_PREFIX "/asec/" +#define SDCARD_DIR_PREFIX "/mnt/asec/" #define DALVIK_CACHE_PREFIX "/data/dalvik-cache/" #define DALVIK_CACHE_POSTFIX "/classes.dex" diff --git a/core/java/android/accounts/AccountManagerService.java b/core/java/android/accounts/AccountManagerService.java index 770554e..d4f4d13 100644 --- a/core/java/android/accounts/AccountManagerService.java +++ b/core/java/android/accounts/AccountManagerService.java @@ -691,11 +691,21 @@ public class AccountManagerService if (account == null) { return; } - ContentValues values = new ContentValues(); - values.put(ACCOUNTS_PASSWORD, password); - mOpenHelper.getWritableDatabase().update(TABLE_ACCOUNTS, values, - ACCOUNTS_NAME + "=? AND " + ACCOUNTS_TYPE+ "=?", - new String[]{account.name, account.type}); + SQLiteDatabase db = mOpenHelper.getWritableDatabase(); + db.beginTransaction(); + try { + final ContentValues values = new ContentValues(); + values.put(ACCOUNTS_PASSWORD, password); + final long accountId = getAccountId(db, account); + if (accountId >= 0) { + final String[] argsAccountId = {String.valueOf(accountId)}; + db.update(TABLE_ACCOUNTS, values, ACCOUNTS_ID + "=?", argsAccountId); + db.delete(TABLE_AUTHTOKENS, AUTHTOKENS_ACCOUNTS_ID + "=?", argsAccountId); + db.setTransactionSuccessful(); + } + } finally { + db.endTransaction(); + } sendAccountsChangedBroadcast(); } @@ -1134,7 +1144,10 @@ public class AccountManagerService long identityToken = clearCallingIdentity(); try { if (features == null || features.length == 0) { - getAccountsByType(type); + Account[] accounts = getAccountsByType(type); + Bundle result = new Bundle(); + result.putParcelableArray(AccountManager.KEY_ACCOUNTS, accounts); + onResult(response, result); return; } new GetAccountsByTypeAndFeatureSession(response, type, features).bind(); diff --git a/core/java/android/app/BackupAgent.java b/core/java/android/app/BackupAgent.java index a2bfc76..4695c21 100644 --- a/core/java/android/app/BackupAgent.java +++ b/core/java/android/app/BackupAgent.java @@ -31,10 +31,18 @@ import android.util.Log; import java.io.IOException; /** - * This is the central interface between an application and Android's - * settings backup mechanism. - * - * <p>STOPSHIP write more documentation about the backup process here. + * This is the central interface between an application and Android's settings + * backup mechanism. Any implementation of a backup agent should perform backup + * and restore actions in + * {@link #onBackup(ParcelFileDescriptor, BackupDataOutput, ParcelFileDescriptor)} + * and {@link #onRestore(BackupDataInput, int, ParcelFileDescriptor)} + * respectively. + * <p> + * A backup agent based on convenient helper classes is available in + * {@link android.backup.BackupHelperAgent} for less complex implementation + * requirements. + * <p> + * STOPSHIP write more documentation about the backup process here. */ public abstract class BackupAgent extends ContextWrapper { private static final String TAG = "BackupAgent"; @@ -51,51 +59,58 @@ public abstract class BackupAgent extends ContextWrapper { } /** - * The application is being asked to write any data changed since the - * last time it performed a backup operation. The state data recorded - * during the last backup pass is provided in the oldState file descriptor. - * If oldState is null, no old state is available and the application should perform - * a full backup. In both cases, a representation of the final backup state after - * this pass should be written to the file pointed to by the newStateFd file descriptor. - * - * @param oldState An open, read-only ParcelFileDescriptor pointing to the last backup - * state provided by the application. May be null, in which - * case no prior state is being provided and the application should - * perform a full backup. - * @param data A structured wrapper around an open, read/write ParcelFileDescriptor - * pointing to the backup data destination. Typically the application will use - * backup helper classes to write to this file. - * @param newState An open, read/write ParcelFileDescriptor pointing to an empty - * file. The application should record the final backup state - * here after writing the requested data to dataFd. + * The application is being asked to write any data changed since the last + * time it performed a backup operation. The state data recorded during the + * last backup pass is provided in the <code>oldState</code> file + * descriptor. If <code>oldState</code> is <code>null</code>, no old state + * is available and the application should perform a full backup. In both + * cases, a representation of the final backup state after this pass should + * be written to the file pointed to by the file descriptor wrapped in + * <code>newState</code>. + * + * @param oldState An open, read-only ParcelFileDescriptor pointing to the + * last backup state provided by the application. May be + * <code>null</code>, in which case no prior state is being + * provided and the application should perform a full backup. + * @param data A structured wrapper around an open, read/write + * ParcelFileDescriptor pointing to the backup data destination. + * Typically the application will use backup helper classes to + * write to this file. + * @param newState An open, read/write ParcelFileDescriptor pointing to an + * empty file. The application should record the final backup + * state here after writing the requested data to dataFd. */ public abstract void onBackup(ParcelFileDescriptor oldState, BackupDataOutput data, ParcelFileDescriptor newState) throws IOException; - + /** - * The application is being restored from backup, and should replace any - * existing data with the contents of the backup. The backup data is - * provided in the file pointed to by the dataFd file descriptor. Once - * the restore is finished, the application should write a representation - * of the final state to the newStateFd file descriptor, - * - * <p>The application is responsible for properly erasing its old data and - * replacing it with the data supplied to this method. No "clear user data" - * operation will be performed automatically by the operating system. The - * exception to this is in the case of a failed restore attempt: if onRestore() - * throws an exception, the OS will assume that the application's data may now - * be in an incoherent state, and will clear it before proceeding. - * - * @param data A structured wrapper around an open, read-only ParcelFileDescriptor - * pointing to a full snapshot of the application's data. Typically the - * application will use helper classes to read this data. - * @param appVersionCode The android:versionCode value of the application that backed - * up this particular data set. This makes it easier for an application's - * agent to distinguish among several possible older data versions when - * asked to perform the restore operation. - * @param newState An open, read/write ParcelFileDescriptor pointing to an empty - * file. The application should record the final backup state - * here after restoring its data from dataFd. + * The application is being restored from backup and should replace any + * existing data with the contents of the backup. The backup data is + * provided in the file descriptor pointed to by the + * {@link android.backup.BackupDataInput} instance <code>data</code>. Once + * the restore is finished, the application should write a representation of + * the final state to the <code>newState</code> file descriptor. + * <p> + * The application is responsible for properly erasing its old data and + * replacing it with the data supplied to this method. No "clear user data" + * operation will be performed automatically by the operating system. The + * exception to this is in the case of a failed restore attempt: if + * onRestore() throws an exception, the OS will assume that the + * application's data may now be in an incoherent state, and will clear it + * before proceeding. + * + * @param data A structured wrapper around an open, read-only + * ParcelFileDescriptor pointing to a full snapshot of the + * application's data. Typically the application will use helper + * classes to read this data. + * @param appVersionCode The android:versionCode value of the application + * that backed up this particular data set. This makes it easier + * for an application's agent to distinguish among several + * possible older data versions when asked to perform the restore + * operation. + * @param newState An open, read/write ParcelFileDescriptor pointing to an + * empty file. The application should record the final backup + * state here after restoring its data from dataFd. */ public abstract void onRestore(BackupDataInput data, int appVersionCode, ParcelFileDescriptor newState) diff --git a/core/java/android/backup/AbsoluteFileBackupHelper.java b/core/java/android/backup/AbsoluteFileBackupHelper.java index 6bf848f..5a8034b 100644 --- a/core/java/android/backup/AbsoluteFileBackupHelper.java +++ b/core/java/android/backup/AbsoluteFileBackupHelper.java @@ -21,7 +21,6 @@ import android.os.ParcelFileDescriptor; import android.util.Log; import java.io.File; -import java.io.FileDescriptor; /** * Like FileBackupHelper, but takes absolute paths for the files instead of diff --git a/core/java/android/backup/BackupDataInput.java b/core/java/android/backup/BackupDataInput.java index 295dc66..a08ee75 100644 --- a/core/java/android/backup/BackupDataInput.java +++ b/core/java/android/backup/BackupDataInput.java @@ -16,8 +16,6 @@ package android.backup; -import android.content.Context; - import java.io.FileDescriptor; import java.io.IOException; diff --git a/core/java/android/backup/BackupDataOutput.java b/core/java/android/backup/BackupDataOutput.java index 672d01f..34879d8 100644 --- a/core/java/android/backup/BackupDataOutput.java +++ b/core/java/android/backup/BackupDataOutput.java @@ -16,8 +16,6 @@ package android.backup; -import android.content.Context; - import java.io.FileDescriptor; import java.io.IOException; diff --git a/core/java/android/backup/BackupHelper.java b/core/java/android/backup/BackupHelper.java index fc48cf2..7eedd01 100644 --- a/core/java/android/backup/BackupHelper.java +++ b/core/java/android/backup/BackupHelper.java @@ -18,16 +18,19 @@ package android.backup; import android.os.ParcelFileDescriptor; -import java.io.InputStream; - /** - * STOPSHIP: document! + * A convenient interface to be used with the + * {@link android.backup.BackupHelperAgent} to implement backup and restore of + * arbitrary data types. + * <p> + * STOPSHOP: document! */ public interface BackupHelper { /** - * Based on oldState, determine which of the files from the application's data directory - * need to be backed up, write them to the data stream, and fill in newState with the - * state as it exists now. + * Based on <code>oldState</code>, determine which of the files from the + * application's data directory need to be backed up, write them to + * <code>data</code>, and fill in <code>newState</code> with the state as it + * exists now. */ public void performBackup(ParcelFileDescriptor oldState, BackupDataOutput data, ParcelFileDescriptor newState); diff --git a/core/java/android/backup/BackupHelperAgent.java b/core/java/android/backup/BackupHelperAgent.java index dc17154f..7fb44f4 100644 --- a/core/java/android/backup/BackupHelperAgent.java +++ b/core/java/android/backup/BackupHelperAgent.java @@ -17,24 +17,19 @@ package android.backup; import android.app.BackupAgent; -import android.backup.BackupHelper; -import android.backup.BackupHelperDispatcher; -import android.backup.BackupDataInput; -import android.backup.BackupDataOutput; import android.os.ParcelFileDescriptor; -import android.util.Log; import java.io.IOException; /** - * A convenient BackupAgent wrapper class that automatically manages heterogeneous - * data sets within the backup data, each identified by a unique key prefix. An - * application will typically extend this class in their own backup agent. Then, - * within the agent's onBackup() and onRestore() methods, it will call - * {@link #addHelper(String, BackupHelper)} one or more times to specify the data - * sets, then invoke super.onBackup() or super.onRestore() to have the BackupHelperAgent - * implementation process the data. - * + * A convenient BackupAgent wrapper class that automatically manages + * heterogeneous data sets within the backup data, each identified by a unique + * key prefix. An application will typically extend this class in their own + * backup agent. Then, within the agent's onBackup() and onRestore() methods, it + * will call {@link #addHelper(String, BackupHelper)} one or more times to + * specify the data sets, then invoke super.onBackup() or super.onRestore() to + * have the BackupHelperAgent implementation process the data. + * <p> * STOPSHIP: document! */ public class BackupHelperAgent extends BackupAgent { diff --git a/core/java/android/backup/BackupHelperDispatcher.java b/core/java/android/backup/BackupHelperDispatcher.java index bf2c44d..68076db 100644 --- a/core/java/android/backup/BackupHelperDispatcher.java +++ b/core/java/android/backup/BackupHelperDispatcher.java @@ -19,12 +19,10 @@ package android.backup; import android.os.ParcelFileDescriptor; import android.util.Log; -import java.io.DataInputStream; -import java.io.DataOutputStream; -import java.io.IOException; import java.io.FileDescriptor; -import java.util.TreeMap; +import java.io.IOException; import java.util.Map; +import java.util.TreeMap; /** @hide */ public class BackupHelperDispatcher { diff --git a/core/java/android/backup/BackupManager.java b/core/java/android/backup/BackupManager.java index 07c08ff..2dff975 100644 --- a/core/java/android/backup/BackupManager.java +++ b/core/java/android/backup/BackupManager.java @@ -23,26 +23,36 @@ import android.os.ServiceManager; import android.util.Log; /** - * BackupManager is the interface to the system's backup service. - * Applications simply instantiate one, and then use that instance - * to communicate with the backup infrastructure. - * - * <p>When your application has made changes to data it wishes to have - * backed up, call {@link #dataChanged()} to notify the backup service. - * The system will then schedule a backup operation to occur in the near - * future. Repeated calls to {@link #dataChanged()} have no further effect - * until the backup operation actually occurs. - * - * <p>The backup operation itself begins with the system launching the - * {@link android.app.BackupAgent} subclass declared in your manifest. See the + * BackupManager is the interface to the system's backup service. Applications + * simply instantiate one, and then use that instance to communicate with the + * backup infrastructure. + * <p> + * When an application has made changes to data which should be backed up, a + * call to {@link #dataChanged()} will notify the backup service. The system + * will then schedule a backup operation to occur in the near future. Repeated + * calls to {@link #dataChanged()} have no further effect until the backup + * operation actually occurs. + * <p> + * The backup operation itself begins with the system launching the + * {@link android.app.BackupAgent} subclass declared in your manifest. See the * documentation for {@link android.app.BackupAgent} for a detailed description * of how the backup then proceeds. - * - * <p>STOPSHIP more documentation here! Include the attributes: - * android:backupAgent - * android:allowBackup - * android:restoreNeedsApplication - * android:killAfterRestore + * <p> + * A simple implementation of a BackupAgent useful for backing up Preferences + * and files is available by using {@link android.backup.BackupHelperAgent}. + * <p> + * STOPSHIP: more documentation! + * <p> + * <b>XML attributes</b> + * <p> + * See {@link android.R.styleable#AndroidManifestApplication + * AndroidManifest.xml's application attributes} + * + * @attr ref android.R.styleable#AndroidManifestApplication_allowBackup + * @attr ref android.R.styleable#AndroidManifestApplication_backupAgent + * @attr ref + * android.R.styleable#AndroidManifestApplication_restoreNeedsApplication + * @attr ref android.R.styleable#AndroidManifestApplication_killAfterRestore */ public class BackupManager { private static final String TAG = "BackupManager"; diff --git a/core/java/android/backup/FileBackupHelper.java b/core/java/android/backup/FileBackupHelper.java index 68b4d42..cc859e2 100644 --- a/core/java/android/backup/FileBackupHelper.java +++ b/core/java/android/backup/FileBackupHelper.java @@ -21,10 +21,23 @@ import android.os.ParcelFileDescriptor; import android.util.Log; import java.io.File; -import java.io.FileDescriptor; /** - * STOPSHIP: document! [manages backup of a set of files; restore is totally opaque] + * A helper class which can be used in conjunction with + * {@link android.backup.BackupHelperAgent} to manage the backup of a set of + * files. Whenever backup is performed, all files changed since the last backup + * will be saved in their entirety. During the first time the backup happens, + * all the files in the list will be backed up. Note that this should only be + * used with small configuration files and not with large binary files. + * <p> + * Any files not present in the list of files during the restore procedure will + * be ignored. If files present in a previous version of an application are + * removed in subsequent versions, it is the responsibility of the developer to + * design a mechanism to remove those files. Otherwise files no longer needed + * will linger and consume space on the device. + * <p> + * STOPSHIP: document! [manages backup of a set of files; restore is totally + * opaque] */ public class FileBackupHelper extends FileBackupHelperBase implements BackupHelper { private static final String TAG = "FileBackupHelper"; @@ -50,9 +63,16 @@ public class FileBackupHelper extends FileBackupHelperBase implements BackupHelp } /** - * Based on oldState, determine which of the files from the application's data directory - * need to be backed up, write them to the data stream, and fill in newState with the - * state as it exists now. + * Based on <code>oldState</code>, determine which of the files from the + * application's data directory need to be backed up, write them to the data + * stream, and fill in <code>newState</code> with the state as it exists + * now. When <code>oldState</code> is <code>null</code>, all the files will + * be backed up. + * <p> + * This should be called from {@link android.backup.BackupHelperAgent} + * directly. See + * {@link android.app.BackupAgent#onBackup(ParcelFileDescriptor, BackupDataOutput, ParcelFileDescriptor)} + * for a description of parameter meanings. */ public void performBackup(ParcelFileDescriptor oldState, BackupDataOutput data, ParcelFileDescriptor newState) { diff --git a/core/java/android/backup/FileBackupHelperBase.java b/core/java/android/backup/FileBackupHelperBase.java index a0ff38b..7cb1ccc 100644 --- a/core/java/android/backup/FileBackupHelperBase.java +++ b/core/java/android/backup/FileBackupHelperBase.java @@ -22,10 +22,12 @@ import android.util.Log; import java.io.File; import java.io.FileDescriptor; -import java.io.FileOutputStream; +/** + * Base class for the {@link android.backup.FileBackupHelper} implementation. + */ class FileBackupHelperBase { - private static final String TAG = "RestoreHelperBase"; + private static final String TAG = "FileBackupHelperBase"; int mPtr; Context mContext; @@ -45,8 +47,8 @@ class FileBackupHelperBase { } /** - * Check the parameters so the native code doens't have to throw all the exceptions - * since it's easier to do that from java. + * Check the parameters so the native code doesn't have to throw all the exceptions + * since it's easier to do that from Java. */ static void performBackup_checked(ParcelFileDescriptor oldState, BackupDataOutput data, ParcelFileDescriptor newState, String[] files, String[] keys) { diff --git a/core/java/android/backup/SharedPreferencesBackupHelper.java b/core/java/android/backup/SharedPreferencesBackupHelper.java index f9c97a3..7ba80db 100644 --- a/core/java/android/backup/SharedPreferencesBackupHelper.java +++ b/core/java/android/backup/SharedPreferencesBackupHelper.java @@ -17,13 +17,19 @@ package android.backup; import android.content.Context; +import android.content.SharedPreferences; import android.os.ParcelFileDescriptor; import android.util.Log; import java.io.File; -import java.io.FileDescriptor; /** + * A helper class which can be used in conjunction with + * {@link android.backup.BackupHelperAgent} to manage the backup of + * {@link android.content.SharedPreferences}. Whenever backup is performed it + * will back up all named shared preferences which have changed since the last + * backup. + * <p> * STOPSHIP: document! */ public class SharedPreferencesBackupHelper extends FileBackupHelperBase implements BackupHelper { diff --git a/core/java/android/backup/package.html b/core/java/android/backup/package.html new file mode 100644 index 0000000..13c0eb8 --- /dev/null +++ b/core/java/android/backup/package.html @@ -0,0 +1,22 @@ +<HTML> +<BODY> +<p>Package containing the backup and restore functionality available to +applications. All backup management is controlled through +{@link android.backup.BackupManager}. Individual backup functionality is +implemented through a subclass {@link android.app.BackupAgent} and a +simple and easy-to-use implementation is provided in +{@link android.backup.BackupHelperAgent}.</p> + +<p>STOPSHIP: add more documenation and remove Dev Guide link if not written!</p> + +<p>The backup APIs let applications:</p> +<ul> + <li>Perform backup of arbitrary data</li> + <li>Easily perform backup of Preferences and files</li> + <li>Handle restore of data</li> +</ul> + +<p>For a detailed guide to using the backup APIs, see the <a +href="{@docRoot}guide/topics/backup.html">Backup Dev Guide topic</a>.</p> +</BODY> +</HTML> diff --git a/core/java/android/bluetooth/BluetoothDevice.java b/core/java/android/bluetooth/BluetoothDevice.java index cf9c58f..e77e76f 100644 --- a/core/java/android/bluetooth/BluetoothDevice.java +++ b/core/java/android/bluetooth/BluetoothDevice.java @@ -664,6 +664,10 @@ public final class BluetoothDevice implements Parcelable { * determine which channel to connect to. * <p>The remote device will be authenticated and communication on this * socket will be encrypted. + * <p>Hint: If you are connecting to a Bluetooth serial board then try + * using the well-known SPP UUID 00001101-0000-1000-8000-00805F9B34FB. + * However if you are connecting to an Android peer then please generate + * your own unique UUID. * <p>Requires {@link android.Manifest.permission#BLUETOOTH} * * @param uuid service record uuid to lookup RFCOMM channel diff --git a/core/java/android/content/pm/IPackageManager.aidl b/core/java/android/content/pm/IPackageManager.aidl index f793a00..399a87d 100644 --- a/core/java/android/content/pm/IPackageManager.aidl +++ b/core/java/android/content/pm/IPackageManager.aidl @@ -309,7 +309,7 @@ interface IPackageManager { * MountService uses this to call into the package manager to update * status of sdcard. */ - void updateExternalMediaStatus(boolean mounted); + boolean updateExternalMediaStatus(boolean mounted); String nextPackageToClean(String lastPackage); diff --git a/core/java/android/content/res/AssetManager.java b/core/java/android/content/res/AssetManager.java index 7f9a5c6..1070f08 100644 --- a/core/java/android/content/res/AssetManager.java +++ b/core/java/android/content/res/AssetManager.java @@ -24,6 +24,7 @@ import android.util.TypedValue; import java.io.FileNotFoundException; import java.io.IOException; import java.io.InputStream; +import java.util.HashMap; /** * Provides access to an application's raw asset files; see {@link Resources} @@ -59,6 +60,8 @@ public final class AssetManager { private static final String TAG = "AssetManager"; private static final boolean localLOGV = Config.LOGV || false; + private static final boolean DEBUG_REFS = false; + private static final Object sSync = new Object(); private static AssetManager sSystem = null; @@ -72,6 +75,7 @@ public final class AssetManager { private int mNumRefs = 1; private boolean mOpen = true; + private HashMap<Integer, RuntimeException> mRefStacks; /** * Create a new AssetManager containing only the basic system assets. @@ -82,6 +86,10 @@ public final class AssetManager { */ public AssetManager() { synchronized (this) { + if (DEBUG_REFS) { + mNumRefs = 0; + incRefsLocked(this.hashCode()); + } init(); if (localLOGV) Log.v(TAG, "New asset manager: " + this); ensureSystemAssets(); @@ -99,6 +107,12 @@ public final class AssetManager { } private AssetManager(boolean isSystem) { + if (DEBUG_REFS) { + synchronized (this) { + mNumRefs = 0; + incRefsLocked(this.hashCode()); + } + } init(); if (localLOGV) Log.v(TAG, "New asset manager: " + this); } @@ -122,7 +136,7 @@ public final class AssetManager { // + ", released=" + mReleased); if (mOpen) { mOpen = false; - decRefsLocked(); + decRefsLocked(this.hashCode()); } } } @@ -298,8 +312,9 @@ public final class AssetManager { } int asset = openAsset(fileName, accessMode); if (asset != 0) { - mNumRefs++; - return new AssetInputStream(asset); + AssetInputStream res = new AssetInputStream(asset); + incRefsLocked(res.hashCode()); + return res; } } throw new FileNotFoundException("Asset file: " + fileName); @@ -389,8 +404,9 @@ public final class AssetManager { } int asset = openNonAssetNative(cookie, fileName, accessMode); if (asset != 0) { - mNumRefs++; - return new AssetInputStream(asset); + AssetInputStream res = new AssetInputStream(asset); + incRefsLocked(res.hashCode()); + return res; } } throw new FileNotFoundException("Asset absolute file: " + fileName); @@ -468,16 +484,17 @@ public final class AssetManager { } int xmlBlock = openXmlAssetNative(cookie, fileName); if (xmlBlock != 0) { - mNumRefs++; - return new XmlBlock(this, xmlBlock); + XmlBlock res = new XmlBlock(this, xmlBlock); + incRefsLocked(res.hashCode()); + return res; } } throw new FileNotFoundException("Asset XML file: " + fileName); } - /*package*/ void xmlBlockGone() { + /*package*/ void xmlBlockGone(int id) { synchronized (this) { - decRefsLocked(); + decRefsLocked(id); } } @@ -486,20 +503,34 @@ public final class AssetManager { if (!mOpen) { throw new RuntimeException("Assetmanager has been closed"); } - mNumRefs++; - return newTheme(); + int res = newTheme(); + incRefsLocked(res); + return res; } } /*package*/ final void releaseTheme(int theme) { synchronized (this) { deleteTheme(theme); - decRefsLocked(); + decRefsLocked(theme); } } protected void finalize() throws Throwable { - destroy(); + try { + if (DEBUG_REFS && mNumRefs != 0) { + Log.w(TAG, "AssetManager " + this + + " finalized with non-zero refs: " + mNumRefs); + if (mRefStacks != null) { + for (RuntimeException e : mRefStacks.values()) { + Log.w(TAG, "Reference from here", e); + } + } + } + destroy(); + } finally { + super.finalize(); + } } public final class AssetInputStream extends InputStream { @@ -526,7 +557,7 @@ public final class AssetManager { if (mAsset != 0) { destroyAsset(mAsset); mAsset = 0; - decRefsLocked(); + decRefsLocked(hashCode()); } } } @@ -710,7 +741,22 @@ public final class AssetManager { private native final void init(); private native final void destroy(); - private final void decRefsLocked() { + private final void incRefsLocked(int id) { + if (DEBUG_REFS) { + if (mRefStacks == null) { + mRefStacks = new HashMap<Integer, RuntimeException>(); + RuntimeException ex = new RuntimeException(); + ex.fillInStackTrace(); + mRefStacks.put(this.hashCode(), ex); + } + } + mNumRefs++; + } + + private final void decRefsLocked(int id) { + if (DEBUG_REFS && mRefStacks != null) { + mRefStacks.remove(id); + } mNumRefs--; //System.out.println("Dec streams: mNumRefs=" + mNumRefs // + " mReleased=" + mReleased); diff --git a/core/java/android/content/res/XmlBlock.java b/core/java/android/content/res/XmlBlock.java index f800232..3c2c30a 100644 --- a/core/java/android/content/res/XmlBlock.java +++ b/core/java/android/content/res/XmlBlock.java @@ -59,7 +59,7 @@ final class XmlBlock { if (mOpenCount == 0) { nativeDestroy(mNative); if (mAssets != null) { - mAssets.xmlBlockGone(); + mAssets.xmlBlockGone(hashCode()); } } } diff --git a/core/java/android/text/Layout.java b/core/java/android/text/Layout.java index 1023036..38ac9b7 100644 --- a/core/java/android/text/Layout.java +++ b/core/java/android/text/Layout.java @@ -1936,6 +1936,11 @@ public abstract class Layout { public static final int DIR_LEFT_TO_RIGHT = 1; public static final int DIR_RIGHT_TO_LEFT = -1; + + /* package */ static final int DIR_REQUEST_LTR = 1; + /* package */ static final int DIR_REQUEST_RTL = -1; + /* package */ static final int DIR_REQUEST_DEFAULT_LTR = 2; + /* package */ static final int DIR_REQUEST_DEFAULT_RTL = -2; public enum Alignment { ALIGN_NORMAL, diff --git a/core/java/android/text/StaticLayout.java b/core/java/android/text/StaticLayout.java index 6de9c65..600ec7e 100644 --- a/core/java/android/text/StaticLayout.java +++ b/core/java/android/text/StaticLayout.java @@ -234,215 +234,9 @@ extends Layout } if (!easy) { - AndroidCharacter.getDirectionalities(chs, chdirs, end - start); - - /* - * Determine primary paragraph direction - */ - - for (int j = start; j < end; j++) { - int d = chdirs[j - start]; - - if (d == Character.DIRECTIONALITY_LEFT_TO_RIGHT) { - dir = DIR_LEFT_TO_RIGHT; - break; - } - if (d == Character.DIRECTIONALITY_RIGHT_TO_LEFT) { - dir = DIR_RIGHT_TO_LEFT; - break; - } - } - - /* - * XXX Explicit overrides should go here - */ - - /* - * Weak type resolution - */ - - final byte SOR = dir == DIR_LEFT_TO_RIGHT ? - Character.DIRECTIONALITY_LEFT_TO_RIGHT : - Character.DIRECTIONALITY_RIGHT_TO_LEFT; - - // dump(chdirs, n, "initial"); - - // W1 non spacing marks - for (int j = 0; j < n; j++) { - if (chdirs[j] == Character.NON_SPACING_MARK) { - if (j == 0) - chdirs[j] = SOR; - else - chdirs[j] = chdirs[j - 1]; - } - } - - // dump(chdirs, n, "W1"); - - // W2 european numbers - byte cur = SOR; - for (int j = 0; j < n; j++) { - byte d = chdirs[j]; - - if (d == Character.DIRECTIONALITY_LEFT_TO_RIGHT || - d == Character.DIRECTIONALITY_RIGHT_TO_LEFT || - d == Character.DIRECTIONALITY_RIGHT_TO_LEFT_ARABIC) - cur = d; - else if (d == Character.DIRECTIONALITY_EUROPEAN_NUMBER) { - if (cur == - Character.DIRECTIONALITY_RIGHT_TO_LEFT_ARABIC) - chdirs[j] = Character.DIRECTIONALITY_ARABIC_NUMBER; - } - } - - // dump(chdirs, n, "W2"); - - // W3 arabic letters - for (int j = 0; j < n; j++) { - if (chdirs[j] == Character.DIRECTIONALITY_RIGHT_TO_LEFT_ARABIC) - chdirs[j] = Character.DIRECTIONALITY_RIGHT_TO_LEFT; - } - - // dump(chdirs, n, "W3"); - - // W4 single separator between numbers - for (int j = 1; j < n - 1; j++) { - byte d = chdirs[j]; - byte prev = chdirs[j - 1]; - byte next = chdirs[j + 1]; - - if (d == Character.DIRECTIONALITY_EUROPEAN_NUMBER_SEPARATOR) { - if (prev == Character.DIRECTIONALITY_EUROPEAN_NUMBER && - next == Character.DIRECTIONALITY_EUROPEAN_NUMBER) - chdirs[j] = Character.DIRECTIONALITY_EUROPEAN_NUMBER; - } else if (d == Character.DIRECTIONALITY_COMMON_NUMBER_SEPARATOR) { - if (prev == Character.DIRECTIONALITY_EUROPEAN_NUMBER && - next == Character.DIRECTIONALITY_EUROPEAN_NUMBER) - chdirs[j] = Character.DIRECTIONALITY_EUROPEAN_NUMBER; - if (prev == Character.DIRECTIONALITY_ARABIC_NUMBER && - next == Character.DIRECTIONALITY_ARABIC_NUMBER) - chdirs[j] = Character.DIRECTIONALITY_ARABIC_NUMBER; - } - } - - // dump(chdirs, n, "W4"); - - // W5 european number terminators - boolean adjacent = false; - for (int j = 0; j < n; j++) { - byte d = chdirs[j]; - - if (d == Character.DIRECTIONALITY_EUROPEAN_NUMBER) - adjacent = true; - else if (d == Character.DIRECTIONALITY_EUROPEAN_NUMBER_TERMINATOR && adjacent) - chdirs[j] = Character.DIRECTIONALITY_EUROPEAN_NUMBER; - else - adjacent = false; - } - - //dump(chdirs, n, "W5"); - - // W5 european number terminators part 2, - // W6 separators and terminators - adjacent = false; - for (int j = n - 1; j >= 0; j--) { - byte d = chdirs[j]; - - if (d == Character.DIRECTIONALITY_EUROPEAN_NUMBER) - adjacent = true; - else if (d == Character.DIRECTIONALITY_EUROPEAN_NUMBER_TERMINATOR) { - if (adjacent) - chdirs[j] = Character.DIRECTIONALITY_EUROPEAN_NUMBER; - else - chdirs[j] = Character.DIRECTIONALITY_OTHER_NEUTRALS; - } - else { - adjacent = false; - - if (d == Character.DIRECTIONALITY_EUROPEAN_NUMBER_SEPARATOR || - d == Character.DIRECTIONALITY_COMMON_NUMBER_SEPARATOR || - d == Character.DIRECTIONALITY_PARAGRAPH_SEPARATOR || - d == Character.DIRECTIONALITY_SEGMENT_SEPARATOR) - chdirs[j] = Character.DIRECTIONALITY_OTHER_NEUTRALS; - } - } - - // dump(chdirs, n, "W6"); - - // W7 strong direction of european numbers - cur = SOR; - for (int j = 0; j < n; j++) { - byte d = chdirs[j]; - - if (d == SOR || - d == Character.DIRECTIONALITY_LEFT_TO_RIGHT || - d == Character.DIRECTIONALITY_RIGHT_TO_LEFT) - cur = d; - - if (d == Character.DIRECTIONALITY_EUROPEAN_NUMBER) - chdirs[j] = cur; - } - - // dump(chdirs, n, "W7"); - - // N1, N2 neutrals - cur = SOR; - for (int j = 0; j < n; j++) { - byte d = chdirs[j]; - - if (d == Character.DIRECTIONALITY_LEFT_TO_RIGHT || - d == Character.DIRECTIONALITY_RIGHT_TO_LEFT) { - cur = d; - } else if (d == Character.DIRECTIONALITY_EUROPEAN_NUMBER || - d == Character.DIRECTIONALITY_ARABIC_NUMBER) { - cur = Character.DIRECTIONALITY_RIGHT_TO_LEFT; - } else { - byte dd = SOR; - int k; - - for (k = j + 1; k < n; k++) { - dd = chdirs[k]; - - if (dd == Character.DIRECTIONALITY_LEFT_TO_RIGHT || - dd == Character.DIRECTIONALITY_RIGHT_TO_LEFT) { - break; - } - if (dd == Character.DIRECTIONALITY_EUROPEAN_NUMBER || - dd == Character.DIRECTIONALITY_ARABIC_NUMBER) { - dd = Character.DIRECTIONALITY_RIGHT_TO_LEFT; - break; - } - } - - for (int y = j; y < k; y++) { - if (dd == cur) - chdirs[y] = cur; - else - chdirs[y] = SOR; - } - - j = k - 1; - } - } - - // dump(chdirs, n, "final"); - - // extra: enforce that all tabs and surrogate characters go the - // primary direction - // TODO: actually do directions right for surrogates - - for (int j = 0; j < n; j++) { - char c = chs[j]; - - if (c == '\t' || (c >= 0xD800 && c <= 0xDFFF)) { - chdirs[j] = SOR; - } - } - - // extra: enforce that object replacements go to the - // primary direction - // and that none of the underlying characters are treated - // as viable breakpoints + // Ensure that none of the underlying characters are treated + // as viable breakpoints, and that the entire run gets the + // same bidi direction. if (source instanceof Spanned) { Spanned sp = (Spanned) source; @@ -453,12 +247,14 @@ extends Layout int b = sp.getSpanEnd(spans[y]); for (int x = a; x < b; x++) { - chdirs[x - start] = SOR; chs[x - start] = '\uFFFC'; } } } + // XXX put override flags, etc. into chdirs + dir = bidi(dir, chs, chdirs, n, false); + // Do mirroring for right-to-left segments for (int i = 0; i < n; i++) { @@ -810,6 +606,239 @@ extends Layout } } + /** + * Runs the unicode bidi algorithm on the first n chars in chs, returning + * the char dirs in chInfo and the base line direction of the first + * paragraph. + * + * XXX change result from dirs to levels + * + * @param dir the direction flag, either DIR_REQUEST_LTR, + * DIR_REQUEST_RTL, DIR_REQUEST_DEFAULT_LTR, or DIR_REQUEST_DEFAULT_RTL. + * @param chs the text to examine + * @param chInfo on input, if hasInfo is true, override and other flags + * representing out-of-band embedding information. On output, the generated + * dirs of the text. + * @param n the length of the text/information in chs and chInfo + * @param hasInfo true if chInfo has input information, otherwise the + * input data in chInfo is ignored. + * @return the resolved direction level of the first paragraph, either + * DIR_LEFT_TO_RIGHT or DIR_RIGHT_TO_LEFT. + */ + /* package */ static int bidi(int dir, char[] chs, byte[] chInfo, int n, + boolean hasInfo) { + + AndroidCharacter.getDirectionalities(chs, chInfo, n); + + /* + * Determine primary paragraph direction if not specified + */ + if (dir != DIR_REQUEST_LTR && dir != DIR_REQUEST_RTL) { + // set up default + dir = dir >= 0 ? DIR_LEFT_TO_RIGHT : DIR_RIGHT_TO_LEFT; + for (int j = 0; j < n; j++) { + int d = chInfo[j]; + + if (d == Character.DIRECTIONALITY_LEFT_TO_RIGHT) { + dir = DIR_LEFT_TO_RIGHT; + break; + } + if (d == Character.DIRECTIONALITY_RIGHT_TO_LEFT) { + dir = DIR_RIGHT_TO_LEFT; + break; + } + } + } + + final byte SOR = dir == DIR_LEFT_TO_RIGHT ? + Character.DIRECTIONALITY_LEFT_TO_RIGHT : + Character.DIRECTIONALITY_RIGHT_TO_LEFT; + + /* + * XXX Explicit overrides should go here + */ + + /* + * Weak type resolution + */ + + // dump(chdirs, n, "initial"); + + // W1 non spacing marks + for (int j = 0; j < n; j++) { + if (chInfo[j] == Character.NON_SPACING_MARK) { + if (j == 0) + chInfo[j] = SOR; + else + chInfo[j] = chInfo[j - 1]; + } + } + + // dump(chdirs, n, "W1"); + + // W2 european numbers + byte cur = SOR; + for (int j = 0; j < n; j++) { + byte d = chInfo[j]; + + if (d == Character.DIRECTIONALITY_LEFT_TO_RIGHT || + d == Character.DIRECTIONALITY_RIGHT_TO_LEFT || + d == Character.DIRECTIONALITY_RIGHT_TO_LEFT_ARABIC) + cur = d; + else if (d == Character.DIRECTIONALITY_EUROPEAN_NUMBER) { + if (cur == + Character.DIRECTIONALITY_RIGHT_TO_LEFT_ARABIC) + chInfo[j] = Character.DIRECTIONALITY_ARABIC_NUMBER; + } + } + + // dump(chdirs, n, "W2"); + + // W3 arabic letters + for (int j = 0; j < n; j++) { + if (chInfo[j] == Character.DIRECTIONALITY_RIGHT_TO_LEFT_ARABIC) + chInfo[j] = Character.DIRECTIONALITY_RIGHT_TO_LEFT; + } + + // dump(chdirs, n, "W3"); + + // W4 single separator between numbers + for (int j = 1; j < n - 1; j++) { + byte d = chInfo[j]; + byte prev = chInfo[j - 1]; + byte next = chInfo[j + 1]; + + if (d == Character.DIRECTIONALITY_EUROPEAN_NUMBER_SEPARATOR) { + if (prev == Character.DIRECTIONALITY_EUROPEAN_NUMBER && + next == Character.DIRECTIONALITY_EUROPEAN_NUMBER) + chInfo[j] = Character.DIRECTIONALITY_EUROPEAN_NUMBER; + } else if (d == Character.DIRECTIONALITY_COMMON_NUMBER_SEPARATOR) { + if (prev == Character.DIRECTIONALITY_EUROPEAN_NUMBER && + next == Character.DIRECTIONALITY_EUROPEAN_NUMBER) + chInfo[j] = Character.DIRECTIONALITY_EUROPEAN_NUMBER; + if (prev == Character.DIRECTIONALITY_ARABIC_NUMBER && + next == Character.DIRECTIONALITY_ARABIC_NUMBER) + chInfo[j] = Character.DIRECTIONALITY_ARABIC_NUMBER; + } + } + + // dump(chdirs, n, "W4"); + + // W5 european number terminators + boolean adjacent = false; + for (int j = 0; j < n; j++) { + byte d = chInfo[j]; + + if (d == Character.DIRECTIONALITY_EUROPEAN_NUMBER) + adjacent = true; + else if (d == Character.DIRECTIONALITY_EUROPEAN_NUMBER_TERMINATOR && adjacent) + chInfo[j] = Character.DIRECTIONALITY_EUROPEAN_NUMBER; + else + adjacent = false; + } + + //dump(chdirs, n, "W5"); + + // W5 european number terminators part 2, + // W6 separators and terminators + adjacent = false; + for (int j = n - 1; j >= 0; j--) { + byte d = chInfo[j]; + + if (d == Character.DIRECTIONALITY_EUROPEAN_NUMBER) + adjacent = true; + else if (d == Character.DIRECTIONALITY_EUROPEAN_NUMBER_TERMINATOR) { + if (adjacent) + chInfo[j] = Character.DIRECTIONALITY_EUROPEAN_NUMBER; + else + chInfo[j] = Character.DIRECTIONALITY_OTHER_NEUTRALS; + } + else { + adjacent = false; + + if (d == Character.DIRECTIONALITY_EUROPEAN_NUMBER_SEPARATOR || + d == Character.DIRECTIONALITY_COMMON_NUMBER_SEPARATOR || + d == Character.DIRECTIONALITY_PARAGRAPH_SEPARATOR || + d == Character.DIRECTIONALITY_SEGMENT_SEPARATOR) + chInfo[j] = Character.DIRECTIONALITY_OTHER_NEUTRALS; + } + } + + // dump(chdirs, n, "W6"); + + // W7 strong direction of european numbers + cur = SOR; + for (int j = 0; j < n; j++) { + byte d = chInfo[j]; + + if (d == SOR || + d == Character.DIRECTIONALITY_LEFT_TO_RIGHT || + d == Character.DIRECTIONALITY_RIGHT_TO_LEFT) + cur = d; + + if (d == Character.DIRECTIONALITY_EUROPEAN_NUMBER) + chInfo[j] = cur; + } + + // dump(chdirs, n, "W7"); + + // N1, N2 neutrals + cur = SOR; + for (int j = 0; j < n; j++) { + byte d = chInfo[j]; + + if (d == Character.DIRECTIONALITY_LEFT_TO_RIGHT || + d == Character.DIRECTIONALITY_RIGHT_TO_LEFT) { + cur = d; + } else if (d == Character.DIRECTIONALITY_EUROPEAN_NUMBER || + d == Character.DIRECTIONALITY_ARABIC_NUMBER) { + cur = Character.DIRECTIONALITY_RIGHT_TO_LEFT; + } else { + byte dd = SOR; + int k; + + for (k = j + 1; k < n; k++) { + dd = chInfo[k]; + + if (dd == Character.DIRECTIONALITY_LEFT_TO_RIGHT || + dd == Character.DIRECTIONALITY_RIGHT_TO_LEFT) { + break; + } + if (dd == Character.DIRECTIONALITY_EUROPEAN_NUMBER || + dd == Character.DIRECTIONALITY_ARABIC_NUMBER) { + dd = Character.DIRECTIONALITY_RIGHT_TO_LEFT; + break; + } + } + + for (int y = j; y < k; y++) { + if (dd == cur) + chInfo[y] = cur; + else + chInfo[y] = SOR; + } + + j = k - 1; + } + } + + // dump(chdirs, n, "final"); + + // extra: enforce that all tabs and surrogate characters go the + // primary direction + // TODO: actually do directions right for surrogates + + for (int j = 0; j < n; j++) { + char c = chs[j]; + + if (c == '\t' || (c >= 0xD800 && c <= 0xDFFF)) { + chInfo[j] = SOR; + } + } + + return dir; + } + private static final char FIRST_CJK = '\u2E80'; /** * Returns true if the specified character is one of those specified diff --git a/core/java/android/webkit/WebView.java b/core/java/android/webkit/WebView.java index adae0cb..067241a 100644 --- a/core/java/android/webkit/WebView.java +++ b/core/java/android/webkit/WebView.java @@ -3351,23 +3351,31 @@ public class WebView extends AbsoluteLayout InputMethodManager imm = (InputMethodManager) getContext().getSystemService(Context.INPUT_METHOD_SERVICE); + // bring it back to the default scale so that user can enter text + boolean zoom = mActualScale < mDefaultScale; + if (zoom) { + mInZoomOverview = false; + mZoomCenterX = mLastTouchX; + mZoomCenterY = mLastTouchY; + // do not change text wrap scale so that there is no reflow + setNewZoomScale(mDefaultScale, false, false); + } if (isTextView) { rebuildWebTextView(); - if (!inEditingMode()) return; - imm.showSoftInput(mWebTextView, 0); - // bring it back to the default scale so that user can enter text - if (mActualScale < mDefaultScale) { - mInZoomOverview = false; - mZoomCenterX = mLastTouchX; - mZoomCenterY = mLastTouchY; - // do not change text wrap scale so that there is no reflow - setNewZoomScale(mDefaultScale, false, false); - didUpdateTextViewBounds(true); + if (inEditingMode()) { + imm.showSoftInput(mWebTextView, 0); + if (zoom) { + didUpdateTextViewBounds(true); + } + return; } } - else { // used by plugins - imm.showSoftInput(this, 0); - } + // Used by plugins. + // Also used if the navigation cache is out of date, and + // does not recognize that a textfield is in focus. In that + // case, use WebView as the targeted view. + // see http://b/issue?id=2457459 + imm.showSoftInput(this, 0); } // Called by WebKit to instruct the UI to hide the keyboard diff --git a/core/java/android/widget/CheckedTextView.java b/core/java/android/widget/CheckedTextView.java index aa9062b..bf63607 100644 --- a/core/java/android/widget/CheckedTextView.java +++ b/core/java/android/widget/CheckedTextView.java @@ -24,6 +24,7 @@ import android.graphics.Canvas; import android.graphics.drawable.Drawable; import android.util.AttributeSet; import android.view.Gravity; +import android.view.ViewDebug; import android.view.accessibility.AccessibilityEvent; @@ -73,7 +74,8 @@ public class CheckedTextView extends TextView implements Checkable { public void toggle() { setChecked(!mChecked); } - + + @ViewDebug.ExportedProperty public boolean isChecked() { return mChecked; } diff --git a/core/java/android/widget/CompoundButton.java b/core/java/android/widget/CompoundButton.java index 98b0976..bf02ad3 100644 --- a/core/java/android/widget/CompoundButton.java +++ b/core/java/android/widget/CompoundButton.java @@ -26,6 +26,7 @@ import android.os.Parcel; import android.os.Parcelable; import android.util.AttributeSet; import android.view.Gravity; +import android.view.ViewDebug; import android.view.accessibility.AccessibilityEvent; /** @@ -98,6 +99,7 @@ public abstract class CompoundButton extends Button implements Checkable { return super.performClick(); } + @ViewDebug.ExportedProperty public boolean isChecked() { return mChecked; } diff --git a/core/java/android/widget/ProgressBar.java b/core/java/android/widget/ProgressBar.java index 1dcb203..6dc9f78 100644 --- a/core/java/android/widget/ProgressBar.java +++ b/core/java/android/widget/ProgressBar.java @@ -36,6 +36,7 @@ import android.graphics.drawable.shapes.Shape; import android.util.AttributeSet; import android.view.Gravity; import android.view.View; +import android.view.ViewDebug; import android.view.animation.AlphaAnimation; import android.view.animation.Animation; import android.view.animation.AnimationUtils; @@ -335,6 +336,7 @@ public class ProgressBar extends View { * * @return true if the progress bar is in indeterminate mode */ + @ViewDebug.ExportedProperty public synchronized boolean isIndeterminate() { return mIndeterminate; } @@ -607,6 +609,7 @@ public class ProgressBar extends View { * @see #setMax(int) * @see #getMax() */ + @ViewDebug.ExportedProperty public synchronized int getProgress() { return mIndeterminate ? 0 : mProgress; } @@ -623,6 +626,7 @@ public class ProgressBar extends View { * @see #setMax(int) * @see #getMax() */ + @ViewDebug.ExportedProperty public synchronized int getSecondaryProgress() { return mIndeterminate ? 0 : mSecondaryProgress; } @@ -636,6 +640,7 @@ public class ProgressBar extends View { * @see #getProgress() * @see #getSecondaryProgress() */ + @ViewDebug.ExportedProperty public synchronized int getMax() { return mMax; } diff --git a/core/java/android/widget/TextView.java b/core/java/android/widget/TextView.java index 7ba0fa1..cea6d3b 100644 --- a/core/java/android/widget/TextView.java +++ b/core/java/android/widget/TextView.java @@ -5731,6 +5731,7 @@ public class TextView extends View implements ViewTreeObserver.OnPreDrawListener /** * Convenience for {@link Selection#getSelectionStart}. */ + @ViewDebug.ExportedProperty public int getSelectionStart() { return Selection.getSelectionStart(getText()); } @@ -5738,6 +5739,7 @@ public class TextView extends View implements ViewTreeObserver.OnPreDrawListener /** * Convenience for {@link Selection#getSelectionEnd}. */ + @ViewDebug.ExportedProperty public int getSelectionEnd() { return Selection.getSelectionEnd(getText()); } diff --git a/core/java/com/android/internal/content/PackageHelper.java b/core/java/com/android/internal/content/PackageHelper.java index bc7dbf4..c5db83f 100644 --- a/core/java/com/android/internal/content/PackageHelper.java +++ b/core/java/com/android/internal/content/PackageHelper.java @@ -36,7 +36,7 @@ public class PackageHelper { 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 boolean localLOGV = true; private static final String TAG = "PackageHelper"; public static IMountService getMountService() { @@ -58,7 +58,7 @@ public class PackageHelper { if ((len - (mbLen * 1024 * 1024)) > 0) { mbLen++; } - if (DEBUG_SD_INSTALL) Log.i(TAG, "Size of resource " + mbLen); + if (localLOGV) Log.i(TAG, "Size of resource " + mbLen); try { int rc = mountService.createSecureContainer( @@ -68,7 +68,7 @@ public class PackageHelper { return null; } String cachePath = mountService.getSecureContainerPath(cid); - if (DEBUG_SD_INSTALL) Log.i(TAG, "Created secure container " + cid + + if (localLOGV) Log.i(TAG, "Created secure container " + cid + " at " + cachePath); return cachePath; } catch (RemoteException e) { @@ -93,7 +93,7 @@ public class PackageHelper { public static boolean unMountSdDir(String cid) { try { - int rc = getMountService().unmountSecureContainer(cid, false); + int rc = getMountService().unmountSecureContainer(cid, true); if (rc != StorageResultCode.OperationSucceeded) { Log.e(TAG, "Failed to unmount " + cid + " with rc " + rc); return false; @@ -148,7 +148,8 @@ public class PackageHelper { public static boolean destroySdDir(String cid) { try { - int rc = getMountService().destroySecureContainer(cid, false); + if (localLOGV) Log.i(TAG, "Forcibly destroying container " + cid); + int rc = getMountService().destroySecureContainer(cid, true); if (rc != StorageResultCode.OperationSucceeded) { Log.i(TAG, "Failed to destroy container " + cid); return false; diff --git a/core/res/res/values/attrs_manifest.xml b/core/res/res/values/attrs_manifest.xml index 33b509b..430c4b8 100644 --- a/core/res/res/values/attrs_manifest.xml +++ b/core/res/res/values/attrs_manifest.xml @@ -592,7 +592,9 @@ backup and restore of the application's settings to external storage. --> <attr name="backupAgent" format="string" /> - <!-- This is not the attribute you are looking for. --> + <!-- Whether to allow the application to participate in backup + infrastructure. + STOPSHIP: more explanation --> <attr name="allowBackup" format="boolean" /> <!-- Whether the application in question should be terminated after its diff --git a/core/tests/coretests/src/android/text/StaticLayoutBidiTest.java b/core/tests/coretests/src/android/text/StaticLayoutBidiTest.java new file mode 100644 index 0000000..ccd0dae --- /dev/null +++ b/core/tests/coretests/src/android/text/StaticLayoutBidiTest.java @@ -0,0 +1,106 @@ +/* + * Copyright (C) 2010 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.text; + +import android.test.suitebuilder.annotation.SmallTest; +import android.util.Log; + +import junit.framework.TestCase; + +/** + * Tests StaticLayout bidi implementation. + */ +public class StaticLayoutBidiTest extends TestCase { + + public static final int REQ_DL = 2; // Layout.DIR_REQUEST_DEFAULT_LTR; + public static final int REQ_DR = -2; // Layout.DIR_REQUEST_DEFAULT_RTL; + public static final int REQ_L = 1; // Layout.DIR_REQUEST_LTR; + public static final int REQ_R = -1; // Layout.DIR_REQUEST_RTL; + public static final int L = Layout.DIR_LEFT_TO_RIGHT; + public static final int R = Layout.DIR_RIGHT_TO_LEFT; + + public static final String SP = " "; + public static final String ALEF = "\u05d0"; + public static final String BET = "\u05d1"; + public static final String GIMEL = "\u05d2"; + public static final String DALET = "\u05d3"; + + @SmallTest + public void testAllLtr() { + expectBidi(REQ_DL, "a test", "000000", L); + } + + @SmallTest + public void testLtrRtl() { + expectBidi(REQ_DL, "abc " + ALEF + BET + GIMEL, "0000111", L); + } + + @SmallTest + public void testAllRtl() { + expectBidi(REQ_DL, ALEF + SP + ALEF + BET + GIMEL + DALET, "111111", R); + } + + @SmallTest + public void testRtlLtr() { + expectBidi(REQ_DL, ALEF + BET + GIMEL + " abc", "1111000", R); + } + + @SmallTest + public void testRAllLtr() { + expectBidi(REQ_R, "a test", "000000", R); + } + + @SmallTest + public void testRLtrRtl() { + expectBidi(REQ_R, "abc " + ALEF + BET + GIMEL, "0001111", R); + } + + @SmallTest + public void testLAllRtl() { + expectBidi(REQ_L, ALEF + SP + ALEF + BET + GIMEL + DALET, "111111", L); + } + + @SmallTest + public void testLRtlLtr() { + expectBidi(REQ_L, ALEF + BET + GIMEL + " abc", "1110000", L); + } + + private void expectBidi(int dir, String text, + String expectedLevels, int expectedDir) { + char[] chs = text.toCharArray(); + int n = chs.length; + byte[] chInfo = new byte[n]; + + int resultDir = StaticLayout.bidi(dir, chs, chInfo, n, false); + + { + StringBuilder sb = new StringBuilder("xdirs:"); + for (int i = 0; i < n; ++i) { + sb.append(" ").append(String.valueOf(chInfo[i])); + } + Log.i("BIDI", sb.toString()); + } + + char[] resultLevelChars = new char[n]; + for (int i = 0; i < n; ++i) { + resultLevelChars[i] = (char)('0' + chInfo[i]); + } + String resultLevels = new String(resultLevelChars); + assertEquals("direction", expectedDir, resultDir); + assertEquals("levels", expectedLevels, resultLevels); + } +} diff --git a/location/java/com/android/internal/location/GpsLocationProvider.java b/location/java/com/android/internal/location/GpsLocationProvider.java index 8e84106..90b50cc 100755 --- a/location/java/com/android/internal/location/GpsLocationProvider.java +++ b/location/java/com/android/internal/location/GpsLocationProvider.java @@ -321,7 +321,9 @@ public class GpsLocationProvider implements LocationProviderInterface { public GpsLocationProvider(Context context, ILocationManager locationManager) { mContext = context; mLocationManager = locationManager; - mNIHandler= new GpsNetInitiatedHandler(context, this); + mNIHandler = new GpsNetInitiatedHandler(context, this); + + mLocation.setExtras(mLocationExtras); // Create a wake lock PowerManager powerManager = (PowerManager) mContext.getSystemService(Context.POWER_SERVICE); @@ -759,7 +761,7 @@ public class GpsLocationProvider implements LocationProviderInterface { positionMode = GPS_POSITION_MODE_STANDALONE; } - if (!native_start(positionMode, false, mFixInterval)) { + if (!native_start(positionMode, false, 1)) { mStarted = false; Log.e(TAG, "native_start failed in startNavigating()"); return; @@ -870,7 +872,12 @@ public class GpsLocationProvider implements LocationProviderInterface { } if (mStarted && mStatus != LocationProvider.AVAILABLE) { - mAlarmManager.cancel(mTimeoutIntent); + // we still want to time out if we do not receive MIN_FIX_COUNT + // within the time out and we are requesting infrequent fixes + if (mFixInterval < NO_FIX_TIMEOUT) { + mAlarmManager.cancel(mTimeoutIntent); + } + // send an intent to notify that the GPS is receiving fixes. Intent intent = new Intent(GPS_FIX_CHANGE_ACTION); intent.putExtra(EXTRA_ENABLED, true); diff --git a/packages/DefaultContainerService/src/com/android/defcontainer/DefaultContainerService.java b/packages/DefaultContainerService/src/com/android/defcontainer/DefaultContainerService.java index 95ab684..a79f0cd 100644 --- a/packages/DefaultContainerService/src/com/android/defcontainer/DefaultContainerService.java +++ b/packages/DefaultContainerService/src/com/android/defcontainer/DefaultContainerService.java @@ -39,7 +39,7 @@ import android.provider.Settings; */ public class DefaultContainerService extends IntentService { private static final String TAG = "DefContainer"; - private static final boolean localLOGV = false; + private static final boolean localLOGV = true; private IMediaContainerService.Stub mBinder = new IMediaContainerService.Stub() { /* @@ -211,6 +211,8 @@ public class DefaultContainerService extends IntentService { if (PackageHelper.isContainerMounted(newCid)) { if (localLOGV) Log.i(TAG, "Unmounting " + newCid + " at path " + newCachePath + " after " + errMsg); + // Force a gc to avoid being killed. + Runtime.getRuntime().gc(); PackageHelper.unMountSdDir(newCid); } else { if (localLOGV) Log.i(TAG, "Container " + newCid + " not mounted"); @@ -328,8 +330,8 @@ public class DefaultContainerService extends IntentService { boolean auto = true; // To make final copy long reqInstallSize = pkgLen; - // For dex files - long reqInternalSize = 1 * pkgLen; + // For dex files. Just ignore and fail when extracting. Max limit of 2Gig for now. + long reqInternalSize = 0; boolean intThresholdOk = (pctNandFree >= LOW_NAND_FLASH_TRESHOLD); boolean intAvailOk = ((reqInstallSize + reqInternalSize) < availInternalFlashSize); boolean fitsOnSd = (reqInstallSize < availSDSize) && intThresholdOk && diff --git a/services/java/com/android/server/PackageManagerService.java b/services/java/com/android/server/PackageManagerService.java index 4a7fc9c..9e0d623 100644 --- a/services/java/com/android/server/PackageManagerService.java +++ b/services/java/com/android/server/PackageManagerService.java @@ -4858,42 +4858,67 @@ class PackageManagerService extends IPackageManager.Stub { String oldCodePath) { String newCacheId = getNextCodePath(oldCodePath, pkgName, "/" + RES_FILE_NAME); String newCachePath = null; - final int RENAME_FAILED = 1; - final int MOUNT_FAILED = 2; - final int PASS = 4; - int errCode = RENAME_FAILED; - String errMsg = "RENAME_FAILED"; - boolean mounted = PackageHelper.isContainerMounted(cid); - if (mounted) { - // Unmount the container - if (!PackageHelper.unMountSdDir(cid)) { - Log.i(TAG, "Failed to unmount " + cid + " before renaming"); + boolean enableRename = false; + if (enableRename) { + if (PackageHelper.isContainerMounted(cid)) { + // Unmount the container + if (!PackageHelper.unMountSdDir(cid)) { + Log.i(TAG, "Failed to unmount " + cid + " before renaming"); + return false; + } + } + if (!PackageHelper.renameSdDir(cid, newCacheId)) { + Log.e(TAG, "Failed to rename " + cid + " to " + newCacheId); return false; } - mounted = false; - } - if (PackageHelper.renameSdDir(cid, newCacheId)) { - errCode = MOUNT_FAILED; - errMsg = "MOUNT_FAILED"; - if ((newCachePath = PackageHelper.mountSdDir(newCacheId, - getEncryptKey(), Process.SYSTEM_UID)) != null) { - errCode = PASS; - errMsg = "PASS"; + if (!PackageHelper.isContainerMounted(newCacheId)) { + Log.w(TAG, "Mounting container " + newCacheId); + newCachePath = PackageHelper.mountSdDir(newCacheId, + getEncryptKey(), Process.SYSTEM_UID); + } else { + newCachePath = PackageHelper.getSdDir(newCacheId); + } + if (newCachePath == null) { + Log.w(TAG, "Failed to get cache path for " + newCacheId); + return false; } - } - if (errCode != PASS) { - Log.i(TAG, "Failed to rename " + cid + " to " + newCacheId + - " at path: " + cachePath + " to new path: " + newCachePath + - "err = " + errMsg); // Mount old container? - return false; + Log.i(TAG, "Succesfully renamed " + cid + + " at path: " + cachePath + " to " + newCacheId + + " at new path: " + newCachePath); + cid = newCacheId; + cachePath = newCachePath; + return true; } else { - Log.i(TAG, "Succesfully renamed " + cid + " to " + newCacheId + - " at path: " + cachePath + " to new path: " + newCachePath); + // STOPSHIP work around for rename + Log.i(TAG, "Copying instead of renaming"); + File srcFile = new File(getCodePath()); + // Create new container + newCachePath = PackageHelper.createSdDir(srcFile, newCacheId, + getEncryptKey(), Process.SYSTEM_UID); + Log.i(TAG, "Created rename container " + newCacheId); + File destFile = new File(newCachePath + "/" + RES_FILE_NAME); + if (!FileUtils.copyFile(srcFile, destFile)) { + Log.e(TAG, "Failed to copy " + srcFile + " to " + destFile); + return false; + } + Log.i(TAG, "Successfully copied resource to " + newCachePath); + if (!PackageHelper.finalizeSdDir(newCacheId)) { + Log.e(TAG, "Failed to finalize " + newCacheId); + PackageHelper.destroySdDir(newCacheId); + return false; + } + Log.i(TAG, "Finalized " + newCacheId); + Runtime.getRuntime().gc(); + // Unmount first + PackageHelper.unMountSdDir(cid); + // Delete old container + PackageHelper.destroySdDir(cid); + // Dont have to mount. Already mounted. + cid = newCacheId; + cachePath = newCachePath; + return true; } - cid = newCacheId; - cachePath = newCachePath; - return true; } int doPostInstall(int status) { @@ -5403,6 +5428,7 @@ class PackageManagerService extends IPackageManager.Stub { res.returnCode = PackageManager.INSTALL_FAILED_INVALID_INSTALL_LOCATION; return; } + if (!args.doRename(res.returnCode, pkgName, oldCodePath)) { res.returnCode = PackageManager.INSTALL_FAILED_INSUFFICIENT_STORAGE; return; @@ -8809,7 +8835,7 @@ class PackageManagerService extends IPackageManager.Stub { } static String getTempContainerId() { - String prefix = "smdl1tmp"; + String prefix = "smdl2tmp"; int tmpIdx = 1; String list[] = PackageHelper.getSecureContainerList(); if (list != null) { @@ -8851,30 +8877,45 @@ class PackageManagerService extends IPackageManager.Stub { return prefix + tmpIdx; } - public void updateExternalMediaStatus(final boolean mediaStatus) { + public boolean updateExternalMediaStatus(final boolean mediaStatus) { synchronized (mPackages) { if (DEBUG_SD_INSTALL) Log.i(TAG, "updateExternalMediaStatus:: mediaStatus=" + mediaStatus+", mMediaMounted=" + mMediaMounted); if (mediaStatus == mMediaMounted) { - return; + return false; } mMediaMounted = mediaStatus; + final HashMap<SdInstallArgs, String> processCids = + new HashMap<SdInstallArgs, String>(); + final int[] uidArr = getExternalMediaPackages(mediaStatus, processCids); + if (processCids.size() == 0) { + return false; + } // Queue up an async operation since the package installation may take a little while. mHandler.post(new Runnable() { public void run() { mHandler.removeCallbacks(this); - updateExternalMediaStatusInner(mediaStatus); + if (mediaStatus) { + if (DEBUG_SD_INSTALL) Log.i(TAG, "Loading packages"); + loadMediaPackages(processCids, uidArr); + startCleaningPackages(); + } else { + if (DEBUG_SD_INSTALL) Log.i(TAG, "Unloading packages"); + unloadMediaPackages(processCids, uidArr); + } } }); + return true; } } - void updateExternalMediaStatusInner(boolean mediaStatus) { + private int[] getExternalMediaPackages(boolean mediaStatus, + Map<SdInstallArgs, String> processCids) { final String list[] = PackageHelper.getSecureContainerList(); if (list == null || list.length == 0) { - return; + return null; } - HashMap<SdInstallArgs, String> processCids = new HashMap<SdInstallArgs, String>(); + int uidList[] = new int[list.length]; int num = 0; synchronized (mPackages) { @@ -8916,14 +8957,7 @@ class PackageManagerService extends IPackageManager.Stub { } } } - if (mediaStatus) { - if (DEBUG_SD_INSTALL) Log.i(TAG, "Loading packages"); - loadMediaPackages(processCids, uidArr); - startCleaningPackages(); - } else { - if (DEBUG_SD_INSTALL) Log.i(TAG, "Unloading packages"); - unloadMediaPackages(processCids, uidArr); - } + return uidArr; } private void sendResourcesChangedBroadcast(boolean mediaStatus, @@ -8943,7 +8977,7 @@ class PackageManagerService extends IPackageManager.Stub { } } - void loadMediaPackages(HashMap<SdInstallArgs, String> processCids, int uidArr[]) { + private void loadMediaPackages(HashMap<SdInstallArgs, String> processCids, int uidArr[]) { ArrayList<String> pkgList = new ArrayList<String>(); Set<SdInstallArgs> keys = processCids.keySet(); for (SdInstallArgs args : keys) { @@ -8993,7 +9027,7 @@ class PackageManagerService extends IPackageManager.Stub { } } - void unloadMediaPackages(HashMap<SdInstallArgs, String> processCids, int uidArr[]) { + private void unloadMediaPackages(HashMap<SdInstallArgs, String> processCids, int uidArr[]) { if (DEBUG_SD_INSTALL) Log.i(TAG, "unloading media packages"); ArrayList<String> pkgList = new ArrayList<String>(); ArrayList<SdInstallArgs> failedList = new ArrayList<SdInstallArgs>(); diff --git a/services/java/com/android/server/am/ActivityManagerService.java b/services/java/com/android/server/am/ActivityManagerService.java index 4169cc5..7b64704 100644 --- a/services/java/com/android/server/am/ActivityManagerService.java +++ b/services/java/com/android/server/am/ActivityManagerService.java @@ -7615,6 +7615,15 @@ public final class ActivityManagerService extends ActivityManagerNative implemen ? cpi.readPermission : cpi.writePermission); } + if (!mSystemReady && !mDidUpdate && !mWaitingUpdate + && !cpi.processName.equals("system")) { + // If this content provider does not run in the system + // process, and the system is not yet ready to run other + // processes, then fail fast instead of hanging. + throw new IllegalArgumentException( + "Attempt to launch content provider before system ready"); + } + cpr = (ContentProviderRecord)mProvidersByClass.get(cpi.name); final boolean firstClass = cpr == null; if (firstClass) { @@ -7862,6 +7871,16 @@ public final class ActivityManagerService extends ActivityManagerNative implemen public static final void installSystemProviders() { ProcessRecord app = mSelf.mProcessNames.get("system", Process.SYSTEM_UID); List providers = mSelf.generateApplicationProvidersLocked(app); + if (providers != null) { + for (int i=providers.size()-1; i>=0; i--) { + ProviderInfo pi = (ProviderInfo)providers.get(i); + if ((pi.applicationInfo.flags&ApplicationInfo.FLAG_SYSTEM) == 0) { + Log.w(TAG, "Not installing system proc provider " + pi.name + + ": not system .apk"); + providers.remove(i); + } + } + } mSystemThread.installSystemProviders(providers); } diff --git a/services/java/com/android/server/am/PendingIntentRecord.java b/services/java/com/android/server/am/PendingIntentRecord.java index 94b5f56..fac47d7 100644 --- a/services/java/com/android/server/am/PendingIntentRecord.java +++ b/services/java/com/android/server/am/PendingIntentRecord.java @@ -145,8 +145,9 @@ class PendingIntentRecord extends IIntentSender.Stub { public String toString() { return "Key{" + typeName() + " pkg=" + packageName - + " intent=" + requestIntent.toShortString(true, false) + " flags=0x" - + Integer.toHexString(flags) + "}"; + + " intent=" + + (requestIntent != null ? requestIntent.toShortString(true, false) : "<null>") + + " flags=0x" + Integer.toHexString(flags) + "}"; } String typeName() { @@ -295,8 +296,10 @@ class PendingIntentRecord extends IIntentSender.Stub { pw.print(prefix); pw.print("requestCode="); pw.print(key.requestCode); pw.print(" requestResolvedType="); pw.println(key.requestResolvedType); } - pw.print(prefix); pw.print("requestIntent="); - pw.println(key.requestIntent.toShortString(true, true)); + if (key.requestIntent != null) { + pw.print(prefix); pw.print("requestIntent="); + pw.println(key.requestIntent.toShortString(true, true)); + } if (sent || canceled) { pw.print(prefix); pw.print("sent="); pw.print(sent); pw.print(" canceled="); pw.println(canceled); diff --git a/tests/AndroidTests/src/com/android/unit_tests/AsecTests.java b/tests/AndroidTests/src/com/android/unit_tests/AsecTests.java index d5d23266..e85254d 100755 --- a/tests/AndroidTests/src/com/android/unit_tests/AsecTests.java +++ b/tests/AndroidTests/src/com/android/unit_tests/AsecTests.java @@ -132,6 +132,14 @@ public class AsecTests extends AndroidTestCase { return ms.destroySecureContainer(fullId, force); } + private boolean isContainerMounted(String localId) throws RemoteException { + Assert.assertTrue(isMediaMounted()); + String fullId = "com.android.unittests.AsecTests." + localId; + + IMountService ms = getMs(); + return ms.isSecureContainerMounted(fullId); + } + private IMountService getMs() { IBinder service = ServiceManager.getService("mount"); if (service != null) { @@ -329,4 +337,25 @@ public class AsecTests extends AndroidTestCase { failStr(e); } } + + public void testIsContainerMountedAfterRename() { + try { + Assert.assertEquals(StorageResultCode.OperationSucceeded, + createContainer("testRenameContainer.1", 4, "none")); + + Assert.assertEquals(StorageResultCode.OperationSucceeded, + unmountContainer("testRenameContainer.1", false)); + + Assert.assertEquals(StorageResultCode.OperationSucceeded, + renameContainer("testRenameContainer.1", "testRenameContainer.2")); + + Assert.assertEquals(false, containerExists("testRenameContainer.1")); + Assert.assertEquals(true, containerExists("testRenameContainer.2")); + // Check if isContainerMounted returns valid value + Assert.assertEquals(true, isContainerMounted("testRenameContainer.2")); + } catch (Exception e) { + failStr(e); + } + } + } diff --git a/tests/AndroidTests/src/com/android/unit_tests/PackageManagerTests.java b/tests/AndroidTests/src/com/android/unit_tests/PackageManagerTests.java index 9c5c44d..d161a88 100755 --- a/tests/AndroidTests/src/com/android/unit_tests/PackageManagerTests.java +++ b/tests/AndroidTests/src/com/android/unit_tests/PackageManagerTests.java @@ -57,17 +57,25 @@ import android.os.Environment; import android.os.Handler; import android.os.IBinder; import android.os.storage.IMountService; +import android.os.storage.IMountServiceListener; +import android.os.storage.StorageEventListener; +import android.os.storage.StorageManager; import android.os.storage.StorageResultCode; import android.os.RemoteException; import android.os.ServiceManager; import android.os.StatFs; import android.provider.Settings; +import android.provider.Settings.SettingNotFoundException; public class PackageManagerTests extends AndroidTestCase { private static final boolean localLOGV = true; public static final String TAG="PackageManagerTests"; public final long MAX_WAIT_TIME=120*1000; public final long WAIT_TIME_INCR=20*1000; + private static final String SECURE_CONTAINERS_PREFIX = "/mnt/asec"; + private static final int APP_INSTALL_AUTO = 0; + private static final int APP_INSTALL_DEVICE = 1; + private static final int APP_INSTALL_SDCARD = 2; void failStr(String errMsg) { Log.w(TAG, "errMsg="+errMsg); @@ -244,9 +252,46 @@ public class PackageManagerTests extends AndroidTestCase { packageParser = null; return pkg; } - - private void assertInstall(String pkgName, int flags) { + private boolean getInstallLoc(int flags, int expInstallLocation) { + // Flags explicitly over ride everything else. + if ((flags & PackageManager.INSTALL_FORWARD_LOCK) != 0 ) { + return false; + } else if ((flags & PackageManager.INSTALL_EXTERNAL) != 0 ) { + return true; + } + // Manifest option takes precedence next + if (expInstallLocation == PackageInfo.INSTALL_LOCATION_PREFER_EXTERNAL) { + return true; + } + // TODO Out of memory checks here. + boolean checkSd = false; + int setLoc = 0; + try { + setLoc = Settings.System.getInt(mContext.getContentResolver(), Settings.System.SET_INSTALL_LOCATION); + } catch (SettingNotFoundException e) { + failStr(e); + } + if (setLoc == 1) { + int userPref = APP_INSTALL_AUTO; + try { + userPref = Settings.System.getInt(mContext.getContentResolver(), Settings.System.DEFAULT_INSTALL_LOCATION); + } catch (SettingNotFoundException e) { + failStr(e); + } + if (userPref == APP_INSTALL_DEVICE) { + checkSd = false; + } else if (userPref == APP_INSTALL_SDCARD) { + checkSd = true; + } else if (userPref == APP_INSTALL_AUTO) { + // Might be determined dynamically. TODO fix this + checkSd = false; + } + } + return checkSd; + } + private void assertInstall(PackageParser.Package pkg, int flags, int expInstallLocation) { try { + String pkgName = pkg.packageName; ApplicationInfo info = getPm().getApplicationInfo(pkgName, 0); assertNotNull(info); assertEquals(pkgName, info.packageName); @@ -264,15 +309,14 @@ public class PackageManagerTests extends AndroidTestCase { assertEquals(publicSrcPath, appInstallPath); } else { assertFalse((info.flags & ApplicationInfo.FLAG_FORWARD_LOCK) != 0); - if ((flags & PackageManager.INSTALL_EXTERNAL) != 0) { - assertTrue((info.flags & ApplicationInfo.FLAG_ON_SDCARD) != 0); - // Hardcoded for now - assertTrue(srcPath.startsWith("/asec")); - assertTrue(publicSrcPath.startsWith("/asec")); - } else { + if (!getInstallLoc(flags, expInstallLocation)) { assertEquals(srcPath, appInstallPath); assertEquals(publicSrcPath, appInstallPath); assertFalse((info.flags & ApplicationInfo.FLAG_ON_SDCARD) != 0); + } else { + assertTrue((info.flags & ApplicationInfo.FLAG_ON_SDCARD) != 0); + assertTrue(srcPath.startsWith(SECURE_CONTAINERS_PREFIX)); + assertTrue(publicSrcPath.startsWith(SECURE_CONTAINERS_PREFIX)); } } } catch (NameNotFoundException e) { @@ -300,7 +344,7 @@ public class PackageManagerTests extends AndroidTestCase { private InstallParams sampleInstallFromRawResource(int flags, boolean cleanUp) { return installFromRawResource("install.apk", R.raw.install, flags, cleanUp, - false, -1); + false, -1, PackageInfo.INSTALL_LOCATION_AUTO); } public void clearSecureContainersForPkg(String pkgName) { @@ -327,7 +371,8 @@ public class PackageManagerTests extends AndroidTestCase { * PackageManager api to install it. */ private InstallParams installFromRawResource(String outFileName, - int rawResId, int flags, boolean cleanUp, boolean fail, int result) { + int rawResId, int flags, boolean cleanUp, boolean fail, int result, + int expInstallLocation) { File filesDir = mContext.getFilesDir(); File outFile = new File(filesDir, outFileName); Uri packageURI = getInstallablePackage(rawResId, outFile); @@ -337,9 +382,7 @@ public class PackageManagerTests extends AndroidTestCase { // Make sure the package doesn't exist getPm().deletePackage(pkg.packageName, null, 0); // Clean up the containers as well - if ((flags & PackageManager.INSTALL_EXTERNAL) != 0) { - clearSecureContainersForPkg(pkg.packageName); - } + clearSecureContainersForPkg(pkg.packageName); try { try { if (fail) { @@ -351,7 +394,7 @@ public class PackageManagerTests extends AndroidTestCase { assertTrue(invokeInstallPackage(packageURI, flags, pkg.packageName, receiver)); // Verify installed information - assertInstall(pkg.packageName, flags); + assertInstall(pkg, flags, expInstallLocation); ip = new InstallParams(pkg, outFileName, packageURI); } } catch (Exception e) { @@ -443,6 +486,7 @@ public class PackageManagerTests extends AndroidTestCase { public void replaceFromRawResource(int flags) { InstallParams ip = sampleInstallFromRawResource(flags, false); boolean replace = ((flags & PackageManager.INSTALL_REPLACE_EXISTING) != 0); + Log.i(TAG, "replace=" + replace); GenericReceiver receiver; if (replace) { receiver = new ReplaceReceiver(ip.pkg.packageName); @@ -455,7 +499,7 @@ public class PackageManagerTests extends AndroidTestCase { assertEquals(invokeInstallPackage(ip.packageURI, flags, ip.pkg.packageName, receiver), replace); if (replace) { - assertInstall(ip.pkg.packageName, flags); + assertInstall(ip.pkg, flags, ip.pkg.installLocation); } } catch (Exception e) { failStr("Failed with exception : " + e); @@ -738,51 +782,73 @@ public class PackageManagerTests extends AndroidTestCase { } } + class StorageListener extends StorageEventListener { + String oldState; + String newState; + String path; + private boolean doneFlag = false; + @Override + public void onStorageStateChanged(String path, String oldState, String newState) { + if (localLOGV) Log.i(TAG, "Storage state changed from " + oldState + " to " + newState); + synchronized (this) { + this.oldState = oldState; + this.newState = newState; + this.path = path; + doneFlag = true; + notifyAll(); + } + } + + public boolean isDone() { + return doneFlag; + } + } + private boolean unmountMedia() { if (!getMediaState()) { return true; } + String path = Environment.getExternalStorageDirectory().toString(); + StorageListener observer = new StorageListener(); + StorageManager sm = (StorageManager) mContext.getSystemService(Context.STORAGE_SERVICE); + sm.registerListener(observer); try { - String mPath = Environment.getExternalStorageDirectory().toString(); - int ret = getMs().unmountVolume(mPath, false); - return ret == StorageResultCode.OperationSucceeded; - } catch (RemoteException e) { - return true; + // Wait on observer + synchronized(observer) { + getMs().unmountVolume(path, false); + long waitTime = 0; + while((!observer.isDone()) && (waitTime < MAX_WAIT_TIME) ) { + observer.wait(WAIT_TIME_INCR); + waitTime += WAIT_TIME_INCR; + } + if(!observer.isDone()) { + throw new Exception("Timed out waiting for packageInstalled callback"); + } + return true; + } + } catch (Exception e) { + return false; + } finally { + sm.unregisterListener(observer); } } - /* - * Install package on sdcard. Unmount and then mount the media. - * (Use PackageManagerService private api for now) - * Make sure the installed package is available. - * STOPSHIP will uncomment when MountService api's to mount/unmount - * are made asynchronous. - */ - public void xxxtestMountSdNormalInternal() { - assertTrue(mountFromRawResource()); - } - private boolean mountFromRawResource() { // Install pkg on sdcard - InstallParams ip = sampleInstallFromRawResource(PackageManager.INSTALL_EXTERNAL | - PackageManager.INSTALL_REPLACE_EXISTING, false); + InstallParams ip = sampleInstallFromRawResource(PackageManager.INSTALL_EXTERNAL, false); if (localLOGV) Log.i(TAG, "Installed pkg on sdcard"); boolean origState = getMediaState(); + boolean registeredReceiver = false; SdMountReceiver receiver = new SdMountReceiver(new String[]{ip.pkg.packageName}); try { if (localLOGV) Log.i(TAG, "Unmounting media"); // Unmount media assertTrue(unmountMedia()); if (localLOGV) Log.i(TAG, "Unmounted media"); - try { - if (localLOGV) Log.i(TAG, "Sleeping for 10 second"); - Thread.sleep(10*1000); - } catch (InterruptedException e) { - failStr(e); - } // Register receiver here PackageManager pm = getPm(); mContext.registerReceiver(receiver, receiver.filter); + registeredReceiver = true; // Wait on receiver synchronized (receiver) { @@ -807,7 +873,7 @@ public class PackageManagerTests extends AndroidTestCase { failStr(e); return false; } finally { - mContext.unregisterReceiver(receiver); + if (registeredReceiver) mContext.unregisterReceiver(receiver); // Restore original media state if (origState) { mountMedia(); @@ -819,6 +885,17 @@ public class PackageManagerTests extends AndroidTestCase { } } + /* + * Install package on sdcard. Unmount and then mount the media. + * (Use PackageManagerService private api for now) + * Make sure the installed package is available. + * STOPSHIP will uncomment when MountService api's to mount/unmount + * are made asynchronous. + */ + public void xxxtestMountSdNormalInternal() { + assertTrue(mountFromRawResource()); + } + void cleanUpInstall(InstallParams ip) { if (ip == null) { return; @@ -834,28 +911,29 @@ public class PackageManagerTests extends AndroidTestCase { public void testManifestInstallLocationInternal() { installFromRawResource("install.apk", R.raw.install_loc_internal, - 0, true, false, -1); + 0, true, false, -1, PackageInfo.INSTALL_LOCATION_INTERNAL_ONLY); } public void testManifestInstallLocationSdcard() { installFromRawResource("install.apk", R.raw.install_loc_sdcard, - 0, true, false, -1); + 0, true, false, -1, PackageInfo.INSTALL_LOCATION_PREFER_EXTERNAL); } public void testManifestInstallLocationAuto() { installFromRawResource("install.apk", R.raw.install_loc_auto, - 0, true, false, -1); + 0, true, false, -1, PackageInfo.INSTALL_LOCATION_AUTO); } public void testManifestInstallLocationUnspecified() { installFromRawResource("install.apk", R.raw.install_loc_unspecified, - 0, true, false, -1); + 0, true, false, -1, PackageInfo.INSTALL_LOCATION_AUTO); } public void testManifestInstallLocationFwdLockedSdcard() { installFromRawResource("install.apk", R.raw.install_loc_sdcard, PackageManager.INSTALL_FORWARD_LOCK, true, true, - PackageManager.INSTALL_FAILED_INVALID_INSTALL_LOCATION); + PackageManager.INSTALL_FAILED_INVALID_INSTALL_LOCATION, + PackageInfo.INSTALL_LOCATION_AUTO); } public void xxxtestClearAllSecureContainers() { diff --git a/tools/layoutlib/bridge/src/com/android/layoutlib/bridge/BridgeContext.java b/tools/layoutlib/bridge/src/com/android/layoutlib/bridge/BridgeContext.java index 73a3986..744bfbe 100644 --- a/tools/layoutlib/bridge/src/com/android/layoutlib/bridge/BridgeContext.java +++ b/tools/layoutlib/bridge/src/com/android/layoutlib/bridge/BridgeContext.java @@ -927,6 +927,7 @@ public final class BridgeContext extends Context { return null; } + @Override public File getExternalCacheDir() { // TODO Auto-generated method stub return null; @@ -964,6 +965,7 @@ public final class BridgeContext extends Context { return null; } + @Override public File getExternalFilesDir(String type) { // TODO Auto-generated method stub return null; diff --git a/tools/layoutlib/bridge/src/com/android/layoutlib/bridge/BridgeTypedArray.java b/tools/layoutlib/bridge/src/com/android/layoutlib/bridge/BridgeTypedArray.java index efd222e..6a98780 100644 --- a/tools/layoutlib/bridge/src/com/android/layoutlib/bridge/BridgeTypedArray.java +++ b/tools/layoutlib/bridge/src/com/android/layoutlib/bridge/BridgeTypedArray.java @@ -392,7 +392,8 @@ public final class BridgeTypedArray extends TypedArray { if (s == null) { return defValue; - } else if (s.equals(BridgeConstants.MATCH_PARENT)) { + } else if (s.equals(BridgeConstants.MATCH_PARENT) || + s.equals(BridgeConstants.FILL_PARENT)) { return LayoutParams.MATCH_PARENT; } else if (s.equals(BridgeConstants.WRAP_CONTENT)) { return LayoutParams.WRAP_CONTENT; @@ -460,7 +461,8 @@ public final class BridgeTypedArray extends TypedArray { if (s == null) { return defValue; - } else if (s.equals(BridgeConstants.MATCH_PARENT)) { + } else if (s.equals(BridgeConstants.MATCH_PARENT) || + s.equals(BridgeConstants.FILL_PARENT)) { return LayoutParams.MATCH_PARENT; } else if (s.equals(BridgeConstants.WRAP_CONTENT)) { return LayoutParams.WRAP_CONTENT; |
