summaryrefslogtreecommitdiffstats
path: root/core/java
diff options
context:
space:
mode:
Diffstat (limited to 'core/java')
-rw-r--r--core/java/android/accessibilityservice/AccessibilityService.java2
-rw-r--r--core/java/android/accessibilityservice/AccessibilityServiceInfo.java20
-rw-r--r--core/java/android/accounts/AbstractAccountAuthenticator.java2
-rw-r--r--core/java/android/accounts/AccountManagerService.java498
-rw-r--r--core/java/android/accounts/GrantCredentialsPermissionActivity.java3
-rw-r--r--core/java/android/accounts/OnAccountsUpdateListener.java4
-rw-r--r--core/java/android/animation/Animator.java4
-rwxr-xr-xcore/java/android/animation/ValueAnimator.java4
-rw-r--r--core/java/android/app/Activity.java28
-rw-r--r--core/java/android/app/ActivityManager.java33
-rw-r--r--core/java/android/app/ActivityManagerNative.java25
-rw-r--r--core/java/android/app/ApplicationErrorReport.java2
-rw-r--r--core/java/android/app/Dialog.java2
-rw-r--r--core/java/android/app/IActivityManager.java10
-rw-r--r--core/java/android/app/PendingIntent.java2
-rw-r--r--core/java/android/app/Service.java16
-rw-r--r--core/java/android/app/admin/DeviceAdminReceiver.java2
-rw-r--r--core/java/android/bluetooth/AtCommandResult.java2
-rw-r--r--core/java/android/bluetooth/BluetoothDevice.java2
-rw-r--r--core/java/android/bluetooth/BluetoothPbap.java2
-rw-r--r--core/java/android/content/ClipData.java84
-rw-r--r--core/java/android/content/ClipDescription.java33
-rw-r--r--core/java/android/content/ComponentCallbacks2.java38
-rw-r--r--core/java/android/content/ContentResolver.java2
-rw-r--r--core/java/android/content/ContentService.java69
-rw-r--r--core/java/android/content/Context.java8
-rw-r--r--core/java/android/content/Intent.java159
-rw-r--r--core/java/android/content/SyncManager.java374
-rw-r--r--core/java/android/content/SyncOperation.java8
-rw-r--r--core/java/android/content/SyncQueue.java21
-rw-r--r--core/java/android/content/SyncStorageEngine.java385
-rw-r--r--core/java/android/content/pm/PackageManager.java4
-rw-r--r--core/java/android/database/Cursor.java4
-rw-r--r--core/java/android/database/CursorWindow.java17
-rw-r--r--core/java/android/database/DatabaseUtils.java97
-rw-r--r--core/java/android/database/sqlite/SQLiteClosable.java55
-rw-r--r--core/java/android/database/sqlite/SQLiteConnection.java65
-rw-r--r--core/java/android/database/sqlite/SQLiteDatabase.java354
-rw-r--r--core/java/android/database/sqlite/SQLiteProgram.java7
-rw-r--r--core/java/android/emoji/EmojiFactory.java2
-rw-r--r--core/java/android/hardware/Camera.java5
-rw-r--r--core/java/android/hardware/CameraSound.java217
-rw-r--r--core/java/android/hardware/Sensor.java4
-rw-r--r--core/java/android/inputmethodservice/InputMethodService.java2
-rw-r--r--core/java/android/net/http/CertificateChainValidator.java15
-rw-r--r--core/java/android/net/http/SslError.java2
-rw-r--r--core/java/android/os/FileUtils.java10
-rw-r--r--core/java/android/os/Parcel.java224
-rw-r--r--core/java/android/os/ParcelUuid.java2
-rw-r--r--core/java/android/os/StrictMode.java2
-rw-r--r--core/java/android/provider/Contacts.java2
-rw-r--r--core/java/android/provider/MediaStore.java6
-rw-r--r--core/java/android/provider/Settings.java12
-rw-r--r--core/java/android/server/BluetoothBondState.java2
-rw-r--r--core/java/android/text/MeasuredText.java26
-rw-r--r--core/java/android/text/TextUtils.java8
-rw-r--r--core/java/android/text/method/BaseMovementMethod.java2
-rw-r--r--core/java/android/view/Choreographer.java345
-rw-r--r--core/java/android/view/HardwareLayer.java2
-rw-r--r--core/java/android/view/HardwareRenderer.java13
-rw-r--r--core/java/android/view/IWindow.aidl1
-rw-r--r--core/java/android/view/TextureView.java2
-rw-r--r--core/java/android/view/View.java200
-rw-r--r--core/java/android/view/ViewConfiguration.java3
-rw-r--r--core/java/android/view/ViewDebug.java6
-rw-r--r--core/java/android/view/ViewGroup.java11
-rw-r--r--core/java/android/view/ViewRootImpl.java49
-rw-r--r--core/java/android/view/WindowManager.java8
-rw-r--r--core/java/android/view/WindowManagerImpl.java36
-rw-r--r--core/java/android/view/accessibility/AccessibilityEvent.java28
-rw-r--r--core/java/android/view/accessibility/AccessibilityNodeInfo.java2
-rw-r--r--core/java/android/view/accessibility/AccessibilityRecord.java2
-rw-r--r--core/java/android/view/textservice/SpellCheckerInfo.java4
-rw-r--r--core/java/android/view/textservice/SpellCheckerSession.java3
-rw-r--r--core/java/android/webkit/CallbackProxy.java13
-rw-r--r--core/java/android/webkit/URLUtil.java2
-rw-r--r--core/java/android/webkit/ViewStateSerializer.java6
-rw-r--r--core/java/android/webkit/WebViewClassic.java669
-rw-r--r--core/java/android/webkit/WebViewClient.java6
-rw-r--r--core/java/android/webkit/WebViewCore.java118
-rw-r--r--core/java/android/widget/CheckedTextView.java16
-rw-r--r--core/java/android/widget/ExpandableListView.java2
-rw-r--r--core/java/android/widget/HorizontalScrollView.java6
-rw-r--r--core/java/android/widget/ImageView.java3
-rw-r--r--core/java/android/widget/ListPopupWindow.java24
-rw-r--r--core/java/android/widget/ProgressBar.java3
-rw-r--r--core/java/android/widget/RadioButton.java10
-rw-r--r--core/java/android/widget/ScrollView.java4
-rw-r--r--core/java/android/widget/SimpleAdapter.java2
-rw-r--r--core/java/android/widget/SimpleCursorAdapter.java2
-rw-r--r--core/java/android/widget/SimpleCursorTreeAdapter.java2
-rw-r--r--core/java/android/widget/SpellChecker.java16
-rw-r--r--core/java/android/widget/Spinner.java173
-rw-r--r--core/java/android/widget/Switch.java165
-rw-r--r--core/java/android/widget/TextView.java65
-rw-r--r--core/java/android/widget/ToggleButton.java10
-rw-r--r--core/java/com/android/internal/os/ZygoteInit.java2
-rw-r--r--core/java/com/android/internal/util/AsyncService.java2
-rw-r--r--core/java/com/android/internal/util/StateMachine.java2
-rw-r--r--core/java/com/android/internal/view/BaseIWindow.java3
-rw-r--r--core/java/com/android/internal/view/menu/ActionMenuItemView.java63
-rw-r--r--core/java/com/android/internal/view/menu/ActionMenuPresenter.java6
-rw-r--r--core/java/com/android/internal/view/menu/ActionMenuView.java7
-rw-r--r--core/java/com/android/internal/widget/ActionBarView.java18
-rw-r--r--core/java/com/android/internal/widget/SlidingTab.java2
105 files changed, 3154 insertions, 1973 deletions
diff --git a/core/java/android/accessibilityservice/AccessibilityService.java b/core/java/android/accessibilityservice/AccessibilityService.java
index a463a62..9ebbe03 100644
--- a/core/java/android/accessibilityservice/AccessibilityService.java
+++ b/core/java/android/accessibilityservice/AccessibilityService.java
@@ -104,7 +104,7 @@ import com.android.internal.os.HandlerCaller;
* </ul>
* <h3>Retrieving window content</h3>
* <p>
- * An service can specify in its declaration that it can retrieve the active window
+ * A service can specify in its declaration that it can retrieve the active window
* content which is represented as a tree of {@link AccessibilityNodeInfo}. Note that
* declaring this capability requires that the service declares its configuration via
* an XML resource referenced by {@link #SERVICE_META_DATA}.
diff --git a/core/java/android/accessibilityservice/AccessibilityServiceInfo.java b/core/java/android/accessibilityservice/AccessibilityServiceInfo.java
index eae0a4c..b55fda4 100644
--- a/core/java/android/accessibilityservice/AccessibilityServiceInfo.java
+++ b/core/java/android/accessibilityservice/AccessibilityServiceInfo.java
@@ -503,26 +503,38 @@ public class AccessibilityServiceInfo implements Parcelable {
public static String feedbackTypeToString(int feedbackType) {
StringBuilder builder = new StringBuilder();
builder.append("[");
- while (feedbackType > 0) {
+ while (feedbackType != 0) {
final int feedbackTypeFlag = 1 << Integer.numberOfTrailingZeros(feedbackType);
feedbackType &= ~feedbackTypeFlag;
- if (builder.length() > 1) {
- builder.append(", ");
- }
switch (feedbackTypeFlag) {
case FEEDBACK_AUDIBLE:
+ if (builder.length() > 1) {
+ builder.append(", ");
+ }
builder.append("FEEDBACK_AUDIBLE");
break;
case FEEDBACK_HAPTIC:
+ if (builder.length() > 1) {
+ builder.append(", ");
+ }
builder.append("FEEDBACK_HAPTIC");
break;
case FEEDBACK_GENERIC:
+ if (builder.length() > 1) {
+ builder.append(", ");
+ }
builder.append("FEEDBACK_GENERIC");
break;
case FEEDBACK_SPOKEN:
+ if (builder.length() > 1) {
+ builder.append(", ");
+ }
builder.append("FEEDBACK_SPOKEN");
break;
case FEEDBACK_VISUAL:
+ if (builder.length() > 1) {
+ builder.append(", ");
+ }
builder.append("FEEDBACK_VISUAL");
break;
}
diff --git a/core/java/android/accounts/AbstractAccountAuthenticator.java b/core/java/android/accounts/AbstractAccountAuthenticator.java
index 7183267..e9535ab 100644
--- a/core/java/android/accounts/AbstractAccountAuthenticator.java
+++ b/core/java/android/accounts/AbstractAccountAuthenticator.java
@@ -59,7 +59,7 @@ import java.util.Arrays;
* "Account & Sync" settings page and one user of the android:smallIcon is the Contact Application's
* tab panels.
* <p>
- * The preferences attribute points to an PreferenceScreen xml hierarchy that contains
+ * The preferences attribute points to a PreferenceScreen xml hierarchy that contains
* a list of PreferenceScreens that can be invoked to manage the authenticator. An example is:
* <pre>
* &lt;PreferenceScreen xmlns:android="http://schemas.android.com/apk/res/android"&gt;
diff --git a/core/java/android/accounts/AccountManagerService.java b/core/java/android/accounts/AccountManagerService.java
index 5fee4de..adc7d35 100644
--- a/core/java/android/accounts/AccountManagerService.java
+++ b/core/java/android/accounts/AccountManagerService.java
@@ -18,6 +18,7 @@ package android.accounts;
import android.Manifest;
import android.app.ActivityManager;
+import android.app.AppGlobals;
import android.app.Notification;
import android.app.NotificationManager;
import android.app.PendingIntent;
@@ -33,6 +34,7 @@ import android.content.pm.PackageInfo;
import android.content.pm.PackageManager;
import android.content.pm.RegisteredServicesCache;
import android.content.pm.RegisteredServicesCacheListener;
+import android.content.pm.UserInfo;
import android.content.res.Resources;
import android.database.Cursor;
import android.database.DatabaseUtils;
@@ -48,11 +50,14 @@ import android.os.Looper;
import android.os.Message;
import android.os.RemoteException;
import android.os.SystemClock;
+import android.os.UserId;
import android.text.TextUtils;
import android.util.Log;
import android.util.Pair;
+import android.util.SparseArray;
import com.android.internal.R;
+import com.android.internal.util.IndentingPrintWriter;
import java.io.File;
import java.io.FileDescriptor;
@@ -62,6 +67,7 @@ import java.util.Arrays;
import java.util.Collection;
import java.util.HashMap;
import java.util.LinkedHashMap;
+import java.util.List;
import java.util.Map;
import java.util.concurrent.atomic.AtomicInteger;
import java.util.concurrent.atomic.AtomicReference;
@@ -94,7 +100,6 @@ public class AccountManagerService
private static final int MESSAGE_TIMED_OUT = 3;
private final IAccountAuthenticatorCache mAuthenticatorCache;
- private final DatabaseHelper mOpenHelper;
private static final String TABLE_ACCOUNTS = "accounts";
private static final String ACCOUNTS_ID = "_id";
@@ -148,14 +153,36 @@ public class AccountManagerService
private final LinkedHashMap<String, Session> mSessions = new LinkedHashMap<String, Session>();
private final AtomicInteger mNotificationIds = new AtomicInteger(1);
- private final HashMap<Pair<Pair<Account, String>, Integer>, Integer>
- mCredentialsPermissionNotificationIds =
- new HashMap<Pair<Pair<Account, String>, Integer>, Integer>();
- private final HashMap<Account, Integer> mSigninRequiredNotificationIds =
- new HashMap<Account, Integer>();
+ static class UserAccounts {
+ private final int userId;
+ private final DatabaseHelper openHelper;
+ private final HashMap<Pair<Pair<Account, String>, Integer>, Integer>
+ credentialsPermissionNotificationIds =
+ new HashMap<Pair<Pair<Account, String>, Integer>, Integer>();
+ private final HashMap<Account, Integer> signinRequiredNotificationIds =
+ new HashMap<Account, Integer>();
+ private final Object cacheLock = new Object();
+ /** protected by the {@link #cacheLock} */
+ private final HashMap<String, Account[]> accountCache = new HashMap<String, Account[]>();
+ /** protected by the {@link #cacheLock} */
+ private HashMap<Account, HashMap<String, String>> userDataCache =
+ new HashMap<Account, HashMap<String, String>>();
+ /** protected by the {@link #cacheLock} */
+ private HashMap<Account, HashMap<String, String>> authTokenCache =
+ new HashMap<Account, HashMap<String, String>>();
+
+ UserAccounts(Context context, int userId) {
+ this.userId = userId;
+ synchronized (cacheLock) {
+ openHelper = new DatabaseHelper(context, userId);
+ }
+ }
+ }
+
+ private final SparseArray<UserAccounts> mUsers = new SparseArray<UserAccounts>();
+
private static AtomicReference<AccountManagerService> sThis =
new AtomicReference<AccountManagerService>();
-
private static final Account[] EMPTY_ACCOUNT_ARRAY = new Account[]{};
static {
@@ -163,15 +190,6 @@ public class AccountManagerService
ACCOUNTS_CHANGED_INTENT.setFlags(Intent.FLAG_RECEIVER_REGISTERED_ONLY_BEFORE_BOOT);
}
- private final Object mCacheLock = new Object();
- /** protected by the {@link #mCacheLock} */
- private final HashMap<String, Account[]> mAccountCache = new HashMap<String, Account[]>();
- /** protected by the {@link #mCacheLock} */
- private HashMap<Account, HashMap<String, String>> mUserDataCache =
- new HashMap<Account, HashMap<String, String>>();
- /** protected by the {@link #mCacheLock} */
- private HashMap<Account, HashMap<String, String>> mAuthTokenCache =
- new HashMap<Account, HashMap<String, String>>();
/**
* This should only be called by system code. One should only call this after the service
@@ -192,10 +210,6 @@ public class AccountManagerService
mContext = context;
mPackageManager = packageManager;
- synchronized (mCacheLock) {
- mOpenHelper = new DatabaseHelper(mContext);
- }
-
mMessageThread = new HandlerThread("AccountManagerService");
mMessageThread.start();
mMessageHandler = new MessageHandler(mMessageThread.getLooper());
@@ -203,6 +217,8 @@ public class AccountManagerService
mAuthenticatorCache = authenticatorCache;
mAuthenticatorCache.setListener(this, null /* Handler */);
+ UserAccounts accounts = initUser(0);
+
sThis.set(this);
IntentFilter intentFilter = new IntentFilter();
@@ -211,17 +227,36 @@ public class AccountManagerService
mContext.registerReceiver(new BroadcastReceiver() {
@Override
public void onReceive(Context context1, Intent intent) {
- purgeOldGrants();
+ purgeOldGrantsAll();
}
}, intentFilter);
- purgeOldGrants();
- validateAccountsAndPopulateCache();
}
- private void purgeOldGrants() {
- synchronized (mCacheLock) {
- final SQLiteDatabase db = mOpenHelper.getWritableDatabase();
+ private UserAccounts initUser(int userId) {
+ synchronized (mUsers) {
+ UserAccounts accounts = mUsers.get(userId);
+ if (accounts == null) {
+ accounts = new UserAccounts(mContext, userId);
+ mUsers.append(userId, accounts);
+ purgeOldGrants(accounts);
+ validateAccountsAndPopulateCache(accounts);
+ }
+ return accounts;
+ }
+ }
+
+ private void purgeOldGrantsAll() {
+ synchronized (mUsers) {
+ for (int i = 0; i < mUsers.size(); i++) {
+ purgeOldGrants(mUsers.valueAt(i));
+ }
+ }
+ }
+
+ private void purgeOldGrants(UserAccounts accounts) {
+ synchronized (accounts.cacheLock) {
+ final SQLiteDatabase db = accounts.openHelper.getWritableDatabase();
final Cursor cursor = db.query(TABLE_GRANTS,
new String[]{GRANTS_GRANTEE_UID},
null, null, GRANTS_GRANTEE_UID, null, null);
@@ -243,15 +278,15 @@ public class AccountManagerService
}
}
- private void validateAccountsAndPopulateCache() {
- synchronized (mCacheLock) {
- final SQLiteDatabase db = mOpenHelper.getWritableDatabase();
+ private void validateAccountsAndPopulateCache(UserAccounts accounts) {
+ synchronized (accounts.cacheLock) {
+ final SQLiteDatabase db = accounts.openHelper.getWritableDatabase();
boolean accountDeleted = false;
Cursor cursor = db.query(TABLE_ACCOUNTS,
new String[]{ACCOUNTS_ID, ACCOUNTS_TYPE, ACCOUNTS_NAME},
null, null, null, null, null);
try {
- mAccountCache.clear();
+ accounts.accountCache.clear();
final HashMap<String, ArrayList<String>> accountNamesByType =
new HashMap<String, ArrayList<String>>();
while (cursor.moveToNext()) {
@@ -265,8 +300,8 @@ public class AccountManagerService
db.delete(TABLE_ACCOUNTS, ACCOUNTS_ID + "=" + accountId, null);
accountDeleted = true;
final Account account = new Account(accountName, accountType);
- mUserDataCache.remove(account);
- mAuthTokenCache.remove(account);
+ accounts.userDataCache.remove(account);
+ accounts.authTokenCache.remove(account);
} else {
ArrayList<String> accountNames = accountNamesByType.get(accountType);
if (accountNames == null) {
@@ -286,19 +321,51 @@ public class AccountManagerService
accountsForType[i] = new Account(accountName, accountType);
++i;
}
- mAccountCache.put(accountType, accountsForType);
+ accounts.accountCache.put(accountType, accountsForType);
}
} finally {
cursor.close();
if (accountDeleted) {
- sendAccountsChangedBroadcast();
+ sendAccountsChangedBroadcast(accounts.userId);
}
}
}
}
+ private UserAccounts getUserAccountsForCaller() {
+ return getUserAccounts(UserId.getCallingUserId());
+ }
+
+ protected UserAccounts getUserAccounts(int userId) {
+ synchronized (mUsers) {
+ UserAccounts accounts = mUsers.get(userId);
+ if (accounts == null) {
+ accounts = initUser(userId);
+ mUsers.append(userId, accounts);
+ }
+ return accounts;
+ }
+ }
+
+ private List<UserInfo> getAllUsers() {
+ try {
+ return AppGlobals.getPackageManager().getUsers();
+ } catch (RemoteException re) {
+ // Local to system process, shouldn't happen
+ }
+ return null;
+ }
+
public void onServiceChanged(AuthenticatorDescription desc, boolean removed) {
- validateAccountsAndPopulateCache();
+ // Validate accounts for all users
+ List<UserInfo> users = getAllUsers();
+ if (users == null) {
+ validateAccountsAndPopulateCache(getUserAccountsForCaller());
+ } else {
+ for (UserInfo user : users) {
+ validateAccountsAndPopulateCache(getUserAccounts(user.id));
+ }
+ }
}
public String getPassword(Account account) {
@@ -310,21 +377,22 @@ public class AccountManagerService
if (account == null) throw new IllegalArgumentException("account is null");
checkAuthenticateAccountsPermission(account);
+ UserAccounts accounts = getUserAccountsForCaller();
long identityToken = clearCallingIdentity();
try {
- return readPasswordInternal(account);
+ return readPasswordInternal(accounts, account);
} finally {
restoreCallingIdentity(identityToken);
}
}
- private String readPasswordInternal(Account account) {
+ private String readPasswordInternal(UserAccounts accounts, Account account) {
if (account == null) {
return null;
}
- synchronized (mCacheLock) {
- final SQLiteDatabase db = mOpenHelper.getReadableDatabase();
+ synchronized (accounts.cacheLock) {
+ final SQLiteDatabase db = accounts.openHelper.getReadableDatabase();
Cursor cursor = db.query(TABLE_ACCOUNTS, new String[]{ACCOUNTS_PASSWORD},
ACCOUNTS_NAME + "=? AND " + ACCOUNTS_TYPE+ "=?",
new String[]{account.name, account.type}, null, null, null);
@@ -349,9 +417,10 @@ public class AccountManagerService
if (account == null) throw new IllegalArgumentException("account is null");
if (key == null) throw new IllegalArgumentException("key is null");
checkAuthenticateAccountsPermission(account);
+ UserAccounts accounts = getUserAccountsForCaller();
long identityToken = clearCallingIdentity();
try {
- return readUserDataInternal(account, key);
+ return readUserDataInternal(accounts, account, key);
} finally {
restoreCallingIdentity(identityToken);
}
@@ -390,21 +459,23 @@ public class AccountManagerService
if (account == null) throw new IllegalArgumentException("account is null");
checkAuthenticateAccountsPermission(account);
+ UserAccounts accounts = getUserAccountsForCaller();
// fails if the account already exists
long identityToken = clearCallingIdentity();
try {
- return addAccountInternal(account, password, extras);
+ return addAccountInternal(accounts, account, password, extras);
} finally {
restoreCallingIdentity(identityToken);
}
}
- private boolean addAccountInternal(Account account, String password, Bundle extras) {
+ private boolean addAccountInternal(UserAccounts accounts, Account account, String password,
+ Bundle extras) {
if (account == null) {
return false;
}
- synchronized (mCacheLock) {
- final SQLiteDatabase db = mOpenHelper.getWritableDatabase();
+ synchronized (accounts.cacheLock) {
+ final SQLiteDatabase db = accounts.openHelper.getWritableDatabase();
db.beginTransaction();
try {
long numMatches = DatabaseUtils.longForQuery(db,
@@ -437,11 +508,11 @@ public class AccountManagerService
}
}
db.setTransactionSuccessful();
- insertAccountIntoCacheLocked(account);
+ insertAccountIntoCacheLocked(accounts, account);
} finally {
db.endTransaction();
}
- sendAccountsChangedBroadcast();
+ sendAccountsChangedBroadcast(accounts.userId);
return true;
}
}
@@ -467,9 +538,10 @@ public class AccountManagerService
if (account == null) throw new IllegalArgumentException("account is null");
if (features == null) throw new IllegalArgumentException("features is null");
checkReadAccountsPermission();
+ UserAccounts accounts = getUserAccountsForCaller();
long identityToken = clearCallingIdentity();
try {
- new TestFeaturesSession(response, account, features).bind();
+ new TestFeaturesSession(accounts, response, account, features).bind();
} finally {
restoreCallingIdentity(identityToken);
}
@@ -479,9 +551,9 @@ public class AccountManagerService
private final String[] mFeatures;
private final Account mAccount;
- public TestFeaturesSession(IAccountManagerResponse response,
+ public TestFeaturesSession(UserAccounts accounts, IAccountManagerResponse response,
Account account, String[] features) {
- super(response, account.type, false /* expectActivityLaunch */,
+ super(accounts, response, account.type, false /* expectActivityLaunch */,
true /* stripAuthTokenFromResult */);
mFeatures = features;
mAccount = account;
@@ -537,21 +609,22 @@ public class AccountManagerService
if (response == null) throw new IllegalArgumentException("response is null");
if (account == null) throw new IllegalArgumentException("account is null");
checkManageAccountsPermission();
+ UserAccounts accounts = getUserAccountsForCaller();
long identityToken = clearCallingIdentity();
- cancelNotification(getSigninRequiredNotificationId(account));
- synchronized(mCredentialsPermissionNotificationIds) {
+ cancelNotification(getSigninRequiredNotificationId(accounts, account));
+ synchronized(accounts.credentialsPermissionNotificationIds) {
for (Pair<Pair<Account, String>, Integer> pair:
- mCredentialsPermissionNotificationIds.keySet()) {
+ accounts.credentialsPermissionNotificationIds.keySet()) {
if (account.equals(pair.first.first)) {
- int id = mCredentialsPermissionNotificationIds.get(pair);
+ int id = accounts.credentialsPermissionNotificationIds.get(pair);
cancelNotification(id);
}
}
}
try {
- new RemoveAccountSession(response, account).bind();
+ new RemoveAccountSession(accounts, response, account).bind();
} finally {
restoreCallingIdentity(identityToken);
}
@@ -559,8 +632,9 @@ public class AccountManagerService
private class RemoveAccountSession extends Session {
final Account mAccount;
- public RemoveAccountSession(IAccountManagerResponse response, Account account) {
- super(response, account.type, false /* expectActivityLaunch */,
+ public RemoveAccountSession(UserAccounts accounts, IAccountManagerResponse response,
+ Account account) {
+ super(accounts, response, account.type, false /* expectActivityLaunch */,
true /* stripAuthTokenFromResult */);
mAccount = account;
}
@@ -579,7 +653,7 @@ public class AccountManagerService
&& !result.containsKey(AccountManager.KEY_INTENT)) {
final boolean removalAllowed = result.getBoolean(AccountManager.KEY_BOOLEAN_RESULT);
if (removalAllowed) {
- removeAccountInternal(mAccount);
+ removeAccountInternal(mAccounts, mAccount);
}
IAccountManagerResponse response = getResponseAndClose();
if (response != null) {
@@ -600,13 +674,18 @@ public class AccountManagerService
}
}
+ /* For testing */
protected void removeAccountInternal(Account account) {
- synchronized (mCacheLock) {
- final SQLiteDatabase db = mOpenHelper.getWritableDatabase();
+ removeAccountInternal(getUserAccountsForCaller(), account);
+ }
+
+ private void removeAccountInternal(UserAccounts accounts, Account account) {
+ synchronized (accounts.cacheLock) {
+ final SQLiteDatabase db = accounts.openHelper.getWritableDatabase();
db.delete(TABLE_ACCOUNTS, ACCOUNTS_NAME + "=? AND " + ACCOUNTS_TYPE+ "=?",
new String[]{account.name, account.type});
- removeAccountFromCacheLocked(account);
- sendAccountsChangedBroadcast();
+ removeAccountFromCacheLocked(accounts, account);
+ sendAccountsChangedBroadcast(accounts.userId);
}
}
@@ -619,13 +698,14 @@ public class AccountManagerService
if (accountType == null) throw new IllegalArgumentException("accountType is null");
if (authToken == null) throw new IllegalArgumentException("authToken is null");
checkManageAccountsOrUseCredentialsPermissions();
+ UserAccounts accounts = getUserAccountsForCaller();
long identityToken = clearCallingIdentity();
try {
- synchronized (mCacheLock) {
- final SQLiteDatabase db = mOpenHelper.getWritableDatabase();
+ synchronized (accounts.cacheLock) {
+ final SQLiteDatabase db = accounts.openHelper.getWritableDatabase();
db.beginTransaction();
try {
- invalidateAuthTokenLocked(db, accountType, authToken);
+ invalidateAuthTokenLocked(accounts, db, accountType, authToken);
db.setTransactionSuccessful();
} finally {
db.endTransaction();
@@ -636,7 +716,8 @@ public class AccountManagerService
}
}
- private void invalidateAuthTokenLocked(SQLiteDatabase db, String accountType, String authToken) {
+ private void invalidateAuthTokenLocked(UserAccounts accounts, SQLiteDatabase db,
+ String accountType, String authToken) {
if (authToken == null || accountType == null) {
return;
}
@@ -657,7 +738,7 @@ public class AccountManagerService
String accountName = cursor.getString(1);
String authTokenType = cursor.getString(2);
db.delete(TABLE_AUTHTOKENS, AUTHTOKENS_ID + "=" + authTokenId, null);
- writeAuthTokenIntoCacheLocked(db, new Account(accountName, accountType),
+ writeAuthTokenIntoCacheLocked(accounts, db, new Account(accountName, accountType),
authTokenType, null);
}
} finally {
@@ -665,13 +746,14 @@ public class AccountManagerService
}
}
- private boolean saveAuthTokenToDatabase(Account account, String type, String authToken) {
+ private boolean saveAuthTokenToDatabase(UserAccounts accounts, Account account, String type,
+ String authToken) {
if (account == null || type == null) {
return false;
}
- cancelNotification(getSigninRequiredNotificationId(account));
- synchronized (mCacheLock) {
- final SQLiteDatabase db = mOpenHelper.getWritableDatabase();
+ cancelNotification(getSigninRequiredNotificationId(accounts, account));
+ synchronized (accounts.cacheLock) {
+ final SQLiteDatabase db = accounts.openHelper.getWritableDatabase();
db.beginTransaction();
try {
long accountId = getAccountIdLocked(db, account);
@@ -687,7 +769,7 @@ public class AccountManagerService
values.put(AUTHTOKENS_AUTHTOKEN, authToken);
if (db.insert(TABLE_AUTHTOKENS, AUTHTOKENS_AUTHTOKEN, values) >= 0) {
db.setTransactionSuccessful();
- writeAuthTokenIntoCacheLocked(db, account, type, authToken);
+ writeAuthTokenIntoCacheLocked(accounts, db, account, type, authToken);
return true;
}
return false;
@@ -707,9 +789,10 @@ public class AccountManagerService
if (account == null) throw new IllegalArgumentException("account is null");
if (authTokenType == null) throw new IllegalArgumentException("authTokenType is null");
checkAuthenticateAccountsPermission(account);
+ UserAccounts accounts = getUserAccountsForCaller();
long identityToken = clearCallingIdentity();
try {
- return readAuthTokenInternal(account, authTokenType);
+ return readAuthTokenInternal(accounts, account, authTokenType);
} finally {
restoreCallingIdentity(identityToken);
}
@@ -725,9 +808,10 @@ public class AccountManagerService
if (account == null) throw new IllegalArgumentException("account is null");
if (authTokenType == null) throw new IllegalArgumentException("authTokenType is null");
checkAuthenticateAccountsPermission(account);
+ UserAccounts accounts = getUserAccountsForCaller();
long identityToken = clearCallingIdentity();
try {
- saveAuthTokenToDatabase(account, authTokenType, authToken);
+ saveAuthTokenToDatabase(accounts, account, authTokenType, authToken);
} finally {
restoreCallingIdentity(identityToken);
}
@@ -741,20 +825,21 @@ public class AccountManagerService
}
if (account == null) throw new IllegalArgumentException("account is null");
checkAuthenticateAccountsPermission(account);
+ UserAccounts accounts = getUserAccountsForCaller();
long identityToken = clearCallingIdentity();
try {
- setPasswordInternal(account, password);
+ setPasswordInternal(accounts, account, password);
} finally {
restoreCallingIdentity(identityToken);
}
}
- private void setPasswordInternal(Account account, String password) {
+ private void setPasswordInternal(UserAccounts accounts, Account account, String password) {
if (account == null) {
return;
}
- synchronized (mCacheLock) {
- final SQLiteDatabase db = mOpenHelper.getWritableDatabase();
+ synchronized (accounts.cacheLock) {
+ final SQLiteDatabase db = accounts.openHelper.getWritableDatabase();
db.beginTransaction();
try {
final ContentValues values = new ContentValues();
@@ -764,20 +849,20 @@ public class AccountManagerService
final String[] argsAccountId = {String.valueOf(accountId)};
db.update(TABLE_ACCOUNTS, values, ACCOUNTS_ID + "=?", argsAccountId);
db.delete(TABLE_AUTHTOKENS, AUTHTOKENS_ACCOUNTS_ID + "=?", argsAccountId);
- mAuthTokenCache.remove(account);
+ accounts.authTokenCache.remove(account);
db.setTransactionSuccessful();
}
} finally {
db.endTransaction();
}
- sendAccountsChangedBroadcast();
+ sendAccountsChangedBroadcast(accounts.userId);
}
}
- private void sendAccountsChangedBroadcast() {
+ private void sendAccountsChangedBroadcast(int userId) {
Log.i(TAG, "the accounts changed, sending broadcast of "
+ ACCOUNTS_CHANGED_INTENT.getAction());
- mContext.sendBroadcast(ACCOUNTS_CHANGED_INTENT);
+ mContext.sendBroadcast(ACCOUNTS_CHANGED_INTENT, userId);
}
public void clearPassword(Account account) {
@@ -788,9 +873,10 @@ public class AccountManagerService
}
if (account == null) throw new IllegalArgumentException("account is null");
checkManageAccountsPermission();
+ UserAccounts accounts = getUserAccountsForCaller();
long identityToken = clearCallingIdentity();
try {
- setPasswordInternal(account, null);
+ setPasswordInternal(accounts, account, null);
} finally {
restoreCallingIdentity(identityToken);
}
@@ -806,20 +892,22 @@ public class AccountManagerService
if (key == null) throw new IllegalArgumentException("key is null");
if (account == null) throw new IllegalArgumentException("account is null");
checkAuthenticateAccountsPermission(account);
+ UserAccounts accounts = getUserAccountsForCaller();
long identityToken = clearCallingIdentity();
try {
- setUserdataInternal(account, key, value);
+ setUserdataInternal(accounts, account, key, value);
} finally {
restoreCallingIdentity(identityToken);
}
}
- private void setUserdataInternal(Account account, String key, String value) {
+ private void setUserdataInternal(UserAccounts accounts, Account account, String key,
+ String value) {
if (account == null || key == null) {
return;
}
- synchronized (mCacheLock) {
- final SQLiteDatabase db = mOpenHelper.getWritableDatabase();
+ synchronized (accounts.cacheLock) {
+ final SQLiteDatabase db = accounts.openHelper.getWritableDatabase();
db.beginTransaction();
try {
long accountId = getAccountIdLocked(db, account);
@@ -840,7 +928,7 @@ public class AccountManagerService
}
}
- writeUserDataIntoCacheLocked(db, account, key, value);
+ writeUserDataIntoCacheLocked(accounts, db, account, key, value);
db.setTransactionSuccessful();
} finally {
db.endTransaction();
@@ -868,15 +956,16 @@ public class AccountManagerService
}
void getAuthTokenLabel(final IAccountManagerResponse response,
- final Account account, final String authTokenType) {
+ final Account account,
+ final String authTokenType, int uid) {
if (account == null) throw new IllegalArgumentException("account is null");
if (authTokenType == null) throw new IllegalArgumentException("authTokenType is null");
checkBinderPermission(Manifest.permission.USE_CREDENTIALS);
-
+ UserAccounts accounts = getUserAccounts(UserId.getUserId(uid));
long identityToken = clearCallingIdentity();
try {
- new Session(response, account.type, false,
+ new Session(accounts, response, account.type, false,
false /* stripAuthTokenFromResult */) {
protected String toDebugString(long now) {
return super.toDebugString(now) + ", getAuthTokenLabel"
@@ -921,6 +1010,7 @@ public class AccountManagerService
if (account == null) throw new IllegalArgumentException("account is null");
if (authTokenType == null) throw new IllegalArgumentException("authTokenType is null");
checkBinderPermission(Manifest.permission.USE_CREDENTIALS);
+ UserAccounts accounts = getUserAccountsForCaller();
AccountAuthenticatorCache.ServiceInfo<AuthenticatorDescription> authenticatorInfo =
mAuthenticatorCache.getServiceInfo(
AuthenticatorDescription.newKey(account.type));
@@ -946,7 +1036,7 @@ public class AccountManagerService
// if the caller has permission, do the peek. otherwise go the more expensive
// route of starting a Session
if (!customTokens && permissionGranted) {
- String authToken = readAuthTokenInternal(account, authTokenType);
+ String authToken = readAuthTokenInternal(accounts, account, authTokenType);
if (authToken != null) {
Bundle result = new Bundle();
result.putString(AccountManager.KEY_AUTHTOKEN, authToken);
@@ -957,7 +1047,7 @@ public class AccountManagerService
}
}
- new Session(response, account.type, expectActivityLaunch,
+ new Session(accounts, response, account.type, expectActivityLaunch,
false /* stripAuthTokenFromResult */) {
protected String toDebugString(long now) {
if (loginOptions != null) loginOptions.keySet();
@@ -1000,14 +1090,14 @@ public class AccountManagerService
return;
}
if (!customTokens) {
- saveAuthTokenToDatabase(new Account(name, type),
+ saveAuthTokenToDatabase(mAccounts, new Account(name, type),
authTokenType, authToken);
}
}
Intent intent = result.getParcelable(AccountManager.KEY_INTENT);
if (intent != null && notifyOnAuthFailure && !customTokens) {
- doNotification(
+ doNotification(mAccounts,
account, result.getString(AccountManager.KEY_AUTH_FAILED_MESSAGE),
intent);
}
@@ -1090,26 +1180,27 @@ public class AccountManagerService
private Integer getCredentialPermissionNotificationId(Account account, String authTokenType,
int uid) {
Integer id;
- synchronized(mCredentialsPermissionNotificationIds) {
+ UserAccounts accounts = getUserAccounts(UserId.getUserId(uid));
+ synchronized (accounts.credentialsPermissionNotificationIds) {
final Pair<Pair<Account, String>, Integer> key =
new Pair<Pair<Account, String>, Integer>(
new Pair<Account, String>(account, authTokenType), uid);
- id = mCredentialsPermissionNotificationIds.get(key);
+ id = accounts.credentialsPermissionNotificationIds.get(key);
if (id == null) {
id = mNotificationIds.incrementAndGet();
- mCredentialsPermissionNotificationIds.put(key, id);
+ accounts.credentialsPermissionNotificationIds.put(key, id);
}
}
return id;
}
- private Integer getSigninRequiredNotificationId(Account account) {
+ private Integer getSigninRequiredNotificationId(UserAccounts accounts, Account account) {
Integer id;
- synchronized(mSigninRequiredNotificationIds) {
- id = mSigninRequiredNotificationIds.get(account);
+ synchronized (accounts.signinRequiredNotificationIds) {
+ id = accounts.signinRequiredNotificationIds.get(account);
if (id == null) {
id = mNotificationIds.incrementAndGet();
- mSigninRequiredNotificationIds.put(account, id);
+ accounts.signinRequiredNotificationIds.put(account, id);
}
}
return id;
@@ -1131,6 +1222,7 @@ public class AccountManagerService
if (accountType == null) throw new IllegalArgumentException("accountType is null");
checkManageAccountsPermission();
+ UserAccounts accounts = getUserAccountsForCaller();
final int pid = Binder.getCallingPid();
final int uid = Binder.getCallingUid();
final Bundle options = (optionsIn == null) ? new Bundle() : optionsIn;
@@ -1139,7 +1231,7 @@ public class AccountManagerService
long identityToken = clearCallingIdentity();
try {
- new Session(response, accountType, expectActivityLaunch,
+ new Session(accounts, response, accountType, expectActivityLaunch,
true /* stripAuthTokenFromResult */) {
public void run() throws RemoteException {
mAuthenticator.addAccount(this, mAccountType, authTokenType, requiredFeatures,
@@ -1172,9 +1264,10 @@ public class AccountManagerService
if (response == null) throw new IllegalArgumentException("response is null");
if (account == null) throw new IllegalArgumentException("account is null");
checkManageAccountsPermission();
+ UserAccounts accounts = getUserAccountsForCaller();
long identityToken = clearCallingIdentity();
try {
- new Session(response, account.type, expectActivityLaunch,
+ new Session(accounts, response, account.type, expectActivityLaunch,
true /* stripAuthTokenFromResult */) {
public void run() throws RemoteException {
mAuthenticator.confirmCredentials(this, account, options);
@@ -1204,9 +1297,10 @@ public class AccountManagerService
if (account == null) throw new IllegalArgumentException("account is null");
if (authTokenType == null) throw new IllegalArgumentException("authTokenType is null");
checkManageAccountsPermission();
+ UserAccounts accounts = getUserAccountsForCaller();
long identityToken = clearCallingIdentity();
try {
- new Session(response, account.type, expectActivityLaunch,
+ new Session(accounts, response, account.type, expectActivityLaunch,
true /* stripAuthTokenFromResult */) {
public void run() throws RemoteException {
mAuthenticator.updateCredentials(this, account, authTokenType, loginOptions);
@@ -1236,9 +1330,10 @@ public class AccountManagerService
if (response == null) throw new IllegalArgumentException("response is null");
if (accountType == null) throw new IllegalArgumentException("accountType is null");
checkManageAccountsPermission();
+ UserAccounts accounts = getUserAccountsForCaller();
long identityToken = clearCallingIdentity();
try {
- new Session(response, accountType, expectActivityLaunch,
+ new Session(accounts, response, accountType, expectActivityLaunch,
true /* stripAuthTokenFromResult */) {
public void run() throws RemoteException {
mAuthenticator.editProperties(this, mAccountType);
@@ -1259,16 +1354,16 @@ public class AccountManagerService
private volatile ArrayList<Account> mAccountsWithFeatures = null;
private volatile int mCurrentAccount = 0;
- public GetAccountsByTypeAndFeatureSession(IAccountManagerResponse response,
- String type, String[] features) {
- super(response, type, false /* expectActivityLaunch */,
+ public GetAccountsByTypeAndFeatureSession(UserAccounts accounts,
+ IAccountManagerResponse response, String type, String[] features) {
+ super(accounts, response, type, false /* expectActivityLaunch */,
true /* stripAuthTokenFromResult */);
mFeatures = features;
}
public void run() throws RemoteException {
- synchronized (mCacheLock) {
- mAccountsOfType = getAccountsFromCacheLocked(mAccountType);
+ synchronized (mAccounts.cacheLock) {
+ mAccountsOfType = getAccountsFromCacheLocked(mAccounts, mAccountType);
}
// check whether each account matches the requested features
mAccountsWithFeatures = new ArrayList<Account>(mAccountsOfType.length);
@@ -1346,6 +1441,23 @@ public class AccountManagerService
}
}
+ /**
+ * Returns the accounts for a specific user
+ * @hide
+ */
+ public Account[] getAccounts(int userId) {
+ checkReadAccountsPermission();
+ UserAccounts accounts = getUserAccounts(userId);
+ long identityToken = clearCallingIdentity();
+ try {
+ synchronized (accounts.cacheLock) {
+ return getAccountsFromCacheLocked(accounts, null);
+ }
+ } finally {
+ restoreCallingIdentity(identityToken);
+ }
+ }
+
public Account[] getAccounts(String type) {
if (Log.isLoggable(TAG, Log.VERBOSE)) {
Log.v(TAG, "getAccounts: accountType " + type
@@ -1353,10 +1465,11 @@ public class AccountManagerService
+ ", pid " + Binder.getCallingPid());
}
checkReadAccountsPermission();
+ UserAccounts accounts = getUserAccountsForCaller();
long identityToken = clearCallingIdentity();
try {
- synchronized (mCacheLock) {
- return getAccountsFromCacheLocked(type);
+ synchronized (accounts.cacheLock) {
+ return getAccountsFromCacheLocked(accounts, type);
}
} finally {
restoreCallingIdentity(identityToken);
@@ -1375,19 +1488,20 @@ public class AccountManagerService
if (response == null) throw new IllegalArgumentException("response is null");
if (type == null) throw new IllegalArgumentException("accountType is null");
checkReadAccountsPermission();
+ UserAccounts userAccounts = getUserAccountsForCaller();
long identityToken = clearCallingIdentity();
try {
if (features == null || features.length == 0) {
Account[] accounts;
- synchronized (mCacheLock) {
- accounts = getAccountsFromCacheLocked(type);
+ synchronized (userAccounts.cacheLock) {
+ accounts = getAccountsFromCacheLocked(userAccounts, type);
}
Bundle result = new Bundle();
result.putParcelableArray(AccountManager.KEY_ACCOUNTS, accounts);
onResult(response, result);
return;
}
- new GetAccountsByTypeAndFeatureSession(response, type, features).bind();
+ new GetAccountsByTypeAndFeatureSession(userAccounts, response, type, features).bind();
} finally {
restoreCallingIdentity(identityToken);
}
@@ -1435,12 +1549,14 @@ public class AccountManagerService
IAccountAuthenticator mAuthenticator = null;
private final boolean mStripAuthTokenFromResult;
+ protected final UserAccounts mAccounts;
- public Session(IAccountManagerResponse response, String accountType,
+ public Session(UserAccounts accounts, IAccountManagerResponse response, String accountType,
boolean expectActivityLaunch, boolean stripAuthTokenFromResult) {
super();
if (response == null) throw new IllegalArgumentException("response is null");
if (accountType == null) throw new IllegalArgumentException("accountType is null");
+ mAccounts = accounts;
mStripAuthTokenFromResult = stripAuthTokenFromResult;
mResponse = response;
mAccountType = accountType;
@@ -1578,7 +1694,7 @@ public class AccountManagerService
String accountType = result.getString(AccountManager.KEY_ACCOUNT_TYPE);
if (!TextUtils.isEmpty(accountName) && !TextUtils.isEmpty(accountType)) {
Account account = new Account(accountName, accountType);
- cancelNotification(getSigninRequiredNotificationId(account));
+ cancelNotification(getSigninRequiredNotificationId(mAccounts, account));
}
}
IAccountManagerResponse response;
@@ -1694,20 +1810,23 @@ public class AccountManagerService
}
}
- private static String getDatabaseName() {
- if(Environment.isEncryptedFilesystemEnabled()) {
- // Hard-coded path in case of encrypted file system
- return Environment.getSystemSecureDirectory().getPath() + File.separator + DATABASE_NAME;
- } else {
- // Regular path in case of non-encrypted file system
- return DATABASE_NAME;
+ private static String getDatabaseName(int userId) {
+ File systemDir = Environment.getSystemSecureDirectory();
+ File databaseFile = new File(systemDir, "users/" + userId + "/" + DATABASE_NAME);
+ if (userId == 0) {
+ // Migrate old file, if it exists, to the new location
+ File oldFile = new File(systemDir, DATABASE_NAME);
+ if (oldFile.exists()) {
+ oldFile.renameTo(databaseFile);
+ }
}
+ return databaseFile.getPath();
}
- private class DatabaseHelper extends SQLiteOpenHelper {
+ static class DatabaseHelper extends SQLiteOpenHelper {
- public DatabaseHelper(Context context) {
- super(context, AccountManagerService.getDatabaseName(), null, DATABASE_VERSION);
+ public DatabaseHelper(Context context, int userId) {
+ super(context, AccountManagerService.getDatabaseName(userId), null, DATABASE_VERSION);
}
/**
@@ -1799,15 +1918,6 @@ public class AccountManagerService
}
}
- private void setMetaValue(String key, String value) {
- ContentValues values = new ContentValues();
- values.put(META_KEY, key);
- values.put(META_VALUE, value);
- synchronized (mCacheLock) {
- mOpenHelper.getWritableDatabase().replace(TABLE_META, META_KEY, values);
- }
- }
-
public IBinder onBind(Intent intent) {
return asBinder();
}
@@ -1837,11 +1947,25 @@ public class AccountManagerService
+ " without permission " + android.Manifest.permission.DUMP);
return;
}
+ final boolean isCheckinRequest = scanArgs(args, "--checkin") || scanArgs(args, "-c");
- synchronized (mCacheLock) {
- final SQLiteDatabase db = mOpenHelper.getReadableDatabase();
+ fout = new IndentingPrintWriter(fout, " ");
+ int size = mUsers.size();
+ for (int i = 0; i < size; i++) {
+ fout.println("User " + mUsers.keyAt(i) + ":");
+ ((IndentingPrintWriter) fout).increaseIndent();
+ dumpUser(mUsers.valueAt(i), fd, fout, args, isCheckinRequest);
+ ((IndentingPrintWriter) fout).decreaseIndent();
+ if (i < size - 1) {
+ fout.println();
+ }
+ }
+ }
- final boolean isCheckinRequest = scanArgs(args, "--checkin") || scanArgs(args, "-c");
+ private void dumpUser(UserAccounts userAccounts, FileDescriptor fd, PrintWriter fout,
+ String[] args, boolean isCheckinRequest) {
+ synchronized (userAccounts.cacheLock) {
+ final SQLiteDatabase db = userAccounts.openHelper.getReadableDatabase();
if (isCheckinRequest) {
// This is a checkin request. *Only* upload the account types and the count of each.
@@ -1858,7 +1982,7 @@ public class AccountManagerService
}
}
} else {
- Account[] accounts = getAccountsFromCacheLocked(null /* type */);
+ Account[] accounts = getAccountsFromCacheLocked(userAccounts, null /* type */);
fout.println("Accounts: " + accounts.length);
for (Account account : accounts) {
fout.println(" " + account);
@@ -1879,7 +2003,8 @@ public class AccountManagerService
}
}
- private void doNotification(Account account, CharSequence message, Intent intent) {
+ private void doNotification(UserAccounts accounts, Account account, CharSequence message,
+ Intent intent) {
long identityToken = clearCallingIdentity();
try {
if (Log.isLoggable(TAG, Log.VERBOSE)) {
@@ -1891,7 +2016,7 @@ public class AccountManagerService
intent.getComponent().getClassName())) {
createNoCredentialsPermissionNotification(account, intent);
} else {
- final Integer notificationId = getSigninRequiredNotificationId(account);
+ final Integer notificationId = getSigninRequiredNotificationId(accounts, account);
intent.addCategory(String.valueOf(notificationId));
Notification n = new Notification(android.R.drawable.stat_sys_warning, null,
0 /* when */);
@@ -1962,7 +2087,7 @@ public class AccountManagerService
final boolean fromAuthenticator = account != null
&& hasAuthenticatorUid(account.type, callerUid);
final boolean hasExplicitGrants = account != null
- && hasExplicitlyGrantedPermission(account, authTokenType);
+ && hasExplicitlyGrantedPermission(account, authTokenType, callerUid);
if (Log.isLoggable(TAG, Log.VERBOSE)) {
Log.v(TAG, "checkGrantsOrCallingUidAgainstAuthenticator: caller uid "
+ callerUid + ", " + account
@@ -1984,13 +2109,15 @@ public class AccountManagerService
return false;
}
- private boolean hasExplicitlyGrantedPermission(Account account, String authTokenType) {
- if (Binder.getCallingUid() == android.os.Process.SYSTEM_UID) {
+ private boolean hasExplicitlyGrantedPermission(Account account, String authTokenType,
+ int callerUid) {
+ if (callerUid == android.os.Process.SYSTEM_UID) {
return true;
}
- synchronized (mCacheLock) {
- final SQLiteDatabase db = mOpenHelper.getReadableDatabase();
- String[] args = {String.valueOf(Binder.getCallingUid()), authTokenType,
+ UserAccounts accounts = getUserAccountsForCaller();
+ synchronized (accounts.cacheLock) {
+ final SQLiteDatabase db = accounts.openHelper.getReadableDatabase();
+ String[] args = { String.valueOf(callerUid), authTokenType,
account.name, account.type};
final boolean permissionGranted =
DatabaseUtils.longForQuery(db, COUNT_OF_MATCHING_GRANTS, args) != 0;
@@ -1998,7 +2125,7 @@ public class AccountManagerService
// TODO: Skip this check when running automated tests. Replace this
// with a more general solution.
Log.d(TAG, "no credentials permission for usage of " + account + ", "
- + authTokenType + " by uid " + Binder.getCallingUid()
+ + authTokenType + " by uid " + callerUid
+ " but ignoring since device is in test harness.");
return true;
}
@@ -2048,8 +2175,9 @@ public class AccountManagerService
Log.e(TAG, "grantAppPermission: called with invalid arguments", new Exception());
return;
}
- synchronized (mCacheLock) {
- final SQLiteDatabase db = mOpenHelper.getWritableDatabase();
+ UserAccounts accounts = getUserAccounts(UserId.getUserId(uid));
+ synchronized (accounts.cacheLock) {
+ final SQLiteDatabase db = accounts.openHelper.getWritableDatabase();
db.beginTransaction();
try {
long accountId = getAccountIdLocked(db, account);
@@ -2081,8 +2209,9 @@ public class AccountManagerService
Log.e(TAG, "revokeAppPermission: called with invalid arguments", new Exception());
return;
}
- synchronized (mCacheLock) {
- final SQLiteDatabase db = mOpenHelper.getWritableDatabase();
+ UserAccounts accounts = getUserAccounts(UserId.getUserId(uid));
+ synchronized (accounts.cacheLock) {
+ final SQLiteDatabase db = accounts.openHelper.getWritableDatabase();
db.beginTransaction();
try {
long accountId = getAccountIdLocked(db, account);
@@ -2105,8 +2234,8 @@ public class AccountManagerService
return value != null ? ("[" + TextUtils.join(",", value) + "]") : null;
}
- private void removeAccountFromCacheLocked(Account account) {
- final Account[] oldAccountsForType = mAccountCache.get(account.type);
+ private void removeAccountFromCacheLocked(UserAccounts accounts, Account account) {
+ final Account[] oldAccountsForType = accounts.accountCache.get(account.type);
if (oldAccountsForType != null) {
ArrayList<Account> newAccountsList = new ArrayList<Account>();
for (Account curAccount : oldAccountsForType) {
@@ -2115,34 +2244,34 @@ public class AccountManagerService
}
}
if (newAccountsList.isEmpty()) {
- mAccountCache.remove(account.type);
+ accounts.accountCache.remove(account.type);
} else {
Account[] newAccountsForType = new Account[newAccountsList.size()];
newAccountsForType = newAccountsList.toArray(newAccountsForType);
- mAccountCache.put(account.type, newAccountsForType);
+ accounts.accountCache.put(account.type, newAccountsForType);
}
}
- mUserDataCache.remove(account);
- mAuthTokenCache.remove(account);
+ accounts.userDataCache.remove(account);
+ accounts.authTokenCache.remove(account);
}
/**
* This assumes that the caller has already checked that the account is not already present.
*/
- private void insertAccountIntoCacheLocked(Account account) {
- Account[] accountsForType = mAccountCache.get(account.type);
+ private void insertAccountIntoCacheLocked(UserAccounts accounts, Account account) {
+ Account[] accountsForType = accounts.accountCache.get(account.type);
int oldLength = (accountsForType != null) ? accountsForType.length : 0;
Account[] newAccountsForType = new Account[oldLength + 1];
if (accountsForType != null) {
System.arraycopy(accountsForType, 0, newAccountsForType, 0, oldLength);
}
newAccountsForType[oldLength] = account;
- mAccountCache.put(account.type, newAccountsForType);
+ accounts.accountCache.put(account.type, newAccountsForType);
}
- protected Account[] getAccountsFromCacheLocked(String accountType) {
+ protected Account[] getAccountsFromCacheLocked(UserAccounts userAccounts, String accountType) {
if (accountType != null) {
- final Account[] accounts = mAccountCache.get(accountType);
+ final Account[] accounts = userAccounts.accountCache.get(accountType);
if (accounts == null) {
return EMPTY_ACCOUNT_ARRAY;
} else {
@@ -2150,7 +2279,7 @@ public class AccountManagerService
}
} else {
int totalLength = 0;
- for (Account[] accounts : mAccountCache.values()) {
+ for (Account[] accounts : userAccounts.accountCache.values()) {
totalLength += accounts.length;
}
if (totalLength == 0) {
@@ -2158,7 +2287,7 @@ public class AccountManagerService
}
Account[] accounts = new Account[totalLength];
totalLength = 0;
- for (Account[] accountsOfType : mAccountCache.values()) {
+ for (Account[] accountsOfType : userAccounts.accountCache.values()) {
System.arraycopy(accountsOfType, 0, accounts, totalLength,
accountsOfType.length);
totalLength += accountsOfType.length;
@@ -2167,12 +2296,12 @@ public class AccountManagerService
}
}
- protected void writeUserDataIntoCacheLocked(final SQLiteDatabase db, Account account,
- String key, String value) {
- HashMap<String, String> userDataForAccount = mUserDataCache.get(account);
+ protected void writeUserDataIntoCacheLocked(UserAccounts accounts, final SQLiteDatabase db,
+ Account account, String key, String value) {
+ HashMap<String, String> userDataForAccount = accounts.userDataCache.get(account);
if (userDataForAccount == null) {
userDataForAccount = readUserDataForAccountFromDatabaseLocked(db, account);
- mUserDataCache.put(account, userDataForAccount);
+ accounts.userDataCache.put(account, userDataForAccount);
}
if (value == null) {
userDataForAccount.remove(key);
@@ -2181,12 +2310,12 @@ public class AccountManagerService
}
}
- protected void writeAuthTokenIntoCacheLocked(final SQLiteDatabase db, Account account,
- String key, String value) {
- HashMap<String, String> authTokensForAccount = mAuthTokenCache.get(account);
+ protected void writeAuthTokenIntoCacheLocked(UserAccounts accounts, final SQLiteDatabase db,
+ Account account, String key, String value) {
+ HashMap<String, String> authTokensForAccount = accounts.authTokenCache.get(account);
if (authTokensForAccount == null) {
authTokensForAccount = readAuthTokensForAccountFromDatabaseLocked(db, account);
- mAuthTokenCache.put(account, authTokensForAccount);
+ accounts.authTokenCache.put(account, authTokensForAccount);
}
if (value == null) {
authTokensForAccount.remove(key);
@@ -2195,27 +2324,28 @@ public class AccountManagerService
}
}
- protected String readAuthTokenInternal(Account account, String authTokenType) {
- synchronized (mCacheLock) {
- HashMap<String, String> authTokensForAccount = mAuthTokenCache.get(account);
+ protected String readAuthTokenInternal(UserAccounts accounts, Account account,
+ String authTokenType) {
+ synchronized (accounts.cacheLock) {
+ HashMap<String, String> authTokensForAccount = accounts.authTokenCache.get(account);
if (authTokensForAccount == null) {
// need to populate the cache for this account
- final SQLiteDatabase db = mOpenHelper.getReadableDatabase();
+ final SQLiteDatabase db = accounts.openHelper.getReadableDatabase();
authTokensForAccount = readAuthTokensForAccountFromDatabaseLocked(db, account);
- mAuthTokenCache.put(account, authTokensForAccount);
+ accounts.authTokenCache.put(account, authTokensForAccount);
}
return authTokensForAccount.get(authTokenType);
}
}
- protected String readUserDataInternal(Account account, String key) {
- synchronized (mCacheLock) {
- HashMap<String, String> userDataForAccount = mUserDataCache.get(account);
+ protected String readUserDataInternal(UserAccounts accounts, Account account, String key) {
+ synchronized (accounts.cacheLock) {
+ HashMap<String, String> userDataForAccount = accounts.userDataCache.get(account);
if (userDataForAccount == null) {
// need to populate the cache for this account
- final SQLiteDatabase db = mOpenHelper.getReadableDatabase();
+ final SQLiteDatabase db = accounts.openHelper.getReadableDatabase();
userDataForAccount = readUserDataForAccountFromDatabaseLocked(db, account);
- mUserDataCache.put(account, userDataForAccount);
+ accounts.userDataCache.put(account, userDataForAccount);
}
return userDataForAccount.get(key);
}
diff --git a/core/java/android/accounts/GrantCredentialsPermissionActivity.java b/core/java/android/accounts/GrantCredentialsPermissionActivity.java
index 0ee683c..4419c8c 100644
--- a/core/java/android/accounts/GrantCredentialsPermissionActivity.java
+++ b/core/java/android/accounts/GrantCredentialsPermissionActivity.java
@@ -113,8 +113,7 @@ public class GrantCredentialsPermissionActivity extends Activity implements View
}
};
- accountManagerService.getAuthTokenLabel(
- response, mAccount, mAuthTokenType);
+ accountManagerService.getAuthTokenLabel(response, mAccount, mAuthTokenType, mUid);
findViewById(R.id.allow_button).setOnClickListener(this);
findViewById(R.id.deny_button).setOnClickListener(this);
diff --git a/core/java/android/accounts/OnAccountsUpdateListener.java b/core/java/android/accounts/OnAccountsUpdateListener.java
index 38b371d..2b4ee50 100644
--- a/core/java/android/accounts/OnAccountsUpdateListener.java
+++ b/core/java/android/accounts/OnAccountsUpdateListener.java
@@ -17,11 +17,11 @@
package android.accounts;
/**
- * An interface that contains the callback used by the AccountMonitor
+ * An interface that contains the callback used by the AccountManager
*/
public interface OnAccountsUpdateListener {
/**
- * This invoked when the AccountMonitor starts up and whenever the account
+ * This invoked when the AccountManager starts up and whenever the account
* set changes.
* @param accounts the current accounts
*/
diff --git a/core/java/android/animation/Animator.java b/core/java/android/animation/Animator.java
index e01fa1a..788765d 100644
--- a/core/java/android/animation/Animator.java
+++ b/core/java/android/animation/Animator.java
@@ -208,7 +208,7 @@ public abstract class Animator implements Cloneable {
* this call to its child objects to tell them to set up the values. A
* ObjectAnimator object will use the information it has about its target object
* and PropertyValuesHolder objects to get the start values for its properties.
- * An ValueAnimator object will ignore the request since it does not have enough
+ * A ValueAnimator object will ignore the request since it does not have enough
* information (such as a target object) to gather these values.
*/
public void setupStartValues() {
@@ -220,7 +220,7 @@ public abstract class Animator implements Cloneable {
* this call to its child objects to tell them to set up the values. A
* ObjectAnimator object will use the information it has about its target object
* and PropertyValuesHolder objects to get the start values for its properties.
- * An ValueAnimator object will ignore the request since it does not have enough
+ * A ValueAnimator object will ignore the request since it does not have enough
* information (such as a target object) to gather these values.
*/
public void setupEndValues() {
diff --git a/core/java/android/animation/ValueAnimator.java b/core/java/android/animation/ValueAnimator.java
index 6fbeee3..f69120a 100755
--- a/core/java/android/animation/ValueAnimator.java
+++ b/core/java/android/animation/ValueAnimator.java
@@ -400,7 +400,7 @@ public class ValueAnimator extends Animator {
/**
* Sets the values, per property, being animated between. This function is called internally
- * by the constructors of ValueAnimator that take a list of values. But an ValueAnimator can
+ * by the constructors of ValueAnimator that take a list of values. But a ValueAnimator can
* be constructed without values and this method can be called to set the values manually
* instead.
*
@@ -645,7 +645,7 @@ public class ValueAnimator extends Animator {
// onAnimate to process the next frame of the animations.
if (!mAnimationScheduled
&& (!mAnimations.isEmpty() || !mDelayedAnims.isEmpty())) {
- mChoreographer.postAnimationCallback(this);
+ mChoreographer.postAnimationCallback(this, null);
mAnimationScheduled = true;
}
}
diff --git a/core/java/android/app/Activity.java b/core/java/android/app/Activity.java
index 8e8d37d..599487d 100644
--- a/core/java/android/app/Activity.java
+++ b/core/java/android/app/Activity.java
@@ -203,8 +203,8 @@ import java.util.HashMap;
* with the user. Between these two methods you can maintain resources that
* are needed to show the activity to the user. For example, you can register
* a {@link android.content.BroadcastReceiver} in onStart() to monitor for changes
- * that impact your UI, and unregister it in onStop() when the user an no
- * longer see what you are displaying. The onStart() and onStop() methods
+ * that impact your UI, and unregister it in onStop() when the user no
+ * longer sees what you are displaying. The onStart() and onStop() methods
* can be called multiple times, as the activity becomes visible and hidden
* to the user.
*
@@ -570,7 +570,18 @@ import java.util.HashMap;
* tag. By doing so, other applications will need to declare a corresponding
* {@link android.R.styleable#AndroidManifestUsesPermission &lt;uses-permission&gt;}
* element in their own manifest to be able to start that activity.
- *
+ *
+ * <p>When starting an Activity you can set {@link Intent#FLAG_GRANT_READ_URI_PERMISSION
+ * Intent.FLAG_GRANT_READ_URI_PERMISSION} and/or {@link Intent#FLAG_GRANT_WRITE_URI_PERMISSION
+ * Intent.FLAG_GRANT_WRITE_URI_PERMISSION} on the Intent. This will grant the
+ * Activity access to the specific URIs in the Intent. Access will remain
+ * until the Activity has finished (it will remain across the hosting
+ * process being killed and other temporary destruction). As of
+ * {@link android.os.Build.VERSION_CODES#GINGERBREAD}, if the Activity
+ * was already created and a new Intent is being delivered to
+ * {@link #onNewIntent(Intent)}, any newly granted URI permissions will be added
+ * to the existing ones it holds.
+ *
* <p>See the <a href="{@docRoot}guide/topics/security/security.html">Security and Permissions</a>
* document for more information on permissions and security in general.
*
@@ -3549,7 +3560,16 @@ public class Activity extends ContextThemeWrapper
/**
* Call this to set the result that your activity will return to its
* caller.
- *
+ *
+ * <p>As of {@link android.os.Build.VERSION_CODES#GINGERBREAD}, the Intent
+ * you supply here can have {@link Intent#FLAG_GRANT_READ_URI_PERMISSION
+ * Intent.FLAG_GRANT_READ_URI_PERMISSION} and/or {@link Intent#FLAG_GRANT_WRITE_URI_PERMISSION
+ * Intent.FLAG_GRANT_WRITE_URI_PERMISSION} set. This will grant the
+ * Activity receiving the result access to the specific URIs in the Intent.
+ * Access will remain until the Activity has finished (it will remain across the hosting
+ * process being killed and other temporary destruction) and will be added
+ * to any existing set of URI permissions it already holds.
+ *
* @param resultCode The result code to propagate back to the originating
* activity, often RESULT_CANCELED or RESULT_OK
* @param data The data to propagate back to the originating activity.
diff --git a/core/java/android/app/ActivityManager.java b/core/java/android/app/ActivityManager.java
index d98d87b..59c803e 100644
--- a/core/java/android/app/ActivityManager.java
+++ b/core/java/android/app/ActivityManager.java
@@ -1145,7 +1145,14 @@ public class ActivityManager {
* @hide
*/
public int flags;
-
+
+ /**
+ * Last memory trim level reported to the process: corresponds to
+ * the values supplied to {@link android.content.ComponentCallbacks2#onTrimMemory(int)
+ * ComponentCallbacks2.onTrimMemory(int)}.
+ */
+ public int lastTrimLevel;
+
/**
* Constant for {@link #importance}: this process is running the
* foreground UI.
@@ -1212,7 +1219,7 @@ public class ActivityManager {
* be maintained in the future.
*/
public int lru;
-
+
/**
* Constant for {@link #importanceReasonCode}: nothing special has
* been specified for the reason for this level.
@@ -1282,6 +1289,7 @@ public class ActivityManager {
dest.writeInt(uid);
dest.writeStringArray(pkgList);
dest.writeInt(this.flags);
+ dest.writeInt(lastTrimLevel);
dest.writeInt(importance);
dest.writeInt(lru);
dest.writeInt(importanceReasonCode);
@@ -1296,6 +1304,7 @@ public class ActivityManager {
uid = source.readInt();
pkgList = source.readStringArray();
flags = source.readInt();
+ lastTrimLevel = source.readInt();
importance = source.readInt();
lru = source.readInt();
importanceReasonCode = source.readInt();
@@ -1349,7 +1358,25 @@ public class ActivityManager {
return null;
}
}
-
+
+ /**
+ * Return global memory state information for the calling process. This
+ * does not fill in all fields of the {@link RunningAppProcessInfo}. The
+ * only fields that will be filled in are
+ * {@link RunningAppProcessInfo#pid},
+ * {@link RunningAppProcessInfo#uid},
+ * {@link RunningAppProcessInfo#lastTrimLevel},
+ * {@link RunningAppProcessInfo#importance},
+ * {@link RunningAppProcessInfo#lru}, and
+ * {@link RunningAppProcessInfo#importanceReasonCode}.
+ */
+ static public void getMyMemoryState(RunningAppProcessInfo outState) {
+ try {
+ ActivityManagerNative.getDefault().getMyMemoryState(outState);
+ } catch (RemoteException e) {
+ }
+ }
+
/**
* Return information about the memory usage of one or more processes.
*
diff --git a/core/java/android/app/ActivityManagerNative.java b/core/java/android/app/ActivityManagerNative.java
index 24079a5..b952649 100644
--- a/core/java/android/app/ActivityManagerNative.java
+++ b/core/java/android/app/ActivityManagerNative.java
@@ -1126,7 +1126,17 @@ public abstract class ActivityManagerNative extends Binder implements IActivityM
reply.writeNoException();
return true;
}
-
+
+ case GET_MY_MEMORY_STATE_TRANSACTION: {
+ data.enforceInterface(IActivityManager.descriptor);
+ ActivityManager.RunningAppProcessInfo info =
+ new ActivityManager.RunningAppProcessInfo();
+ getMyMemoryState(info);
+ reply.writeNoException();
+ info.writeToParcel(reply, 0);
+ return true;
+ }
+
case GET_DEVICE_CONFIGURATION_TRANSACTION: {
data.enforceInterface(IActivityManager.descriptor);
ConfigurationInfo config = getDeviceConfigurationInfo();
@@ -2973,6 +2983,19 @@ class ActivityManagerProxy implements IActivityManager
reply.recycle();
}
+ public void getMyMemoryState(ActivityManager.RunningAppProcessInfo outInfo)
+ throws RemoteException
+ {
+ Parcel data = Parcel.obtain();
+ Parcel reply = Parcel.obtain();
+ data.writeInterfaceToken(IActivityManager.descriptor);
+ mRemote.transact(GET_MY_MEMORY_STATE_TRANSACTION, data, reply, 0);
+ reply.readException();
+ outInfo.readFromParcel(reply);
+ reply.recycle();
+ data.recycle();
+ }
+
public ConfigurationInfo getDeviceConfigurationInfo() throws RemoteException
{
Parcel data = Parcel.obtain();
diff --git a/core/java/android/app/ApplicationErrorReport.java b/core/java/android/app/ApplicationErrorReport.java
index 588125d..ebf4261 100644
--- a/core/java/android/app/ApplicationErrorReport.java
+++ b/core/java/android/app/ApplicationErrorReport.java
@@ -98,7 +98,7 @@ public class ApplicationErrorReport implements Parcelable {
/**
* Package name of the application which installed the application this
* report pertains to.
- * This identifies which Market the application came from.
+ * This identifies which market the application came from.
*/
public String installerPackageName;
diff --git a/core/java/android/app/Dialog.java b/core/java/android/app/Dialog.java
index f1ce2bb..f04ff6a 100644
--- a/core/java/android/app/Dialog.java
+++ b/core/java/android/app/Dialog.java
@@ -591,7 +591,7 @@ public class Dialog implements DialogInterface, Window.Callback,
}
/**
- * Called when an key shortcut event is not handled by any of the views in the Dialog.
+ * Called when a key shortcut event is not handled by any of the views in the Dialog.
* Override this method to implement global key shortcuts for the Dialog.
* Key shortcuts can also be implemented by setting the
* {@link MenuItem#setShortcut(char, char) shortcut} property of menu items.
diff --git a/core/java/android/app/IActivityManager.java b/core/java/android/app/IActivityManager.java
index 53a71db..ea2545f 100644
--- a/core/java/android/app/IActivityManager.java
+++ b/core/java/android/app/IActivityManager.java
@@ -278,13 +278,16 @@ public interface IActivityManager extends IInterface {
* SIGUSR1 is delivered. All others are ignored.
*/
public void signalPersistentProcesses(int signal) throws RemoteException;
- // Retrieve info of applications installed on external media that are currently
- // running.
+ // Retrieve running application processes in the system
public List<ActivityManager.RunningAppProcessInfo> getRunningAppProcesses()
throws RemoteException;
- // Retrieve running application processes in the system
+ // Retrieve info of applications installed on external media that are currently
+ // running.
public List<ApplicationInfo> getRunningExternalApplications()
throws RemoteException;
+ // Get memory information about the calling process.
+ public void getMyMemoryState(ActivityManager.RunningAppProcessInfo outInfo)
+ throws RemoteException;
// Get device configuration
public ConfigurationInfo getDeviceConfigurationInfo() throws RemoteException;
@@ -606,4 +609,5 @@ public interface IActivityManager extends IInterface {
int KILL_ALL_BACKGROUND_PROCESSES_TRANSACTION = IBinder.FIRST_CALL_TRANSACTION+139;
int GET_CONTENT_PROVIDER_EXTERNAL_TRANSACTION = IBinder.FIRST_CALL_TRANSACTION+140;
int REMOVE_CONTENT_PROVIDER_EXTERNAL_TRANSACTION = IBinder.FIRST_CALL_TRANSACTION+141;
+ int GET_MY_MEMORY_STATE_TRANSACTION = IBinder.FIRST_CALL_TRANSACTION+142;
}
diff --git a/core/java/android/app/PendingIntent.java b/core/java/android/app/PendingIntent.java
index b0637a7..c95066c 100644
--- a/core/java/android/app/PendingIntent.java
+++ b/core/java/android/app/PendingIntent.java
@@ -351,7 +351,7 @@ public final class PendingIntent implements Parcelable {
/**
* Cancel a currently active PendingIntent. Only the original application
- * owning an PendingIntent can cancel it.
+ * owning a PendingIntent can cancel it.
*/
public void cancel() {
try {
diff --git a/core/java/android/app/Service.java b/core/java/android/app/Service.java
index 35bd8c0..207ae76 100644
--- a/core/java/android/app/Service.java
+++ b/core/java/android/app/Service.java
@@ -163,7 +163,19 @@ import java.io.PrintWriter;
* {@link android.R.styleable#AndroidManifestUsesPermission &lt;uses-permission&gt;}
* element in their own manifest to be able to start, stop, or bind to
* the service.
- *
+ *
+ * <p>As of {@link android.os.Build.VERSION_CODES#GINGERBREAD}, when using
+ * {@link Context#startService(Intent) Context.startService(Intent)}, you can
+ * also set {@link Intent#FLAG_GRANT_READ_URI_PERMISSION
+ * Intent.FLAG_GRANT_READ_URI_PERMISSION} and/or {@link Intent#FLAG_GRANT_WRITE_URI_PERMISSION
+ * Intent.FLAG_GRANT_WRITE_URI_PERMISSION} on the Intent. This will grant the
+ * Service temporary access to the specific URIs in the Intent. Access will
+ * remain until the Service has called {@link #stopSelf(int)} for that start
+ * command or a later one, or until the Service has been completely stopped.
+ * This works for granting access to the other apps that have not requested
+ * the permission protecting the Service, or even when the Service is not
+ * exported at all.
+ *
* <p>In addition, a service can protect individual IPC calls into it with
* permissions, by calling the
* {@link #checkCallingPermission}
@@ -441,7 +453,7 @@ public abstract class Service extends ContextWrapper implements ComponentCallbac
/**
* Called by the system to notify a Service that it is no longer used and is being removed. The
- * service should clean up an resources it holds (threads, registered
+ * service should clean up any resources it holds (threads, registered
* receivers, etc) at this point. Upon return, there will be no more calls
* in to this Service object and it is effectively dead. Do not call this method directly.
*/
diff --git a/core/java/android/app/admin/DeviceAdminReceiver.java b/core/java/android/app/admin/DeviceAdminReceiver.java
index 43cd330..30b65de 100644
--- a/core/java/android/app/admin/DeviceAdminReceiver.java
+++ b/core/java/android/app/admin/DeviceAdminReceiver.java
@@ -165,7 +165,7 @@ public class DeviceAdminReceiver extends BroadcastReceiver {
= "android.app.action.ACTION_PASSWORD_EXPIRING";
/**
- * Name under which an DevicePolicy component publishes information
+ * Name under which a DevicePolicy component publishes information
* about itself. This meta-data must reference an XML resource containing
* a device-admin tag. XXX TO DO: describe syntax.
*/
diff --git a/core/java/android/bluetooth/AtCommandResult.java b/core/java/android/bluetooth/AtCommandResult.java
index 375a6dd..9675234 100644
--- a/core/java/android/bluetooth/AtCommandResult.java
+++ b/core/java/android/bluetooth/AtCommandResult.java
@@ -17,7 +17,7 @@
package android.bluetooth;
/**
- * The result of execution of an single AT command.<p>
+ * The result of execution of a single AT command.<p>
*
*
* This class can represent the final response to an AT command line, and also
diff --git a/core/java/android/bluetooth/BluetoothDevice.java b/core/java/android/bluetooth/BluetoothDevice.java
index 189e8fc..04af5f7 100644
--- a/core/java/android/bluetooth/BluetoothDevice.java
+++ b/core/java/android/bluetooth/BluetoothDevice.java
@@ -193,7 +193,7 @@ public final class BluetoothDevice implements Parcelable {
public static final String EXTRA_RSSI = "android.bluetooth.device.extra.RSSI";
/**
- * Used as an Parcelable {@link BluetoothClass} extra field in {@link
+ * Used as a Parcelable {@link BluetoothClass} extra field in {@link
* #ACTION_FOUND} and {@link #ACTION_CLASS_CHANGED} intents.
*/
public static final String EXTRA_CLASS = "android.bluetooth.device.extra.CLASS";
diff --git a/core/java/android/bluetooth/BluetoothPbap.java b/core/java/android/bluetooth/BluetoothPbap.java
index 2683bef..639ae1a 100644
--- a/core/java/android/bluetooth/BluetoothPbap.java
+++ b/core/java/android/bluetooth/BluetoothPbap.java
@@ -60,7 +60,7 @@ public class BluetoothPbap {
public static final String PBAP_PREVIOUS_STATE =
"android.bluetooth.pbap.intent.PBAP_PREVIOUS_STATE";
- /** Indicates the state of an pbap connection state has changed.
+ /** Indicates the state of a pbap connection state has changed.
* This intent will always contain PBAP_STATE, PBAP_PREVIOUS_STATE and
* BluetoothIntent.ADDRESS extras.
*/
diff --git a/core/java/android/content/ClipData.java b/core/java/android/content/ClipData.java
index a8b1bf4..a655dd4 100644
--- a/core/java/android/content/ClipData.java
+++ b/core/java/android/content/ClipData.java
@@ -153,7 +153,7 @@ public class ClipData implements Parcelable {
final Bitmap mIcon;
- final ArrayList<Item> mItems = new ArrayList<Item>();
+ final ArrayList<Item> mItems;
/**
* Description of a single item in a ClippedData.
@@ -321,6 +321,33 @@ public class ClipData implements Parcelable {
return "";
}
//END_INCLUDE(coerceToText)
+
+ @Override
+ public String toString() {
+ StringBuilder b = new StringBuilder(128);
+
+ b.append("ClipData.Item { ");
+ toShortString(b);
+ b.append(" }");
+
+ return b.toString();
+ }
+
+ /** @hide */
+ public void toShortString(StringBuilder b) {
+ if (mText != null) {
+ b.append("T:");
+ b.append(mText);
+ } else if (mUri != null) {
+ b.append("U:");
+ b.append(mUri);
+ } else if (mIntent != null) {
+ b.append("I:");
+ mIntent.toShortString(b, true, true, true, true);
+ } else {
+ b.append("NULL");
+ }
+ }
}
/**
@@ -336,6 +363,7 @@ public class ClipData implements Parcelable {
throw new NullPointerException("item is null");
}
mIcon = null;
+ mItems = new ArrayList<Item>();
mItems.add(item);
}
@@ -351,10 +379,23 @@ public class ClipData implements Parcelable {
throw new NullPointerException("item is null");
}
mIcon = null;
+ mItems = new ArrayList<Item>();
mItems.add(item);
}
/**
+ * Create a new clip that is a copy of another clip. This does a deep-copy
+ * of all items in the clip.
+ *
+ * @param other The existing ClipData that is to be copied.
+ */
+ public ClipData(ClipData other) {
+ mClipDescription = other.mClipDescription;
+ mIcon = other.mIcon;
+ mItems = new ArrayList<Item>(other.mItems);
+ }
+
+ /**
* Create a new ClipData holding data of the type
* {@link ClipDescription#MIMETYPE_TEXT_PLAIN}.
*
@@ -475,6 +516,46 @@ public class ClipData implements Parcelable {
}
@Override
+ public String toString() {
+ StringBuilder b = new StringBuilder(128);
+
+ b.append("ClipData { ");
+ toShortString(b);
+ b.append(" }");
+
+ return b.toString();
+ }
+
+ /** @hide */
+ public void toShortString(StringBuilder b) {
+ boolean first;
+ if (mClipDescription != null) {
+ first = !mClipDescription.toShortString(b);
+ } else {
+ first = true;
+ }
+ if (mIcon != null) {
+ if (!first) {
+ b.append(' ');
+ }
+ first = false;
+ b.append("I:");
+ b.append(mIcon.getWidth());
+ b.append('x');
+ b.append(mIcon.getHeight());
+ }
+ for (int i=0; i<mItems.size(); i++) {
+ if (!first) {
+ b.append(' ');
+ }
+ first = false;
+ b.append('{');
+ mItems.get(i).toShortString(b);
+ b.append('}');
+ }
+ }
+
+ @Override
public int describeContents() {
return 0;
}
@@ -515,6 +596,7 @@ public class ClipData implements Parcelable {
} else {
mIcon = null;
}
+ mItems = new ArrayList<Item>();
final int N = in.readInt();
for (int i=0; i<N; i++) {
CharSequence text = TextUtils.CHAR_SEQUENCE_CREATOR.createFromParcel(in);
diff --git a/core/java/android/content/ClipDescription.java b/core/java/android/content/ClipDescription.java
index b5fa20c..c6b51ef 100644
--- a/core/java/android/content/ClipDescription.java
+++ b/core/java/android/content/ClipDescription.java
@@ -184,6 +184,39 @@ public class ClipDescription implements Parcelable {
}
@Override
+ public String toString() {
+ StringBuilder b = new StringBuilder(128);
+
+ b.append("ClipDescription { ");
+ toShortString(b);
+ b.append(" }");
+
+ return b.toString();
+ }
+
+ /** @hide */
+ public boolean toShortString(StringBuilder b) {
+ boolean first = true;
+ for (int i=0; i<mMimeTypes.length; i++) {
+ if (!first) {
+ b.append(' ');
+ }
+ first = false;
+ b.append(mMimeTypes[i]);
+ }
+ if (mLabel != null) {
+ if (!first) {
+ b.append(' ');
+ }
+ first = false;
+ b.append('"');
+ b.append(mLabel);
+ b.append('"');
+ }
+ return !first;
+ }
+
+ @Override
public int describeContents() {
return 0;
}
diff --git a/core/java/android/content/ComponentCallbacks2.java b/core/java/android/content/ComponentCallbacks2.java
index 8b9f97c..85294dd 100644
--- a/core/java/android/content/ComponentCallbacks2.java
+++ b/core/java/android/content/ComponentCallbacks2.java
@@ -52,15 +52,49 @@ public interface ComponentCallbacks2 extends ComponentCallbacks {
static final int TRIM_MEMORY_UI_HIDDEN = 20;
/**
+ * Level for {@link #onTrimMemory(int)}: the process is not an expendable
+ * background process, but the device is running extremely low on memory
+ * and is about to not be able to keep any background processes running.
+ * Your running process should free up as many non-critical resources as it
+ * can to allow that memory to be used elsewhere. The next thing that
+ * will happen after this is {@link #onLowMemory()} called to report that
+ * nothing at all can be kept in the background, a situation that can start
+ * to notably impact the user.
+ */
+ static final int TRIM_MEMORY_RUNNING_CRITICAL = 15;
+
+ /**
+ * Level for {@link #onTrimMemory(int)}: the process is not an expendable
+ * background process, but the device is running low on memory.
+ * Your running process should free up unneeded resources to allow that
+ * memory to be used elsewhere.
+ */
+ static final int TRIM_MEMORY_RUNNING_LOW = 10;
+
+
+ /**
+ * Level for {@link #onTrimMemory(int)}: the process is not an expendable
+ * background process, but the device is running moderately low on memory.
+ * Your running process may want to release some unneeded resources for
+ * use elsewhere.
+ */
+ static final int TRIM_MEMORY_RUNNING_MODERATE = 5;
+
+ /**
* Called when the operating system has determined that it is a good
* time for a process to trim unneeded memory from its process. This will
* happen for example when it goes in the background and there is not enough
- * memory to keep as many background processes running as desired.
+ * memory to keep as many background processes running as desired. You
+ * should never compare to exact values of the level, since new intermediate
+ * values may be added -- you will typically want to compare if the value
+ * is greater or equal to a level you are interested in.
*
* @param level The context of the trim, giving a hint of the amount of
* trimming the application may like to perform. May be
* {@link #TRIM_MEMORY_COMPLETE}, {@link #TRIM_MEMORY_MODERATE},
- * {@link #TRIM_MEMORY_BACKGROUND}, or {@link #TRIM_MEMORY_UI_HIDDEN}.
+ * {@link #TRIM_MEMORY_BACKGROUND}, {@link #TRIM_MEMORY_UI_HIDDEN},
+ * {@link #TRIM_MEMORY_RUNNING_CRITICAL}, {@link #TRIM_MEMORY_RUNNING_LOW},
+ * or {@link #TRIM_MEMORY_RUNNING_MODERATE}.
*/
void onTrimMemory(int level);
}
diff --git a/core/java/android/content/ContentResolver.java b/core/java/android/content/ContentResolver.java
index 7a612bc..2930998 100644
--- a/core/java/android/content/ContentResolver.java
+++ b/core/java/android/content/ContentResolver.java
@@ -917,7 +917,7 @@ public abstract class ContentResolver {
}
/**
- * Call an provider-defined method. This can be used to implement
+ * Call a provider-defined method. This can be used to implement
* read or write interfaces which are cheaper than using a Cursor and/or
* do not fit into the traditional table model.
*
diff --git a/core/java/android/content/ContentService.java b/core/java/android/content/ContentService.java
index fc4c262..f827c3d 100644
--- a/core/java/android/content/ContentService.java
+++ b/core/java/android/content/ContentService.java
@@ -26,6 +26,7 @@ import android.os.IBinder;
import android.os.Parcel;
import android.os.RemoteException;
import android.os.ServiceManager;
+import android.os.UserId;
import android.util.Log;
import android.util.SparseIntArray;
import android.Manifest;
@@ -163,6 +164,8 @@ public final class ContentService extends IContentService.Stub {
Log.v(TAG, "Notifying update of " + uri + " from observer " + observer
+ ", syncToNetwork " + syncToNetwork);
}
+
+ int userId = UserId.getCallingUserId();
// This makes it so that future permission checks will be in the context of this
// process rather than the caller's process. We will restore this before returning.
long identityToken = clearCallingIdentity();
@@ -201,7 +204,8 @@ public final class ContentService extends IContentService.Stub {
if (syncToNetwork) {
SyncManager syncManager = getSyncManager();
if (syncManager != null) {
- syncManager.scheduleLocalSync(null /* all accounts */, uri.getAuthority());
+ syncManager.scheduleLocalSync(null /* all accounts */, userId,
+ uri.getAuthority());
}
}
} finally {
@@ -229,13 +233,15 @@ public final class ContentService extends IContentService.Stub {
public void requestSync(Account account, String authority, Bundle extras) {
ContentResolver.validateSyncExtrasBundle(extras);
+ int userId = UserId.getCallingUserId();
+
// This makes it so that future permission checks will be in the context of this
// process rather than the caller's process. We will restore this before returning.
long identityToken = clearCallingIdentity();
try {
SyncManager syncManager = getSyncManager();
if (syncManager != null) {
- syncManager.scheduleSync(account, authority, extras, 0 /* no delay */,
+ syncManager.scheduleSync(account, userId, authority, extras, 0 /* no delay */,
false /* onlyThoseWithUnkownSyncableState */);
}
} finally {
@@ -250,14 +256,16 @@ public final class ContentService extends IContentService.Stub {
* @param authority filter the pending and active syncs to cancel using this authority
*/
public void cancelSync(Account account, String authority) {
+ int userId = UserId.getCallingUserId();
+
// This makes it so that future permission checks will be in the context of this
// process rather than the caller's process. We will restore this before returning.
long identityToken = clearCallingIdentity();
try {
SyncManager syncManager = getSyncManager();
if (syncManager != null) {
- syncManager.clearScheduledSyncOperations(account, authority);
- syncManager.cancelActiveSync(account, authority);
+ syncManager.clearScheduledSyncOperations(account, userId, authority);
+ syncManager.cancelActiveSync(account, userId, authority);
}
} finally {
restoreCallingIdentity(identityToken);
@@ -283,12 +291,14 @@ public final class ContentService extends IContentService.Stub {
public boolean getSyncAutomatically(Account account, String providerName) {
mContext.enforceCallingOrSelfPermission(Manifest.permission.READ_SYNC_SETTINGS,
"no permission to read the sync settings");
+ int userId = UserId.getCallingUserId();
+
long identityToken = clearCallingIdentity();
try {
SyncManager syncManager = getSyncManager();
if (syncManager != null) {
return syncManager.getSyncStorageEngine().getSyncAutomatically(
- account, providerName);
+ account, userId, providerName);
}
} finally {
restoreCallingIdentity(identityToken);
@@ -299,12 +309,14 @@ public final class ContentService extends IContentService.Stub {
public void setSyncAutomatically(Account account, String providerName, boolean sync) {
mContext.enforceCallingOrSelfPermission(Manifest.permission.WRITE_SYNC_SETTINGS,
"no permission to write the sync settings");
+ int userId = UserId.getCallingUserId();
+
long identityToken = clearCallingIdentity();
try {
SyncManager syncManager = getSyncManager();
if (syncManager != null) {
syncManager.getSyncStorageEngine().setSyncAutomatically(
- account, providerName, sync);
+ account, userId, providerName, sync);
}
} finally {
restoreCallingIdentity(identityToken);
@@ -315,10 +327,12 @@ public final class ContentService extends IContentService.Stub {
long pollFrequency) {
mContext.enforceCallingOrSelfPermission(Manifest.permission.WRITE_SYNC_SETTINGS,
"no permission to write the sync settings");
+ int userId = UserId.getCallingUserId();
+
long identityToken = clearCallingIdentity();
try {
getSyncManager().getSyncStorageEngine().addPeriodicSync(
- account, authority, extras, pollFrequency);
+ account, userId, authority, extras, pollFrequency);
} finally {
restoreCallingIdentity(identityToken);
}
@@ -327,9 +341,12 @@ public final class ContentService extends IContentService.Stub {
public void removePeriodicSync(Account account, String authority, Bundle extras) {
mContext.enforceCallingOrSelfPermission(Manifest.permission.WRITE_SYNC_SETTINGS,
"no permission to write the sync settings");
+ int userId = UserId.getCallingUserId();
+
long identityToken = clearCallingIdentity();
try {
- getSyncManager().getSyncStorageEngine().removePeriodicSync(account, authority, extras);
+ getSyncManager().getSyncStorageEngine().removePeriodicSync(account, userId, authority,
+ extras);
} finally {
restoreCallingIdentity(identityToken);
}
@@ -338,10 +355,12 @@ public final class ContentService extends IContentService.Stub {
public List<PeriodicSync> getPeriodicSyncs(Account account, String providerName) {
mContext.enforceCallingOrSelfPermission(Manifest.permission.READ_SYNC_SETTINGS,
"no permission to read the sync settings");
+ int userId = UserId.getCallingUserId();
+
long identityToken = clearCallingIdentity();
try {
return getSyncManager().getSyncStorageEngine().getPeriodicSyncs(
- account, providerName);
+ account, userId, providerName);
} finally {
restoreCallingIdentity(identityToken);
}
@@ -350,12 +369,14 @@ public final class ContentService extends IContentService.Stub {
public int getIsSyncable(Account account, String providerName) {
mContext.enforceCallingOrSelfPermission(Manifest.permission.READ_SYNC_SETTINGS,
"no permission to read the sync settings");
+ int userId = UserId.getCallingUserId();
+
long identityToken = clearCallingIdentity();
try {
SyncManager syncManager = getSyncManager();
if (syncManager != null) {
return syncManager.getSyncStorageEngine().getIsSyncable(
- account, providerName);
+ account, userId, providerName);
}
} finally {
restoreCallingIdentity(identityToken);
@@ -366,12 +387,14 @@ public final class ContentService extends IContentService.Stub {
public void setIsSyncable(Account account, String providerName, int syncable) {
mContext.enforceCallingOrSelfPermission(Manifest.permission.WRITE_SYNC_SETTINGS,
"no permission to write the sync settings");
+ int userId = UserId.getCallingUserId();
+
long identityToken = clearCallingIdentity();
try {
SyncManager syncManager = getSyncManager();
if (syncManager != null) {
syncManager.getSyncStorageEngine().setIsSyncable(
- account, providerName, syncable);
+ account, userId, providerName, syncable);
}
} finally {
restoreCallingIdentity(identityToken);
@@ -381,11 +404,13 @@ public final class ContentService extends IContentService.Stub {
public boolean getMasterSyncAutomatically() {
mContext.enforceCallingOrSelfPermission(Manifest.permission.READ_SYNC_SETTINGS,
"no permission to read the sync settings");
+ int userId = UserId.getCallingUserId();
+
long identityToken = clearCallingIdentity();
try {
SyncManager syncManager = getSyncManager();
if (syncManager != null) {
- return syncManager.getSyncStorageEngine().getMasterSyncAutomatically();
+ return syncManager.getSyncStorageEngine().getMasterSyncAutomatically(userId);
}
} finally {
restoreCallingIdentity(identityToken);
@@ -396,11 +421,13 @@ public final class ContentService extends IContentService.Stub {
public void setMasterSyncAutomatically(boolean flag) {
mContext.enforceCallingOrSelfPermission(Manifest.permission.WRITE_SYNC_SETTINGS,
"no permission to write the sync settings");
+ int userId = UserId.getCallingUserId();
+
long identityToken = clearCallingIdentity();
try {
SyncManager syncManager = getSyncManager();
if (syncManager != null) {
- syncManager.getSyncStorageEngine().setMasterSyncAutomatically(flag);
+ syncManager.getSyncStorageEngine().setMasterSyncAutomatically(flag, userId);
}
} finally {
restoreCallingIdentity(identityToken);
@@ -410,12 +437,14 @@ public final class ContentService extends IContentService.Stub {
public boolean isSyncActive(Account account, String authority) {
mContext.enforceCallingOrSelfPermission(Manifest.permission.READ_SYNC_STATS,
"no permission to read the sync stats");
+ int userId = UserId.getCallingUserId();
+
long identityToken = clearCallingIdentity();
try {
SyncManager syncManager = getSyncManager();
if (syncManager != null) {
return syncManager.getSyncStorageEngine().isSyncActive(
- account, authority);
+ account, userId, authority);
}
} finally {
restoreCallingIdentity(identityToken);
@@ -426,9 +455,11 @@ public final class ContentService extends IContentService.Stub {
public List<SyncInfo> getCurrentSyncs() {
mContext.enforceCallingOrSelfPermission(Manifest.permission.READ_SYNC_STATS,
"no permission to read the sync stats");
+ int userId = UserId.getCallingUserId();
+
long identityToken = clearCallingIdentity();
try {
- return getSyncManager().getSyncStorageEngine().getCurrentSyncs();
+ return getSyncManager().getSyncStorageEngine().getCurrentSyncs(userId);
} finally {
restoreCallingIdentity(identityToken);
}
@@ -437,12 +468,14 @@ public final class ContentService extends IContentService.Stub {
public SyncStatusInfo getSyncStatus(Account account, String authority) {
mContext.enforceCallingOrSelfPermission(Manifest.permission.READ_SYNC_STATS,
"no permission to read the sync stats");
+ int userId = UserId.getCallingUserId();
+
long identityToken = clearCallingIdentity();
try {
SyncManager syncManager = getSyncManager();
if (syncManager != null) {
return syncManager.getSyncStorageEngine().getStatusByAccountAndAuthority(
- account, authority);
+ account, userId, authority);
}
} finally {
restoreCallingIdentity(identityToken);
@@ -453,11 +486,13 @@ public final class ContentService extends IContentService.Stub {
public boolean isSyncPending(Account account, String authority) {
mContext.enforceCallingOrSelfPermission(Manifest.permission.READ_SYNC_STATS,
"no permission to read the sync stats");
+ int userId = UserId.getCallingUserId();
+
long identityToken = clearCallingIdentity();
try {
SyncManager syncManager = getSyncManager();
if (syncManager != null) {
- return syncManager.getSyncStorageEngine().isSyncPending(account, authority);
+ return syncManager.getSyncStorageEngine().isSyncPending(account, userId, authority);
}
} finally {
restoreCallingIdentity(identityToken);
diff --git a/core/java/android/content/Context.java b/core/java/android/content/Context.java
index 111f45e..0e9e256 100644
--- a/core/java/android/content/Context.java
+++ b/core/java/android/content/Context.java
@@ -1270,7 +1270,7 @@ public abstract class Context {
/**
* Connect to an application service, creating it if needed. This defines
* a dependency between your application and the service. The given
- * <var>conn</var> will receive the service object when its created and be
+ * <var>conn</var> will receive the service object when it is created and be
* told if it dies and restarts. The service will be considered required
* by the system only for as long as the calling context exists. For
* example, if this Context is an Activity that is stopped, the service will
@@ -1279,15 +1279,15 @@ public abstract class Context {
* <p>This function will throw {@link SecurityException} if you do not
* have permission to bind to the given service.
*
- * <p class="note">Note: this method <em>can not be called from an
+ * <p class="note">Note: this method <em>can not be called from a
* {@link BroadcastReceiver} component</em>. A pattern you can use to
- * communicate from an BroadcastReceiver to a Service is to call
+ * communicate from a BroadcastReceiver to a Service is to call
* {@link #startService} with the arguments containing the command to be
* sent, with the service calling its
* {@link android.app.Service#stopSelf(int)} method when done executing
* that command. See the API demo App/Service/Service Start Arguments
* Controller for an illustration of this. It is okay, however, to use
- * this method from an BroadcastReceiver that has been registered with
+ * this method from a BroadcastReceiver that has been registered with
* {@link #registerReceiver}, since the lifetime of this BroadcastReceiver
* is tied to another object (the one that registered it).</p>
*
diff --git a/core/java/android/content/Intent.java b/core/java/android/content/Intent.java
index ab62c44..6cf5b43 100644
--- a/core/java/android/content/Intent.java
+++ b/core/java/android/content/Intent.java
@@ -843,10 +843,10 @@ public class Intent implements Parcelable, Cloneable {
* just say what kind of data is desired, not a URI of existing data from
* which the user can pick. A ACTION_GET_CONTENT could allow the user to
* create the data as it runs (for example taking a picture or recording a
- * sound), let them browser over the web and download the desired data,
+ * sound), let them browse over the web and download the desired data,
* etc.
* <p>
- * There are two main ways to use this action: if you want an specific kind
+ * There are two main ways to use this action: if you want a specific kind
* of data, such as a person contact, you set the MIME type to the kind of
* data you want and launch it with {@link Context#startActivity(Intent)}.
* The system will then launch the best application to select that kind
@@ -864,12 +864,12 @@ public class Intent implements Parcelable, Cloneable {
* broad MIME type (such as image/* or {@literal *}/*), resulting in a
* broad range of content types the user can select from.
* <p>
- * When using such a broad GET_CONTENT action, it is often desireable to
+ * When using such a broad GET_CONTENT action, it is often desirable to
* only pick from data that can be represented as a stream. This is
* accomplished by requiring the {@link #CATEGORY_OPENABLE} in the Intent.
* <p>
* Callers can optionally specify {@link #EXTRA_LOCAL_ONLY} to request that
- * the launched content chooser only return results representing data that
+ * the launched content chooser only returns results representing data that
* is locally available on the device. For example, if this extra is set
* to true then an image picker should not show any pictures that are available
* from a remote server but not already on the local device (thus requiring
@@ -1137,7 +1137,7 @@ public class Intent implements Parcelable, Cloneable {
/**
* Activity Action: The user pressed the "Report" button in the crash/ANR dialog.
* This intent is delivered to the package which installed the application, usually
- * the Market.
+ * Google Play.
* <p>Input: No data is specified. The bug report is passed in using
* an {@link #EXTRA_BUG_REPORT} field.
* <p>Output: Nothing.
@@ -1908,14 +1908,14 @@ public class Intent implements Parcelable, Cloneable {
// location; they are not general-purpose actions.
/**
- * Broadcast Action: An GTalk connection has been established.
+ * Broadcast Action: A GTalk connection has been established.
*/
@SdkConstant(SdkConstantType.BROADCAST_INTENT_ACTION)
public static final String ACTION_GTALK_SERVICE_CONNECTED =
"android.intent.action.GTALK_CONNECTED";
/**
- * Broadcast Action: An GTalk connection has been disconnected.
+ * Broadcast Action: A GTalk connection has been disconnected.
*/
@SdkConstant(SdkConstantType.BROADCAST_INTENT_ACTION)
public static final String ACTION_GTALK_SERVICE_DISCONNECTED =
@@ -2200,7 +2200,7 @@ public class Intent implements Parcelable, Cloneable {
@SdkConstant(SdkConstantType.INTENT_CATEGORY)
public static final String CATEGORY_SELECTED_ALTERNATIVE = "android.intent.category.SELECTED_ALTERNATIVE";
/**
- * Intended to be used as a tab inside of an containing TabActivity.
+ * Intended to be used as a tab inside of a containing TabActivity.
*/
@SdkConstant(SdkConstantType.INTENT_CATEGORY)
public static final String CATEGORY_TAB = "android.intent.category.TAB";
@@ -2256,7 +2256,7 @@ public class Intent implements Parcelable, Cloneable {
*/
public static final String CATEGORY_UNIT_TEST = "android.intent.category.UNIT_TEST";
/**
- * To be used as an sample code example (not part of the normal user
+ * To be used as a sample code example (not part of the normal user
* experience).
*/
public static final String CATEGORY_SAMPLE_CODE = "android.intent.category.SAMPLE_CODE";
@@ -2506,7 +2506,7 @@ public class Intent implements Parcelable, Cloneable {
public static final String EXTRA_KEY_CONFIRM = "android.intent.extra.KEY_CONFIRM";
/**
- * Used as an boolean extra field in {@link android.content.Intent#ACTION_PACKAGE_REMOVED} or
+ * Used as a boolean extra field in {@link android.content.Intent#ACTION_PACKAGE_REMOVED} or
* {@link android.content.Intent#ACTION_PACKAGE_CHANGED} intents to override the default action
* of restarting the application.
*/
@@ -2688,12 +2688,20 @@ public class Intent implements Parcelable, Cloneable {
/**
* If set, the recipient of this Intent will be granted permission to
- * perform read operations on the Uri in the Intent's data.
+ * perform read operations on the Uri in the Intent's data and any URIs
+ * specified in its ClipData. When applying to an Intent's ClipData,
+ * all URIs as well as recursive traversals through data or other ClipData
+ * in Intent items will be granted; only the grant flags of the top-level
+ * Intent are used.
*/
public static final int FLAG_GRANT_READ_URI_PERMISSION = 0x00000001;
/**
* If set, the recipient of this Intent will be granted permission to
- * perform write operations on the Uri in the Intent's data.
+ * perform write operations on the Uri in the Intent's data and any URIs
+ * specified in its ClipData. When applying to an Intent's ClipData,
+ * all URIs as well as recursive traversals through data or other ClipData
+ * in Intent items will be granted; only the grant flags of the top-level
+ * Intent are used.
*/
public static final int FLAG_GRANT_WRITE_URI_PERMISSION = 0x00000002;
/**
@@ -3018,6 +3026,7 @@ public class Intent implements Parcelable, Cloneable {
private Bundle mExtras;
private Rect mSourceBounds;
private Intent mSelector;
+ private ClipData mClipData;
// ---------------------------------------------------------------------
@@ -3049,6 +3058,9 @@ public class Intent implements Parcelable, Cloneable {
if (o.mSelector != null) {
this.mSelector = new Intent(o.mSelector);
}
+ if (o.mClipData != null) {
+ this.mClipData = new ClipData(o.mClipData);
+ }
}
@Override
@@ -3692,7 +3704,7 @@ public class Intent implements Parcelable, Cloneable {
}
/**
- * Check if an category exists in the intent.
+ * Check if a category exists in the intent.
*
* @param category The category to check.
*
@@ -3729,6 +3741,16 @@ public class Intent implements Parcelable, Cloneable {
}
/**
+ * Return the {@link ClipData} associated with this Intent. If there is
+ * none, returns null. See {@link #setClipData} for more information.
+ *
+ * @see #setClipData;
+ */
+ public ClipData getClipData() {
+ return mClipData;
+ }
+
+ /**
* Sets the ClassLoader that will be used when unmarshalling
* any Parcelable values from the extras of this Intent.
*
@@ -4601,7 +4623,7 @@ public class Intent implements Parcelable, Cloneable {
/**
* Add a new category to the intent. Categories provide additional detail
- * about the action the intent is perform. When resolving an intent, only
+ * about the action the intent performs. When resolving an intent, only
* activities that provide <em>all</em> of the requested categories will be
* used.
*
@@ -4624,7 +4646,7 @@ public class Intent implements Parcelable, Cloneable {
}
/**
- * Remove an category from an intent.
+ * Remove a category from an intent.
*
* @param category The category to remove.
*
@@ -4683,6 +4705,37 @@ public class Intent implements Parcelable, Cloneable {
}
/**
+ * Set a {@link ClipData} associated with this Intent. This replaces any
+ * previously set ClipData.
+ *
+ * <p>The ClipData in an intent is not used for Intent matching or other
+ * such operations. Semantically it is like extras, used to transmit
+ * additional data with the Intent. The main feature of using this over
+ * the extras for data is that {@link #FLAG_GRANT_READ_URI_PERMISSION}
+ * and {@link #FLAG_GRANT_WRITE_URI_PERMISSION} will operate on any URI
+ * items included in the clip data. This is useful, in particular, if
+ * you want to transmit an Intent containing multiple <code>content:</code>
+ * URIs for which the recipient may not have global permission to access the
+ * content provider.
+ *
+ * <p>If the ClipData contains items that are themselves Intents, any
+ * grant flags in those Intents will be ignored. Only the top-level flags
+ * of the main Intent are respected, and will be applied to all Uri or
+ * Intent items in the clip (or sub-items of the clip).
+ *
+ * <p>The MIME type, label, and icon in the ClipData object are not
+ * directly used by Intent. Applications should generally rely on the
+ * MIME type of the Intent itself, not what it may find in the ClipData.
+ * A common practice is to construct a ClipData for use with an Intent
+ * with a MIME type of "*\/*".
+ *
+ * @param clip The new clip to set. May be null to clear the current clip.
+ */
+ public void setClipData(ClipData clip) {
+ mClipData = clip;
+ }
+
+ /**
* Add extended data to the intent. The name must include a package
* prefix, for example the app com.android.contacts would use names
* like "com.android.contacts.ShowAll".
@@ -5660,6 +5713,12 @@ public class Intent implements Parcelable, Cloneable {
public static final int FILL_IN_SELECTOR = 1<<6;
/**
+ * Use with {@link #fillIn} to allow the current ClipData to be
+ * overwritten, even if it is already set.
+ */
+ public static final int FILL_IN_CLIP_DATA = 1<<7;
+
+ /**
* Copy the contents of <var>other</var> in to this object, but only
* where fields are not defined by this object. For purposes of a field
* being defined, the following pieces of data in the Intent are
@@ -5673,19 +5732,22 @@ public class Intent implements Parcelable, Cloneable {
* <li> package, as set by {@link #setPackage}.
* <li> component, as set by {@link #setComponent(ComponentName)} or
* related methods.
- * <li> source bounds, as set by {@link #setSourceBounds}
+ * <li> source bounds, as set by {@link #setSourceBounds}.
+ * <li> selector, as set by {@link #setSelector(Intent)}.
+ * <li> clip data, as set by {@link #setClipData(ClipData)}.
* <li> each top-level name in the associated extras.
* </ul>
*
* <p>In addition, you can use the {@link #FILL_IN_ACTION},
* {@link #FILL_IN_DATA}, {@link #FILL_IN_CATEGORIES}, {@link #FILL_IN_PACKAGE},
- * {@link #FILL_IN_COMPONENT}, {@link #FILL_IN_SOURCE_BOUNDS}, and
- * {@link #FILL_IN_SELECTOR} to override the restriction where the
- * corresponding field will not be replaced if it is already set.
+ * {@link #FILL_IN_COMPONENT}, {@link #FILL_IN_SOURCE_BOUNDS},
+ * {@link #FILL_IN_SELECTOR}, and {@link #FILL_IN_CLIP_DATA} to override
+ * the restriction where the corresponding field will not be replaced if
+ * it is already set.
*
- * <p>Note: The component field will only be copied if {@link #FILL_IN_COMPONENT} is explicitly
- * specified. The selector will only be copied if {@link #FILL_IN_SELECTOR} is
- * explicitly specified.
+ * <p>Note: The component field will only be copied if {@link #FILL_IN_COMPONENT}
+ * is explicitly specified. The selector will only be copied if
+ * {@link #FILL_IN_SELECTOR} is explicitly specified.
*
* <p>For example, consider Intent A with {data="foo", categories="bar"}
* and Intent B with {action="gotit", data-type="some/thing",
@@ -5742,6 +5804,11 @@ public class Intent implements Parcelable, Cloneable {
changes |= FILL_IN_SELECTOR;
}
}
+ if (other.mClipData != null
+ && (mClipData == null || (flags&FILL_IN_CLIP_DATA) != 0)) {
+ mClipData = other.mClipData;
+ changes |= FILL_IN_CLIP_DATA;
+ }
// Component is special: it can -only- be set if explicitly allowed,
// since otherwise the sender could force the intent somewhere the
// originator didn't intend.
@@ -5938,7 +6005,7 @@ public class Intent implements Parcelable, Cloneable {
StringBuilder b = new StringBuilder(128);
b.append("Intent { ");
- toShortString(b, true, true, true);
+ toShortString(b, true, true, true, false);
b.append(" }");
return b.toString();
@@ -5949,21 +6016,33 @@ public class Intent implements Parcelable, Cloneable {
StringBuilder b = new StringBuilder(128);
b.append("Intent { ");
- toShortString(b, false, true, true);
+ toShortString(b, false, true, true, false);
+ b.append(" }");
+
+ return b.toString();
+ }
+
+ /** @hide */
+ public String toInsecureStringWithClip() {
+ StringBuilder b = new StringBuilder(128);
+
+ b.append("Intent { ");
+ toShortString(b, false, true, true, true);
b.append(" }");
return b.toString();
}
/** @hide */
- public String toShortString(boolean secure, boolean comp, boolean extras) {
+ public String toShortString(boolean secure, boolean comp, boolean extras, boolean clip) {
StringBuilder b = new StringBuilder(128);
- toShortString(b, secure, comp, extras);
+ toShortString(b, secure, comp, extras, clip);
return b.toString();
}
/** @hide */
- public void toShortString(StringBuilder b, boolean secure, boolean comp, boolean extras) {
+ public void toShortString(StringBuilder b, boolean secure, boolean comp, boolean extras,
+ boolean clip) {
boolean first = true;
if (mAction != null) {
b.append("act=").append(mAction);
@@ -6031,6 +6110,19 @@ public class Intent implements Parcelable, Cloneable {
first = false;
b.append("bnds=").append(mSourceBounds.toShortString());
}
+ if (mClipData != null) {
+ if (!first) {
+ b.append(' ');
+ }
+ first = false;
+ if (clip) {
+ b.append("clip={");
+ mClipData.toShortString(b);
+ b.append('}');
+ } else {
+ b.append("(has clip)");
+ }
+ }
if (extras && mExtras != null) {
if (!first) {
b.append(' ');
@@ -6040,7 +6132,7 @@ public class Intent implements Parcelable, Cloneable {
}
if (mSelector != null) {
b.append(" sel={");
- mSelector.toShortString(b, secure, comp, extras);
+ mSelector.toShortString(b, secure, comp, extras, clip);
b.append("}");
}
}
@@ -6209,6 +6301,13 @@ public class Intent implements Parcelable, Cloneable {
out.writeInt(0);
}
+ if (mClipData != null) {
+ out.writeInt(1);
+ mClipData.writeToParcel(out, flags);
+ } else {
+ out.writeInt(0);
+ }
+
out.writeBundle(mExtras);
}
@@ -6254,6 +6353,10 @@ public class Intent implements Parcelable, Cloneable {
mSelector = new Intent(in);
}
+ if (in.readInt() != 0) {
+ mClipData = new ClipData(in);
+ }
+
mExtras = in.readBundle();
}
diff --git a/core/java/android/content/SyncManager.java b/core/java/android/content/SyncManager.java
index ba24036..b7dfe92 100644
--- a/core/java/android/content/SyncManager.java
+++ b/core/java/android/content/SyncManager.java
@@ -23,18 +23,23 @@ import com.google.android.collect.Maps;
import android.accounts.Account;
import android.accounts.AccountManager;
+import android.accounts.AccountManagerService;
import android.accounts.OnAccountsUpdateListener;
import android.app.ActivityManager;
import android.app.AlarmManager;
+import android.app.AppGlobals;
import android.app.Notification;
import android.app.NotificationManager;
import android.app.PendingIntent;
+import android.app.DownloadManager.Request;
+import android.content.SyncStorageEngine.OnSyncRequestListener;
import android.content.pm.ApplicationInfo;
import android.content.pm.PackageManager;
import android.content.pm.ProviderInfo;
import android.content.pm.RegisteredServicesCache;
import android.content.pm.RegisteredServicesCacheListener;
import android.content.pm.ResolveInfo;
+import android.content.pm.UserInfo;
import android.net.ConnectivityManager;
import android.net.NetworkInfo;
import android.os.Bundle;
@@ -48,6 +53,7 @@ import android.os.Process;
import android.os.RemoteException;
import android.os.SystemClock;
import android.os.SystemProperties;
+import android.os.UserId;
import android.os.WorkSource;
import android.provider.Settings;
import android.text.format.DateUtils;
@@ -132,7 +138,9 @@ public class SyncManager implements OnAccountsUpdateListener {
private Context mContext;
- private volatile Account[] mAccounts = INITIAL_ACCOUNTS_ARRAY;
+ private static final AccountAndUser[] INITIAL_ACCOUNTS_ARRAY = new AccountAndUser[0];
+
+ private volatile AccountAndUser[] mAccounts = INITIAL_ACCOUNTS_ARRAY;
volatile private PowerManager.WakeLock mHandleAlarmWakeLock;
volatile private PowerManager.WakeLock mSyncManagerWakeLock;
@@ -166,7 +174,8 @@ public class SyncManager implements OnAccountsUpdateListener {
Log.v(TAG, "Internal storage is low.");
}
mStorageIsLow = true;
- cancelActiveSync(null /* any account */, null /* any authority */);
+ cancelActiveSync(null /* any account */, UserId.USER_ALL,
+ null /* any authority */);
} else if (Intent.ACTION_DEVICE_STORAGE_OK.equals(action)) {
if (Log.isLoggable(TAG, Log.VERBOSE)) {
Log.v(TAG, "Internal storage is ok.");
@@ -186,28 +195,73 @@ public class SyncManager implements OnAccountsUpdateListener {
private BroadcastReceiver mBackgroundDataSettingChanged = new BroadcastReceiver() {
public void onReceive(Context context, Intent intent) {
if (getConnectivityManager().getBackgroundDataSetting()) {
- scheduleSync(null /* account */, null /* authority */, new Bundle(), 0 /* delay */,
+ scheduleSync(null /* account */, UserId.USER_ALL, null /* authority */,
+ new Bundle(), 0 /* delay */,
false /* onlyThoseWithUnknownSyncableState */);
}
}
};
- private static final Account[] INITIAL_ACCOUNTS_ARRAY = new Account[0];
-
private final PowerManager mPowerManager;
private static final long SYNC_ALARM_TIMEOUT_MIN = 30 * 1000; // 30 seconds
private static final long SYNC_ALARM_TIMEOUT_MAX = 2 * 60 * 60 * 1000; // two hours
+ private List<UserInfo> getAllUsers() {
+ try {
+ return AppGlobals.getPackageManager().getUsers();
+ } catch (RemoteException re) {
+ // Local to system process, shouldn't happen
+ }
+ return null;
+ }
+
+ private boolean containsAccountAndUser(AccountAndUser[] accounts, Account account, int userId) {
+ boolean found = false;
+ for (int i = 0; i < accounts.length; i++) {
+ if (accounts[i].userId == userId
+ && accounts[i].account.equals(account)) {
+ found = true;
+ break;
+ }
+ }
+ return found;
+ }
+
public void onAccountsUpdated(Account[] accounts) {
// remember if this was the first time this was called after an update
final boolean justBootedUp = mAccounts == INITIAL_ACCOUNTS_ARRAY;
- mAccounts = accounts;
- // if a sync is in progress yet it is no longer in the accounts list,
- // cancel it
+ List<UserInfo> users = getAllUsers();
+ if (users == null) return;
+
+ int count = 0;
+
+ // For all known users on the system, get their accounts and add them to the list
+ // TODO: Limit this to active users, when such a concept exists.
+ for (UserInfo user : users) {
+ accounts = AccountManagerService.getSingleton().getAccounts(user.id);
+ count += accounts.length;
+ }
+
+ AccountAndUser[] allAccounts = new AccountAndUser[count];
+ int index = 0;
+ for (UserInfo user : users) {
+ accounts = AccountManagerService.getSingleton().getAccounts(user.id);
+ for (Account account : accounts) {
+ allAccounts[index++] = new AccountAndUser(account, user.id);
+ }
+ if (mBootCompleted) {
+ mSyncStorageEngine.doDatabaseCleanup(accounts, user.id);
+ }
+ }
+
+ mAccounts = allAccounts;
+
for (ActiveSyncContext currentSyncContext : mActiveSyncContexts) {
- if (!ArrayUtils.contains(accounts, currentSyncContext.mSyncOperation.account)) {
+ if (!containsAccountAndUser(allAccounts,
+ currentSyncContext.mSyncOperation.account,
+ currentSyncContext.mSyncOperation.userId)) {
Log.d(TAG, "canceling sync since the account has been removed");
sendSyncFinishedOrCanceledMessage(currentSyncContext,
null /* no result since this is a cancel */);
@@ -218,11 +272,7 @@ public class SyncManager implements OnAccountsUpdateListener {
// the accounts are not set yet
sendCheckAlarmsMessage();
- if (mBootCompleted) {
- mSyncStorageEngine.doDatabaseCleanup(accounts);
- }
-
- if (accounts.length > 0) {
+ if (allAccounts.length > 0) {
// If this is the first time this was called after a bootup then
// the accounts haven't really changed, instead they were just loaded
// from the AccountManager. Otherwise at least one of the accounts
@@ -238,7 +288,8 @@ public class SyncManager implements OnAccountsUpdateListener {
// a chance to set their syncable state.
boolean onlyThoseWithUnkownSyncableState = justBootedUp;
- scheduleSync(null, null, null, 0 /* no delay */, onlyThoseWithUnkownSyncableState);
+ scheduleSync(null, UserId.USER_ALL, null, null, 0 /* no delay */,
+ onlyThoseWithUnkownSyncableState);
}
}
@@ -277,10 +328,36 @@ public class SyncManager implements OnAccountsUpdateListener {
private static final String ACTION_SYNC_ALARM = "android.content.syncmanager.SYNC_ALARM";
private final SyncHandler mSyncHandler;
- private final Handler mMainHandler;
private volatile boolean mBootCompleted = false;
+ static class AccountAndUser {
+ Account account;
+ int userId;
+
+ AccountAndUser(Account account, int userId) {
+ this.account = account;
+ this.userId = userId;
+ }
+
+ public boolean equals(Object o) {
+ if (this == o) return true;
+ if (!(o instanceof AccountAndUser)) return false;
+ final AccountAndUser other = (AccountAndUser) o;
+ return this.account.equals(other.account)
+ && this.userId == other.userId;
+ }
+
+ @Override
+ public int hashCode() {
+ return account.hashCode() + userId;
+ }
+
+ public String toString() {
+ return account.toString() + " u" + userId;
+ }
+ }
+
private ConnectivityManager getConnectivityManager() {
synchronized (this) {
if (mConnManagerDoNotUseDirectly == null) {
@@ -297,6 +374,13 @@ public class SyncManager implements OnAccountsUpdateListener {
mContext = context;
SyncStorageEngine.init(context);
mSyncStorageEngine = SyncStorageEngine.getSingleton();
+ mSyncStorageEngine.setOnSyncRequestListener(new OnSyncRequestListener() {
+ public void onSyncRequest(Account account, int userId, String authority,
+ Bundle extras) {
+ scheduleSync(account, userId, authority, extras, 0, false);
+ }
+ });
+
mSyncAdapters = new SyncAdaptersCache(mContext);
mSyncQueue = new SyncQueue(mSyncStorageEngine, mSyncAdapters);
@@ -304,12 +388,11 @@ public class SyncManager implements OnAccountsUpdateListener {
Process.THREAD_PRIORITY_BACKGROUND);
syncThread.start();
mSyncHandler = new SyncHandler(syncThread.getLooper());
- mMainHandler = new Handler(mContext.getMainLooper());
mSyncAdapters.setListener(new RegisteredServicesCacheListener<SyncAdapterType>() {
public void onServiceChanged(SyncAdapterType type, boolean removed) {
if (!removed) {
- scheduleSync(null, type.authority, null, 0 /* no delay */,
+ scheduleSync(null, UserId.USER_ALL, type.authority, null, 0 /* no delay */,
false /* onlyThoseWithUnkownSyncableState */);
}
}
@@ -376,7 +459,7 @@ public class SyncManager implements OnAccountsUpdateListener {
AccountManager.get(mContext).addOnAccountsUpdatedListener(SyncManager.this,
mSyncHandler, false /* updateImmediately */);
// do this synchronously to ensure we have the accounts before this call returns
- onAccountsUpdated(AccountManager.get(mContext).getAccounts());
+ onAccountsUpdated(null);
}
}
@@ -404,82 +487,6 @@ public class SyncManager implements OnAccountsUpdateListener {
}
}
- private void initializeSyncAdapter(Account account, String authority) {
- if (Log.isLoggable(TAG, Log.VERBOSE)) {
- Log.v(TAG, "initializeSyncAdapter: " + account + ", authority " + authority);
- }
- SyncAdapterType syncAdapterType = SyncAdapterType.newKey(authority, account.type);
- RegisteredServicesCache.ServiceInfo<SyncAdapterType> syncAdapterInfo =
- mSyncAdapters.getServiceInfo(syncAdapterType);
- if (syncAdapterInfo == null) {
- Log.w(TAG, "can't find a sync adapter for " + syncAdapterType + ", removing");
- mSyncStorageEngine.removeAuthority(account, authority);
- return;
- }
-
- Intent intent = new Intent();
- intent.setAction("android.content.SyncAdapter");
- intent.setComponent(syncAdapterInfo.componentName);
- if (!mContext.bindService(intent,
- new InitializerServiceConnection(account, authority, mContext, mMainHandler),
- Context.BIND_AUTO_CREATE | Context.BIND_NOT_FOREGROUND
- | Context.BIND_ALLOW_OOM_MANAGEMENT)) {
- Log.w(TAG, "initializeSyncAdapter: failed to bind to " + intent);
- }
- }
-
- private static class InitializerServiceConnection implements ServiceConnection {
- private final Account mAccount;
- private final String mAuthority;
- private final Handler mHandler;
- private volatile Context mContext;
- private volatile boolean mInitialized;
-
- public InitializerServiceConnection(Account account, String authority, Context context,
- Handler handler) {
- mAccount = account;
- mAuthority = authority;
- mContext = context;
- mHandler = handler;
- mInitialized = false;
- }
-
- public void onServiceConnected(ComponentName name, IBinder service) {
- try {
- if (!mInitialized) {
- mInitialized = true;
- if (Log.isLoggable(TAG, Log.VERBOSE)) {
- Log.v(TAG, "calling initialize: " + mAccount + ", authority " + mAuthority);
- }
- ISyncAdapter.Stub.asInterface(service).initialize(mAccount, mAuthority);
- }
- } catch (RemoteException e) {
- // doesn't matter, we will retry again later
- Log.d(TAG, "error while initializing: " + mAccount + ", authority " + mAuthority,
- e);
- } finally {
- // give the sync adapter time to initialize before unbinding from it
- // TODO: change this API to not rely on this timing, http://b/2500805
- mHandler.postDelayed(new Runnable() {
- public void run() {
- if (mContext != null) {
- mContext.unbindService(InitializerServiceConnection.this);
- mContext = null;
- }
- }
- }, INITIALIZATION_UNBIND_DELAY_MS);
- }
- }
-
- public void onServiceDisconnected(ComponentName name) {
- if (mContext != null) {
- mContext.unbindService(InitializerServiceConnection.this);
- mContext = null;
- }
- }
-
- }
-
/**
* Initiate a sync. This can start a sync for all providers
* (pass null to url, set onlyTicklable to false), only those
@@ -500,6 +507,8 @@ public class SyncManager implements OnAccountsUpdateListener {
* <p>You'll start getting callbacks after this.
*
* @param requestedAccount the account to sync, may be null to signify all accounts
+ * @param userId the id of the user whose accounts are to be synced. If userId is USER_ALL,
+ * then all users' accounts are considered.
* @param requestedAuthority the authority to sync, may be null to indicate all authorities
* @param extras a Map of SyncAdapter-specific information to control
* syncs of a specific provider. Can be null. Is ignored
@@ -507,7 +516,7 @@ public class SyncManager implements OnAccountsUpdateListener {
* @param delay how many milliseconds in the future to wait before performing this
* @param onlyThoseWithUnkownSyncableState
*/
- public void scheduleSync(Account requestedAccount, String requestedAuthority,
+ public void scheduleSync(Account requestedAccount, int userId, String requestedAuthority,
Bundle extras, long delay, boolean onlyThoseWithUnkownSyncableState) {
boolean isLoggable = Log.isLoggable(TAG, Log.VERBOSE);
@@ -521,9 +530,9 @@ public class SyncManager implements OnAccountsUpdateListener {
delay = -1; // this means schedule at the front of the queue
}
- Account[] accounts;
- if (requestedAccount != null) {
- accounts = new Account[]{requestedAccount};
+ AccountAndUser[] accounts;
+ if (requestedAccount != null && userId != UserId.USER_ALL) {
+ accounts = new AccountAndUser[] { new AccountAndUser(requestedAccount, userId) };
} else {
// if the accounts aren't configured yet then we can't support an account-less
// sync request
@@ -574,24 +583,23 @@ public class SyncManager implements OnAccountsUpdateListener {
if (hasSyncAdapter) syncableAuthorities.add(requestedAuthority);
}
- final boolean masterSyncAutomatically = mSyncStorageEngine.getMasterSyncAutomatically();
-
for (String authority : syncableAuthorities) {
- for (Account account : accounts) {
- int isSyncable = mSyncStorageEngine.getIsSyncable(account, authority);
+ for (AccountAndUser account : accounts) {
+ int isSyncable = mSyncStorageEngine.getIsSyncable(account.account, account.userId,
+ authority);
if (isSyncable == 0) {
continue;
}
final RegisteredServicesCache.ServiceInfo<SyncAdapterType> syncAdapterInfo =
mSyncAdapters.getServiceInfo(
- SyncAdapterType.newKey(authority, account.type));
+ SyncAdapterType.newKey(authority, account.account.type));
if (syncAdapterInfo == null) {
continue;
}
final boolean allowParallelSyncs = syncAdapterInfo.type.allowParallelSyncs();
final boolean isAlwaysSyncable = syncAdapterInfo.type.isAlwaysSyncable();
if (isSyncable < 0 && isAlwaysSyncable) {
- mSyncStorageEngine.setIsSyncable(account, authority, 1);
+ mSyncStorageEngine.setIsSyncable(account.account, account.userId, authority, 1);
isSyncable = 1;
}
if (onlyThoseWithUnkownSyncableState && isSyncable >= 0) {
@@ -605,8 +613,10 @@ public class SyncManager implements OnAccountsUpdateListener {
boolean syncAllowed =
(isSyncable < 0)
|| ignoreSettings
- || (backgroundDataUsageAllowed && masterSyncAutomatically
- && mSyncStorageEngine.getSyncAutomatically(account, authority));
+ || (backgroundDataUsageAllowed
+ && mSyncStorageEngine.getMasterSyncAutomatically(account.userId)
+ && mSyncStorageEngine.getSyncAutomatically(account.account,
+ account.userId, authority));
if (!syncAllowed) {
if (isLoggable) {
Log.d(TAG, "scheduleSync: sync of " + account + ", " + authority
@@ -615,8 +625,10 @@ public class SyncManager implements OnAccountsUpdateListener {
continue;
}
- Pair<Long, Long> backoff = mSyncStorageEngine.getBackoff(account, authority);
- long delayUntil = mSyncStorageEngine.getDelayUntilTime(account, authority);
+ Pair<Long, Long> backoff = mSyncStorageEngine
+ .getBackoff(account.account, account.userId, authority);
+ long delayUntil = mSyncStorageEngine.getDelayUntilTime(account.account,
+ account.userId, authority);
final long backoffTime = backoff != null ? backoff.first : 0;
if (isSyncable < 0) {
Bundle newExtras = new Bundle();
@@ -630,9 +642,8 @@ public class SyncManager implements OnAccountsUpdateListener {
+ ", extras " + newExtras);
}
scheduleSyncOperation(
- new SyncOperation(account, source, authority, newExtras, 0,
- backoffTime, delayUntil,
- allowParallelSyncs));
+ new SyncOperation(account.account, account.userId, source, authority,
+ newExtras, 0, backoffTime, delayUntil, allowParallelSyncs));
}
if (!onlyThoseWithUnkownSyncableState) {
if (isLoggable) {
@@ -644,18 +655,17 @@ public class SyncManager implements OnAccountsUpdateListener {
+ ", extras " + extras);
}
scheduleSyncOperation(
- new SyncOperation(account, source, authority, extras, delay,
- backoffTime, delayUntil,
- allowParallelSyncs));
+ new SyncOperation(account.account, account.userId, source, authority,
+ extras, delay, backoffTime, delayUntil, allowParallelSyncs));
}
}
}
}
- public void scheduleLocalSync(Account account, String authority) {
+ public void scheduleLocalSync(Account account, int userId, String authority) {
final Bundle extras = new Bundle();
extras.putBoolean(ContentResolver.SYNC_EXTRAS_UPLOAD, true);
- scheduleSync(account, authority, extras, LOCAL_SYNC_DELAY,
+ scheduleSync(account, userId, authority, extras, LOCAL_SYNC_DELAY,
false /* onlyThoseWithUnkownSyncableState */);
}
@@ -691,11 +701,13 @@ public class SyncManager implements OnAccountsUpdateListener {
mSyncHandler.sendMessage(msg);
}
- private void sendCancelSyncsMessage(final Account account, final String authority) {
+ private void sendCancelSyncsMessage(final Account account, final int userId,
+ final String authority) {
if (Log.isLoggable(TAG, Log.VERBOSE)) Log.v(TAG, "sending MESSAGE_CANCEL");
Message msg = mSyncHandler.obtainMessage();
msg.what = SyncHandler.MESSAGE_CANCEL;
msg.obj = Pair.create(account, authority);
+ msg.arg1 = userId;
mSyncHandler.sendMessage(msg);
}
@@ -717,10 +729,10 @@ public class SyncManager implements OnAccountsUpdateListener {
}
private void clearBackoffSetting(SyncOperation op) {
- mSyncStorageEngine.setBackoff(op.account, op.authority,
+ mSyncStorageEngine.setBackoff(op.account, op.userId, op.authority,
SyncStorageEngine.NOT_IN_BACKOFF_MODE, SyncStorageEngine.NOT_IN_BACKOFF_MODE);
synchronized (mSyncQueue) {
- mSyncQueue.onBackoffChanged(op.account, op.authority, 0);
+ mSyncQueue.onBackoffChanged(op.account, op.userId, op.authority, 0);
}
}
@@ -728,7 +740,7 @@ public class SyncManager implements OnAccountsUpdateListener {
final long now = SystemClock.elapsedRealtime();
final Pair<Long, Long> previousSettings =
- mSyncStorageEngine.getBackoff(op.account, op.authority);
+ mSyncStorageEngine.getBackoff(op.account, op.userId, op.authority);
long newDelayInMs = -1;
if (previousSettings != null) {
// don't increase backoff before current backoff is expired. This will happen for op's
@@ -759,14 +771,14 @@ public class SyncManager implements OnAccountsUpdateListener {
final long backoff = now + newDelayInMs;
- mSyncStorageEngine.setBackoff(op.account, op.authority,
+ mSyncStorageEngine.setBackoff(op.account, op.userId, op.authority,
backoff, newDelayInMs);
op.backoff = backoff;
op.updateEffectiveRunTime();
synchronized (mSyncQueue) {
- mSyncQueue.onBackoffChanged(op.account, op.authority, backoff);
+ mSyncQueue.onBackoffChanged(op.account, op.userId, op.authority, backoff);
}
}
@@ -779,7 +791,8 @@ public class SyncManager implements OnAccountsUpdateListener {
} else {
newDelayUntilTime = 0;
}
- mSyncStorageEngine.setDelayUntilTime(op.account, op.authority, newDelayUntilTime);
+ mSyncStorageEngine
+ .setDelayUntilTime(op.account, op.userId, op.authority, newDelayUntilTime);
synchronized (mSyncQueue) {
mSyncQueue.onDelayUntilTimeChanged(op.account, op.authority, newDelayUntilTime);
}
@@ -790,8 +803,8 @@ public class SyncManager implements OnAccountsUpdateListener {
* @param account limit the cancelations to syncs with this account, if non-null
* @param authority limit the cancelations to syncs with this authority, if non-null
*/
- public void cancelActiveSync(Account account, String authority) {
- sendCancelSyncsMessage(account, authority);
+ public void cancelActiveSync(Account account, int userId, String authority) {
+ sendCancelSyncsMessage(account, userId, authority);
}
/**
@@ -823,11 +836,11 @@ public class SyncManager implements OnAccountsUpdateListener {
* @param account limit the removals to operations with this account, if non-null
* @param authority limit the removals to operations with this authority, if non-null
*/
- public void clearScheduledSyncOperations(Account account, String authority) {
+ public void clearScheduledSyncOperations(Account account, int userId, String authority) {
synchronized (mSyncQueue) {
- mSyncQueue.remove(account, authority);
+ mSyncQueue.remove(account, userId, authority);
}
- mSyncStorageEngine.setBackoff(account, authority,
+ mSyncStorageEngine.setBackoff(account, userId, authority,
SyncStorageEngine.NOT_IN_BACKOFF_MODE, SyncStorageEngine.NOT_IN_BACKOFF_MODE);
}
@@ -875,7 +888,8 @@ public class SyncManager implements OnAccountsUpdateListener {
Log.d(TAG, "retrying sync operation that failed because there was already a "
+ "sync in progress: " + operation);
}
- scheduleSyncOperation(new SyncOperation(operation.account, operation.syncSource,
+ scheduleSyncOperation(new SyncOperation(operation.account, operation.userId,
+ operation.syncSource,
operation.authority, operation.extras,
DELAY_RETRY_SYNC_IN_PROGRESS_IN_SECONDS * 1000,
operation.backoff, operation.delayUntil, operation.allowParallelSyncs));
@@ -979,7 +993,8 @@ public class SyncManager implements OnAccountsUpdateListener {
mBound = true;
final boolean bindResult = mContext.bindService(intent, this,
Context.BIND_AUTO_CREATE | Context.BIND_NOT_FOREGROUND
- | Context.BIND_ALLOW_OOM_MANAGEMENT);
+ | Context.BIND_ALLOW_OOM_MANAGEMENT,
+ mSyncOperation.userId);
if (!bindResult) {
mBound = false;
}
@@ -1034,10 +1049,19 @@ public class SyncManager implements OnAccountsUpdateListener {
protected void dumpSyncState(PrintWriter pw) {
pw.print("data connected: "); pw.println(mDataConnectionIsConnected);
- pw.print("auto sync: "); pw.println(mSyncStorageEngine.getMasterSyncAutomatically());
+ pw.print("auto sync: ");
+ List<UserInfo> users = getAllUsers();
+ if (users != null) {
+ for (UserInfo user : users) {
+ pw.print("u" + user.id + "="
+ + mSyncStorageEngine.getMasterSyncAutomatically(user.id));
+ }
+ pw.println();
+ }
pw.print("memory low: "); pw.println(mStorageIsLow);
- final Account[] accounts = mAccounts;
+ final AccountAndUser[] accounts = mAccounts;
+
pw.print("accounts: ");
if (accounts != INITIAL_ACCOUNTS_ARRAY) {
pw.println(accounts.length);
@@ -1090,18 +1114,20 @@ public class SyncManager implements OnAccountsUpdateListener {
// join the installed sync adapter with the accounts list and emit for everything
pw.println();
pw.println("Sync Status");
- for (Account account : accounts) {
- pw.print(" Account "); pw.print(account.name);
- pw.print(" "); pw.print(account.type);
+ for (AccountAndUser account : accounts) {
+ pw.print(" Account "); pw.print(account.account.name);
+ pw.print(" u"); pw.print(account.userId);
+ pw.print(" "); pw.print(account.account.type);
pw.println(":");
for (RegisteredServicesCache.ServiceInfo<SyncAdapterType> syncAdapterType :
mSyncAdapters.getAllServices()) {
- if (!syncAdapterType.type.accountType.equals(account.type)) {
+ if (!syncAdapterType.type.accountType.equals(account.account.type)) {
continue;
}
- SyncStorageEngine.AuthorityInfo settings = mSyncStorageEngine.getOrCreateAuthority(
- account, syncAdapterType.type.authority);
+ SyncStorageEngine.AuthorityInfo settings =
+ mSyncStorageEngine.getOrCreateAuthority(
+ account.account, account.userId, syncAdapterType.type.authority);
SyncStatusInfo status = mSyncStorageEngine.getOrCreateSyncStatus(settings);
pw.print(" "); pw.print(settings.authority);
pw.println(":");
@@ -1554,7 +1580,16 @@ public class SyncManager implements OnAccountsUpdateListener {
private volatile CountDownLatch mReadyToRunLatch = new CountDownLatch(1);
public void onBootCompleted() {
mBootCompleted = true;
- mSyncStorageEngine.doDatabaseCleanup(AccountManager.get(mContext).getAccounts());
+ // TODO: Handle bootcompleted event for specific user. Now let's just iterate through
+ // all the users.
+ List<UserInfo> users = getAllUsers();
+ if (users != null) {
+ for (UserInfo user : users) {
+ mSyncStorageEngine.doDatabaseCleanup(
+ AccountManagerService.getSingleton().getAccounts(user.id),
+ user.id);
+ }
+ }
if (mReadyToRunLatch != null) {
mReadyToRunLatch.countDown();
}
@@ -1635,7 +1670,7 @@ public class SyncManager implements OnAccountsUpdateListener {
Log.d(TAG, "handleSyncHandlerMessage: MESSAGE_SERVICE_CANCEL: "
+ payload.first + ", " + payload.second);
}
- cancelActiveSyncLocked(payload.first, payload.second);
+ cancelActiveSyncLocked(payload.first, msg.arg1, payload.second);
nextPendingSyncTime = maybeStartNextSyncLocked();
break;
}
@@ -1740,22 +1775,28 @@ public class SyncManager implements OnAccountsUpdateListener {
final boolean backgroundDataUsageAllowed =
getConnectivityManager().getBackgroundDataSetting();
long earliestFuturePollTime = Long.MAX_VALUE;
- if (!backgroundDataUsageAllowed || !mSyncStorageEngine.getMasterSyncAutomatically()) {
+ if (!backgroundDataUsageAllowed) {
return earliestFuturePollTime;
}
+
+ AccountAndUser[] accounts = mAccounts;
+
final long nowAbsolute = System.currentTimeMillis();
ArrayList<SyncStorageEngine.AuthorityInfo> infos = mSyncStorageEngine.getAuthorities();
for (SyncStorageEngine.AuthorityInfo info : infos) {
// skip the sync if the account of this operation no longer exists
- if (!ArrayUtils.contains(mAccounts, info.account)) {
+ if (!containsAccountAndUser(accounts, info.account, info.userId)) {
continue;
}
- if (!mSyncStorageEngine.getSyncAutomatically(info.account, info.authority)) {
+ if (!mSyncStorageEngine.getMasterSyncAutomatically(info.userId)
+ || !mSyncStorageEngine.getSyncAutomatically(info.account, info.userId,
+ info.authority)) {
continue;
}
- if (mSyncStorageEngine.getIsSyncable(info.account, info.authority) == 0) {
+ if (mSyncStorageEngine.getIsSyncable(info.account, info.userId, info.authority)
+ == 0) {
continue;
}
@@ -1772,8 +1813,8 @@ public class SyncManager implements OnAccountsUpdateListener {
: lastPollTimeAbsolute + periodInSeconds * 1000;
// if it is ready to run then schedule it and mark it as having been scheduled
if (nextPollTimeAbsolute <= nowAbsolute) {
- final Pair<Long, Long> backoff =
- mSyncStorageEngine.getBackoff(info.account, info.authority);
+ final Pair<Long, Long> backoff = mSyncStorageEngine.getBackoff(
+ info.account, info.userId, info.authority);
final RegisteredServicesCache.ServiceInfo<SyncAdapterType> syncAdapterInfo =
mSyncAdapters.getServiceInfo(
SyncAdapterType.newKey(info.authority, info.account.type));
@@ -1781,11 +1822,12 @@ public class SyncManager implements OnAccountsUpdateListener {
continue;
}
scheduleSyncOperation(
- new SyncOperation(info.account, SyncStorageEngine.SOURCE_PERIODIC,
+ new SyncOperation(info.account, info.userId,
+ SyncStorageEngine.SOURCE_PERIODIC,
info.authority, extras, 0 /* delay */,
backoff != null ? backoff.first : 0,
mSyncStorageEngine.getDelayUntilTime(
- info.account, info.authority),
+ info.account, info.userId, info.authority),
syncAdapterInfo.type.allowParallelSyncs()));
status.setPeriodicSyncTime(i, nowAbsolute);
} else {
@@ -1830,7 +1872,7 @@ public class SyncManager implements OnAccountsUpdateListener {
// If the accounts aren't known yet then we aren't ready to run. We will be kicked
// when the account lookup request does complete.
- Account[] accounts = mAccounts;
+ AccountAndUser[] accounts = mAccounts;
if (accounts == INITIAL_ACCOUNTS_ARRAY) {
if (isLoggable) {
Log.v(TAG, "maybeStartNextSync: accounts not known, skipping");
@@ -1843,7 +1885,6 @@ public class SyncManager implements OnAccountsUpdateListener {
// start it, otherwise just get out.
final boolean backgroundDataUsageAllowed =
getConnectivityManager().getBackgroundDataSetting();
- final boolean masterSyncAutomatically = mSyncStorageEngine.getMasterSyncAutomatically();
final long now = SystemClock.elapsedRealtime();
@@ -1863,14 +1904,15 @@ public class SyncManager implements OnAccountsUpdateListener {
final SyncOperation op = operationIterator.next();
// drop the sync if the account of this operation no longer exists
- if (!ArrayUtils.contains(mAccounts, op.account)) {
+ if (!containsAccountAndUser(accounts, op.account, op.userId)) {
operationIterator.remove();
mSyncStorageEngine.deleteFromPending(op.pendingOperation);
continue;
}
// drop this sync request if it isn't syncable
- int syncableState = mSyncStorageEngine.getIsSyncable(op.account, op.authority);
+ int syncableState = mSyncStorageEngine.getIsSyncable(
+ op.account, op.userId, op.authority);
if (syncableState == 0) {
operationIterator.remove();
mSyncStorageEngine.deleteFromPending(op.pendingOperation);
@@ -1905,11 +1947,11 @@ public class SyncManager implements OnAccountsUpdateListener {
// disconnected for the target UID.
if (!op.extras.getBoolean(ContentResolver.SYNC_EXTRAS_IGNORE_SETTINGS, false)
&& (syncableState > 0)
- && (!masterSyncAutomatically
+ && (!mSyncStorageEngine.getMasterSyncAutomatically(op.userId)
|| !backgroundDataUsageAllowed
|| !uidNetworkConnected
|| !mSyncStorageEngine.getSyncAutomatically(
- op.account, op.authority))) {
+ op.account, op.userId, op.authority))) {
operationIterator.remove();
mSyncStorageEngine.deleteFromPending(op.pendingOperation);
continue;
@@ -1946,6 +1988,7 @@ public class SyncManager implements OnAccountsUpdateListener {
}
if (activeOp.account.type.equals(candidate.account.type)
&& activeOp.authority.equals(candidate.authority)
+ && activeOp.userId == candidate.userId
&& (!activeOp.allowParallelSyncs
|| activeOp.account.name.equals(candidate.account.name))) {
conflict = activeSyncContext;
@@ -2033,7 +2076,7 @@ public class SyncManager implements OnAccountsUpdateListener {
if (syncAdapterInfo == null) {
Log.d(TAG, "can't find a sync adapter for " + syncAdapterType
+ ", removing settings for it");
- mSyncStorageEngine.removeAuthority(op.account, op.authority);
+ mSyncStorageEngine.removeAuthority(op.account, op.userId, op.authority);
return false;
}
@@ -2074,7 +2117,7 @@ public class SyncManager implements OnAccountsUpdateListener {
}
}
- private void cancelActiveSyncLocked(Account account, String authority) {
+ private void cancelActiveSyncLocked(Account account, int userId, String authority) {
ArrayList<ActiveSyncContext> activeSyncs =
new ArrayList<ActiveSyncContext>(mActiveSyncContexts);
for (ActiveSyncContext activeSyncContext : activeSyncs) {
@@ -2082,15 +2125,20 @@ public class SyncManager implements OnAccountsUpdateListener {
// if an authority was specified then only cancel the sync if it matches
if (account != null) {
if (!account.equals(activeSyncContext.mSyncOperation.account)) {
- return;
+ continue;
}
}
// if an account was specified then only cancel the sync if it matches
if (authority != null) {
if (!authority.equals(activeSyncContext.mSyncOperation.authority)) {
- return;
+ continue;
}
}
+ // check if the userid matches
+ if (userId != UserId.USER_ALL
+ && userId != activeSyncContext.mSyncOperation.userId) {
+ continue;
+ }
runSyncFinishedOrCanceledLocked(null /* no result since this is a cancel */,
activeSyncContext);
}
@@ -2169,7 +2217,7 @@ public class SyncManager implements OnAccountsUpdateListener {
}
if (syncResult != null && syncResult.fullSyncRequested) {
- scheduleSyncOperation(new SyncOperation(syncOperation.account,
+ scheduleSyncOperation(new SyncOperation(syncOperation.account, syncOperation.userId,
syncOperation.syncSource, syncOperation.authority, new Bundle(), 0,
syncOperation.backoff, syncOperation.delayUntil,
syncOperation.allowParallelSyncs));
@@ -2180,7 +2228,8 @@ public class SyncManager implements OnAccountsUpdateListener {
private void closeActiveSyncContext(ActiveSyncContext activeSyncContext) {
activeSyncContext.close();
mActiveSyncContexts.remove(activeSyncContext);
- mSyncStorageEngine.removeActiveSync(activeSyncContext.mSyncInfo);
+ mSyncStorageEngine.removeActiveSync(activeSyncContext.mSyncInfo,
+ activeSyncContext.mSyncOperation.userId);
}
/**
@@ -2446,7 +2495,8 @@ public class SyncManager implements OnAccountsUpdateListener {
syncOperation.account.name.hashCode());
return mSyncStorageEngine.insertStartSyncEvent(
- syncOperation.account, syncOperation.authority, now, source);
+ syncOperation.account, syncOperation.userId, syncOperation.authority,
+ now, source);
}
public void stopSyncEvent(long rowId, SyncOperation syncOperation, String resultMessage,
diff --git a/core/java/android/content/SyncOperation.java b/core/java/android/content/SyncOperation.java
index 94c2247..4e86ef8 100644
--- a/core/java/android/content/SyncOperation.java
+++ b/core/java/android/content/SyncOperation.java
@@ -26,6 +26,7 @@ import android.os.SystemClock;
*/
public class SyncOperation implements Comparable {
public final Account account;
+ public final int userId;
public int syncSource;
public String authority;
public final boolean allowParallelSyncs;
@@ -38,9 +39,10 @@ public class SyncOperation implements Comparable {
public long delayUntil;
public long effectiveRunTime;
- public SyncOperation(Account account, int source, String authority, Bundle extras,
+ public SyncOperation(Account account, int userId, int source, String authority, Bundle extras,
long delayInMs, long backoff, long delayUntil, boolean allowParallelSyncs) {
this.account = account;
+ this.userId = userId;
this.syncSource = source;
this.authority = authority;
this.allowParallelSyncs = allowParallelSyncs;
@@ -75,6 +77,7 @@ public class SyncOperation implements Comparable {
SyncOperation(SyncOperation other) {
this.account = other.account;
+ this.userId = other.userId;
this.syncSource = other.syncSource;
this.authority = other.authority;
this.extras = new Bundle(other.extras);
@@ -120,7 +123,8 @@ public class SyncOperation implements Comparable {
private String toKey() {
StringBuilder sb = new StringBuilder();
sb.append("authority: ").append(authority);
- sb.append(" account {name=" + account.name + ", type=" + account.type + "}");
+ sb.append(" account {name=" + account.name + ", user=" + userId + ", type=" + account.type
+ + "}");
sb.append(" extras: ");
extrasToStringBuilder(extras, sb);
return sb.toString();
diff --git a/core/java/android/content/SyncQueue.java b/core/java/android/content/SyncQueue.java
index bfdf4a1..06da6fa 100644
--- a/core/java/android/content/SyncQueue.java
+++ b/core/java/android/content/SyncQueue.java
@@ -49,7 +49,8 @@ public class SyncQueue {
final int N = ops.size();
for (int i=0; i<N; i++) {
SyncStorageEngine.PendingOperation op = ops.get(i);
- final Pair<Long, Long> backoff = syncStorageEngine.getBackoff(op.account, op.authority);
+ final Pair<Long, Long> backoff =
+ syncStorageEngine.getBackoff(op.account, op.userId, op.authority);
final RegisteredServicesCache.ServiceInfo<SyncAdapterType> syncAdapterInfo =
syncAdapters.getServiceInfo(
SyncAdapterType.newKey(op.authority, op.account.type));
@@ -57,9 +58,9 @@ public class SyncQueue {
continue;
}
SyncOperation syncOperation = new SyncOperation(
- op.account, op.syncSource, op.authority, op.extras, 0 /* delay */,
+ op.account, op.userId, op.syncSource, op.authority, op.extras, 0 /* delay */,
backoff != null ? backoff.first : 0,
- syncStorageEngine.getDelayUntilTime(op.account, op.authority),
+ syncStorageEngine.getDelayUntilTime(op.account, op.userId, op.authority),
syncAdapterInfo.type.allowParallelSyncs());
syncOperation.expedited = op.expedited;
syncOperation.pendingOperation = op;
@@ -102,8 +103,8 @@ public class SyncQueue {
operation.pendingOperation = pop;
if (operation.pendingOperation == null) {
pop = new SyncStorageEngine.PendingOperation(
- operation.account, operation.syncSource,
- operation.authority, operation.extras, operation.expedited);
+ operation.account, operation.userId, operation.syncSource,
+ operation.authority, operation.extras, operation.expedited);
pop = mSyncStorageEngine.insertIntoPending(pop);
if (pop == null) {
throw new IllegalStateException("error adding pending sync operation "
@@ -131,11 +132,12 @@ public class SyncQueue {
}
}
- public void onBackoffChanged(Account account, String providerName, long backoff) {
+ public void onBackoffChanged(Account account, int userId, String providerName, long backoff) {
// for each op that matches the account and provider update its
// backoff and effectiveStartTime
for (SyncOperation op : mOperationsMap.values()) {
- if (op.account.equals(account) && op.authority.equals(providerName)) {
+ if (op.account.equals(account) && op.authority.equals(providerName)
+ && op.userId == userId) {
op.backoff = backoff;
op.updateEffectiveRunTime();
}
@@ -153,7 +155,7 @@ public class SyncQueue {
}
}
- public void remove(Account account, String authority) {
+ public void remove(Account account, int userId, String authority) {
Iterator<Map.Entry<String, SyncOperation>> entries = mOperationsMap.entrySet().iterator();
while (entries.hasNext()) {
Map.Entry<String, SyncOperation> entry = entries.next();
@@ -164,6 +166,9 @@ public class SyncQueue {
if (authority != null && !syncOperation.authority.equals(authority)) {
continue;
}
+ if (userId != syncOperation.userId) {
+ continue;
+ }
entries.remove();
if (!mSyncStorageEngine.deleteFromPending(syncOperation.pendingOperation)) {
final String errorMessage = "unable to find pending row for " + syncOperation;
diff --git a/core/java/android/content/SyncStorageEngine.java b/core/java/android/content/SyncStorageEngine.java
index a1e174b..7bb9866 100644
--- a/core/java/android/content/SyncStorageEngine.java
+++ b/core/java/android/content/SyncStorageEngine.java
@@ -25,6 +25,7 @@ import org.xmlpull.v1.XmlPullParserException;
import org.xmlpull.v1.XmlSerializer;
import android.accounts.Account;
+import android.content.SyncManager.AccountAndUser;
import android.database.Cursor;
import android.database.sqlite.SQLiteDatabase;
import android.database.sqlite.SQLiteException;
@@ -58,9 +59,16 @@ import java.util.List;
* @hide
*/
public class SyncStorageEngine extends Handler {
+
private static final String TAG = "SyncManager";
private static final boolean DEBUG_FILE = false;
+ private static final String XML_ATTR_NEXT_AUTHORITY_ID = "nextAuthorityId";
+ private static final String XML_ATTR_LISTEN_FOR_TICKLES = "listen-for-tickles";
+ private static final String XML_ATTR_ENABLED = "enabled";
+ private static final String XML_ATTR_USER = "user";
+ private static final String XML_TAG_LISTEN_FOR_TICKLES = "listenForTickles";
+
private static final long DEFAULT_POLL_FREQUENCY_SECONDS = 60 * 60 * 24; // One day
// @VisibleForTesting
@@ -133,6 +141,7 @@ public class SyncStorageEngine extends Handler {
public static class PendingOperation {
final Account account;
+ final int userId;
final int syncSource;
final String authority;
final Bundle extras; // note: read-only.
@@ -141,9 +150,10 @@ public class SyncStorageEngine extends Handler {
int authorityId;
byte[] flatExtras;
- PendingOperation(Account account, int source,
+ PendingOperation(Account account, int userId, int source,
String authority, Bundle extras, boolean expedited) {
this.account = account;
+ this.userId = userId;
this.syncSource = source;
this.authority = authority;
this.extras = extras != null ? new Bundle(extras) : extras;
@@ -153,6 +163,7 @@ public class SyncStorageEngine extends Handler {
PendingOperation(PendingOperation other) {
this.account = other.account;
+ this.userId = other.userId;
this.syncSource = other.syncSource;
this.authority = other.authority;
this.extras = other.extras;
@@ -162,17 +173,18 @@ public class SyncStorageEngine extends Handler {
}
static class AccountInfo {
- final Account account;
+ final AccountAndUser accountAndUser;
final HashMap<String, AuthorityInfo> authorities =
new HashMap<String, AuthorityInfo>();
- AccountInfo(Account account) {
- this.account = account;
+ AccountInfo(AccountAndUser accountAndUser) {
+ this.accountAndUser = accountAndUser;
}
}
public static class AuthorityInfo {
final Account account;
+ final int userId;
final String authority;
final int ident;
boolean enabled;
@@ -182,8 +194,9 @@ public class SyncStorageEngine extends Handler {
long delayUntil;
final ArrayList<Pair<Bundle, Long>> periodicSyncs;
- AuthorityInfo(Account account, String authority, int ident) {
+ AuthorityInfo(Account account, int userId, String authority, int ident) {
this.account = account;
+ this.userId = userId;
this.authority = authority;
this.ident = ident;
enabled = SYNC_ENABLED_DEFAULT;
@@ -219,17 +232,29 @@ public class SyncStorageEngine extends Handler {
}
}
+ interface OnSyncRequestListener {
+ /**
+ * Called when a sync is needed on an account(s) due to some change in state.
+ * @param account
+ * @param userId
+ * @param authority
+ * @param extras
+ */
+ public void onSyncRequest(Account account, int userId, String authority, Bundle extras);
+ }
+
// Primary list of all syncable authorities. Also our global lock.
private final SparseArray<AuthorityInfo> mAuthorities =
new SparseArray<AuthorityInfo>();
- private final HashMap<Account, AccountInfo> mAccounts =
- new HashMap<Account, AccountInfo>();
+ private final HashMap<AccountAndUser, AccountInfo> mAccounts
+ = new HashMap<AccountAndUser, AccountInfo>();
private final ArrayList<PendingOperation> mPendingOperations =
new ArrayList<PendingOperation>();
- private final ArrayList<SyncInfo> mCurrentSyncs = new ArrayList<SyncInfo>();
+ private final SparseArray<ArrayList<SyncInfo>> mCurrentSyncs
+ = new SparseArray<ArrayList<SyncInfo>>();
private final SparseArray<SyncStatusInfo> mSyncStatus =
new SparseArray<SyncStatusInfo>();
@@ -282,7 +307,9 @@ public class SyncStorageEngine extends Handler {
private int mNumPendingFinished = 0;
private int mNextHistoryId = 0;
- private boolean mMasterSyncAutomatically = true;
+ private SparseArray<Boolean> mMasterSyncAutomatically = new SparseArray<Boolean>();
+
+ private OnSyncRequestListener mSyncRequestListener;
private SyncStorageEngine(Context context, File dataDir) {
mContext = context;
@@ -330,6 +357,12 @@ public class SyncStorageEngine extends Handler {
return sSyncStorageEngine;
}
+ protected void setOnSyncRequestListener(OnSyncRequestListener listener) {
+ if (mSyncRequestListener == null) {
+ mSyncRequestListener = listener;
+ }
+ }
+
@Override public void handleMessage(Message msg) {
if (msg.what == MSG_WRITE_STATUS) {
synchronized (mAuthorities) {
@@ -389,10 +422,10 @@ public class SyncStorageEngine extends Handler {
}
}
- public boolean getSyncAutomatically(Account account, String providerName) {
+ public boolean getSyncAutomatically(Account account, int userId, String providerName) {
synchronized (mAuthorities) {
if (account != null) {
- AuthorityInfo authority = getAuthorityLocked(account, providerName,
+ AuthorityInfo authority = getAuthorityLocked(account, userId, providerName,
"getSyncAutomatically");
return authority != null && authority.enabled;
}
@@ -402,6 +435,7 @@ public class SyncStorageEngine extends Handler {
i--;
AuthorityInfo authority = mAuthorities.valueAt(i);
if (authority.authority.equals(providerName)
+ && authority.userId == userId
&& authority.enabled) {
return true;
}
@@ -410,11 +444,13 @@ public class SyncStorageEngine extends Handler {
}
}
- public void setSyncAutomatically(Account account, String providerName, boolean sync) {
- Log.d(TAG, "setSyncAutomatically: " + /*account +*/ ", provider " + providerName
- + " -> " + sync);
+ public void setSyncAutomatically(Account account, int userId, String providerName,
+ boolean sync) {
+ Log.d(TAG, "setSyncAutomatically: " + /* account + */" provider " + providerName
+ + ", user " + userId + " -> " + sync);
synchronized (mAuthorities) {
- AuthorityInfo authority = getOrCreateAuthorityLocked(account, providerName, -1, false);
+ AuthorityInfo authority = getOrCreateAuthorityLocked(account, userId, providerName, -1,
+ false);
if (authority.enabled == sync) {
Log.d(TAG, "setSyncAutomatically: already set to " + sync + ", doing nothing");
return;
@@ -424,15 +460,15 @@ public class SyncStorageEngine extends Handler {
}
if (sync) {
- ContentResolver.requestSync(account, providerName, new Bundle());
+ requestSync(account, userId, providerName, new Bundle());
}
reportChange(ContentResolver.SYNC_OBSERVER_TYPE_SETTINGS);
}
- public int getIsSyncable(Account account, String providerName) {
+ public int getIsSyncable(Account account, int userId, String providerName) {
synchronized (mAuthorities) {
if (account != null) {
- AuthorityInfo authority = getAuthorityLocked(account, providerName,
+ AuthorityInfo authority = getAuthorityLocked(account, userId, providerName,
"getIsSyncable");
if (authority == null) {
return -1;
@@ -452,15 +488,17 @@ public class SyncStorageEngine extends Handler {
}
}
- public void setIsSyncable(Account account, String providerName, int syncable) {
+ public void setIsSyncable(Account account, int userId, String providerName, int syncable) {
if (syncable > 1) {
syncable = 1;
} else if (syncable < -1) {
syncable = -1;
}
- Log.d(TAG, "setIsSyncable: " + account + ", provider " + providerName + " -> " + syncable);
+ Log.d(TAG, "setIsSyncable: " + account + ", provider " + providerName
+ + ", user " + userId + " -> " + syncable);
synchronized (mAuthorities) {
- AuthorityInfo authority = getOrCreateAuthorityLocked(account, providerName, -1, false);
+ AuthorityInfo authority = getOrCreateAuthorityLocked(account, userId, providerName, -1,
+ false);
if (authority.syncable == syncable) {
Log.d(TAG, "setIsSyncable: already set to " + syncable + ", doing nothing");
return;
@@ -470,14 +508,15 @@ public class SyncStorageEngine extends Handler {
}
if (syncable > 0) {
- ContentResolver.requestSync(account, providerName, new Bundle());
+ requestSync(account, userId, providerName, new Bundle());
}
reportChange(ContentResolver.SYNC_OBSERVER_TYPE_SETTINGS);
}
- public Pair<Long, Long> getBackoff(Account account, String providerName) {
+ public Pair<Long, Long> getBackoff(Account account, int userId, String providerName) {
synchronized (mAuthorities) {
- AuthorityInfo authority = getAuthorityLocked(account, providerName, "getBackoff");
+ AuthorityInfo authority = getAuthorityLocked(account, userId, providerName,
+ "getBackoff");
if (authority == null || authority.backoffTime < 0) {
return null;
}
@@ -485,17 +524,21 @@ public class SyncStorageEngine extends Handler {
}
}
- public void setBackoff(Account account, String providerName,
+ public void setBackoff(Account account, int userId, String providerName,
long nextSyncTime, long nextDelay) {
if (Log.isLoggable(TAG, Log.VERBOSE)) {
Log.v(TAG, "setBackoff: " + account + ", provider " + providerName
+ + ", user " + userId
+ " -> nextSyncTime " + nextSyncTime + ", nextDelay " + nextDelay);
}
boolean changed = false;
synchronized (mAuthorities) {
if (account == null || providerName == null) {
for (AccountInfo accountInfo : mAccounts.values()) {
- if (account != null && !account.equals(accountInfo.account)) continue;
+ if (account != null && !account.equals(accountInfo.accountAndUser.account)
+ && userId != accountInfo.accountAndUser.userId) {
+ continue;
+ }
for (AuthorityInfo authorityInfo : accountInfo.authorities.values()) {
if (providerName != null && !providerName.equals(authorityInfo.authority)) {
continue;
@@ -510,7 +553,8 @@ public class SyncStorageEngine extends Handler {
}
} else {
AuthorityInfo authority =
- getOrCreateAuthorityLocked(account, providerName, -1 /* ident */, true);
+ getOrCreateAuthorityLocked(account, userId, providerName, -1 /* ident */,
+ true);
if (authority.backoffTime == nextSyncTime && authority.backoffDelay == nextDelay) {
return;
}
@@ -535,13 +579,15 @@ public class SyncStorageEngine extends Handler {
if (Log.isLoggable(TAG, Log.VERBOSE)) {
Log.v(TAG, "clearAllBackoffs:"
+ " authority:" + authorityInfo.authority
- + " account:" + accountInfo.account.name
+ + " account:" + accountInfo.accountAndUser.account.name
+ + " user:" + accountInfo.accountAndUser.userId
+ " backoffTime was: " + authorityInfo.backoffTime
+ " backoffDelay was: " + authorityInfo.backoffDelay);
}
authorityInfo.backoffTime = NOT_IN_BACKOFF_MODE;
authorityInfo.backoffDelay = NOT_IN_BACKOFF_MODE;
- syncQueue.onBackoffChanged(accountInfo.account, authorityInfo.authority, 0);
+ syncQueue.onBackoffChanged(accountInfo.accountAndUser.account,
+ accountInfo.accountAndUser.userId, authorityInfo.authority, 0);
changed = true;
}
}
@@ -553,14 +599,15 @@ public class SyncStorageEngine extends Handler {
}
}
- public void setDelayUntilTime(Account account, String providerName, long delayUntil) {
+ public void setDelayUntilTime(Account account, int userId, String providerName,
+ long delayUntil) {
if (Log.isLoggable(TAG, Log.VERBOSE)) {
Log.v(TAG, "setDelayUntil: " + account + ", provider " + providerName
- + " -> delayUntil " + delayUntil);
+ + ", user " + userId + " -> delayUntil " + delayUntil);
}
synchronized (mAuthorities) {
AuthorityInfo authority = getOrCreateAuthorityLocked(
- account, providerName, -1 /* ident */, true);
+ account, userId, providerName, -1 /* ident */, true);
if (authority.delayUntil == delayUntil) {
return;
}
@@ -570,9 +617,10 @@ public class SyncStorageEngine extends Handler {
reportChange(ContentResolver.SYNC_OBSERVER_TYPE_SETTINGS);
}
- public long getDelayUntilTime(Account account, String providerName) {
+ public long getDelayUntilTime(Account account, int userId, String providerName) {
synchronized (mAuthorities) {
- AuthorityInfo authority = getAuthorityLocked(account, providerName, "getDelayUntil");
+ AuthorityInfo authority = getAuthorityLocked(account, userId, providerName,
+ "getDelayUntil");
if (authority == null) {
return 0;
}
@@ -580,7 +628,8 @@ public class SyncStorageEngine extends Handler {
}
}
- private void updateOrRemovePeriodicSync(Account account, String providerName, Bundle extras,
+ private void updateOrRemovePeriodicSync(Account account, int userId, String providerName,
+ Bundle extras,
long period, boolean add) {
if (period <= 0) {
period = 0;
@@ -589,13 +638,14 @@ public class SyncStorageEngine extends Handler {
extras = new Bundle();
}
if (Log.isLoggable(TAG, Log.VERBOSE)) {
- Log.v(TAG, "addOrRemovePeriodicSync: " + account + ", provider " + providerName
+ Log.v(TAG, "addOrRemovePeriodicSync: " + account + ", user " + userId
+ + ", provider " + providerName
+ " -> period " + period + ", extras " + extras);
}
synchronized (mAuthorities) {
try {
AuthorityInfo authority =
- getOrCreateAuthorityLocked(account, providerName, -1, false);
+ getOrCreateAuthorityLocked(account, userId, providerName, -1, false);
if (add) {
// add this periodic sync if one with the same extras doesn't already
// exist in the periodicSyncs array
@@ -652,61 +702,67 @@ public class SyncStorageEngine extends Handler {
reportChange(ContentResolver.SYNC_OBSERVER_TYPE_SETTINGS);
}
- public void addPeriodicSync(Account account, String providerName, Bundle extras,
+ public void addPeriodicSync(Account account, int userId, String providerName, Bundle extras,
long pollFrequency) {
- updateOrRemovePeriodicSync(account, providerName, extras, pollFrequency, true /* add */);
+ updateOrRemovePeriodicSync(account, userId, providerName, extras, pollFrequency,
+ true /* add */);
}
- public void removePeriodicSync(Account account, String providerName, Bundle extras) {
- updateOrRemovePeriodicSync(account, providerName, extras, 0 /* period, ignored */,
+ public void removePeriodicSync(Account account, int userId, String providerName,
+ Bundle extras) {
+ updateOrRemovePeriodicSync(account, userId, providerName, extras, 0 /* period, ignored */,
false /* remove */);
}
- public List<PeriodicSync> getPeriodicSyncs(Account account, String providerName) {
+ public List<PeriodicSync> getPeriodicSyncs(Account account, int userId, String providerName) {
ArrayList<PeriodicSync> syncs = new ArrayList<PeriodicSync>();
synchronized (mAuthorities) {
- AuthorityInfo authority = getAuthorityLocked(account, providerName, "getPeriodicSyncs");
+ AuthorityInfo authority = getAuthorityLocked(account, userId, providerName,
+ "getPeriodicSyncs");
if (authority != null) {
for (Pair<Bundle, Long> item : authority.periodicSyncs) {
- syncs.add(new PeriodicSync(account, providerName, item.first, item.second));
+ syncs.add(new PeriodicSync(account, providerName, item.first,
+ item.second));
}
}
}
return syncs;
}
- public void setMasterSyncAutomatically(boolean flag) {
+ public void setMasterSyncAutomatically(boolean flag, int userId) {
synchronized (mAuthorities) {
- if (mMasterSyncAutomatically == flag) {
+ Boolean auto = mMasterSyncAutomatically.get(userId);
+ if (auto != null && (boolean) auto == flag) {
return;
}
- mMasterSyncAutomatically = flag;
+ mMasterSyncAutomatically.put(userId, flag);
writeAccountInfoLocked();
}
if (flag) {
- ContentResolver.requestSync(null, null, new Bundle());
+ requestSync(null, userId, null, new Bundle());
}
reportChange(ContentResolver.SYNC_OBSERVER_TYPE_SETTINGS);
mContext.sendBroadcast(SYNC_CONNECTION_SETTING_CHANGED_INTENT);
}
- public boolean getMasterSyncAutomatically() {
+ public boolean getMasterSyncAutomatically(int userId) {
synchronized (mAuthorities) {
- return mMasterSyncAutomatically;
+ Boolean auto = mMasterSyncAutomatically.get(userId);
+ return auto == null ? true : auto;
}
}
- public AuthorityInfo getOrCreateAuthority(Account account, String authority) {
+ public AuthorityInfo getOrCreateAuthority(Account account, int userId, String authority) {
synchronized (mAuthorities) {
- return getOrCreateAuthorityLocked(account, authority,
+ return getOrCreateAuthorityLocked(account, userId, authority,
-1 /* assign a new identifier if creating a new authority */,
true /* write to storage if this results in a change */);
}
}
- public void removeAuthority(Account account, String authority) {
+ public void removeAuthority(Account account, int userId, String authority) {
synchronized (mAuthorities) {
- removeAuthorityLocked(account, authority, true /* doWrite */);
+ removeAuthorityLocked(account, userId, authority, true /* doWrite */);
}
}
@@ -720,12 +776,13 @@ public class SyncStorageEngine extends Handler {
* Returns true if there is currently a sync operation for the given
* account or authority actively being processed.
*/
- public boolean isSyncActive(Account account, String authority) {
+ public boolean isSyncActive(Account account, int userId, String authority) {
synchronized (mAuthorities) {
- for (SyncInfo syncInfo : mCurrentSyncs) {
+ for (SyncInfo syncInfo : getCurrentSyncs(userId)) {
AuthorityInfo ainfo = getAuthority(syncInfo.authorityId);
if (ainfo != null && ainfo.account.equals(account)
- && ainfo.authority.equals(authority)) {
+ && ainfo.authority.equals(authority)
+ && ainfo.userId == userId) {
return true;
}
}
@@ -738,12 +795,13 @@ public class SyncStorageEngine extends Handler {
synchronized (mAuthorities) {
if (Log.isLoggable(TAG, Log.VERBOSE)) {
Log.v(TAG, "insertIntoPending: account=" + op.account
- + " auth=" + op.authority
- + " src=" + op.syncSource
- + " extras=" + op.extras);
+ + " user=" + op.userId
+ + " auth=" + op.authority
+ + " src=" + op.syncSource
+ + " extras=" + op.extras);
}
- AuthorityInfo authority = getOrCreateAuthorityLocked(op.account,
+ AuthorityInfo authority = getOrCreateAuthorityLocked(op.account, op.userId,
op.authority,
-1 /* desired identifier */,
true /* write accounts to storage */);
@@ -769,6 +827,7 @@ public class SyncStorageEngine extends Handler {
synchronized (mAuthorities) {
if (Log.isLoggable(TAG, Log.VERBOSE)) {
Log.v(TAG, "deleteFromPending: account=" + op.account
+ + " user=" + op.userId
+ " auth=" + op.authority
+ " src=" + op.syncSource
+ " extras=" + op.extras);
@@ -782,7 +841,7 @@ public class SyncStorageEngine extends Handler {
mNumPendingFinished++;
}
- AuthorityInfo authority = getAuthorityLocked(op.account, op.authority,
+ AuthorityInfo authority = getAuthorityLocked(op.account, op.userId, op.authority,
"deleteFromPending");
if (authority != null) {
if (Log.isLoggable(TAG, Log.VERBOSE)) Log.v(TAG, "removing - " + authority);
@@ -791,7 +850,8 @@ public class SyncStorageEngine extends Handler {
for (int i=0; i<N; i++) {
PendingOperation cur = mPendingOperations.get(i);
if (cur.account.equals(op.account)
- && cur.authority.equals(op.authority)) {
+ && cur.authority.equals(op.authority)
+ && cur.userId == op.userId) {
morePending = true;
break;
}
@@ -812,24 +872,6 @@ public class SyncStorageEngine extends Handler {
return res;
}
- public int clearPending() {
- int num;
- synchronized (mAuthorities) {
- if (Log.isLoggable(TAG, Log.VERBOSE)) {
- Log.v(TAG, "clearPending");
- }
- num = mPendingOperations.size();
- mPendingOperations.clear();
- final int N = mSyncStatus.size();
- for (int i=0; i<N; i++) {
- mSyncStatus.valueAt(i).pending = false;
- }
- writePendingOperationsLocked();
- }
- reportChange(ContentResolver.SYNC_OBSERVER_TYPE_PENDING);
- return num;
- }
-
/**
* Return a copy of the current array of pending operations. The
* PendingOperation objects are the real objects stored inside, so that
@@ -854,17 +896,18 @@ public class SyncStorageEngine extends Handler {
* Called when the set of account has changed, given the new array of
* active accounts.
*/
- public void doDatabaseCleanup(Account[] accounts) {
+ public void doDatabaseCleanup(Account[] accounts, int userId) {
synchronized (mAuthorities) {
if (Log.isLoggable(TAG, Log.VERBOSE)) Log.w(TAG, "Updating for new accounts...");
SparseArray<AuthorityInfo> removing = new SparseArray<AuthorityInfo>();
Iterator<AccountInfo> accIt = mAccounts.values().iterator();
while (accIt.hasNext()) {
AccountInfo acc = accIt.next();
- if (!ArrayUtils.contains(accounts, acc.account)) {
+ if (!ArrayUtils.contains(accounts, acc.accountAndUser.account)
+ && acc.accountAndUser.userId == userId) {
// This account no longer exists...
if (Log.isLoggable(TAG, Log.VERBOSE)) {
- Log.w(TAG, "Account removed: " + acc.account);
+ Log.w(TAG, "Account removed: " + acc.accountAndUser);
}
for (AuthorityInfo auth : acc.authorities.values()) {
removing.put(auth.ident, auth);
@@ -919,13 +962,14 @@ public class SyncStorageEngine extends Handler {
}
AuthorityInfo authority = getOrCreateAuthorityLocked(
activeSyncContext.mSyncOperation.account,
+ activeSyncContext.mSyncOperation.userId,
activeSyncContext.mSyncOperation.authority,
-1 /* assign a new identifier if creating a new authority */,
true /* write to storage if this results in a change */);
syncInfo = new SyncInfo(authority.ident,
authority.account, authority.authority,
activeSyncContext.mStartTime);
- mCurrentSyncs.add(syncInfo);
+ getCurrentSyncs(authority.userId).add(syncInfo);
}
reportActiveChange();
@@ -935,13 +979,14 @@ public class SyncStorageEngine extends Handler {
/**
* Called to indicate that a previously active sync is no longer active.
*/
- public void removeActiveSync(SyncInfo syncInfo) {
+ public void removeActiveSync(SyncInfo syncInfo, int userId) {
synchronized (mAuthorities) {
if (Log.isLoggable(TAG, Log.VERBOSE)) {
- Log.v(TAG, "removeActiveSync: account="
- + syncInfo.account + " auth=" + syncInfo.authority);
+ Log.v(TAG, "removeActiveSync: account=" + syncInfo.account
+ + " user=" + userId
+ + " auth=" + syncInfo.authority);
}
- mCurrentSyncs.remove(syncInfo);
+ getCurrentSyncs(userId).remove(syncInfo);
}
reportActiveChange();
@@ -957,15 +1002,15 @@ public class SyncStorageEngine extends Handler {
/**
* Note that sync has started for the given account and authority.
*/
- public long insertStartSyncEvent(Account accountName, String authorityName,
+ public long insertStartSyncEvent(Account accountName, int userId, String authorityName,
long now, int source) {
long id;
synchronized (mAuthorities) {
if (Log.isLoggable(TAG, Log.VERBOSE)) {
- Log.v(TAG, "insertStartSyncEvent: account=" + accountName
+ Log.v(TAG, "insertStartSyncEvent: account=" + accountName + "user=" + userId
+ " auth=" + authorityName + " source=" + source);
}
- AuthorityInfo authority = getAuthorityLocked(accountName, authorityName,
+ AuthorityInfo authority = getAuthorityLocked(accountName, userId, authorityName,
"insertStartSyncEvent");
if (authority == null) {
return -1;
@@ -1119,9 +1164,14 @@ public class SyncStorageEngine extends Handler {
* Return a list of the currently active syncs. Note that the returned items are the
* real, live active sync objects, so be careful what you do with it.
*/
- public List<SyncInfo> getCurrentSyncs() {
+ public List<SyncInfo> getCurrentSyncs(int userId) {
synchronized (mAuthorities) {
- return new ArrayList<SyncInfo>(mCurrentSyncs);
+ ArrayList<SyncInfo> syncs = mCurrentSyncs.get(userId);
+ if (syncs == null) {
+ syncs = new ArrayList<SyncInfo>();
+ mCurrentSyncs.put(userId, syncs);
+ }
+ return new ArrayList<SyncInfo>(syncs);
}
}
@@ -1164,7 +1214,8 @@ public class SyncStorageEngine extends Handler {
* @param authority the authority whose row should be selected
* @return the SyncStatusInfo for the authority
*/
- public SyncStatusInfo getStatusByAccountAndAuthority(Account account, String authority) {
+ public SyncStatusInfo getStatusByAccountAndAuthority(Account account, int userId,
+ String authority) {
if (account == null || authority == null) {
throw new IllegalArgumentException();
}
@@ -1174,8 +1225,9 @@ public class SyncStorageEngine extends Handler {
SyncStatusInfo cur = mSyncStatus.valueAt(i);
AuthorityInfo ainfo = mAuthorities.get(cur.authorityId);
- if (ainfo != null && ainfo.authority.equals(authority) &&
- account.equals(ainfo.account)) {
+ if (ainfo != null && ainfo.authority.equals(authority)
+ && ainfo.userId == userId
+ && account.equals(ainfo.account)) {
return cur;
}
}
@@ -1186,7 +1238,7 @@ public class SyncStorageEngine extends Handler {
/**
* Return true if the pending status is true of any matching authorities.
*/
- public boolean isSyncPending(Account account, String authority) {
+ public boolean isSyncPending(Account account, int userId, String authority) {
synchronized (mAuthorities) {
final int N = mSyncStatus.size();
for (int i=0; i<N; i++) {
@@ -1195,6 +1247,9 @@ public class SyncStorageEngine extends Handler {
if (ainfo == null) {
continue;
}
+ if (userId != ainfo.userId) {
+ continue;
+ }
if (account != null && !ainfo.account.equals(account)) {
continue;
}
@@ -1235,34 +1290,6 @@ public class SyncStorageEngine extends Handler {
}
}
- /**
- * If sync is failing for any of the provider/accounts then determine the time at which it
- * started failing and return the earliest time over all the provider/accounts. If none are
- * failing then return 0.
- */
- public long getInitialSyncFailureTime() {
- synchronized (mAuthorities) {
- if (!mMasterSyncAutomatically) {
- return 0;
- }
-
- long oldest = 0;
- int i = mSyncStatus.size();
- while (i > 0) {
- i--;
- SyncStatusInfo stats = mSyncStatus.valueAt(i);
- AuthorityInfo authority = mAuthorities.get(stats.authorityId);
- if (authority != null && authority.enabled) {
- if (oldest == 0 || stats.initialFailureTime < oldest) {
- oldest = stats.initialFailureTime;
- }
- }
- }
-
- return oldest;
- }
- }
-
private int getCurrentDayLocked() {
mCal.setTimeInMillis(System.currentTimeMillis());
final int dayOfYear = mCal.get(Calendar.DAY_OF_YEAR);
@@ -1283,18 +1310,19 @@ public class SyncStorageEngine extends Handler {
* @param tag If non-null, this will be used in a log message if the
* requested authority does not exist.
*/
- private AuthorityInfo getAuthorityLocked(Account accountName, String authorityName,
+ private AuthorityInfo getAuthorityLocked(Account accountName, int userId, String authorityName,
String tag) {
- AccountInfo account = mAccounts.get(accountName);
- if (account == null) {
+ AccountAndUser au = new AccountAndUser(accountName, userId);
+ AccountInfo accountInfo = mAccounts.get(au);
+ if (accountInfo == null) {
if (tag != null) {
if (Log.isLoggable(TAG, Log.VERBOSE)) {
- Log.v(TAG, tag + ": unknown account " + accountName);
+ Log.v(TAG, tag + ": unknown account " + au);
}
}
return null;
}
- AuthorityInfo authority = account.authorities.get(authorityName);
+ AuthorityInfo authority = accountInfo.authorities.get(authorityName);
if (authority == null) {
if (tag != null) {
if (Log.isLoggable(TAG, Log.VERBOSE)) {
@@ -1307,12 +1335,13 @@ public class SyncStorageEngine extends Handler {
return authority;
}
- private AuthorityInfo getOrCreateAuthorityLocked(Account accountName,
+ private AuthorityInfo getOrCreateAuthorityLocked(Account accountName, int userId,
String authorityName, int ident, boolean doWrite) {
- AccountInfo account = mAccounts.get(accountName);
+ AccountAndUser au = new AccountAndUser(accountName, userId);
+ AccountInfo account = mAccounts.get(au);
if (account == null) {
- account = new AccountInfo(accountName);
- mAccounts.put(accountName, account);
+ account = new AccountInfo(au);
+ mAccounts.put(au, account);
}
AuthorityInfo authority = account.authorities.get(authorityName);
if (authority == null) {
@@ -1323,9 +1352,10 @@ public class SyncStorageEngine extends Handler {
}
if (Log.isLoggable(TAG, Log.VERBOSE)) {
Log.v(TAG, "created a new AuthorityInfo for " + accountName
- + ", provider " + authorityName);
+ + ", user " + userId
+ + ", provider " + authorityName);
}
- authority = new AuthorityInfo(accountName, authorityName, ident);
+ authority = new AuthorityInfo(accountName, userId, authorityName, ident);
account.authorities.put(authorityName, authority);
mAuthorities.put(ident, authority);
if (doWrite) {
@@ -1336,8 +1366,9 @@ public class SyncStorageEngine extends Handler {
return authority;
}
- private void removeAuthorityLocked(Account account, String authorityName, boolean doWrite) {
- AccountInfo accountInfo = mAccounts.get(account);
+ private void removeAuthorityLocked(Account account, int userId, String authorityName,
+ boolean doWrite) {
+ AccountInfo accountInfo = mAccounts.get(new AccountAndUser(account, userId));
if (accountInfo != null) {
final AuthorityInfo authorityInfo = accountInfo.authorities.remove(authorityName);
if (authorityInfo != null) {
@@ -1419,8 +1450,7 @@ public class SyncStorageEngine extends Handler {
}
String tagName = parser.getName();
if ("accounts".equals(tagName)) {
- String listen = parser.getAttributeValue(
- null, "listen-for-tickles");
+ String listen = parser.getAttributeValue(null, XML_ATTR_LISTEN_FOR_TICKLES);
String versionString = parser.getAttributeValue(null, "version");
int version;
try {
@@ -1428,14 +1458,14 @@ public class SyncStorageEngine extends Handler {
} catch (NumberFormatException e) {
version = 0;
}
- String nextIdString = parser.getAttributeValue(null, "nextAuthorityId");
+ String nextIdString = parser.getAttributeValue(null, XML_ATTR_NEXT_AUTHORITY_ID);
try {
int id = (nextIdString == null) ? 0 : Integer.parseInt(nextIdString);
mNextAuthorityId = Math.max(mNextAuthorityId, id);
} catch (NumberFormatException e) {
// don't care
}
- mMasterSyncAutomatically = listen == null || Boolean.parseBoolean(listen);
+ mMasterSyncAutomatically.put(0, listen == null || Boolean.parseBoolean(listen));
eventType = parser.next();
AuthorityInfo authority = null;
Pair<Bundle, Long> periodicSync = null;
@@ -1449,6 +1479,8 @@ public class SyncStorageEngine extends Handler {
if (authority.ident > highestAuthorityId) {
highestAuthorityId = authority.ident;
}
+ } else if (XML_TAG_LISTEN_FOR_TICKLES.equals(tagName)) {
+ parseListenForTickles(parser);
}
} else if (parser.getDepth() == 3) {
if ("periodicSync".equals(tagName) && authority != null) {
@@ -1511,25 +1543,41 @@ public class SyncStorageEngine extends Handler {
}
// if we already have a record of this new authority then don't copy over the settings
- if (getAuthorityLocked(authority.account, newAuthorityName, "cleanup") != null) {
+ if (getAuthorityLocked(authority.account, authority.userId, newAuthorityName, "cleanup")
+ != null) {
continue;
}
AuthorityInfo newAuthority = getOrCreateAuthorityLocked(authority.account,
- newAuthorityName, -1 /* ident */, false /* doWrite */);
+ authority.userId, newAuthorityName, -1 /* ident */, false /* doWrite */);
newAuthority.enabled = true;
writeNeeded = true;
}
for (AuthorityInfo authorityInfo : authoritiesToRemove) {
- removeAuthorityLocked(authorityInfo.account, authorityInfo.authority,
- false /* doWrite */);
+ removeAuthorityLocked(authorityInfo.account, authorityInfo.userId,
+ authorityInfo.authority, false /* doWrite */);
writeNeeded = true;
}
return writeNeeded;
}
+ private void parseListenForTickles(XmlPullParser parser) {
+ String user = parser.getAttributeValue(null, XML_ATTR_USER);
+ int userId = 0;
+ try {
+ userId = Integer.parseInt(user);
+ } catch (NumberFormatException e) {
+ Log.e(TAG, "error parsing the user for listen-for-tickles", e);
+ } catch (NullPointerException e) {
+ Log.e(TAG, "the user in listen-for-tickles is null", e);
+ }
+ String enabled = parser.getAttributeValue(null, XML_ATTR_ENABLED);
+ boolean listen = enabled == null || Boolean.parseBoolean(enabled);
+ mMasterSyncAutomatically.put(userId, listen);
+ }
+
private AuthorityInfo parseAuthority(XmlPullParser parser, int version) {
AuthorityInfo authority = null;
int id = -1;
@@ -1543,10 +1591,12 @@ public class SyncStorageEngine extends Handler {
}
if (id >= 0) {
String authorityName = parser.getAttributeValue(null, "authority");
- String enabled = parser.getAttributeValue(null, "enabled");
+ String enabled = parser.getAttributeValue(null, XML_ATTR_ENABLED);
String syncable = parser.getAttributeValue(null, "syncable");
String accountName = parser.getAttributeValue(null, "account");
String accountType = parser.getAttributeValue(null, "type");
+ String user = parser.getAttributeValue(null, XML_ATTR_USER);
+ int userId = user == null ? 0 : Integer.parseInt(user);
if (accountType == null) {
accountType = "com.google";
syncable = "unknown";
@@ -1554,12 +1604,13 @@ public class SyncStorageEngine extends Handler {
authority = mAuthorities.get(id);
if (DEBUG_FILE) Log.v(TAG, "Adding authority: account="
+ accountName + " auth=" + authorityName
+ + " user=" + userId
+ " enabled=" + enabled
+ " syncable=" + syncable);
if (authority == null) {
if (DEBUG_FILE) Log.v(TAG, "Creating entry");
authority = getOrCreateAuthorityLocked(
- new Account(accountName, accountType), authorityName, id, false);
+ new Account(accountName, accountType), userId, authorityName, id, false);
// If the version is 0 then we are upgrading from a file format that did not
// know about periodic syncs. In that case don't clear the list since we
// want the default, which is a daily periodioc sync.
@@ -1653,9 +1704,17 @@ public class SyncStorageEngine extends Handler {
out.startTag(null, "accounts");
out.attribute(null, "version", Integer.toString(ACCOUNTS_VERSION));
- out.attribute(null, "nextAuthorityId", Integer.toString(mNextAuthorityId));
- if (!mMasterSyncAutomatically) {
- out.attribute(null, "listen-for-tickles", "false");
+ out.attribute(null, XML_ATTR_NEXT_AUTHORITY_ID, Integer.toString(mNextAuthorityId));
+
+ // Write the Sync Automatically flags for each user
+ final int M = mMasterSyncAutomatically.size();
+ for (int m = 0; m < M; m++) {
+ int userId = mMasterSyncAutomatically.keyAt(m);
+ Boolean listen = mMasterSyncAutomatically.valueAt(m);
+ out.startTag(null, XML_TAG_LISTEN_FOR_TICKLES);
+ out.attribute(null, XML_ATTR_USER, Integer.toString(userId));
+ out.attribute(null, XML_ATTR_ENABLED, Boolean.toString(listen));
+ out.endTag(null, XML_TAG_LISTEN_FOR_TICKLES);
}
final int N = mAuthorities.size();
@@ -1664,9 +1723,10 @@ public class SyncStorageEngine extends Handler {
out.startTag(null, "authority");
out.attribute(null, "id", Integer.toString(authority.ident));
out.attribute(null, "account", authority.account.name);
+ out.attribute(null, XML_ATTR_USER, Integer.toString(authority.userId));
out.attribute(null, "type", authority.account.type);
out.attribute(null, "authority", authority.authority);
- out.attribute(null, "enabled", Boolean.toString(authority.enabled));
+ out.attribute(null, XML_ATTR_ENABLED, Boolean.toString(authority.enabled));
if (authority.syncable < 0) {
out.attribute(null, "syncable", "unknown");
} else {
@@ -1788,7 +1848,7 @@ public class SyncStorageEngine extends Handler {
}
String authorityName = c.getString(c.getColumnIndex("authority"));
AuthorityInfo authority = this.getOrCreateAuthorityLocked(
- new Account(accountName, accountType),
+ new Account(accountName, accountType), 0 /* legacy is single-user */,
authorityName, -1, false);
if (authority != null) {
int i = mSyncStatus.size();
@@ -1833,7 +1893,7 @@ public class SyncStorageEngine extends Handler {
String value = c.getString(c.getColumnIndex("value"));
if (name == null) continue;
if (name.equals("listen_for_tickles")) {
- setMasterSyncAutomatically(value == null || Boolean.parseBoolean(value));
+ setMasterSyncAutomatically(value == null || Boolean.parseBoolean(value), 0);
} else if (name.startsWith("sync_provider_")) {
String provider = name.substring("sync_provider_".length(),
name.length());
@@ -1964,7 +2024,7 @@ public class SyncStorageEngine extends Handler {
extras = new Bundle();
}
PendingOperation op = new PendingOperation(
- authority.account, syncSource,
+ authority.account, authority.userId, syncSource,
authority.authority, extras, expedited);
op.authorityId = authorityId;
op.flatExtras = flatExtras;
@@ -2084,6 +2144,19 @@ public class SyncStorageEngine extends Handler {
return bundle;
}
+ private void requestSync(Account account, int userId, String authority, Bundle extras) {
+ // If this is happening in the system process, then call the syncrequest listener
+ // to make a request back to the SyncManager directly.
+ // If this is probably a test instance, then call back through the ContentResolver
+ // which will know which userId to apply based on the Binder id.
+ if (android.os.Process.myUid() == android.os.Process.SYSTEM_UID
+ && mSyncRequestListener != null) {
+ mSyncRequestListener.onSyncRequest(account, userId, authority, extras);
+ } else {
+ ContentResolver.requestSync(account, authority, extras);
+ }
+ }
+
public static final int STATISTICS_FILE_END = 0;
public static final int STATISTICS_FILE_ITEM_OLD = 100;
public static final int STATISTICS_FILE_ITEM = 101;
diff --git a/core/java/android/content/pm/PackageManager.java b/core/java/android/content/pm/PackageManager.java
index f2133d8..544bd9c 100644
--- a/core/java/android/content/pm/PackageManager.java
+++ b/core/java/android/content/pm/PackageManager.java
@@ -119,7 +119,7 @@ public abstract class PackageManager {
* {@link PackageInfo} flag: return the
* {@link PackageInfo#gids group ids} that are associated with an
* application.
- * This applies for any API returning an PackageInfo class, either
+ * This applies for any API returning a PackageInfo class, either
* directly or nested inside of another.
*/
public static final int GET_GIDS = 0x00000100;
@@ -142,7 +142,7 @@ public abstract class PackageManager {
* {@link ProviderInfo} flag: return the
* {@link ProviderInfo#uriPermissionPatterns URI permission patterns}
* that are associated with a content provider.
- * This applies for any API returning an ProviderInfo class, either
+ * This applies for any API returning a ProviderInfo class, either
* directly or nested inside of another.
*/
public static final int GET_URI_PERMISSION_PATTERNS = 0x00000800;
diff --git a/core/java/android/database/Cursor.java b/core/java/android/database/Cursor.java
index 59ec89d..907833d 100644
--- a/core/java/android/database/Cursor.java
+++ b/core/java/android/database/Cursor.java
@@ -20,6 +20,8 @@ import android.content.ContentResolver;
import android.net.Uri;
import android.os.Bundle;
+import java.io.Closeable;
+
/**
* This interface provides random read-write access to the result set returned
* by a database query.
@@ -27,7 +29,7 @@ import android.os.Bundle;
* Cursor implementations are not required to be synchronized so code using a Cursor from multiple
* threads should perform its own synchronization when using the Cursor.
*/
-public interface Cursor {
+public interface Cursor extends Closeable {
/*
* Values returned by {@link #getType(int)}.
* These should be consistent with the corresponding types defined in CursorWindow.h
diff --git a/core/java/android/database/CursorWindow.java b/core/java/android/database/CursorWindow.java
index 85f570c..f1f3017 100644
--- a/core/java/android/database/CursorWindow.java
+++ b/core/java/android/database/CursorWindow.java
@@ -169,14 +169,6 @@ public class CursorWindow extends SQLiteClosable implements Parcelable {
}
/**
- * Closes the cursor window and frees its underlying resources when all other
- * remaining references have been released.
- */
- public void close() {
- releaseReference();
- }
-
- /**
* Clears out the existing contents of the window, making it safe to reuse
* for new data.
* <p>
@@ -703,8 +695,13 @@ public class CursorWindow extends SQLiteClosable implements Parcelable {
}
public void writeToParcel(Parcel dest, int flags) {
- dest.writeInt(mStartPos);
- nativeWriteToParcel(mWindowPtr, dest);
+ acquireReference();
+ try {
+ dest.writeInt(mStartPos);
+ nativeWriteToParcel(mWindowPtr, dest);
+ } finally {
+ releaseReference();
+ }
if ((flags & Parcelable.PARCELABLE_WRITE_RETURN_VALUE) != 0) {
releaseReference();
diff --git a/core/java/android/database/DatabaseUtils.java b/core/java/android/database/DatabaseUtils.java
index 0022118..99d260e 100644
--- a/core/java/android/database/DatabaseUtils.java
+++ b/core/java/android/database/DatabaseUtils.java
@@ -269,63 +269,56 @@ public class DatabaseUtils {
if (position < 0 || position >= cursor.getCount()) {
return;
}
- window.acquireReference();
- try {
- final int oldPos = cursor.getPosition();
- final int numColumns = cursor.getColumnCount();
- window.clear();
- window.setStartPosition(position);
- window.setNumColumns(numColumns);
- if (cursor.moveToPosition(position)) {
- do {
- if (!window.allocRow()) {
- break;
- }
- for (int i = 0; i < numColumns; i++) {
- final int type = cursor.getType(i);
- final boolean success;
- switch (type) {
- case Cursor.FIELD_TYPE_NULL:
- success = window.putNull(position, i);
- break;
-
- case Cursor.FIELD_TYPE_INTEGER:
- success = window.putLong(cursor.getLong(i), position, i);
- break;
-
- case Cursor.FIELD_TYPE_FLOAT:
- success = window.putDouble(cursor.getDouble(i), position, i);
- break;
-
- case Cursor.FIELD_TYPE_BLOB: {
- final byte[] value = cursor.getBlob(i);
- success = value != null ? window.putBlob(value, position, i)
- : window.putNull(position, i);
- break;
- }
-
- default: // assume value is convertible to String
- case Cursor.FIELD_TYPE_STRING: {
- final String value = cursor.getString(i);
- success = value != null ? window.putString(value, position, i)
- : window.putNull(position, i);
- break;
- }
+ final int oldPos = cursor.getPosition();
+ final int numColumns = cursor.getColumnCount();
+ window.clear();
+ window.setStartPosition(position);
+ window.setNumColumns(numColumns);
+ if (cursor.moveToPosition(position)) {
+ do {
+ if (!window.allocRow()) {
+ break;
+ }
+ for (int i = 0; i < numColumns; i++) {
+ final int type = cursor.getType(i);
+ final boolean success;
+ switch (type) {
+ case Cursor.FIELD_TYPE_NULL:
+ success = window.putNull(position, i);
+ break;
+
+ case Cursor.FIELD_TYPE_INTEGER:
+ success = window.putLong(cursor.getLong(i), position, i);
+ break;
+
+ case Cursor.FIELD_TYPE_FLOAT:
+ success = window.putDouble(cursor.getDouble(i), position, i);
+ break;
+
+ case Cursor.FIELD_TYPE_BLOB: {
+ final byte[] value = cursor.getBlob(i);
+ success = value != null ? window.putBlob(value, position, i)
+ : window.putNull(position, i);
+ break;
}
- if (!success) {
- window.freeLastRow();
+
+ default: // assume value is convertible to String
+ case Cursor.FIELD_TYPE_STRING: {
+ final String value = cursor.getString(i);
+ success = value != null ? window.putString(value, position, i)
+ : window.putNull(position, i);
break;
}
}
- position += 1;
- } while (cursor.moveToNext());
- }
- cursor.moveToPosition(oldPos);
- } catch (IllegalStateException e){
- // simply ignore it
- } finally {
- window.releaseReference();
+ if (!success) {
+ window.freeLastRow();
+ break;
+ }
+ }
+ position += 1;
+ } while (cursor.moveToNext());
}
+ cursor.moveToPosition(oldPos);
}
/**
diff --git a/core/java/android/database/sqlite/SQLiteClosable.java b/core/java/android/database/sqlite/SQLiteClosable.java
index 7e91a7b..adfbc6e 100644
--- a/core/java/android/database/sqlite/SQLiteClosable.java
+++ b/core/java/android/database/sqlite/SQLiteClosable.java
@@ -16,15 +16,39 @@
package android.database.sqlite;
+import java.io.Closeable;
+
/**
* An object created from a SQLiteDatabase that can be closed.
+ *
+ * This class implements a primitive reference counting scheme for database objects.
*/
-public abstract class SQLiteClosable {
+public abstract class SQLiteClosable implements Closeable {
private int mReferenceCount = 1;
+ /**
+ * Called when the last reference to the object was released by
+ * a call to {@link #releaseReference()} or {@link #close()}.
+ */
protected abstract void onAllReferencesReleased();
- protected void onAllReferencesReleasedFromContainer() {}
+ /**
+ * Called when the last reference to the object was released by
+ * a call to {@link #releaseReferenceFromContainer()}.
+ *
+ * @deprecated Do not use.
+ */
+ @Deprecated
+ protected void onAllReferencesReleasedFromContainer() {
+ onAllReferencesReleased();
+ }
+
+ /**
+ * Acquires a reference to the object.
+ *
+ * @throws IllegalStateException if the last reference to the object has already
+ * been released.
+ */
public void acquireReference() {
synchronized(this) {
if (mReferenceCount <= 0) {
@@ -35,6 +59,12 @@ public abstract class SQLiteClosable {
}
}
+ /**
+ * Releases a reference to the object, closing the object if the last reference
+ * was released.
+ *
+ * @see #onAllReferencesReleased()
+ */
public void releaseReference() {
boolean refCountIsZero = false;
synchronized(this) {
@@ -45,6 +75,14 @@ public abstract class SQLiteClosable {
}
}
+ /**
+ * Releases a reference to the object that was owned by the container of the object,
+ * closing the object if the last reference was released.
+ *
+ * @see #onAllReferencesReleasedFromContainer()
+ * @deprecated Do not use.
+ */
+ @Deprecated
public void releaseReferenceFromContainer() {
boolean refCountIsZero = false;
synchronized(this) {
@@ -54,4 +92,17 @@ public abstract class SQLiteClosable {
onAllReferencesReleasedFromContainer();
}
}
+
+ /**
+ * Releases a reference to the object, closing the object if the last reference
+ * was released.
+ *
+ * Calling this method is equivalent to calling {@link #releaseReference}.
+ *
+ * @see #releaseReference()
+ * @see #onAllReferencesReleased()
+ */
+ public void close() {
+ releaseReference();
+ }
}
diff --git a/core/java/android/database/sqlite/SQLiteConnection.java b/core/java/android/database/sqlite/SQLiteConnection.java
index d16f29f..0db3e4f 100644
--- a/core/java/android/database/sqlite/SQLiteConnection.java
+++ b/core/java/android/database/sqlite/SQLiteConnection.java
@@ -704,44 +704,49 @@ public final class SQLiteConnection implements CancellationSignal.OnCancelListen
throw new IllegalArgumentException("window must not be null.");
}
- int actualPos = -1;
- int countedRows = -1;
- int filledRows = -1;
- final int cookie = mRecentOperations.beginOperation("executeForCursorWindow",
- sql, bindArgs);
+ window.acquireReference();
try {
- final PreparedStatement statement = acquirePreparedStatement(sql);
+ int actualPos = -1;
+ int countedRows = -1;
+ int filledRows = -1;
+ final int cookie = mRecentOperations.beginOperation("executeForCursorWindow",
+ sql, bindArgs);
try {
- throwIfStatementForbidden(statement);
- bindArguments(statement, bindArgs);
- applyBlockGuardPolicy(statement);
- attachCancellationSignal(cancellationSignal);
+ final PreparedStatement statement = acquirePreparedStatement(sql);
try {
- final long result = nativeExecuteForCursorWindow(
- mConnectionPtr, statement.mStatementPtr, window.mWindowPtr,
- startPos, requiredPos, countAllRows);
- actualPos = (int)(result >> 32);
- countedRows = (int)result;
- filledRows = window.getNumRows();
- window.setStartPosition(actualPos);
- return countedRows;
+ throwIfStatementForbidden(statement);
+ bindArguments(statement, bindArgs);
+ applyBlockGuardPolicy(statement);
+ attachCancellationSignal(cancellationSignal);
+ try {
+ final long result = nativeExecuteForCursorWindow(
+ mConnectionPtr, statement.mStatementPtr, window.mWindowPtr,
+ startPos, requiredPos, countAllRows);
+ actualPos = (int)(result >> 32);
+ countedRows = (int)result;
+ filledRows = window.getNumRows();
+ window.setStartPosition(actualPos);
+ return countedRows;
+ } finally {
+ detachCancellationSignal(cancellationSignal);
+ }
} finally {
- detachCancellationSignal(cancellationSignal);
+ releasePreparedStatement(statement);
}
+ } catch (RuntimeException ex) {
+ mRecentOperations.failOperation(cookie, ex);
+ throw ex;
} finally {
- releasePreparedStatement(statement);
+ if (mRecentOperations.endOperationDeferLog(cookie)) {
+ mRecentOperations.logOperation(cookie, "window='" + window
+ + "', startPos=" + startPos
+ + ", actualPos=" + actualPos
+ + ", filledRows=" + filledRows
+ + ", countedRows=" + countedRows);
+ }
}
- } catch (RuntimeException ex) {
- mRecentOperations.failOperation(cookie, ex);
- throw ex;
} finally {
- if (mRecentOperations.endOperationDeferLog(cookie)) {
- mRecentOperations.logOperation(cookie, "window='" + window
- + "', startPos=" + startPos
- + ", actualPos=" + actualPos
- + ", filledRows=" + filledRows
- + ", countedRows=" + countedRows);
- }
+ window.releaseReference();
}
}
diff --git a/core/java/android/database/sqlite/SQLiteDatabase.java b/core/java/android/database/sqlite/SQLiteDatabase.java
index 604247e..d41b484 100644
--- a/core/java/android/database/sqlite/SQLiteDatabase.java
+++ b/core/java/android/database/sqlite/SQLiteDatabase.java
@@ -492,9 +492,16 @@ public final class SQLiteDatabase extends SQLiteClosable {
private void beginTransaction(SQLiteTransactionListener transactionListener,
boolean exclusive) {
- getThreadSession().beginTransaction(exclusive ? SQLiteSession.TRANSACTION_MODE_EXCLUSIVE :
- SQLiteSession.TRANSACTION_MODE_IMMEDIATE, transactionListener,
- getThreadDefaultConnectionFlags(false /*readOnly*/), null);
+ acquireReference();
+ try {
+ getThreadSession().beginTransaction(
+ exclusive ? SQLiteSession.TRANSACTION_MODE_EXCLUSIVE :
+ SQLiteSession.TRANSACTION_MODE_IMMEDIATE,
+ transactionListener,
+ getThreadDefaultConnectionFlags(false /*readOnly*/), null);
+ } finally {
+ releaseReference();
+ }
}
/**
@@ -502,7 +509,12 @@ public final class SQLiteDatabase extends SQLiteClosable {
* are committed and rolled back.
*/
public void endTransaction() {
- getThreadSession().endTransaction(null);
+ acquireReference();
+ try {
+ getThreadSession().endTransaction(null);
+ } finally {
+ releaseReference();
+ }
}
/**
@@ -515,7 +527,12 @@ public final class SQLiteDatabase extends SQLiteClosable {
* transaction is already marked as successful.
*/
public void setTransactionSuccessful() {
- getThreadSession().setTransactionSuccessful();
+ acquireReference();
+ try {
+ getThreadSession().setTransactionSuccessful();
+ } finally {
+ releaseReference();
+ }
}
/**
@@ -524,7 +541,12 @@ public final class SQLiteDatabase extends SQLiteClosable {
* @return True if the current thread is in a transaction.
*/
public boolean inTransaction() {
- return getThreadSession().hasTransaction();
+ acquireReference();
+ try {
+ return getThreadSession().hasTransaction();
+ } finally {
+ releaseReference();
+ }
}
/**
@@ -540,7 +562,12 @@ public final class SQLiteDatabase extends SQLiteClosable {
* @return True if the current thread is holding an active connection to the database.
*/
public boolean isDbLockedByCurrentThread() {
- return getThreadSession().hasConnection();
+ acquireReference();
+ try {
+ return getThreadSession().hasConnection();
+ } finally {
+ releaseReference();
+ }
}
/**
@@ -599,7 +626,12 @@ public final class SQLiteDatabase extends SQLiteClosable {
}
private boolean yieldIfContendedHelper(boolean throwIfUnsafe, long sleepAfterYieldDelay) {
- return getThreadSession().yieldTransaction(sleepAfterYieldDelay, throwIfUnsafe, null);
+ acquireReference();
+ try {
+ return getThreadSession().yieldTransaction(sleepAfterYieldDelay, throwIfUnsafe, null);
+ } finally {
+ releaseReference();
+ }
}
/**
@@ -788,13 +820,6 @@ public final class SQLiteDatabase extends SQLiteClosable {
}
/**
- * Close the database.
- */
- public void close() {
- dispose(false);
- }
-
- /**
* Registers a CustomFunction callback as a function that can be called from
* SQLite database triggers.
*
@@ -948,8 +973,12 @@ public final class SQLiteDatabase extends SQLiteClosable {
* {@link SQLiteStatement}s are not synchronized, see the documentation for more details.
*/
public SQLiteStatement compileStatement(String sql) throws SQLException {
- throwIfNotOpen(); // fail fast
- return new SQLiteStatement(this, sql, null);
+ acquireReference();
+ try {
+ return new SQLiteStatement(this, sql, null);
+ } finally {
+ releaseReference();
+ }
}
/**
@@ -1110,12 +1139,16 @@ public final class SQLiteDatabase extends SQLiteClosable {
boolean distinct, String table, String[] columns,
String selection, String[] selectionArgs, String groupBy,
String having, String orderBy, String limit, CancellationSignal cancellationSignal) {
- throwIfNotOpen(); // fail fast
- String sql = SQLiteQueryBuilder.buildQueryString(
- distinct, table, columns, selection, groupBy, having, orderBy, limit);
+ acquireReference();
+ try {
+ String sql = SQLiteQueryBuilder.buildQueryString(
+ distinct, table, columns, selection, groupBy, having, orderBy, limit);
- return rawQueryWithFactory(cursorFactory, sql, selectionArgs,
- findEditTable(table), cancellationSignal);
+ return rawQueryWithFactory(cursorFactory, sql, selectionArgs,
+ findEditTable(table), cancellationSignal);
+ } finally {
+ releaseReference();
+ }
}
/**
@@ -1260,12 +1293,15 @@ public final class SQLiteDatabase extends SQLiteClosable {
public Cursor rawQueryWithFactory(
CursorFactory cursorFactory, String sql, String[] selectionArgs,
String editTable, CancellationSignal cancellationSignal) {
- throwIfNotOpen(); // fail fast
-
- SQLiteCursorDriver driver = new SQLiteDirectCursorDriver(this, sql, editTable,
- cancellationSignal);
- return driver.query(cursorFactory != null ? cursorFactory : mCursorFactory,
- selectionArgs);
+ acquireReference();
+ try {
+ SQLiteCursorDriver driver = new SQLiteDirectCursorDriver(this, sql, editTable,
+ cancellationSignal);
+ return driver.query(cursorFactory != null ? cursorFactory : mCursorFactory,
+ selectionArgs);
+ } finally {
+ releaseReference();
+ }
}
/**
@@ -1384,38 +1420,44 @@ public final class SQLiteDatabase extends SQLiteClosable {
*/
public long insertWithOnConflict(String table, String nullColumnHack,
ContentValues initialValues, int conflictAlgorithm) {
- StringBuilder sql = new StringBuilder();
- sql.append("INSERT");
- sql.append(CONFLICT_VALUES[conflictAlgorithm]);
- sql.append(" INTO ");
- sql.append(table);
- sql.append('(');
-
- Object[] bindArgs = null;
- int size = (initialValues != null && initialValues.size() > 0) ? initialValues.size() : 0;
- if (size > 0) {
- bindArgs = new Object[size];
- int i = 0;
- for (String colName : initialValues.keySet()) {
- sql.append((i > 0) ? "," : "");
- sql.append(colName);
- bindArgs[i++] = initialValues.get(colName);
+ acquireReference();
+ try {
+ StringBuilder sql = new StringBuilder();
+ sql.append("INSERT");
+ sql.append(CONFLICT_VALUES[conflictAlgorithm]);
+ sql.append(" INTO ");
+ sql.append(table);
+ sql.append('(');
+
+ Object[] bindArgs = null;
+ int size = (initialValues != null && initialValues.size() > 0)
+ ? initialValues.size() : 0;
+ if (size > 0) {
+ bindArgs = new Object[size];
+ int i = 0;
+ for (String colName : initialValues.keySet()) {
+ sql.append((i > 0) ? "," : "");
+ sql.append(colName);
+ bindArgs[i++] = initialValues.get(colName);
+ }
+ sql.append(')');
+ sql.append(" VALUES (");
+ for (i = 0; i < size; i++) {
+ sql.append((i > 0) ? ",?" : "?");
+ }
+ } else {
+ sql.append(nullColumnHack + ") VALUES (NULL");
}
sql.append(')');
- sql.append(" VALUES (");
- for (i = 0; i < size; i++) {
- sql.append((i > 0) ? ",?" : "?");
- }
- } else {
- sql.append(nullColumnHack + ") VALUES (NULL");
- }
- sql.append(')');
- SQLiteStatement statement = new SQLiteStatement(this, sql.toString(), bindArgs);
- try {
- return statement.executeInsert();
+ SQLiteStatement statement = new SQLiteStatement(this, sql.toString(), bindArgs);
+ try {
+ return statement.executeInsert();
+ } finally {
+ statement.close();
+ }
} finally {
- statement.close();
+ releaseReference();
}
}
@@ -1430,12 +1472,17 @@ public final class SQLiteDatabase extends SQLiteClosable {
* whereClause.
*/
public int delete(String table, String whereClause, String[] whereArgs) {
- SQLiteStatement statement = new SQLiteStatement(this, "DELETE FROM " + table +
- (!TextUtils.isEmpty(whereClause) ? " WHERE " + whereClause : ""), whereArgs);
+ acquireReference();
try {
- return statement.executeUpdateDelete();
+ SQLiteStatement statement = new SQLiteStatement(this, "DELETE FROM " + table +
+ (!TextUtils.isEmpty(whereClause) ? " WHERE " + whereClause : ""), whereArgs);
+ try {
+ return statement.executeUpdateDelete();
+ } finally {
+ statement.close();
+ }
} finally {
- statement.close();
+ releaseReference();
}
}
@@ -1470,38 +1517,43 @@ public final class SQLiteDatabase extends SQLiteClosable {
throw new IllegalArgumentException("Empty values");
}
- StringBuilder sql = new StringBuilder(120);
- sql.append("UPDATE ");
- sql.append(CONFLICT_VALUES[conflictAlgorithm]);
- sql.append(table);
- sql.append(" SET ");
-
- // move all bind args to one array
- int setValuesSize = values.size();
- int bindArgsSize = (whereArgs == null) ? setValuesSize : (setValuesSize + whereArgs.length);
- Object[] bindArgs = new Object[bindArgsSize];
- int i = 0;
- for (String colName : values.keySet()) {
- sql.append((i > 0) ? "," : "");
- sql.append(colName);
- bindArgs[i++] = values.get(colName);
- sql.append("=?");
- }
- if (whereArgs != null) {
- for (i = setValuesSize; i < bindArgsSize; i++) {
- bindArgs[i] = whereArgs[i - setValuesSize];
+ acquireReference();
+ try {
+ StringBuilder sql = new StringBuilder(120);
+ sql.append("UPDATE ");
+ sql.append(CONFLICT_VALUES[conflictAlgorithm]);
+ sql.append(table);
+ sql.append(" SET ");
+
+ // move all bind args to one array
+ int setValuesSize = values.size();
+ int bindArgsSize = (whereArgs == null) ? setValuesSize : (setValuesSize + whereArgs.length);
+ Object[] bindArgs = new Object[bindArgsSize];
+ int i = 0;
+ for (String colName : values.keySet()) {
+ sql.append((i > 0) ? "," : "");
+ sql.append(colName);
+ bindArgs[i++] = values.get(colName);
+ sql.append("=?");
+ }
+ if (whereArgs != null) {
+ for (i = setValuesSize; i < bindArgsSize; i++) {
+ bindArgs[i] = whereArgs[i - setValuesSize];
+ }
+ }
+ if (!TextUtils.isEmpty(whereClause)) {
+ sql.append(" WHERE ");
+ sql.append(whereClause);
}
- }
- if (!TextUtils.isEmpty(whereClause)) {
- sql.append(" WHERE ");
- sql.append(whereClause);
- }
- SQLiteStatement statement = new SQLiteStatement(this, sql.toString(), bindArgs);
- try {
- return statement.executeUpdateDelete();
+ SQLiteStatement statement = new SQLiteStatement(this, sql.toString(), bindArgs);
+ try {
+ return statement.executeUpdateDelete();
+ } finally {
+ statement.close();
+ }
} finally {
- statement.close();
+ releaseReference();
}
}
@@ -1579,24 +1631,29 @@ public final class SQLiteDatabase extends SQLiteClosable {
}
private int executeSql(String sql, Object[] bindArgs) throws SQLException {
- if (DatabaseUtils.getSqlStatementType(sql) == DatabaseUtils.STATEMENT_ATTACH) {
- boolean disableWal = false;
- synchronized (mLock) {
- if (!mHasAttachedDbsLocked) {
- mHasAttachedDbsLocked = true;
- disableWal = true;
+ acquireReference();
+ try {
+ if (DatabaseUtils.getSqlStatementType(sql) == DatabaseUtils.STATEMENT_ATTACH) {
+ boolean disableWal = false;
+ synchronized (mLock) {
+ if (!mHasAttachedDbsLocked) {
+ mHasAttachedDbsLocked = true;
+ disableWal = true;
+ }
+ }
+ if (disableWal) {
+ disableWriteAheadLogging();
}
}
- if (disableWal) {
- disableWriteAheadLogging();
- }
- }
- SQLiteStatement statement = new SQLiteStatement(this, sql, bindArgs);
- try {
- return statement.executeUpdateDelete();
+ SQLiteStatement statement = new SQLiteStatement(this, sql, bindArgs);
+ try {
+ return statement.executeUpdateDelete();
+ } finally {
+ statement.close();
+ }
} finally {
- statement.close();
+ releaseReference();
}
}
@@ -1881,26 +1938,32 @@ public final class SQLiteDatabase extends SQLiteClosable {
attachedDbs.add(new Pair<String, String>("main", mConfigurationLocked.path));
return attachedDbs;
}
+
+ acquireReference();
}
- // has attached databases. query sqlite to get the list of attached databases.
- Cursor c = null;
try {
- c = rawQuery("pragma database_list;", null);
- while (c.moveToNext()) {
- // sqlite returns a row for each database in the returned list of databases.
- // in each row,
- // 1st column is the database name such as main, or the database
- // name specified on the "ATTACH" command
- // 2nd column is the database file path.
- attachedDbs.add(new Pair<String, String>(c.getString(1), c.getString(2)));
+ // has attached databases. query sqlite to get the list of attached databases.
+ Cursor c = null;
+ try {
+ c = rawQuery("pragma database_list;", null);
+ while (c.moveToNext()) {
+ // sqlite returns a row for each database in the returned list of databases.
+ // in each row,
+ // 1st column is the database name such as main, or the database
+ // name specified on the "ATTACH" command
+ // 2nd column is the database file path.
+ attachedDbs.add(new Pair<String, String>(c.getString(1), c.getString(2)));
+ }
+ } finally {
+ if (c != null) {
+ c.close();
+ }
}
+ return attachedDbs;
} finally {
- if (c != null) {
- c.close();
- }
+ releaseReference();
}
- return attachedDbs;
}
/**
@@ -1917,35 +1980,38 @@ public final class SQLiteDatabase extends SQLiteClosable {
* false otherwise.
*/
public boolean isDatabaseIntegrityOk() {
- throwIfNotOpen(); // fail fast
-
- List<Pair<String, String>> attachedDbs = null;
+ acquireReference();
try {
- attachedDbs = getAttachedDbs();
- if (attachedDbs == null) {
- throw new IllegalStateException("databaselist for: " + getPath() + " couldn't " +
- "be retrieved. probably because the database is closed");
+ List<Pair<String, String>> attachedDbs = null;
+ try {
+ attachedDbs = getAttachedDbs();
+ if (attachedDbs == null) {
+ throw new IllegalStateException("databaselist for: " + getPath() + " couldn't " +
+ "be retrieved. probably because the database is closed");
+ }
+ } catch (SQLiteException e) {
+ // can't get attachedDb list. do integrity check on the main database
+ attachedDbs = new ArrayList<Pair<String, String>>();
+ attachedDbs.add(new Pair<String, String>("main", getPath()));
}
- } catch (SQLiteException e) {
- // can't get attachedDb list. do integrity check on the main database
- attachedDbs = new ArrayList<Pair<String, String>>();
- attachedDbs.add(new Pair<String, String>("main", getPath()));
- }
- for (int i = 0; i < attachedDbs.size(); i++) {
- Pair<String, String> p = attachedDbs.get(i);
- SQLiteStatement prog = null;
- try {
- prog = compileStatement("PRAGMA " + p.first + ".integrity_check(1);");
- String rslt = prog.simpleQueryForString();
- if (!rslt.equalsIgnoreCase("ok")) {
- // integrity_checker failed on main or attached databases
- Log.e(TAG, "PRAGMA integrity_check on " + p.second + " returned: " + rslt);
- return false;
+ for (int i = 0; i < attachedDbs.size(); i++) {
+ Pair<String, String> p = attachedDbs.get(i);
+ SQLiteStatement prog = null;
+ try {
+ prog = compileStatement("PRAGMA " + p.first + ".integrity_check(1);");
+ String rslt = prog.simpleQueryForString();
+ if (!rslt.equalsIgnoreCase("ok")) {
+ // integrity_checker failed on main or attached databases
+ Log.e(TAG, "PRAGMA integrity_check on " + p.second + " returned: " + rslt);
+ return false;
+ }
+ } finally {
+ if (prog != null) prog.close();
}
- } finally {
- if (prog != null) prog.close();
}
+ } finally {
+ releaseReference();
}
return true;
}
@@ -1955,12 +2021,6 @@ public final class SQLiteDatabase extends SQLiteClosable {
return "SQLiteDatabase: " + getPath();
}
- private void throwIfNotOpen() {
- synchronized (mConnectionPoolLocked) {
- throwIfNotOpenLocked();
- }
- }
-
private void throwIfNotOpenLocked() {
if (mConnectionPoolLocked == null) {
throw new IllegalStateException("The database '" + mConfigurationLocked.label
diff --git a/core/java/android/database/sqlite/SQLiteProgram.java b/core/java/android/database/sqlite/SQLiteProgram.java
index 9f0edfb..94a23cb 100644
--- a/core/java/android/database/sqlite/SQLiteProgram.java
+++ b/core/java/android/database/sqlite/SQLiteProgram.java
@@ -190,13 +190,6 @@ public abstract class SQLiteProgram extends SQLiteClosable {
}
/**
- * Release this program's resources, making it invalid.
- */
- public void close() {
- releaseReference();
- }
-
- /**
* Given an array of String bindArgs, this method binds all of them in one single call.
*
* @param bindArgs the String array of bind args, none of which must be null.
diff --git a/core/java/android/emoji/EmojiFactory.java b/core/java/android/emoji/EmojiFactory.java
index e0b12ae..8fd8695 100644
--- a/core/java/android/emoji/EmojiFactory.java
+++ b/core/java/android/emoji/EmojiFactory.java
@@ -33,7 +33,7 @@ public final class EmojiFactory {
private int sCacheSize = 100;
- // HashMap for caching Bitmap object. In order not to make an cache object
+ // HashMap for caching Bitmap object. In order not to make a cache object
// blow up, we use LinkedHashMap with size limit.
private class CustomLinkedHashMap<K, V> extends LinkedHashMap<K, V> {
public CustomLinkedHashMap() {
diff --git a/core/java/android/hardware/Camera.java b/core/java/android/hardware/Camera.java
index cca208a..2775c7b 100644
--- a/core/java/android/hardware/Camera.java
+++ b/core/java/android/hardware/Camera.java
@@ -36,7 +36,6 @@ import java.util.HashMap;
import java.util.List;
import java.util.StringTokenizer;
-
/**
* The Camera class is used to set image capture settings, start/stop preview,
* snap pictures, and retrieve frames for encoding for video. This class is a
@@ -1359,7 +1358,7 @@ public class Camera {
/**
* Returns an empty {@link Parameters} for testing purpose.
*
- * @return an Parameter object.
+ * @return a Parameter object.
*
* @hide
*/
@@ -3128,7 +3127,7 @@ public class Camera {
public void getFocusDistances(float[] output) {
if (output == null || output.length != 3) {
throw new IllegalArgumentException(
- "output must be an float array with three elements.");
+ "output must be a float array with three elements.");
}
splitFloat(get(KEY_FOCUS_DISTANCES), output);
}
diff --git a/core/java/android/hardware/CameraSound.java b/core/java/android/hardware/CameraSound.java
deleted file mode 100644
index dc97ff0..0000000
--- a/core/java/android/hardware/CameraSound.java
+++ /dev/null
@@ -1,217 +0,0 @@
-/*
- * Copyright (C) 2011 The Android Open Source Project
- *
- * Licensed under the Apache License, Version 2.0 (the "License");
- * you may not use this file except in compliance with the License.
- * You may obtain a copy of the License at
- *
- * http://www.apache.org/licenses/LICENSE-2.0
- *
- * Unless required by applicable law or agreed to in writing, software
- * distributed under the License is distributed on an "AS IS" BASIS,
- * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
- * See the License for the specific language governing permissions and
- * limitations under the License.
- */
-
-package android.hardware;
-
-import android.media.AudioManager;
-import android.media.MediaPlayer;
-import android.os.SystemProperties;
-import android.util.Log;
-
-import java.io.IOException;
-
-/**
- * <p>Use this class to play an appropriate sound when implementing a custom
- * still or video recording mechanism through the preview callbacks.</p>
- *
- * <p>There is no need to play sounds when using {@link #android.hardware.Camera#takePicture}
- * or {@link android.media.MediaRecorder} for still images or video,
- * respectively, as these play their own sounds when needed.</p>
- *
- * @hide
- */
-public class CameraSound {
- private static final String TAG = "CameraSound";
- /**
- * The sound used by {@link android.hardware.Camera#takePicture} to
- * indicate still image capture.
- */
- public static final int SHUTTER_CLICK = 0;
-
- /**
- * A sound to indicate that focusing has completed. Because deciding
- * when this occurs is application-dependent, this sound is not used by
- * any methods in the Camera class.
- */
- public static final int FOCUS_COMPLETE = 1;
-
- /**
- * The sound used by {@link android.media.MediaRecorder#start} to
- * indicate the start of video recording.
- */
- public static final int START_VIDEO_RECORDING = 2;
-
- /**
- * The sound used by {@link android.media.MediaRecorder#stop} to
- * indicate the end of video recording.
- */
- public static final int STOP_VIDEO_RECORDING = 3;
-
- private static final int NUM_SOUNDS = 4;
- private CameraSoundPlayer[] mCameraSoundPlayers;
-
- public CameraSound() {
- }
-
- /**
- * <p>Play one of the predefined platform sounds for camera actions.</p>
- *
- * <p>Use this method to play a platform-specific sound for various camera
- * actions. The sound playing is done asynchronously, with the same behavior
- * and content as the sounds played by {@link #takePicture takePicture},
- * {@link android.media.MediaRecorder#start MediaRecorder.start}, and
- * {@link android.media.MediaRecorder#stop MediaRecorder.stop}.</p>
- *
- * <p>Using this method makes it easy to match the default device sounds
- * when recording or capturing data through the preview callbacks.</p>
- *
- * @param soundId The type of sound to play, selected from SHUTTER_CLICK,
- * FOCUS_COMPLETE, START_VIDEO_RECORDING, or STOP_VIDEO_RECORDING.
- * @see android.hardware#takePicture
- * @see android.media.MediaRecorder
- * @see #SHUTTER_CLICK
- * @see #FOCUS_COMPLETE
- * @see #START_VIDEO_RECORDING
- * @see #STOP_VIDEO_RECORDING
- */
- public void playSound(int soundId) {
- if (mCameraSoundPlayers == null) {
- mCameraSoundPlayers = new CameraSoundPlayer[NUM_SOUNDS];
- }
- if (mCameraSoundPlayers[soundId] == null) {
- mCameraSoundPlayers[soundId] = new CameraSoundPlayer(soundId);
- }
- mCameraSoundPlayers[soundId].play();
- }
-
- public void release() {
- if (mCameraSoundPlayers != null) {
- for (CameraSoundPlayer csp: mCameraSoundPlayers) {
- if (csp != null) {
- csp.release();
- }
- }
- mCameraSoundPlayers = null;
- }
- }
-
- private static class CameraSoundPlayer implements Runnable {
- private int mSoundId;
- private MediaPlayer mPlayer;
- private Thread mThread;
- private boolean mExit;
- private int mPlayCount;
-
- private static final String mShutterSound =
- "/system/media/audio/ui/camera_click.ogg";
- private static final String mFocusSound =
- "/system/media/audio/ui/camera_focus.ogg";
- private static final String mVideoStartSound =
- "/system/media/audio/ui/VideoRecord.ogg";
- private static final String mVideoStopSound =
- "/system/media/audio/ui/VideoRecord.ogg";
-
- @Override
- public void run() {
- String soundFilePath;
- switch (mSoundId) {
- case SHUTTER_CLICK:
- soundFilePath = mShutterSound;
- break;
- case FOCUS_COMPLETE:
- soundFilePath = mFocusSound;
- break;
- case START_VIDEO_RECORDING:
- soundFilePath = mVideoStartSound;
- break;
- case STOP_VIDEO_RECORDING:
- soundFilePath = mVideoStopSound;
- break;
- default:
- Log.e(TAG, "Unknown sound " + mSoundId + " requested.");
- return;
- }
- mPlayer = new MediaPlayer();
- try {
- mPlayer.setAudioStreamType(AudioManager.STREAM_SYSTEM_ENFORCED);
- mPlayer.setDataSource(soundFilePath);
- mPlayer.setLooping(false);
- mPlayer.prepare();
- } catch(IOException e) {
- Log.e(TAG, "Error setting up sound " + mSoundId, e);
- return;
- }
-
- while(true) {
- try {
- synchronized (this) {
- while(true) {
- if (mExit) {
- return;
- } else if (mPlayCount <= 0) {
- wait();
- } else {
- mPlayCount--;
- break;
- }
- }
- }
- mPlayer.start();
- } catch (Exception e) {
- Log.e(TAG, "Error playing sound " + mSoundId, e);
- }
- }
- }
-
- public CameraSoundPlayer(int soundId) {
- mSoundId = soundId;
- }
-
- public void play() {
- if (mThread == null) {
- mThread = new Thread(this);
- mThread.start();
- }
- synchronized (this) {
- mPlayCount++;
- notifyAll();
- }
- }
-
- public void release() {
- if (mThread != null) {
- synchronized (this) {
- mExit = true;
- notifyAll();
- }
- try {
- mThread.join();
- } catch (InterruptedException e) {
- }
- mThread = null;
- }
- if (mPlayer != null) {
- mPlayer.release();
- mPlayer = null;
- }
- }
-
- @Override
- protected void finalize() {
- release();
- }
- }
-} \ No newline at end of file
diff --git a/core/java/android/hardware/Sensor.java b/core/java/android/hardware/Sensor.java
index 68fc101..63fb32d 100644
--- a/core/java/android/hardware/Sensor.java
+++ b/core/java/android/hardware/Sensor.java
@@ -57,7 +57,7 @@ public class Sensor {
public static final int TYPE_GYROSCOPE = 4;
/**
- * A constant describing an light sensor type. See
+ * A constant describing a light sensor type. See
* {@link android.hardware.SensorEvent#values SensorEvent.values} for more
* details.
*/
@@ -77,7 +77,7 @@ public class Sensor {
public static final int TYPE_TEMPERATURE = 7;
/**
- * A constant describing an proximity sensor type. See
+ * A constant describing a proximity sensor type. See
* {@link android.hardware.SensorEvent#values SensorEvent.values} for more
* details.
*/
diff --git a/core/java/android/inputmethodservice/InputMethodService.java b/core/java/android/inputmethodservice/InputMethodService.java
index 53cdf21..ba7dc4a 100644
--- a/core/java/android/inputmethodservice/InputMethodService.java
+++ b/core/java/android/inputmethodservice/InputMethodService.java
@@ -2173,7 +2173,7 @@ public class InputMethodService extends AbstractInputMethodService {
* This is called when, while currently displayed in extract mode, the
* current input target changes. The default implementation will
* auto-hide the IME if the new target is not a full editor, since this
- * can be an confusing experience for the user.
+ * can be a confusing experience for the user.
*/
public void onExtractingInputChanged(EditorInfo ei) {
if (ei.inputType == InputType.TYPE_NULL) {
diff --git a/core/java/android/net/http/CertificateChainValidator.java b/core/java/android/net/http/CertificateChainValidator.java
index 06c6c6e..6ad8fe3 100644
--- a/core/java/android/net/http/CertificateChainValidator.java
+++ b/core/java/android/net/http/CertificateChainValidator.java
@@ -18,6 +18,8 @@ package android.net.http;
import java.io.IOException;
+import java.security.GeneralSecurityException;
+import java.security.KeyManagementException;
import java.security.cert.Certificate;
import java.security.cert.CertificateException;
import java.security.cert.X509Certificate;
@@ -128,10 +130,13 @@ public class CertificateChainValidator {
*/
public static void handleTrustStorageUpdate() {
- X509TrustManager x509TrustManager = SSLParametersImpl.getDefaultTrustManager();
- if( x509TrustManager instanceof TrustManagerImpl ) {
- TrustManagerImpl trustManager = (TrustManagerImpl) x509TrustManager;
- trustManager.handleTrustStorageUpdate();
+ try {
+ X509TrustManager x509TrustManager = SSLParametersImpl.getDefaultTrustManager();
+ if( x509TrustManager instanceof TrustManagerImpl ) {
+ TrustManagerImpl trustManager = (TrustManagerImpl) x509TrustManager;
+ trustManager.handleTrustStorageUpdate();
+ }
+ } catch (KeyManagementException ignored) {
}
}
@@ -165,7 +170,7 @@ public class CertificateChainValidator {
try {
SSLParametersImpl.getDefaultTrustManager().checkServerTrusted(chain, authType);
return null; // No errors.
- } catch (CertificateException e) {
+ } catch (GeneralSecurityException e) {
if (HttpLog.LOGV) {
HttpLog.v("failed to validate the certificate chain, error: " +
e.getMessage());
diff --git a/core/java/android/net/http/SslError.java b/core/java/android/net/http/SslError.java
index 863304c..1cd73d2 100644
--- a/core/java/android/net/http/SslError.java
+++ b/core/java/android/net/http/SslError.java
@@ -64,7 +64,7 @@ public class SslError {
public static final int SSL_MAX_ERROR = 6;
/**
- * The SSL error set bitfield (each individual error is an bit index;
+ * The SSL error set bitfield (each individual error is a bit index;
* multiple individual errors can be OR-ed)
*/
int mErrors;
diff --git a/core/java/android/os/FileUtils.java b/core/java/android/os/FileUtils.java
index 215e836..6c1445d 100644
--- a/core/java/android/os/FileUtils.java
+++ b/core/java/android/os/FileUtils.java
@@ -28,6 +28,8 @@ import java.util.regex.Pattern;
import java.util.zip.CRC32;
import java.util.zip.CheckedInputStream;
+import libcore.io.Os;
+import libcore.io.StructStat;
/**
* Tools for managing files. Not for public consumption.
@@ -52,8 +54,10 @@ public class FileUtils {
/**
* File status information. This class maps directly to the POSIX stat structure.
+ * @deprecated use {@link StructStat} instead.
* @hide
*/
+ @Deprecated
public static final class FileStatus {
public int dev;
public int ino;
@@ -77,7 +81,9 @@ public class FileUtils {
* exists.
* @return true if the file exists and false if it does not exist. If you do not have
* permission to stat the file, then this method will return false.
+ * @deprecated use {@link Os#stat(String)} instead.
*/
+ @Deprecated
public static boolean getFileStatus(String path, FileStatus status) {
StrictMode.noteDiskRead();
return getFileStatusNative(path, status);
@@ -90,6 +96,10 @@ public class FileUtils {
public static native int setPermissions(String file, int mode, int uid, int gid);
+ /**
+ * @deprecated use {@link Os#stat(String)} instead.
+ */
+ @Deprecated
public static native int getPermissions(String file, int[] outPermissions);
public static native int setUMask(int mask);
diff --git a/core/java/android/os/Parcel.java b/core/java/android/os/Parcel.java
index 15e3af4..788ab74 100644
--- a/core/java/android/os/Parcel.java
+++ b/core/java/android/os/Parcel.java
@@ -180,9 +180,14 @@ public final class Parcel {
private static final String TAG = "Parcel";
@SuppressWarnings({"UnusedDeclaration"})
- private int mObject; // used by native code
- @SuppressWarnings({"UnusedDeclaration"})
- private int mOwnObject; // used by native code
+ private int mNativePtr; // used by native code
+
+ /**
+ * Flag indicating if {@link #mNativePtr} was allocated by this object,
+ * indicating that we're responsible for its lifecycle.
+ */
+ private boolean mOwnsNativeParcelObject;
+
private RuntimeException mStack;
private static final int POOL_SIZE = 6;
@@ -224,6 +229,48 @@ public final class Parcel {
private static final int EX_ILLEGAL_STATE = -5;
private static final int EX_HAS_REPLY_HEADER = -128; // special; see below
+ private static native int nativeDataSize(int nativePtr);
+ private static native int nativeDataAvail(int nativePtr);
+ private static native int nativeDataPosition(int nativePtr);
+ private static native int nativeDataCapacity(int nativePtr);
+ private static native void nativeSetDataSize(int nativePtr, int size);
+ private static native void nativeSetDataPosition(int nativePtr, int pos);
+ private static native void nativeSetDataCapacity(int nativePtr, int size);
+
+ private static native boolean nativePushAllowFds(int nativePtr, boolean allowFds);
+ private static native void nativeRestoreAllowFds(int nativePtr, boolean lastValue);
+
+ private static native void nativeWriteByteArray(int nativePtr, byte[] b, int offset, int len);
+ private static native void nativeWriteInt(int nativePtr, int val);
+ private static native void nativeWriteLong(int nativePtr, long val);
+ private static native void nativeWriteFloat(int nativePtr, float val);
+ private static native void nativeWriteDouble(int nativePtr, double val);
+ private static native void nativeWriteString(int nativePtr, String val);
+ private static native void nativeWriteStrongBinder(int nativePtr, IBinder val);
+ private static native void nativeWriteFileDescriptor(int nativePtr, FileDescriptor val);
+
+ private static native byte[] nativeCreateByteArray(int nativePtr);
+ private static native int nativeReadInt(int nativePtr);
+ private static native long nativeReadLong(int nativePtr);
+ private static native float nativeReadFloat(int nativePtr);
+ private static native double nativeReadDouble(int nativePtr);
+ private static native String nativeReadString(int nativePtr);
+ private static native IBinder nativeReadStrongBinder(int nativePtr);
+ private static native FileDescriptor nativeReadFileDescriptor(int nativePtr);
+
+ private static native int nativeCreate();
+ private static native void nativeFreeBuffer(int nativePtr);
+ private static native void nativeDestroy(int nativePtr);
+
+ private static native byte[] nativeMarshall(int nativePtr);
+ private static native void nativeUnmarshall(
+ int nativePtr, byte[] data, int offest, int length);
+ private static native void nativeAppendFrom(
+ int thisNativePtr, int otherNativePtr, int offset, int length);
+ private static native boolean nativeHasFileDescriptors(int nativePtr);
+ private static native void nativeWriteInterfaceToken(int nativePtr, String interfaceName);
+ private static native void nativeEnforceInterface(int nativePtr, String interfaceName);
+
public final static Parcelable.Creator<String> STRING_CREATOR
= new Parcelable.Creator<String>() {
public String createFromParcel(Parcel source) {
@@ -262,7 +309,15 @@ public final class Parcel {
public final void recycle() {
if (DEBUG_RECYCLE) mStack = null;
freeBuffer();
- final Parcel[] pool = mOwnObject != 0 ? sOwnedPool : sHolderPool;
+
+ final Parcel[] pool;
+ if (mOwnsNativeParcelObject) {
+ pool = sOwnedPool;
+ } else {
+ mNativePtr = 0;
+ pool = sHolderPool;
+ }
+
synchronized (pool) {
for (int i=0; i<POOL_SIZE; i++) {
if (pool[i] == null) {
@@ -276,19 +331,25 @@ public final class Parcel {
/**
* Returns the total amount of data contained in the parcel.
*/
- public final native int dataSize();
+ public final int dataSize() {
+ return nativeDataSize(mNativePtr);
+ }
/**
* Returns the amount of data remaining to be read from the
* parcel. That is, {@link #dataSize}-{@link #dataPosition}.
*/
- public final native int dataAvail();
+ public final int dataAvail() {
+ return nativeDataAvail(mNativePtr);
+ }
/**
* Returns the current position in the parcel data. Never
* more than {@link #dataSize}.
*/
- public final native int dataPosition();
+ public final int dataPosition() {
+ return nativeDataPosition(mNativePtr);
+ }
/**
* Returns the total amount of space in the parcel. This is always
@@ -296,7 +357,9 @@ public final class Parcel {
* amount of room left until the parcel needs to re-allocate its
* data buffer.
*/
- public final native int dataCapacity();
+ public final int dataCapacity() {
+ return nativeDataCapacity(mNativePtr);
+ }
/**
* Change the amount of data in the parcel. Can be either smaller or
@@ -305,14 +368,18 @@ public final class Parcel {
*
* @param size The new number of bytes in the Parcel.
*/
- public final native void setDataSize(int size);
+ public final void setDataSize(int size) {
+ nativeSetDataSize(mNativePtr, size);
+ }
/**
* Move the current read/write position in the parcel.
* @param pos New offset in the parcel; must be between 0 and
* {@link #dataSize}.
*/
- public final native void setDataPosition(int pos);
+ public final void setDataPosition(int pos) {
+ nativeSetDataPosition(mNativePtr, pos);
+ }
/**
* Change the capacity (current available space) of the parcel.
@@ -321,13 +388,19 @@ public final class Parcel {
* less than {@link #dataSize} -- that is, you can not drop existing data
* with this method.
*/
- public final native void setDataCapacity(int size);
+ public final void setDataCapacity(int size) {
+ nativeSetDataCapacity(mNativePtr, size);
+ }
/** @hide */
- public final native boolean pushAllowFds(boolean allowFds);
+ public final boolean pushAllowFds(boolean allowFds) {
+ return nativePushAllowFds(mNativePtr, allowFds);
+ }
/** @hide */
- public final native void restoreAllowFds(boolean lastValue);
+ public final void restoreAllowFds(boolean lastValue) {
+ nativeRestoreAllowFds(mNativePtr, lastValue);
+ }
/**
* Returns the raw bytes of the parcel.
@@ -340,27 +413,40 @@ public final class Parcel {
* such does not attempt to maintain compatibility with data created
* in different versions of the platform.
*/
- public final native byte[] marshall();
+ public final byte[] marshall() {
+ return nativeMarshall(mNativePtr);
+ }
/**
* Set the bytes in data to be the raw bytes of this Parcel.
*/
- public final native void unmarshall(byte[] data, int offest, int length);
+ public final void unmarshall(byte[] data, int offest, int length) {
+ nativeUnmarshall(mNativePtr, data, offest, length);
+ }
- public final native void appendFrom(Parcel parcel, int offset, int length);
+ public final void appendFrom(Parcel parcel, int offset, int length) {
+ nativeAppendFrom(mNativePtr, parcel.mNativePtr, offset, length);
+ }
/**
* Report whether the parcel contains any marshalled file descriptors.
*/
- public final native boolean hasFileDescriptors();
+ public final boolean hasFileDescriptors() {
+ return nativeHasFileDescriptors(mNativePtr);
+ }
/**
* Store or read an IBinder interface token in the parcel at the current
* {@link #dataPosition}. This is used to validate that the marshalled
* transaction is intended for the target interface.
*/
- public final native void writeInterfaceToken(String interfaceName);
- public final native void enforceInterface(String interfaceName);
+ public final void writeInterfaceToken(String interfaceName) {
+ nativeWriteInterfaceToken(mNativePtr, interfaceName);
+ }
+
+ public final void enforceInterface(String interfaceName) {
+ nativeEnforceInterface(mNativePtr, interfaceName);
+ }
/**
* Write a byte array into the parcel at the current {@link #dataPosition},
@@ -372,7 +458,7 @@ public final class Parcel {
}
/**
- * Write an byte array into the parcel at the current {@link #dataPosition},
+ * Write a byte array into the parcel at the current {@link #dataPosition},
* growing {@link #dataCapacity} if needed.
* @param b Bytes to place into the parcel.
* @param offset Index of first byte to be written.
@@ -384,40 +470,48 @@ public final class Parcel {
return;
}
Arrays.checkOffsetAndCount(b.length, offset, len);
- writeNative(b, offset, len);
+ nativeWriteByteArray(mNativePtr, b, offset, len);
}
- private native void writeNative(byte[] b, int offset, int len);
-
/**
* Write an integer value into the parcel at the current dataPosition(),
* growing dataCapacity() if needed.
*/
- public final native void writeInt(int val);
+ public final void writeInt(int val) {
+ nativeWriteInt(mNativePtr, val);
+ }
/**
* Write a long integer value into the parcel at the current dataPosition(),
* growing dataCapacity() if needed.
*/
- public final native void writeLong(long val);
+ public final void writeLong(long val) {
+ nativeWriteLong(mNativePtr, val);
+ }
/**
* Write a floating point value into the parcel at the current
* dataPosition(), growing dataCapacity() if needed.
*/
- public final native void writeFloat(float val);
+ public final void writeFloat(float val) {
+ nativeWriteFloat(mNativePtr, val);
+ }
/**
* Write a double precision floating point value into the parcel at the
* current dataPosition(), growing dataCapacity() if needed.
*/
- public final native void writeDouble(double val);
+ public final void writeDouble(double val) {
+ nativeWriteDouble(mNativePtr, val);
+ }
/**
* Write a string value into the parcel at the current dataPosition(),
* growing dataCapacity() if needed.
*/
- public final native void writeString(String val);
+ public final void writeString(String val) {
+ nativeWriteString(mNativePtr, val);
+ }
/**
* Write a CharSequence value into the parcel at the current dataPosition(),
@@ -432,7 +526,9 @@ public final class Parcel {
* Write an object into the parcel at the current dataPosition(),
* growing dataCapacity() if needed.
*/
- public final native void writeStrongBinder(IBinder val);
+ public final void writeStrongBinder(IBinder val) {
+ nativeWriteStrongBinder(mNativePtr, val);
+ }
/**
* Write an object into the parcel at the current dataPosition(),
@@ -452,10 +548,12 @@ public final class Parcel {
* accepts contextual flags and will close the original file descriptor
* if {@link Parcelable#PARCELABLE_WRITE_RETURN_VALUE} is set.</p>
*/
- public final native void writeFileDescriptor(FileDescriptor val);
+ public final void writeFileDescriptor(FileDescriptor val) {
+ nativeWriteFileDescriptor(mNativePtr, val);
+ }
/**
- * Write an byte value into the parcel at the current dataPosition(),
+ * Write a byte value into the parcel at the current dataPosition(),
* growing dataCapacity() if needed.
*/
public final void writeByte(byte val) {
@@ -1341,29 +1439,39 @@ public final class Parcel {
/**
* Read an integer value from the parcel at the current dataPosition().
*/
- public final native int readInt();
+ public final int readInt() {
+ return nativeReadInt(mNativePtr);
+ }
/**
* Read a long integer value from the parcel at the current dataPosition().
*/
- public final native long readLong();
+ public final long readLong() {
+ return nativeReadLong(mNativePtr);
+ }
/**
* Read a floating point value from the parcel at the current
* dataPosition().
*/
- public final native float readFloat();
+ public final float readFloat() {
+ return nativeReadFloat(mNativePtr);
+ }
/**
* Read a double precision floating point value from the parcel at the
* current dataPosition().
*/
- public final native double readDouble();
+ public final double readDouble() {
+ return nativeReadDouble(mNativePtr);
+ }
/**
* Read a string value from the parcel at the current dataPosition().
*/
- public final native String readString();
+ public final String readString() {
+ return nativeReadString(mNativePtr);
+ }
/**
* Read a CharSequence value from the parcel at the current dataPosition().
@@ -1376,17 +1484,18 @@ public final class Parcel {
/**
* Read an object from the parcel at the current dataPosition().
*/
- public final native IBinder readStrongBinder();
+ public final IBinder readStrongBinder() {
+ return nativeReadStrongBinder(mNativePtr);
+ }
/**
* Read a FileDescriptor from the parcel at the current dataPosition().
*/
public final ParcelFileDescriptor readFileDescriptor() {
- FileDescriptor fd = internalReadFileDescriptor();
+ FileDescriptor fd = nativeReadFileDescriptor(mNativePtr);
return fd != null ? new ParcelFileDescriptor(fd) : null;
}
- private native FileDescriptor internalReadFileDescriptor();
/*package*/ static native FileDescriptor openFileDescriptor(String file,
int mode) throws FileNotFoundException;
/*package*/ static native FileDescriptor dupFileDescriptor(FileDescriptor orig)
@@ -1471,7 +1580,9 @@ public final class Parcel {
/**
* Read and return a byte[] object from the parcel.
*/
- public final native byte[] createByteArray();
+ public final byte[] createByteArray() {
+ return nativeCreateByteArray(mNativePtr);
+ }
/**
* Read a byte[] object from the parcel and copy it into the
@@ -2065,12 +2176,37 @@ public final class Parcel {
return new Parcel(obj);
}
- private Parcel(int obj) {
+ private Parcel(int nativePtr) {
if (DEBUG_RECYCLE) {
mStack = new RuntimeException();
}
//Log.i(TAG, "Initializing obj=0x" + Integer.toHexString(obj), mStack);
- init(obj);
+ init(nativePtr);
+ }
+
+ private void init(int nativePtr) {
+ if (nativePtr != 0) {
+ mNativePtr = nativePtr;
+ mOwnsNativeParcelObject = false;
+ } else {
+ mNativePtr = nativeCreate();
+ mOwnsNativeParcelObject = true;
+ }
+ }
+
+ private void freeBuffer() {
+ if (mOwnsNativeParcelObject) {
+ nativeFreeBuffer(mNativePtr);
+ }
+ }
+
+ private void destroy() {
+ if (mNativePtr != 0) {
+ if (mOwnsNativeParcelObject) {
+ nativeDestroy(mNativePtr);
+ }
+ mNativePtr = 0;
+ }
}
@Override
@@ -2083,10 +2219,6 @@ public final class Parcel {
destroy();
}
- private native void freeBuffer();
- private native void init(int obj);
- private native void destroy();
-
/* package */ void readMapInternal(Map outVal, int N,
ClassLoader loader) {
while (N > 0) {
diff --git a/core/java/android/os/ParcelUuid.java b/core/java/android/os/ParcelUuid.java
index 88fcfc5..2c68ddd 100644
--- a/core/java/android/os/ParcelUuid.java
+++ b/core/java/android/os/ParcelUuid.java
@@ -42,7 +42,7 @@ public final class ParcelUuid implements Parcelable {
*
* @param uuid
* the UUID string to parse.
- * @return an ParcelUuid instance.
+ * @return a ParcelUuid instance.
* @throws NullPointerException
* if {@code uuid} is {@code null}.
* @throws IllegalArgumentException
diff --git a/core/java/android/os/StrictMode.java b/core/java/android/os/StrictMode.java
index a0ad9c0..759be91 100644
--- a/core/java/android/os/StrictMode.java
+++ b/core/java/android/os/StrictMode.java
@@ -109,7 +109,7 @@ import java.util.concurrent.atomic.AtomicInteger;
* effort mechanism. Notably, disk or network access from JNI calls
* won't necessarily trigger it. Future versions of Android may catch
* more (or fewer) operations, so you should never leave StrictMode
- * enabled in shipping applications on the Android Market.
+ * enabled in applications distributed on Google Play.
*/
public final class StrictMode {
private static final String TAG = "StrictMode";
diff --git a/core/java/android/provider/Contacts.java b/core/java/android/provider/Contacts.java
index a29ecb5..c7e3c08 100644
--- a/core/java/android/provider/Contacts.java
+++ b/core/java/android/provider/Contacts.java
@@ -84,7 +84,7 @@ public class Contacts {
@Deprecated
public static final int KIND_ORGANIZATION = 4;
/**
- * Signifies an Phone row that is stored in the Phones table
+ * Signifies a Phone row that is stored in the Phones table
* @deprecated see {@link android.provider.ContactsContract}
*/
@Deprecated
diff --git a/core/java/android/provider/MediaStore.java b/core/java/android/provider/MediaStore.java
index 6c6b118..38945c2 100644
--- a/core/java/android/provider/MediaStore.java
+++ b/core/java/android/provider/MediaStore.java
@@ -192,7 +192,7 @@ public final class MediaStore {
/**
* Standard Intent action that can be sent to have the camera application
- * capture an video and return it.
+ * capture a video and return it.
* <p>
* The caller may pass in an extra EXTRA_VIDEO_QUALITY to control the video quality.
* <p>
@@ -441,12 +441,12 @@ public final class MediaStore {
public static final int MEDIA_TYPE_AUDIO = 2;
/**
- * Constant for the {@link #MEDIA_TYPE} column indicating that file is an video file.
+ * Constant for the {@link #MEDIA_TYPE} column indicating that file is a video file.
*/
public static final int MEDIA_TYPE_VIDEO = 3;
/**
- * Constant for the {@link #MEDIA_TYPE} column indicating that file is an playlist file.
+ * Constant for the {@link #MEDIA_TYPE} column indicating that file is a playlist file.
*/
public static final int MEDIA_TYPE_PLAYLIST = 4;
}
diff --git a/core/java/android/provider/Settings.java b/core/java/android/provider/Settings.java
index b42417a..fbb3273 100644
--- a/core/java/android/provider/Settings.java
+++ b/core/java/android/provider/Settings.java
@@ -2644,10 +2644,10 @@ public final class Settings {
/**
* Whether the package installer should allow installation of apps downloaded from
- * sources other than the Android Market (vending machine).
+ * sources other than Google Play.
*
* 1 = allow installing from other sources
- * 0 = only allow installing from the Android Market
+ * 0 = only allow installing from Google Play
*/
public static final String INSTALL_NON_MARKET_APPS = "install_non_market_apps";
@@ -3129,6 +3129,14 @@ public final class Settings {
"wifi_watchdog_arp_interval_ms";
/**
+ * ms delay interval between rssi polling when the signal is known to be weak
+ * @hide
+ */
+ public static final String WIFI_WATCHDOG_RSSI_FETCH_INTERVAL_MS =
+ "wifi_watchdog_rssi_fetch_interval_ms";
+
+
+ /**
* ms delay before rechecking a connect SSID for walled garden with a http download.
* @hide
*/
diff --git a/core/java/android/server/BluetoothBondState.java b/core/java/android/server/BluetoothBondState.java
index fbc1c27..0446f02 100644
--- a/core/java/android/server/BluetoothBondState.java
+++ b/core/java/android/server/BluetoothBondState.java
@@ -140,7 +140,7 @@ class BluetoothBondState {
return;
}
- // Check if this was an pending outgoing bonding.
+ // Check if this was a pending outgoing bonding.
// If yes, reset the state.
if (oldState == BluetoothDevice.BOND_BONDING) {
if (address.equals(mPendingOutgoingBonding)) {
diff --git a/core/java/android/text/MeasuredText.java b/core/java/android/text/MeasuredText.java
index a52e2ba..715d1f2 100644
--- a/core/java/android/text/MeasuredText.java
+++ b/core/java/android/text/MeasuredText.java
@@ -222,23 +222,27 @@ class MeasuredText {
return wid;
}
- int breakText(int start, int limit, boolean forwards, float width) {
+ int breakText(int limit, boolean forwards, float width) {
float[] w = mWidths;
if (forwards) {
- for (int i = start; i < limit; ++i) {
- if ((width -= w[i]) < 0) {
- return i - start;
- }
+ int i = 0;
+ while (i < limit) {
+ width -= w[i];
+ if (width < 0.0f) break;
+ i++;
}
+ while (i > 0 && mChars[i - 1] == ' ') i--;
+ return i;
} else {
- for (int i = limit; --i >= start;) {
- if ((width -= w[i]) < 0) {
- return limit - i -1;
- }
+ int i = limit - 1;
+ while (i >= 0) {
+ width -= w[i];
+ if (width < 0.0f) break;
+ i--;
}
+ while (i < limit - 1 && mChars[i + 1] == ' ') i++;
+ return limit - i - 1;
}
-
- return limit - start;
}
float measure(int start, int limit) {
diff --git a/core/java/android/text/TextUtils.java b/core/java/android/text/TextUtils.java
index afae5bb2..270624c 100644
--- a/core/java/android/text/TextUtils.java
+++ b/core/java/android/text/TextUtils.java
@@ -1091,13 +1091,13 @@ public class TextUtils {
if (avail < 0) {
// it all goes
} else if (where == TruncateAt.START) {
- right = len - mt.breakText(0, len, false, avail);
+ right = len - mt.breakText(len, false, avail);
} else if (where == TruncateAt.END || where == TruncateAt.END_SMALL) {
- left = mt.breakText(0, len, true, avail);
+ left = mt.breakText(len, true, avail);
} else {
- right = len - mt.breakText(0, len, false, avail / 2);
+ right = len - mt.breakText(len, false, avail / 2);
avail -= mt.measure(right, len);
- left = mt.breakText(0, right, true, avail);
+ left = mt.breakText(right, true, avail);
}
if (callback != null) {
diff --git a/core/java/android/text/method/BaseMovementMethod.java b/core/java/android/text/method/BaseMovementMethod.java
index f554b90..113a4be 100644
--- a/core/java/android/text/method/BaseMovementMethod.java
+++ b/core/java/android/text/method/BaseMovementMethod.java
@@ -350,7 +350,7 @@ public class BaseMovementMethod implements MovementMethod {
}
/**
- * Performs an line-end movement action.
+ * Performs a line-end movement action.
* Moves the cursor or scrolls to the end of the line.
*
* @param widget The text view.
diff --git a/core/java/android/view/Choreographer.java b/core/java/android/view/Choreographer.java
index f4d7af9..d217cab 100644
--- a/core/java/android/view/Choreographer.java
+++ b/core/java/android/view/Choreographer.java
@@ -81,8 +81,8 @@ public final class Choreographer {
private static final int MSG_DO_ANIMATION = 0;
private static final int MSG_DO_DRAW = 1;
private static final int MSG_DO_SCHEDULE_VSYNC = 2;
- private static final int MSG_POST_DELAYED_ANIMATION = 3;
- private static final int MSG_POST_DELAYED_DRAW = 4;
+ private static final int MSG_DO_SCHEDULE_ANIMATION = 3;
+ private static final int MSG_DO_SCHEDULE_DRAW = 4;
private final Object mLock = new Object();
@@ -92,8 +92,8 @@ public final class Choreographer {
private Callback mCallbackPool;
- private Callback mAnimationCallbacks;
- private Callback mDrawCallbacks;
+ private final CallbackQueue mAnimationCallbackQueue = new CallbackQueue();
+ private final CallbackQueue mDrawCallbackQueue = new CallbackQueue();
private boolean mAnimationScheduled;
private boolean mDrawScheduled;
@@ -152,134 +152,176 @@ public final class Choreographer {
}
/**
+ * Subtracts typical frame delay time from a delay interval in milliseconds.
+ *
+ * This method can be used to compensate for animation delay times that have baked
+ * in assumptions about the frame delay. For example, it's quite common for code to
+ * assume a 60Hz frame time and bake in a 16ms delay. When we call
+ * {@link #postAnimationCallbackDelayed} we want to know how long to wait before
+ * posting the animation callback but let the animation timer take care of the remaining
+ * frame delay time.
+ *
+ * This method is somewhat conservative about how much of the frame delay it
+ * subtracts. It uses the same value returned by {@link #getFrameDelay} which by
+ * default is 10ms even though many parts of the system assume 16ms. Consequently,
+ * we might still wait 6ms before posting an animation callback that we want to run
+ * on the next frame, but this is much better than waiting a whole 16ms and likely
+ * missing the deadline.
+ *
+ * @param delayMillis The original delay time including an assumed frame delay.
+ * @return The adjusted delay time with the assumed frame delay subtracted out.
+ */
+ public static long subtractFrameDelay(long delayMillis) {
+ final long frameDelay = sFrameDelay;
+ return delayMillis <= frameDelay ? 0 : delayMillis - frameDelay;
+ }
+
+ /**
* Posts a callback to run on the next animation cycle.
* The callback only runs once and then is automatically removed.
*
- * @param runnable The callback to run during the next animation cycle.
+ * @param action The callback action to run during the next animation cycle.
+ * @param token The callback token, or null if none.
*
* @see #removeAnimationCallback
*/
- public void postAnimationCallback(Runnable runnable) {
- if (runnable == null) {
- throw new IllegalArgumentException("runnable must not be null");
- }
- postAnimationCallbackUnchecked(runnable);
- }
-
- private void postAnimationCallbackUnchecked(Runnable runnable) {
- synchronized (mLock) {
- mAnimationCallbacks = addCallbackLocked(mAnimationCallbacks, runnable);
- scheduleAnimationLocked();
- }
+ public void postAnimationCallback(Runnable action, Object token) {
+ postAnimationCallbackDelayed(action, token, 0);
}
/**
* Posts a callback to run on the next animation cycle following the specified delay.
* The callback only runs once and then is automatically removed.
*
- * @param runnable The callback to run during the next animation cycle following
+ * @param action The callback action to run during the next animation cycle after
* the specified delay.
+ * @param token The callback token, or null if none.
* @param delayMillis The delay time in milliseconds.
*
* @see #removeAnimationCallback
*/
- public void postAnimationCallbackDelayed(Runnable runnable, long delayMillis) {
- if (runnable == null) {
- throw new IllegalArgumentException("runnable must not be null");
+ public void postAnimationCallbackDelayed(Runnable action, Object token, long delayMillis) {
+ if (action == null) {
+ throw new IllegalArgumentException("action must not be null");
}
- if (delayMillis <= 0) {
- postAnimationCallbackUnchecked(runnable);
- } else {
- Message msg = mHandler.obtainMessage(MSG_POST_DELAYED_ANIMATION, runnable);
- mHandler.sendMessageDelayed(msg, delayMillis);
+
+ if (DEBUG) {
+ Log.d(TAG, "PostAnimationCallback: " + action + ", token=" + token
+ + ", delayMillis=" + delayMillis);
+ }
+
+ synchronized (mLock) {
+ final long now = SystemClock.uptimeMillis();
+ final long dueTime = now + delayMillis;
+ mAnimationCallbackQueue.addCallbackLocked(dueTime, action, token);
+
+ if (dueTime <= now) {
+ scheduleAnimationLocked(now);
+ } else {
+ Message msg = mHandler.obtainMessage(MSG_DO_SCHEDULE_ANIMATION, action);
+ mHandler.sendMessageAtTime(msg, dueTime);
+ }
}
}
/**
- * Removes animation callbacks for the specified runnable.
- * Does nothing if the specified animation callback has not been posted or has already
- * been removed.
+ * Removes animation callbacks that have the specified action and token.
*
- * @param runnable The animation callback to remove.
+ * @param action The action property of the callbacks to remove, or null to remove
+ * callbacks with any action.
+ * @param token The token property of the callbacks to remove, or null to remove
+ * callbacks with any token.
*
* @see #postAnimationCallback
* @see #postAnimationCallbackDelayed
*/
- public void removeAnimationCallbacks(Runnable runnable) {
- if (runnable == null) {
- throw new IllegalArgumentException("runnable must not be null");
+ public void removeAnimationCallbacks(Runnable action, Object token) {
+ if (DEBUG) {
+ Log.d(TAG, "RemoveAnimationCallbacks: " + action + ", token=" + token);
}
+
synchronized (mLock) {
- mAnimationCallbacks = removeCallbacksLocked(mAnimationCallbacks, runnable);
+ mAnimationCallbackQueue.removeCallbacksLocked(action, token);
+ if (action != null && token == null) {
+ mHandler.removeMessages(MSG_DO_SCHEDULE_ANIMATION, action);
+ }
}
- mHandler.removeMessages(MSG_POST_DELAYED_ANIMATION, runnable);
}
/**
* Posts a callback to run on the next draw cycle.
* The callback only runs once and then is automatically removed.
*
- * @param runnable The callback to run during the next draw cycle.
+ * @param action The callback action to run during the next draw cycle.
+ * @param token The callback token, or null if none.
*
* @see #removeDrawCallback
*/
- public void postDrawCallback(Runnable runnable) {
- if (runnable == null) {
- throw new IllegalArgumentException("runnable must not be null");
- }
- postDrawCallbackUnchecked(runnable);
- }
-
- private void postDrawCallbackUnchecked(Runnable runnable) {
- synchronized (mLock) {
- mDrawCallbacks = addCallbackLocked(mDrawCallbacks, runnable);
- scheduleDrawLocked();
- }
+ public void postDrawCallback(Runnable action, Object token) {
+ postDrawCallbackDelayed(action, token, 0);
}
/**
* Posts a callback to run on the next draw cycle following the specified delay.
* The callback only runs once and then is automatically removed.
*
- * @param runnable The callback to run during the next draw cycle following
+ * @param action The callback action to run during the next animation cycle after
* the specified delay.
+ * @param token The callback token, or null if none.
* @param delayMillis The delay time in milliseconds.
*
* @see #removeDrawCallback
*/
- public void postDrawCallbackDelayed(Runnable runnable, long delayMillis) {
- if (runnable == null) {
- throw new IllegalArgumentException("runnable must not be null");
+ public void postDrawCallbackDelayed(Runnable action, Object token, long delayMillis) {
+ if (action == null) {
+ throw new IllegalArgumentException("action must not be null");
}
- if (delayMillis <= 0) {
- postDrawCallbackUnchecked(runnable);
- } else {
- Message msg = mHandler.obtainMessage(MSG_POST_DELAYED_DRAW, runnable);
- mHandler.sendMessageDelayed(msg, delayMillis);
+
+ if (DEBUG) {
+ Log.d(TAG, "PostDrawCallback: " + action + ", token=" + token
+ + ", delayMillis=" + delayMillis);
+ }
+
+ synchronized (mLock) {
+ final long now = SystemClock.uptimeMillis();
+ final long dueTime = now + delayMillis;
+ mDrawCallbackQueue.addCallbackLocked(dueTime, action, token);
+ scheduleDrawLocked(now);
+
+ if (dueTime <= now) {
+ scheduleDrawLocked(now);
+ } else {
+ Message msg = mHandler.obtainMessage(MSG_DO_SCHEDULE_DRAW, action);
+ mHandler.sendMessageAtTime(msg, dueTime);
+ }
}
}
/**
- * Removes draw callbacks for the specified runnable.
- * Does nothing if the specified draw callback has not been posted or has already
- * been removed.
+ * Removes draw callbacks that have the specified action and token.
*
- * @param runnable The draw callback to remove.
+ * @param action The action property of the callbacks to remove, or null to remove
+ * callbacks with any action.
+ * @param token The token property of the callbacks to remove, or null to remove
+ * callbacks with any token.
*
* @see #postDrawCallback
* @see #postDrawCallbackDelayed
*/
- public void removeDrawCallbacks(Runnable runnable) {
- if (runnable == null) {
- throw new IllegalArgumentException("runnable must not be null");
+ public void removeDrawCallbacks(Runnable action, Object token) {
+ if (DEBUG) {
+ Log.d(TAG, "RemoveDrawCallbacks: " + action + ", token=" + token);
}
+
synchronized (mLock) {
- mDrawCallbacks = removeCallbacksLocked(mDrawCallbacks, runnable);
+ mDrawCallbackQueue.removeCallbacksLocked(action, token);
+ if (action != null && token == null) {
+ mHandler.removeMessages(MSG_DO_SCHEDULE_DRAW, action);
+ }
}
- mHandler.removeMessages(MSG_POST_DELAYED_DRAW, runnable);
}
- private void scheduleAnimationLocked() {
+ private void scheduleAnimationLocked(long now) {
if (!mAnimationScheduled) {
mAnimationScheduled = true;
if (USE_VSYNC) {
@@ -291,14 +333,13 @@ public final class Choreographer {
// otherwise post a message to schedule the vsync from the UI thread
// as soon as possible.
if (isRunningOnLooperThreadLocked()) {
- doScheduleVsyncLocked();
+ scheduleVsyncLocked();
} else {
Message msg = mHandler.obtainMessage(MSG_DO_SCHEDULE_VSYNC);
msg.setAsynchronous(true);
mHandler.sendMessageAtFrontOfQueue(msg);
}
} else {
- final long now = SystemClock.uptimeMillis();
final long nextAnimationTime = Math.max(mLastAnimationTime + sFrameDelay, now);
if (DEBUG) {
Log.d(TAG, "Scheduling animation in " + (nextAnimationTime - now) + " ms.");
@@ -310,18 +351,18 @@ public final class Choreographer {
}
}
- private void scheduleDrawLocked() {
+ private void scheduleDrawLocked(long now) {
if (!mDrawScheduled) {
mDrawScheduled = true;
if (USE_ANIMATION_TIMER_FOR_DRAW) {
- scheduleAnimationLocked();
+ scheduleAnimationLocked(now);
} else {
if (DEBUG) {
Log.d(TAG, "Scheduling draw immediately.");
}
Message msg = mHandler.obtainMessage(MSG_DO_DRAW);
msg.setAsynchronous(true);
- mHandler.sendMessage(msg);
+ mHandler.sendMessageAtTime(msg, now);
}
}
}
@@ -336,7 +377,7 @@ public final class Choreographer {
void doAnimationInner() {
final long start;
- final Callback callbacks;
+ Callback callbacks;
synchronized (mLock) {
if (!mAnimationScheduled) {
return; // no work to do
@@ -350,8 +391,7 @@ public final class Choreographer {
}
mLastAnimationTime = start;
- callbacks = mAnimationCallbacks;
- mAnimationCallbacks = null;
+ callbacks = mAnimationCallbackQueue.extractDueCallbacksLocked(start);
}
if (callbacks != null) {
@@ -368,7 +408,7 @@ public final class Choreographer {
void doDraw() {
final long start;
- final Callback callbacks;
+ Callback callbacks;
synchronized (mLock) {
if (!mDrawScheduled) {
return; // no work to do
@@ -382,8 +422,7 @@ public final class Choreographer {
}
mLastDrawTime = start;
- callbacks = mDrawCallbacks;
- mDrawCallbacks = null;
+ callbacks = mDrawCallbackQueue.extractDueCallbacksLocked(start);
}
if (callbacks != null) {
@@ -400,55 +439,45 @@ public final class Choreographer {
void doScheduleVsync() {
synchronized (mLock) {
- doScheduleVsyncLocked();
+ if (mAnimationScheduled) {
+ scheduleVsyncLocked();
+ }
}
}
- private void doScheduleVsyncLocked() {
- if (mAnimationScheduled) {
- mDisplayEventReceiver.scheduleVsync();
+ void doScheduleAnimation() {
+ synchronized (mLock) {
+ final long now = SystemClock.uptimeMillis();
+ if (mAnimationCallbackQueue.hasDueCallbacksLocked(now)) {
+ scheduleAnimationLocked(now);
+ }
}
}
- private boolean isRunningOnLooperThreadLocked() {
- return Looper.myLooper() == mLooper;
+ void doScheduleDraw() {
+ synchronized (mLock) {
+ final long now = SystemClock.uptimeMillis();
+ if (mDrawCallbackQueue.hasDueCallbacksLocked(now)) {
+ scheduleDrawLocked(now);
+ }
+ }
}
- private Callback addCallbackLocked(Callback head, Runnable runnable) {
- Callback callback = obtainCallbackLocked(runnable);
- if (head == null) {
- return callback;
- }
- Callback tail = head;
- while (tail.next != null) {
- tail = tail.next;
- }
- tail.next = callback;
- return head;
+ private void scheduleVsyncLocked() {
+ mDisplayEventReceiver.scheduleVsync();
}
- private Callback removeCallbacksLocked(Callback head, Runnable runnable) {
- Callback predecessor = null;
- for (Callback callback = head; callback != null;) {
- final Callback next = callback.next;
- if (callback.runnable == runnable) {
- if (predecessor != null) {
- predecessor.next = next;
- } else {
- head = next;
- }
- recycleCallbackLocked(callback);
- } else {
- predecessor = callback;
- }
- callback = next;
- }
- return head;
+ private boolean isRunningOnLooperThreadLocked() {
+ return Looper.myLooper() == mLooper;
}
private void runCallbacks(Callback head) {
while (head != null) {
- head.runnable.run();
+ if (DEBUG) {
+ Log.d(TAG, "RunCallback: " + head.action + ", token=" + head.token
+ + ", waitMillis=" + (SystemClock.uptimeMillis() - head.dueTime));
+ }
+ head.action.run();
head = head.next;
}
}
@@ -461,7 +490,7 @@ public final class Choreographer {
}
}
- private Callback obtainCallbackLocked(Runnable runnable) {
+ private Callback obtainCallbackLocked(long dueTime, Runnable action, Object token) {
Callback callback = mCallbackPool;
if (callback == null) {
callback = new Callback();
@@ -469,12 +498,15 @@ public final class Choreographer {
mCallbackPool = callback.next;
callback.next = null;
}
- callback.runnable = runnable;
+ callback.dueTime = dueTime;
+ callback.action = action;
+ callback.token = token;
return callback;
}
private void recycleCallbackLocked(Callback callback) {
- callback.runnable = null;
+ callback.action = null;
+ callback.token = null;
callback.next = mCallbackPool;
mCallbackPool = callback;
}
@@ -496,11 +528,11 @@ public final class Choreographer {
case MSG_DO_SCHEDULE_VSYNC:
doScheduleVsync();
break;
- case MSG_POST_DELAYED_ANIMATION:
- postAnimationCallbackUnchecked((Runnable)msg.obj);
+ case MSG_DO_SCHEDULE_ANIMATION:
+ doScheduleAnimation();
break;
- case MSG_POST_DELAYED_DRAW:
- postDrawCallbackUnchecked((Runnable)msg.obj);
+ case MSG_DO_SCHEDULE_DRAW:
+ doScheduleDraw();
break;
}
}
@@ -519,6 +551,77 @@ public final class Choreographer {
private static final class Callback {
public Callback next;
- public Runnable runnable;
+ public long dueTime;
+ public Runnable action;
+ public Object token;
+ }
+
+ private final class CallbackQueue {
+ private Callback mHead;
+
+ public boolean hasDueCallbacksLocked(long now) {
+ return mHead != null && mHead.dueTime <= now;
+ }
+
+ public Callback extractDueCallbacksLocked(long now) {
+ Callback callbacks = mHead;
+ if (callbacks == null || callbacks.dueTime > now) {
+ return null;
+ }
+
+ Callback last = callbacks;
+ Callback next = last.next;
+ while (next != null) {
+ if (next.dueTime > now) {
+ last.next = null;
+ break;
+ }
+ last = next;
+ next = next.next;
+ }
+ mHead = next;
+ return callbacks;
+ }
+
+ public void addCallbackLocked(long dueTime, Runnable action, Object token) {
+ Callback callback = obtainCallbackLocked(dueTime, action, token);
+ Callback entry = mHead;
+ if (entry == null) {
+ mHead = callback;
+ return;
+ }
+ if (dueTime < entry.dueTime) {
+ callback.next = entry;
+ mHead = callback;
+ return;
+ }
+ while (entry.next != null) {
+ if (dueTime < entry.next.dueTime) {
+ callback.next = entry.next;
+ break;
+ }
+ entry = entry.next;
+ }
+ entry.next = callback;
+ }
+
+ public void removeCallbacksLocked(Runnable action, Object token) {
+ Callback predecessor = null;
+ for (Callback callback = mHead; callback != null;) {
+ final Callback next = callback.next;
+ if ((action == null || callback.action == action)
+ && (token == null || callback.token == token)) {
+ if (predecessor != null) {
+ predecessor.next = next;
+ } else {
+ mHead = next;
+ }
+ recycleCallbackLocked(callback);
+ } else {
+ predecessor = callback;
+ }
+ callback = next;
+ }
+ }
}
}
diff --git a/core/java/android/view/HardwareLayer.java b/core/java/android/view/HardwareLayer.java
index 49450bd..a97167b 100644
--- a/core/java/android/view/HardwareLayer.java
+++ b/core/java/android/view/HardwareLayer.java
@@ -167,7 +167,7 @@ abstract class HardwareLayer {
/**
* Specifies the display list to use to refresh the layer.
- *
+ *
* @param displayList The display list containing the drawing commands to
* execute in this layer
* @param dirtyRect The dirty region of the layer that needs to be redrawn
diff --git a/core/java/android/view/HardwareRenderer.java b/core/java/android/view/HardwareRenderer.java
index ec95863..bf91700 100644
--- a/core/java/android/view/HardwareRenderer.java
+++ b/core/java/android/view/HardwareRenderer.java
@@ -1284,15 +1284,10 @@ public abstract class HardwareRenderer {
usePbufferSurface(managedContext.getContext());
}
- switch (level) {
- case ComponentCallbacks2.TRIM_MEMORY_UI_HIDDEN:
- case ComponentCallbacks2.TRIM_MEMORY_BACKGROUND:
- case ComponentCallbacks2.TRIM_MEMORY_MODERATE:
- GLES20Canvas.flushCaches(GLES20Canvas.FLUSH_CACHES_MODERATE);
- break;
- case ComponentCallbacks2.TRIM_MEMORY_COMPLETE:
- GLES20Canvas.flushCaches(GLES20Canvas.FLUSH_CACHES_FULL);
- break;
+ if (level >= ComponentCallbacks2.TRIM_MEMORY_COMPLETE) {
+ GLES20Canvas.flushCaches(GLES20Canvas.FLUSH_CACHES_FULL);
+ } else if (level >= ComponentCallbacks2.TRIM_MEMORY_UI_HIDDEN) {
+ GLES20Canvas.flushCaches(GLES20Canvas.FLUSH_CACHES_MODERATE);
}
}
diff --git a/core/java/android/view/IWindow.aidl b/core/java/android/view/IWindow.aidl
index 715fa7b..497bc90b 100644
--- a/core/java/android/view/IWindow.aidl
+++ b/core/java/android/view/IWindow.aidl
@@ -49,6 +49,7 @@ oneway interface IWindow {
boolean reportDraw, in Configuration newConfig);
void dispatchAppVisibility(boolean visible);
void dispatchGetNewSurface();
+ void dispatchScreenState(boolean on);
/**
* Tell the window that it is either gaining or losing focus. Keep it up
diff --git a/core/java/android/view/TextureView.java b/core/java/android/view/TextureView.java
index c1e9946..fc02cc1 100644
--- a/core/java/android/view/TextureView.java
+++ b/core/java/android/view/TextureView.java
@@ -315,7 +315,7 @@ public class TextureView extends View {
}
@Override
- HardwareLayer getHardwareLayer(boolean immediateRefresh) {
+ HardwareLayer getHardwareLayer() {
if (mLayer == null) {
if (mAttachInfo == null || mAttachInfo.mHardwareRenderer == null) {
return null;
diff --git a/core/java/android/view/View.java b/core/java/android/view/View.java
index ecfca74..7a1923b 100644
--- a/core/java/android/view/View.java
+++ b/core/java/android/view/View.java
@@ -1994,6 +1994,20 @@ public class View implements Drawable.Callback, Drawable.Callback2, KeyEvent.Cal
public static final int FIND_VIEWS_WITH_ACCESSIBILITY_NODE_PROVIDERS = 0x00000004;
/**
+ * Indicates that the screen has changed state and is now off.
+ *
+ * @see #onScreenStateChanged(int)
+ */
+ public static final int SCREEN_STATE_OFF = 0x0;
+
+ /**
+ * Indicates that the screen has changed state and is now on.
+ *
+ * @see #onScreenStateChanged(int)
+ */
+ public static final int SCREEN_STATE_ON = 0x1;
+
+ /**
* Controls the over-scroll mode for this view.
* See {@link #overScrollBy(int, int, int, int, int, int, int, int, boolean)},
* {@link #OVER_SCROLL_ALWAYS}, {@link #OVER_SCROLL_IF_CONTENT_SCROLLS},
@@ -3093,13 +3107,13 @@ public class View implements Drawable.Callback, Drawable.Callback2, KeyEvent.Cal
setBackgroundDrawable(background);
}
- mUserPaddingRelative = (startPadding >= 0 || endPadding >= 0);
-
// Cache user padding as we cannot fully resolve padding here (we dont have yet the resolved
// layout direction). Those cached values will be used later during padding resolution.
mUserPaddingStart = startPadding;
mUserPaddingEnd = endPadding;
+ updateUserPaddingRelative();
+
if (padding >= 0) {
leftPadding = padding;
topPadding = padding;
@@ -3146,6 +3160,10 @@ public class View implements Drawable.Callback, Drawable.Callback2, KeyEvent.Cal
computeOpaqueFlags();
}
+ private void updateUserPaddingRelative() {
+ mUserPaddingRelative = (mUserPaddingStart >= 0 || mUserPaddingEnd >= 0);
+ }
+
/**
* Non-public constructor for use in testing
*/
@@ -3934,6 +3952,24 @@ public class View implements Drawable.Callback, Drawable.Callback2, KeyEvent.Cal
}
/**
+ * Convenience method for sending a {@link AccessibilityEvent#TYPE_ANNOUNCEMENT}
+ * {@link AccessibilityEvent} to make an announcement which is related to some
+ * sort of a context change for which none of the events representing UI transitions
+ * is a good fit. For example, announcing a new page in a book. If accessibility
+ * is not enabled this method does nothing.
+ *
+ * @param text The announcement text.
+ */
+ public void announceForAccessibility(CharSequence text) {
+ if (AccessibilityManager.getInstance(mContext).isEnabled()) {
+ AccessibilityEvent event = AccessibilityEvent.obtain(
+ AccessibilityEvent.TYPE_ANNOUNCEMENT);
+ event.getText().add(text);
+ sendAccessibilityEventUnchecked(event);
+ }
+ }
+
+ /**
* @see #sendAccessibilityEvent(int)
*
* Note: Called from the default {@link AccessibilityDelegate}.
@@ -5247,6 +5283,7 @@ public class View implements Drawable.Callback, Drawable.Callback2, KeyEvent.Cal
if (mNextFocusForwardId == View.NO_ID) return null;
return findViewInsideOutShouldExist(root, mNextFocusForwardId);
case FOCUS_BACKWARD: {
+ if (mID == View.NO_ID) return null;
final int id = mID;
return root.findViewByPredicateInsideOut(this, new Predicate<View>() {
@Override
@@ -8774,6 +8811,52 @@ public class View implements Drawable.Callback, Drawable.Callback2, KeyEvent.Cal
}
/**
+ * <p>Causes the Runnable to execute on the next animation time step.
+ * The runnable will be run on the user interface thread.</p>
+ *
+ * <p>This method can be invoked from outside of the UI thread
+ * only when this View is attached to a window.</p>
+ *
+ * @param action The Runnable that will be executed.
+ *
+ * @hide
+ */
+ public void postOnAnimation(Runnable action) {
+ final AttachInfo attachInfo = mAttachInfo;
+ if (attachInfo != null) {
+ attachInfo.mViewRootImpl.mChoreographer.postAnimationCallback(action, null);
+ } else {
+ // Assume that post will succeed later
+ ViewRootImpl.getRunQueue().post(action);
+ }
+ }
+
+ /**
+ * <p>Causes the Runnable to execute on the next animation time step,
+ * after the specified amount of time elapses.
+ * The runnable will be run on the user interface thread.</p>
+ *
+ * <p>This method can be invoked from outside of the UI thread
+ * only when this View is attached to a window.</p>
+ *
+ * @param action The Runnable that will be executed.
+ * @param delayMillis The delay (in milliseconds) until the Runnable
+ * will be executed.
+ *
+ * @hide
+ */
+ public void postOnAnimationDelayed(Runnable action, long delayMillis) {
+ final AttachInfo attachInfo = mAttachInfo;
+ if (attachInfo != null) {
+ attachInfo.mViewRootImpl.mChoreographer.postAnimationCallbackDelayed(
+ action, null, delayMillis);
+ } else {
+ // Assume that post will succeed later
+ ViewRootImpl.getRunQueue().postDelayed(action, delayMillis);
+ }
+ }
+
+ /**
* <p>Removes the specified Runnable from the message queue.</p>
*
* <p>This method can be invoked from outside of the UI thread
@@ -8787,12 +8870,15 @@ public class View implements Drawable.Callback, Drawable.Callback2, KeyEvent.Cal
* (for instance, if the Runnable was not in the queue already.)
*/
public boolean removeCallbacks(Runnable action) {
- final AttachInfo attachInfo = mAttachInfo;
- if (attachInfo != null) {
- attachInfo.mHandler.removeCallbacks(action);
- } else {
- // Assume that post will succeed later
- ViewRootImpl.getRunQueue().removeCallbacks(action);
+ if (action != null) {
+ final AttachInfo attachInfo = mAttachInfo;
+ if (attachInfo != null) {
+ attachInfo.mHandler.removeCallbacks(action);
+ attachInfo.mViewRootImpl.mChoreographer.removeAnimationCallbacks(action, null);
+ } else {
+ // Assume that post will succeed later
+ ViewRootImpl.getRunQueue().removeCallbacks(action);
+ }
}
return true;
}
@@ -9599,6 +9685,25 @@ public class View implements Drawable.Callback, Drawable.Callback2, KeyEvent.Cal
}
/**
+ * @see #onScreenStateChanged(int)
+ */
+ void dispatchScreenStateChanged(int screenState) {
+ onScreenStateChanged(screenState);
+ }
+
+ /**
+ * This method is called whenever the state of the screen this view is
+ * attached to changes. A state change will usually occurs when the screen
+ * turns on or off (whether it happens automatically or the user does it
+ * manually.)
+ *
+ * @param screenState The new state of the screen. Can be either
+ * {@link #SCREEN_STATE_ON} or {@link #SCREEN_STATE_OFF}
+ */
+ public void onScreenStateChanged(int screenState) {
+ }
+
+ /**
* Resolve and cache the layout direction. LTR is set initially. This is implicitly supposing
* that the parent directionality can and will be resolved before its children.
*/
@@ -9643,6 +9748,8 @@ public class View implements Drawable.Callback, Drawable.Callback2, KeyEvent.Cal
// Set to resolved
mPrivateFlags2 |= LAYOUT_DIRECTION_RESOLVED;
onResolvedLayoutDirectionChanged();
+ // Resolve padding
+ resolvePadding();
}
/**
@@ -9697,7 +9804,11 @@ public class View implements Drawable.Callback, Drawable.Callback2, KeyEvent.Cal
mUserPaddingBottom = (mUserPaddingBottom >= 0) ? mUserPaddingBottom : mPaddingBottom;
- recomputePadding();
+ if(isPaddingRelative()) {
+ setPaddingRelative(mUserPaddingStart, mPaddingTop, mUserPaddingEnd, mUserPaddingBottom);
+ } else {
+ recomputePadding();
+ }
onPaddingChanged(resolvedLayoutDirection);
}
@@ -10220,7 +10331,7 @@ public class View implements Drawable.Callback, Drawable.Callback2, KeyEvent.Cal
if (mAttachInfo.mHardwareRenderer != null &&
mAttachInfo.mHardwareRenderer.isEnabled() &&
mAttachInfo.mHardwareRenderer.validate()) {
- getHardwareLayer(true);
+ getHardwareLayer();
}
break;
case LAYER_TYPE_SOFTWARE:
@@ -10242,7 +10353,7 @@ public class View implements Drawable.Callback, Drawable.Callback2, KeyEvent.Cal
*
* @return A HardwareLayer ready to render, or null if an error occurred.
*/
- HardwareLayer getHardwareLayer(boolean immediateRefresh) {
+ HardwareLayer getHardwareLayer() {
if (mAttachInfo == null || mAttachInfo.mHardwareRenderer == null ||
!mAttachInfo.mHardwareRenderer.isEnabled()) {
return null;
@@ -10272,33 +10383,8 @@ public class View implements Drawable.Callback, Drawable.Callback2, KeyEvent.Cal
return null;
}
- if (!immediateRefresh) {
- mHardwareLayer.redraw(getDisplayList(), mLocalDirtyRect);
- mLocalDirtyRect.setEmpty();
- } else {
- HardwareCanvas currentCanvas = mAttachInfo.mHardwareCanvas;
- final HardwareCanvas canvas = mHardwareLayer.start(currentCanvas);
-
- // Make sure all the GPU resources have been properly allocated
- if (canvas == null) {
- mHardwareLayer.end(currentCanvas);
- return null;
- }
-
- mAttachInfo.mHardwareCanvas = canvas;
- try {
- canvas.setViewport(width, height);
- canvas.onPreDraw(mLocalDirtyRect);
- mLocalDirtyRect.setEmpty();
-
- canvas.drawDisplayList(getDisplayList(), mRight - mLeft, mBottom - mTop, null,
- DisplayList.FLAG_CLIP_CHILDREN);
- } finally {
- canvas.onPostDraw();
- mHardwareLayer.end(currentCanvas);
- mAttachInfo.mHardwareCanvas = currentCanvas;
- }
- }
+ mHardwareLayer.redraw(getDisplayList(), mLocalDirtyRect);
+ mLocalDirtyRect.setEmpty();
}
return mHardwareLayer;
@@ -10990,13 +11076,13 @@ public class View implements Drawable.Callback, Drawable.Callback2, KeyEvent.Cal
}
/**
- * <p>Indicates whether this view is attached to an hardware accelerated
+ * <p>Indicates whether this view is attached to a hardware accelerated
* window or not.</p>
*
* <p>Even if this method returns true, it does not mean that every call
* to {@link #draw(android.graphics.Canvas)} will be made with an hardware
* accelerated {@link android.graphics.Canvas}. For instance, if this view
- * is drawn onto an offscren {@link android.graphics.Bitmap} and its
+ * is drawn onto an offscreen {@link android.graphics.Bitmap} and its
* window is hardware accelerated,
* {@link android.graphics.Canvas#isHardwareAccelerated()} will likely
* return false, and this method will return true.</p>
@@ -11265,7 +11351,7 @@ public class View implements Drawable.Callback, Drawable.Callback2, KeyEvent.Cal
if (hasNoCache) {
boolean layerRendered = false;
if (layerType == LAYER_TYPE_HARDWARE) {
- final HardwareLayer layer = getHardwareLayer(false);
+ final HardwareLayer layer = getHardwareLayer();
if (layer != null && layer.isValid()) {
mLayerPaint.setAlpha((int) (alpha * 255));
((HardwareCanvas) canvas).drawHardwareLayer(layer, 0, 0, mLayerPaint);
@@ -11861,10 +11947,12 @@ public class View implements Drawable.Callback, Drawable.Callback2, KeyEvent.Cal
*/
public void scheduleDrawable(Drawable who, Runnable what, long when) {
if (verifyDrawable(who) && what != null) {
+ final long delay = when - SystemClock.uptimeMillis();
if (mAttachInfo != null) {
- mAttachInfo.mHandler.postAtTime(what, who, when);
+ mAttachInfo.mViewRootImpl.mChoreographer.postAnimationCallbackDelayed(
+ what, who, Choreographer.subtractFrameDelay(delay));
} else {
- ViewRootImpl.getRunQueue().postDelayed(what, when - SystemClock.uptimeMillis());
+ ViewRootImpl.getRunQueue().postDelayed(what, delay);
}
}
}
@@ -11878,7 +11966,7 @@ public class View implements Drawable.Callback, Drawable.Callback2, KeyEvent.Cal
public void unscheduleDrawable(Drawable who, Runnable what) {
if (verifyDrawable(who) && what != null) {
if (mAttachInfo != null) {
- mAttachInfo.mHandler.removeCallbacks(what, who);
+ mAttachInfo.mViewRootImpl.mChoreographer.removeAnimationCallbacks(what, who);
} else {
ViewRootImpl.getRunQueue().removeCallbacks(what);
}
@@ -11895,8 +11983,8 @@ public class View implements Drawable.Callback, Drawable.Callback2, KeyEvent.Cal
* @see #drawableStateChanged
*/
public void unscheduleDrawable(Drawable who) {
- if (mAttachInfo != null) {
- mAttachInfo.mHandler.removeCallbacksAndMessages(who);
+ if (mAttachInfo != null && who != null) {
+ mAttachInfo.mViewRootImpl.mChoreographer.removeAnimationCallbacks(null, who);
}
}
@@ -12257,15 +12345,20 @@ public class View implements Drawable.Callback, Drawable.Callback2, KeyEvent.Cal
* @param bottom the bottom padding in pixels
*/
public void setPadding(int left, int top, int right, int bottom) {
- boolean changed = false;
-
+ mUserPaddingStart = -1;
+ mUserPaddingEnd = -1;
mUserPaddingRelative = false;
+ internalSetPadding(left, top, right, bottom);
+ }
+
+ private void internalSetPadding(int left, int top, int right, int bottom) {
mUserPaddingLeft = left;
mUserPaddingRight = right;
mUserPaddingBottom = bottom;
final int viewFlags = mViewFlags;
+ boolean changed = false;
// Common case is there are no scroll bars.
if ((viewFlags & (SCROLLBARS_VERTICAL|SCROLLBARS_HORIZONTAL)) != 0) {
@@ -12334,18 +12427,17 @@ public class View implements Drawable.Callback, Drawable.Callback2, KeyEvent.Cal
* @param bottom the bottom padding in pixels
*/
public void setPaddingRelative(int start, int top, int end, int bottom) {
- mUserPaddingRelative = true;
-
mUserPaddingStart = start;
mUserPaddingEnd = end;
+ mUserPaddingRelative = true;
switch(getResolvedLayoutDirection()) {
case LAYOUT_DIRECTION_RTL:
- setPadding(end, top, start, bottom);
+ internalSetPadding(end, top, start, bottom);
break;
case LAYOUT_DIRECTION_LTR:
default:
- setPadding(start, top, end, bottom);
+ internalSetPadding(start, top, end, bottom);
}
}
@@ -13053,7 +13145,7 @@ public class View implements Drawable.Callback, Drawable.Callback2, KeyEvent.Cal
}
/**
- * Creates an string of whitespaces used for indentation.
+ * Creates a string of whitespaces used for indentation.
*
* @param depth the indentation level
* @return a String containing (depth * 2 + 3) * 2 white spaces
@@ -14895,6 +14987,8 @@ public class View implements Drawable.Callback, Drawable.Callback2, KeyEvent.Cal
boolean mHardwareAccelerationRequested;
HardwareRenderer mHardwareRenderer;
+ boolean mScreenOn;
+
/**
* Scale factor used by the compatibility mode
*/
diff --git a/core/java/android/view/ViewConfiguration.java b/core/java/android/view/ViewConfiguration.java
index b455ad5..20183ee 100644
--- a/core/java/android/view/ViewConfiguration.java
+++ b/core/java/android/view/ViewConfiguration.java
@@ -305,8 +305,9 @@ public class ViewConfiguration {
mScaledTouchExplorationTapSlop = (int) (density * TOUCH_EXPLORATION_TAP_SLOP + 0.5f);
mWindowTouchSlop = (int) (sizeAndDensity * WINDOW_TOUCH_SLOP + 0.5f);
+ final Display display = WindowManagerImpl.getDefault().getDefaultDisplay();
// Size of the screen in bytes, in ARGB_8888 format
- mMaximumDrawingCacheSize = 4 * metrics.widthPixels * metrics.heightPixels;
+ mMaximumDrawingCacheSize = 4 * display.getRawWidth() * display.getRawHeight();
mOverscrollDistance = (int) (sizeAndDensity * OVERSCROLL_DISTANCE + 0.5f);
mOverflingDistance = (int) (sizeAndDensity * OVERFLING_DISTANCE + 0.5f);
diff --git a/core/java/android/view/ViewDebug.java b/core/java/android/view/ViewDebug.java
index 2a17845..8f6badf 100644
--- a/core/java/android/view/ViewDebug.java
+++ b/core/java/android/view/ViewDebug.java
@@ -264,7 +264,7 @@ public class ViewDebug {
/**
* Defines a mapping from an int value to a String. Such a mapping can be used
- * in a @ExportedProperty to provide more meaningful values to the end user.
+ * in an @ExportedProperty to provide more meaningful values to the end user.
*
* @see android.view.ViewDebug.ExportedProperty
*/
@@ -287,8 +287,8 @@ public class ViewDebug {
}
/**
- * Defines a mapping from an flag to a String. Such a mapping can be used
- * in a @ExportedProperty to provide more meaningful values to the end user.
+ * Defines a mapping from a flag to a String. Such a mapping can be used
+ * in an @ExportedProperty to provide more meaningful values to the end user.
*
* @see android.view.ViewDebug.ExportedProperty
*/
diff --git a/core/java/android/view/ViewGroup.java b/core/java/android/view/ViewGroup.java
index 0c63286..c9e0242 100644
--- a/core/java/android/view/ViewGroup.java
+++ b/core/java/android/view/ViewGroup.java
@@ -2254,6 +2254,17 @@ public abstract class ViewGroup extends View implements ViewParent, ViewManager
}
@Override
+ void dispatchScreenStateChanged(int screenState) {
+ super.dispatchScreenStateChanged(screenState);
+
+ final int count = mChildrenCount;
+ final View[] children = mChildren;
+ for (int i = 0; i < count; i++) {
+ children[i].dispatchScreenStateChanged(screenState);
+ }
+ }
+
+ @Override
boolean dispatchPopulateAccessibilityEventInternal(AccessibilityEvent event) {
boolean handled = super.dispatchPopulateAccessibilityEventInternal(event);
if (handled) {
diff --git a/core/java/android/view/ViewRootImpl.java b/core/java/android/view/ViewRootImpl.java
index 80d4c53..72365c7 100644
--- a/core/java/android/view/ViewRootImpl.java
+++ b/core/java/android/view/ViewRootImpl.java
@@ -46,6 +46,7 @@ import android.os.LatencyTimer;
import android.os.Looper;
import android.os.Message;
import android.os.ParcelFileDescriptor;
+import android.os.PowerManager;
import android.os.Process;
import android.os.RemoteException;
import android.os.SystemClock;
@@ -390,6 +391,9 @@ public final class ViewRootImpl implements ViewParent,
mProfileRendering = Boolean.parseBoolean(
SystemProperties.get(PROPERTY_PROFILE_RENDERING, "false"));
mChoreographer = Choreographer.getInstance();
+
+ PowerManager powerManager = (PowerManager) context.getSystemService(Context.POWER_SERVICE);
+ mAttachInfo.mScreenOn = powerManager.isScreenOn();
}
/**
@@ -757,6 +761,19 @@ public final class ViewRootImpl implements ViewParent,
scheduleTraversals();
}
+ void handleScreenStateChange(boolean on) {
+ if (on != mAttachInfo.mScreenOn) {
+ mAttachInfo.mScreenOn = on;
+ if (mView != null) {
+ mView.dispatchScreenStateChanged(on ? View.SCREEN_STATE_ON : View.SCREEN_STATE_OFF);
+ }
+ if (on) {
+ mFullRedrawNeeded = true;
+ scheduleTraversals();
+ }
+ }
+ }
+
/**
* {@inheritDoc}
*/
@@ -867,7 +884,7 @@ public final class ViewRootImpl implements ViewParent,
void scheduleFrame() {
if (!mFrameScheduled) {
mFrameScheduled = true;
- mChoreographer.postDrawCallback(mFrameRunnable);
+ mChoreographer.postDrawCallback(mFrameRunnable, null);
}
}
@@ -876,7 +893,7 @@ public final class ViewRootImpl implements ViewParent,
if (mFrameScheduled) {
mFrameScheduled = false;
- mChoreographer.removeDrawCallbacks(mFrameRunnable);
+ mChoreographer.removeDrawCallbacks(mFrameRunnable, null);
}
}
@@ -1886,6 +1903,8 @@ public final class ViewRootImpl implements ViewParent,
}
private void performDraw() {
+ if (!mAttachInfo.mScreenOn) return;
+
final long drawStartTime;
if (ViewDebug.DEBUG_LATENCY) {
drawStartTime = System.nanoTime();
@@ -2018,8 +2037,7 @@ public final class ViewRootImpl implements ViewParent,
}
if (!dirty.isEmpty() || mIsAnimating) {
- if (mAttachInfo.mHardwareRenderer != null
- && mAttachInfo.mHardwareRenderer.isEnabled()) {
+ if (mAttachInfo.mHardwareRenderer != null && mAttachInfo.mHardwareRenderer.isEnabled()) {
// Draw with hardware renderer.
mIsAnimating = false;
mHardwareYOffset = yoff;
@@ -2485,6 +2503,7 @@ public final class ViewRootImpl implements ViewParent,
private final static int MSG_FIND_ACCESSIBLITY_NODE_INFO_BY_VIEW_ID = 21;
private final static int MSG_FIND_ACCESSIBLITY_NODE_INFO_BY_TEXT = 22;
private final static int MSG_PROCESS_INPUT_EVENTS = 23;
+ private final static int MSG_DISPATCH_SCREEN_STATE = 24;
final class ViewRootHandler extends Handler {
@Override
@@ -2741,6 +2760,11 @@ public final class ViewRootImpl implements ViewParent,
.findAccessibilityNodeInfosByTextUiThread(msg);
}
} break;
+ case MSG_DISPATCH_SCREEN_STATE: {
+ if (mView != null) {
+ handleScreenStateChange(msg.arg1 == 1);
+ }
+ } break;
}
}
}
@@ -4027,7 +4051,7 @@ public final class ViewRootImpl implements ViewParent,
}
if (mPosted && mViews.isEmpty() && mViewRects.isEmpty()) {
- mChoreographer.removeAnimationCallbacks(this);
+ mChoreographer.removeAnimationCallbacks(this, null);
mPosted = false;
}
}
@@ -4068,7 +4092,7 @@ public final class ViewRootImpl implements ViewParent,
private void postIfNeededLocked() {
if (!mPosted) {
- mChoreographer.postAnimationCallback(this);
+ mChoreographer.postAnimationCallback(this, null);
mPosted = true;
}
}
@@ -4121,6 +4145,12 @@ public final class ViewRootImpl implements ViewParent,
mHandler.sendMessage(msg);
}
+ public void dispatchScreenStateChange(boolean on) {
+ Message msg = mHandler.obtainMessage(MSG_DISPATCH_SCREEN_STATE);
+ msg.arg1 = on ? 1 : 0;
+ mHandler.sendMessage(msg);
+ }
+
public void dispatchGetNewSurface() {
Message msg = mHandler.obtainMessage(MSG_DISPATCH_GET_NEW_SURFACE);
mHandler.sendMessage(msg);
@@ -4322,6 +4352,13 @@ public final class ViewRootImpl implements ViewParent,
}
}
+ public void dispatchScreenState(boolean on) {
+ final ViewRootImpl viewAncestor = mViewAncestor.get();
+ if (viewAncestor != null) {
+ viewAncestor.dispatchScreenStateChange(on);
+ }
+ }
+
public void dispatchGetNewSurface() {
final ViewRootImpl viewAncestor = mViewAncestor.get();
if (viewAncestor != null) {
diff --git a/core/java/android/view/WindowManager.java b/core/java/android/view/WindowManager.java
index c0eb65b..f3ef329 100644
--- a/core/java/android/view/WindowManager.java
+++ b/core/java/android/view/WindowManager.java
@@ -787,10 +787,10 @@ public interface WindowManager extends ViewManager {
* hardware accelerated. This is used for the starting preview windows
* in the system process, which don't need to have the overhead of
* hardware acceleration (they are just a static rendering), but should
- * be rendered as much to match the actual window of the app even if it
+ * be rendered as such to match the actual window of the app even if it
* is hardware accelerated.
* Even if the window isn't hardware accelerated, still do its rendering
- * as if it is.
+ * as if it was.
* Like {@link #FLAG_HARDWARE_ACCELERATED} except for trusted system windows
* that need hardware acceleration (e.g. LockScreen), where hardware acceleration
* is generally disabled. This flag must be specified in addition to
@@ -803,7 +803,7 @@ public interface WindowManager extends ViewManager {
/**
* In the system process, we globally do not use hardware acceleration
- * because there are many threads doing UI there and they an conflict.
+ * because there are many threads doing UI there and they conflict.
* If certain parts of the UI that really do want to use hardware
* acceleration, this flag can be set to force it. This is basically
* for the lock screen. Anyone else using it, you are probably wrong.
@@ -814,7 +814,7 @@ public interface WindowManager extends ViewManager {
/**
* By default, wallpapers are sent new offsets when the wallpaper is scrolled. Wallpapers
- * may elect to skp these notifications if they are no doing anything productive with
+ * may elect to skip these notifications if they are not doing anything productive with
* them (they do not affect the wallpaper scrolling operation) by calling
* {@link
* android.service.wallpaper.WallpaperService.Engine#setOffsetNotificationsEnabled(boolean)}.
diff --git a/core/java/android/view/WindowManagerImpl.java b/core/java/android/view/WindowManagerImpl.java
index d482b35..0e4a30f 100644
--- a/core/java/android/view/WindowManagerImpl.java
+++ b/core/java/android/view/WindowManagerImpl.java
@@ -432,29 +432,25 @@ public class WindowManagerImpl implements WindowManager {
*/
public void trimMemory(int level) {
if (HardwareRenderer.isAvailable()) {
- switch (level) {
- case ComponentCallbacks2.TRIM_MEMORY_COMPLETE:
- case ComponentCallbacks2.TRIM_MEMORY_MODERATE:
- // On low and medium end gfx devices
- if (!ActivityManager.isHighEndGfx(getDefaultDisplay())) {
- // Destroy all hardware surfaces and resources associated to
- // known windows
- synchronized (this) {
- if (mViews == null) return;
- int count = mViews.length;
- for (int i = 0; i < count; i++) {
- mRoots[i].terminateHardwareResources();
- }
+ // On low and medium end gfx devices
+ if (!ActivityManager.isHighEndGfx(getDefaultDisplay())) {
+ if (level >= ComponentCallbacks2.TRIM_MEMORY_MODERATE) {
+ // Destroy all hardware surfaces and resources associated to
+ // known windows
+ synchronized (this) {
+ if (mViews == null) return;
+ int count = mViews.length;
+ for (int i = 0; i < count; i++) {
+ mRoots[i].terminateHardwareResources();
}
- // Force a full memory flush
- HardwareRenderer.trimMemory(ComponentCallbacks2.TRIM_MEMORY_COMPLETE);
- mNeedsEglTerminate = true;
- break;
}
- // high end gfx devices fall through to next case
- default:
- HardwareRenderer.trimMemory(level);
+ // Force a full memory flush
+ HardwareRenderer.trimMemory(ComponentCallbacks2.TRIM_MEMORY_COMPLETE);
+ mNeedsEglTerminate = true;
+ return;
+ }
}
+ HardwareRenderer.trimMemory(level);
}
}
diff --git a/core/java/android/view/accessibility/AccessibilityEvent.java b/core/java/android/view/accessibility/AccessibilityEvent.java
index 75b875a..58844fc 100644
--- a/core/java/android/view/accessibility/AccessibilityEvent.java
+++ b/core/java/android/view/accessibility/AccessibilityEvent.java
@@ -429,6 +429,26 @@ import java.util.List;
* view.</br>
* </p>
* <p>
+ * <b>MISCELLANEOUS TYPES</b></br>
+ * </p>
+ * <p>
+ * <b>Announcement</b> - represents the event of an application making an
+ * announcement. Usually this announcement is related to some sort of a context
+ * change for which none of the events representing UI transitions is a good fit.
+ * For example, announcing a new page in a book.</br>
+ * <em>Type:</em> {@link #TYPE_ANNOUNCEMENT}</br>
+ * <em>Properties:</em></br>
+ * <ul>
+ * <li>{@link #getEventType()} - The type of the event.</li>
+ * <li>{@link #getSource()} - The source info (for registered clients).</li>
+ * <li>{@link #getClassName()} - The class name of the source.</li>
+ * <li>{@link #getPackageName()} - The package name of the source.</li>
+ * <li>{@link #getEventTime()} - The event time.</li>
+ * <li>{@link #getText()} - The text of the announcement.</li>
+ * <li>{@link #isEnabled()} - Whether the source is enabled.</li>
+ * </ul>
+ * </p>
+ * <p>
* <b>Security note</b>
* <p>
* Since an event contains the text of its source privacy can be compromised by leaking
@@ -538,6 +558,11 @@ public final class AccessibilityEvent extends AccessibilityRecord implements Par
public static final int TYPE_VIEW_TEXT_SELECTION_CHANGED = 0x00002000;
/**
+ * Represents the event of an application making an announcement.
+ */
+ public static final int TYPE_ANNOUNCEMENT = 0x00004000;
+
+ /**
* Mask for {@link AccessibilityEvent} all types.
*
* @see #TYPE_VIEW_CLICKED
@@ -554,6 +579,7 @@ public final class AccessibilityEvent extends AccessibilityRecord implements Par
* @see #TYPE_WINDOW_CONTENT_CHANGED
* @see #TYPE_VIEW_SCROLLED
* @see #TYPE_VIEW_TEXT_SELECTION_CHANGED
+ * @see #TYPE_ANNOUNCEMENT
*/
public static final int TYPES_ALL_MASK = 0xFFFFFFFF;
@@ -984,6 +1010,8 @@ public final class AccessibilityEvent extends AccessibilityRecord implements Par
return "TYPE_VIEW_TEXT_SELECTION_CHANGED";
case TYPE_VIEW_SCROLLED:
return "TYPE_VIEW_SCROLLED";
+ case TYPE_ANNOUNCEMENT:
+ return "TYPE_ANNOUNCEMENT";
default:
return null;
}
diff --git a/core/java/android/view/accessibility/AccessibilityNodeInfo.java b/core/java/android/view/accessibility/AccessibilityNodeInfo.java
index c094fda..03c6211 100644
--- a/core/java/android/view/accessibility/AccessibilityNodeInfo.java
+++ b/core/java/android/view/accessibility/AccessibilityNodeInfo.java
@@ -994,7 +994,7 @@ public class AccessibilityNodeInfo implements Parcelable {
protected void enforceNotSealed() {
if (isSealed()) {
throw new IllegalStateException("Cannot perform this "
- + "action on an sealed instance.");
+ + "action on a sealed instance.");
}
}
diff --git a/core/java/android/view/accessibility/AccessibilityRecord.java b/core/java/android/view/accessibility/AccessibilityRecord.java
index 23b235c..bc6074f 100644
--- a/core/java/android/view/accessibility/AccessibilityRecord.java
+++ b/core/java/android/view/accessibility/AccessibilityRecord.java
@@ -632,7 +632,7 @@ public class AccessibilityRecord {
void enforceNotSealed() {
if (isSealed()) {
throw new IllegalStateException("Cannot perform this "
- + "action on an sealed instance.");
+ + "action on a sealed instance.");
}
}
diff --git a/core/java/android/view/textservice/SpellCheckerInfo.java b/core/java/android/view/textservice/SpellCheckerInfo.java
index 9d8475d..137743a 100644
--- a/core/java/android/view/textservice/SpellCheckerInfo.java
+++ b/core/java/android/view/textservice/SpellCheckerInfo.java
@@ -38,7 +38,7 @@ import java.io.IOException;
import java.util.ArrayList;
/**
- * This class is used to specify meta information of an spell checker.
+ * This class is used to specify meta information of a spell checker.
*/
public final class SpellCheckerInfo implements Parcelable {
private static final String TAG = SpellCheckerInfo.class.getSimpleName();
@@ -53,7 +53,7 @@ public final class SpellCheckerInfo implements Parcelable {
private final String mSettingsActivityName;
/**
- * The array of the subtypes.
+ * The array of subtypes.
*/
private final ArrayList<SpellCheckerSubtype> mSubtypes = new ArrayList<SpellCheckerSubtype>();
diff --git a/core/java/android/view/textservice/SpellCheckerSession.java b/core/java/android/view/textservice/SpellCheckerSession.java
index 9105f19..6ff3b9b 100644
--- a/core/java/android/view/textservice/SpellCheckerSession.java
+++ b/core/java/android/view/textservice/SpellCheckerSession.java
@@ -281,9 +281,6 @@ public class SpellCheckerSession {
if (DBG) {
Log.w(TAG, "Get suggestions from the spell checker.");
}
- if (scp.mTextInfos.length != 1) {
- throw new IllegalArgumentException();
- }
try {
session.onGetSentenceSuggestionsMultiple(
scp.mTextInfos, scp.mSuggestionsLimit);
diff --git a/core/java/android/webkit/CallbackProxy.java b/core/java/android/webkit/CallbackProxy.java
index 2afb841..800ebc8 100644
--- a/core/java/android/webkit/CallbackProxy.java
+++ b/core/java/android/webkit/CallbackProxy.java
@@ -76,6 +76,8 @@ class CallbackProxy extends Handler {
private volatile WebBackForwardListClient mWebBackForwardListClient;
// Used to call startActivity during url override.
private final Context mContext;
+ // block messages flag for destroy
+ private boolean mBlockMessages;
// Message IDs
private static final int PAGE_STARTED = 100;
@@ -155,10 +157,18 @@ class CallbackProxy extends Handler {
mBackForwardList = new WebBackForwardList(this);
}
+ protected synchronized void blockMessages() {
+ mBlockMessages = true;
+ }
+
+ protected synchronized boolean messagesBlocked() {
+ return mBlockMessages;
+ }
+
protected void shutdown() {
+ removeCallbacksAndMessages(null);
setWebViewClient(null);
setWebChromeClient(null);
- removeCallbacksAndMessages(null);
}
/**
@@ -265,6 +275,7 @@ class CallbackProxy extends Handler {
// in the UI thread. The WebViewClient and WebChromeClient functions
// that check for a non-null callback are ok because java ensures atomic
// 32-bit reads and writes.
+ if (messagesBlocked()) return;
switch (msg.what) {
case PAGE_STARTED:
String startedUrl = msg.getData().getString("url");
diff --git a/core/java/android/webkit/URLUtil.java b/core/java/android/webkit/URLUtil.java
index 542dd21..9970c93 100644
--- a/core/java/android/webkit/URLUtil.java
+++ b/core/java/android/webkit/URLUtil.java
@@ -182,7 +182,7 @@ public final class URLUtil {
}
/**
- * @return True iff the url is an proxy url to allow cookieless network
+ * @return True iff the url is a proxy url to allow cookieless network
* requests from a file url.
* @deprecated Cookieless proxy is no longer supported.
*/
diff --git a/core/java/android/webkit/ViewStateSerializer.java b/core/java/android/webkit/ViewStateSerializer.java
index a22fc26..e672b62 100644
--- a/core/java/android/webkit/ViewStateSerializer.java
+++ b/core/java/android/webkit/ViewStateSerializer.java
@@ -52,12 +52,12 @@ class ViewStateSerializer {
throws IOException {
DataInputStream dis = new DataInputStream(stream);
int version = dis.readInt();
- if (version != VERSION) {
+ if (version > VERSION) {
throw new IOException("Unexpected version: " + version);
}
int contentWidth = dis.readInt();
int contentHeight = dis.readInt();
- int baseLayer = nativeDeserializeViewState(dis,
+ int baseLayer = nativeDeserializeViewState(version, dis,
new byte[WORKING_STREAM_STORAGE]);
final WebViewCore.DrawData draw = new WebViewCore.DrawData();
@@ -76,7 +76,7 @@ class ViewStateSerializer {
OutputStream stream, byte[] storage);
// Returns a pointer to the BaseLayer
- private static native int nativeDeserializeViewState(
+ private static native int nativeDeserializeViewState(int version,
InputStream stream, byte[] storage);
private ViewStateSerializer() {}
diff --git a/core/java/android/webkit/WebViewClassic.java b/core/java/android/webkit/WebViewClassic.java
index c9a3ff1..856f787 100644
--- a/core/java/android/webkit/WebViewClassic.java
+++ b/core/java/android/webkit/WebViewClassic.java
@@ -1121,7 +1121,7 @@ public final class WebViewClassic implements WebViewProvider, WebViewProvider.Sc
static final int WEBCORE_INITIALIZED_MSG_ID = 107;
static final int UPDATE_TEXTFIELD_TEXT_MSG_ID = 108;
static final int UPDATE_ZOOM_RANGE = 109;
- static final int UNHANDLED_NAV_KEY = 110;
+ static final int TAKE_FOCUS = 110;
static final int CLEAR_TEXT_ENTRY = 111;
static final int UPDATE_TEXT_SELECTION_MSG_ID = 112;
static final int SHOW_RECT_MSG_ID = 113;
@@ -1357,14 +1357,7 @@ public final class WebViewClassic implements WebViewProvider, WebViewProvider.Sc
}
/**
- * Construct a new WebView with layout parameters, a default style and a set
- * of custom Javscript interfaces to be added to the WebView at initialization
- * time. This guarantees that these interfaces will be available when the JS
- * context is initialized.
- * @param javaScriptInterfaces is a Map of interface names, as keys, and
- * object implementing those interfaces, as values.
- * @param privateBrowsing If true the web view will be initialized in private mode.
- * @hide This is an implementation detail.
+ * See {@link WebViewProvider#init(Map, boolean)}
*/
@Override
public void init(Map<String, Object> javaScriptInterfaces, boolean privateBrowsing) {
@@ -1867,36 +1860,36 @@ public final class WebViewClassic implements WebViewProvider, WebViewProvider.Sc
}
/**
- * Specify whether the horizontal scrollbar has overlay style.
- * @param overlay TRUE if horizontal scrollbar should have overlay style.
+ * See {@link WebView#setHorizontalScrollbarOverlay(boolean)}
*/
+ @Override
public void setHorizontalScrollbarOverlay(boolean overlay) {
checkThread();
mOverlayHorizontalScrollbar = overlay;
}
/**
- * Specify whether the vertical scrollbar has overlay style.
- * @param overlay TRUE if vertical scrollbar should have overlay style.
+ * See {@link WebView#setVerticalScrollbarOverlay(boolean)
*/
+ @Override
public void setVerticalScrollbarOverlay(boolean overlay) {
checkThread();
mOverlayVerticalScrollbar = overlay;
}
/**
- * Return whether horizontal scrollbar has overlay style
- * @return TRUE if horizontal scrollbar has overlay style.
+ * See {@link WebView#overlayHorizontalScrollbar()}
*/
+ @Override
public boolean overlayHorizontalScrollbar() {
checkThread();
return mOverlayHorizontalScrollbar;
}
/**
- * Return whether vertical scrollbar has overlay style
- * @return TRUE if vertical scrollbar has overlay style.
+ * See {@link WebView#overlayVerticalScrollbar()}
*/
+ @Override
public boolean overlayVerticalScrollbar() {
checkThread();
return mOverlayVerticalScrollbar;
@@ -1934,11 +1927,9 @@ public final class WebViewClassic implements WebViewProvider, WebViewProvider.Sc
}
/**
- * Return the visible height (in pixels) of the embedded title bar (if any).
- *
- * @return This method is obsolete and always returns 0.
- * @deprecated This method is now obsolete.
+ * See {@link WebView#getVisibleTitleHeight()}
*/
+ @Override
@Deprecated
public int getVisibleTitleHeight() {
// Actually, this method returns the height of the embedded title bar if one is set via the
@@ -1985,17 +1976,18 @@ public final class WebViewClassic implements WebViewProvider, WebViewProvider.Sc
}
/**
- * @return The SSL certificate for the main top-level page or null if
- * there is no certificate (the site is not secure).
+ * See {@link WebView#getCertificate()}
*/
+ @Override
public SslCertificate getCertificate() {
checkThread();
return mCertificate;
}
/**
- * Sets the SSL certificate for the main top-level page.
+ * See {@link WebView#setCertificate(SslCertificate)}
*/
+ @Override
public void setCertificate(SslCertificate certificate) {
checkThread();
if (DebugFlags.WEB_VIEW) {
@@ -2010,26 +2002,18 @@ public final class WebViewClassic implements WebViewProvider, WebViewProvider.Sc
//-------------------------------------------------------------------------
/**
- * Save the username and password for a particular host in the WebView's
- * internal database.
- * @param host The host that required the credentials.
- * @param username The username for the given host.
- * @param password The password for the given host.
+ * See {@link WebView#savePassword(String, String, String)}
*/
+ @Override
public void savePassword(String host, String username, String password) {
checkThread();
mDatabase.setUsernamePassword(host, username, password);
}
/**
- * Set the HTTP authentication credentials for a given host and realm.
- *
- * @param host The host for the credentials.
- * @param realm The realm for the credentials.
- * @param username The username for the password. If it is null, it means
- * password can't be saved.
- * @param password The password
+ * See {@link WebView#setHttpAuthUsernamePassword(String, String, String, String)}
*/
+ @Override
public void setHttpAuthUsernamePassword(String host, String realm,
String username, String password) {
checkThread();
@@ -2037,14 +2021,9 @@ public final class WebViewClassic implements WebViewProvider, WebViewProvider.Sc
}
/**
- * Retrieve the HTTP authentication username and password for a given
- * host & realm pair
- *
- * @param host The host for which the credentials apply.
- * @param realm The realm for which the credentials apply.
- * @return String[] if found, String[0] is username, which can be null and
- * String[1] is password. Return null if it can't find anything.
+ * See {@link WebView#getHttpAuthUsernamePassword(String, String)}
*/
+ @Override
public String[] getHttpAuthUsernamePassword(String host, String realm) {
checkThread();
return mDatabase.getHttpAuthUsernamePassword(host, realm);
@@ -2082,16 +2061,16 @@ public final class WebViewClassic implements WebViewProvider, WebViewProvider.Sc
}
/**
- * Destroy the internal state of the WebView. This method should be called
- * after the WebView has been removed from the view system. No other
- * methods may be called on a WebView after destroy.
+ * See {@link WebView#destroy()}
*/
+ @Override
public void destroy() {
checkThread();
destroyImpl();
}
private void destroyImpl() {
+ mCallbackProxy.blockMessages();
clearHelpers();
if (mListBoxDialog != null) {
mListBoxDialog.dismiss();
@@ -2115,10 +2094,7 @@ public final class WebViewClassic implements WebViewProvider, WebViewProvider.Sc
}
/**
- * Enables platform notifications of data state and proxy changes.
- * Notifications are enabled by default.
- *
- * @deprecated This method is now obsolete.
+ * See {@link WebView#enablePlatformNotifications()}
*/
@Deprecated
public static void enablePlatformNotifications() {
@@ -2132,10 +2108,7 @@ public final class WebViewClassic implements WebViewProvider, WebViewProvider.Sc
}
/**
- * Disables platform notifications of data state and proxy changes.
- * Notifications are enabled by default.
- *
- * @deprecated This method is now obsolete.
+ * See {@link WebView#disablePlatformNotifications()}
*/
@Deprecated
public static void disablePlatformNotifications() {
@@ -2161,11 +2134,9 @@ public final class WebViewClassic implements WebViewProvider, WebViewProvider.Sc
}
/**
- * Inform WebView of the network state. This is used to set
- * the JavaScript property window.navigator.isOnline and
- * generates the online/offline event as specified in HTML5, sec. 5.7.7
- * @param networkUp boolean indicating if network is available
+ * See {@link WebView#setNetworkAvailable(boolean)}
*/
+ @Override
public void setNetworkAvailable(boolean networkUp) {
checkThread();
mWebViewCore.sendMessage(EventHub.SET_NETWORK_STATE,
@@ -2183,19 +2154,11 @@ public final class WebViewClassic implements WebViewProvider, WebViewProvider.Sc
map.put("subtype", subtype);
mWebViewCore.sendMessage(EventHub.SET_NETWORK_TYPE, map);
}
+
/**
- * Save the state of this WebView used in
- * {@link android.app.Activity#onSaveInstanceState}. Please note that this
- * method no longer stores the display data for this WebView. The previous
- * behavior could potentially leak files if {@link #restoreState} was never
- * called. See {@link #savePicture} and {@link #restorePicture} for saving
- * and restoring the display data.
- * @param outState The Bundle to store the WebView state.
- * @return The same copy of the back/forward list used to save the state. If
- * saveState fails, the returned list will be null.
- * @see #savePicture
- * @see #restorePicture
+ * See {@link WebView#saveState(Bundle)}
*/
+ @Override
public WebBackForwardList saveState(Bundle outState) {
checkThread();
if (outState == null) {
@@ -2244,14 +2207,9 @@ public final class WebViewClassic implements WebViewProvider, WebViewProvider.Sc
}
/**
- * Save the current display data to the Bundle given. Used in conjunction
- * with {@link #saveState}.
- * @param b A Bundle to store the display data.
- * @param dest The file to store the serialized picture data. Will be
- * overwritten with this WebView's picture data.
- * @return True if the picture was successfully saved.
- * @deprecated This method is now obsolete.
+ * See {@link WebView#savePicture(Bundle, File)}
*/
+ @Override
@Deprecated
public boolean savePicture(Bundle b, final File dest) {
checkThread();
@@ -2311,15 +2269,9 @@ public final class WebViewClassic implements WebViewProvider, WebViewProvider.Sc
}
/**
- * Restore the display data that was save in {@link #savePicture}. Used in
- * conjunction with {@link #restoreState}.
- *
- * Note that this will not work if the WebView is hardware accelerated.
- * @param b A Bundle containing the saved display data.
- * @param src The file where the picture data was stored.
- * @return True if the picture was successfully restored.
- * @deprecated This method is now obsolete.
+ * See {@link WebView#restorePicture(Bundle, File)};
*/
+ @Override
@Deprecated
public boolean restorePicture(Bundle b, File src) {
checkThread();
@@ -2402,7 +2354,6 @@ public final class WebViewClassic implements WebViewProvider, WebViewProvider.Sc
/**
* Clears the view state set with {@link #loadViewState(InputStream)}.
* This WebView will then switch to showing the content from webkit
- * @hide
*/
public void clearViewState() {
mBlockWebkitViewMessages = false;
@@ -2411,19 +2362,9 @@ public final class WebViewClassic implements WebViewProvider, WebViewProvider.Sc
}
/**
- * Restore the state of this WebView from the given map used in
- * {@link android.app.Activity#onRestoreInstanceState}. This method should
- * be called to restore the state of the WebView before using the object. If
- * it is called after the WebView has had a chance to build state (load
- * pages, create a back/forward list, etc.) there may be undesirable
- * side-effects. Please note that this method no longer restores the
- * display data for this WebView. See {@link #savePicture} and {@link
- * #restorePicture} for saving and restoring the display data.
- * @param inState The incoming Bundle of state.
- * @return The restored back/forward list or null if restoreState failed.
- * @see #savePicture
- * @see #restorePicture
+ * See {@link WebView#restoreState(Bundle)}
*/
+ @Override
public WebBackForwardList restoreState(Bundle inState) {
checkThread();
WebBackForwardList returnList = null;
@@ -2478,15 +2419,9 @@ public final class WebViewClassic implements WebViewProvider, WebViewProvider.Sc
}
/**
- * Load the given URL with the specified additional HTTP headers.
- * @param url The URL of the resource to load.
- * @param additionalHttpHeaders The additional headers to be used in the
- * HTTP request for this URL, specified as a map from name to
- * value. Note that if this map contains any of the headers
- * that are set by default by the WebView, such as those
- * controlling caching, accept types or the User-Agent, their
- * values may be overriden by the WebView's defaults.
+ * See {@link WebView#loadUrl(String, Map)}
*/
+ @Override
public void loadUrl(String url, Map<String, String> additionalHttpHeaders) {
checkThread();
loadUrlImpl(url, additionalHttpHeaders);
@@ -2502,9 +2437,9 @@ public final class WebViewClassic implements WebViewProvider, WebViewProvider.Sc
}
/**
- * Load the given URL.
- * @param url The URL of the resource to load.
+ * See {@link WebView#loadUrl(String)}
*/
+ @Override
public void loadUrl(String url) {
checkThread();
loadUrlImpl(url);
@@ -2518,13 +2453,9 @@ public final class WebViewClassic implements WebViewProvider, WebViewProvider.Sc
}
/**
- * Load the url with postData using "POST" method into the WebView. If url
- * is not a network url, it will be loaded with {link
- * {@link #loadUrl(String)} instead.
- *
- * @param url The url of the resource to load.
- * @param postData The data will be passed to "POST" request.
+ * See {@link WebView#postUrl(String, byte[])}
*/
+ @Override
public void postUrl(String url, byte[] postData) {
checkThread();
if (URLUtil.isNetworkUrl(url)) {
@@ -2540,31 +2471,9 @@ public final class WebViewClassic implements WebViewProvider, WebViewProvider.Sc
}
/**
- * Load the given data into the WebView using a 'data' scheme URL.
- * <p>
- * Note that JavaScript's same origin policy means that script running in a
- * page loaded using this method will be unable to access content loaded
- * using any scheme other than 'data', including 'http(s)'. To avoid this
- * restriction, use {@link
- * #loadDataWithBaseURL(String,String,String,String,String)
- * loadDataWithBaseURL()} with an appropriate base URL.
- * <p>
- * If the value of the encoding parameter is 'base64', then the data must
- * be encoded as base64. Otherwise, the data must use ASCII encoding for
- * octets inside the range of safe URL characters and use the standard %xx
- * hex encoding of URLs for octets outside that range. For example,
- * '#', '%', '\', '?' should be replaced by %23, %25, %27, %3f respectively.
- * <p>
- * The 'data' scheme URL formed by this method uses the default US-ASCII
- * charset. If you need need to set a different charset, you should form a
- * 'data' scheme URL which explicitly specifies a charset parameter in the
- * mediatype portion of the URL and call {@link #loadUrl(String)} instead.
- * Note that the charset obtained from the mediatype portion of a data URL
- * always overrides that specified in the HTML or XML document itself.
- * @param data A String of data in the given encoding.
- * @param mimeType The MIME type of the data, e.g. 'text/html'.
- * @param encoding The encoding of the data.
+ * See {@link WebView#loadData(String, String, String)}
*/
+ @Override
public void loadData(String data, String mimeType, String encoding) {
checkThread();
loadDataImpl(data, mimeType, encoding);
@@ -2582,27 +2491,9 @@ public final class WebViewClassic implements WebViewProvider, WebViewProvider.Sc
}
/**
- * Load the given data into the WebView, using baseUrl as the base URL for
- * the content. The base URL is used both to resolve relative URLs and when
- * applying JavaScript's same origin policy. The historyUrl is used for the
- * history entry.
- * <p>
- * Note that content specified in this way can access local device files
- * (via 'file' scheme URLs) only if baseUrl specifies a scheme other than
- * 'http', 'https', 'ftp', 'ftps', 'about' or 'javascript'.
- * <p>
- * If the base URL uses the data scheme, this method is equivalent to
- * calling {@link #loadData(String,String,String) loadData()} and the
- * historyUrl is ignored.
- * @param baseUrl URL to use as the page's base URL. If null defaults to
- * 'about:blank'
- * @param data A String of data in the given encoding.
- * @param mimeType The MIMEType of the data, e.g. 'text/html'. If null,
- * defaults to 'text/html'.
- * @param encoding The encoding of the data.
- * @param historyUrl URL to use as the history entry, if null defaults to
- * 'about:blank'.
+ * See {@link WebView#loadDataWithBaseURL(String, String, String, String, String)}
*/
+ @Override
public void loadDataWithBaseURL(String baseUrl, String data,
String mimeType, String encoding, String historyUrl) {
checkThread();
@@ -2623,10 +2514,9 @@ public final class WebViewClassic implements WebViewProvider, WebViewProvider.Sc
}
/**
- * Saves the current view as a web archive.
- *
- * @param filename The filename where the archive should be placed.
+ * See {@link WebView#saveWebArchive(String)}
*/
+ @Override
public void saveWebArchive(String filename) {
checkThread();
saveWebArchiveImpl(filename, false, null);
@@ -2646,17 +2536,9 @@ public final class WebViewClassic implements WebViewProvider, WebViewProvider.Sc
}
/**
- * Saves the current view as a web archive.
- *
- * @param basename The filename where the archive should be placed.
- * @param autoname If false, takes basename to be a file. If true, basename
- * is assumed to be a directory in which a filename will be
- * chosen according to the url of the current page.
- * @param callback Called after the web archive has been saved. The
- * parameter for onReceiveValue will either be the filename
- * under which the file was saved, or null if saving the
- * file failed.
+ * See {@link WebView#saveWebArchive(String, boolean, ValueCallback)}
*/
+ @Override
public void saveWebArchive(String basename, boolean autoname, ValueCallback<String> callback) {
checkThread();
saveWebArchiveImpl(basename, autoname, callback);
@@ -2669,8 +2551,9 @@ public final class WebViewClassic implements WebViewProvider, WebViewProvider.Sc
}
/**
- * Stop the current load.
+ * See {@link WebView#stopLoading()}
*/
+ @Override
public void stopLoading() {
checkThread();
// TODO: should we clear all the messages in the queue before sending
@@ -2680,8 +2563,9 @@ public final class WebViewClassic implements WebViewProvider, WebViewProvider.Sc
}
/**
- * Reload the current url.
+ * See {@link WebView#reload()}
*/
+ @Override
public void reload() {
checkThread();
clearHelpers();
@@ -2690,9 +2574,9 @@ public final class WebViewClassic implements WebViewProvider, WebViewProvider.Sc
}
/**
- * Return true if this WebView has a back history item.
- * @return True iff this WebView has a back history item.
+ * See {@link WebView#canGoBack()}
*/
+ @Override
public boolean canGoBack() {
checkThread();
WebBackForwardList l = mCallbackProxy.getBackForwardList();
@@ -2706,17 +2590,18 @@ public final class WebViewClassic implements WebViewProvider, WebViewProvider.Sc
}
/**
- * Go back in the history of this WebView.
+ * See {@link WebView#goBack()}
*/
+ @Override
public void goBack() {
checkThread();
goBackOrForwardImpl(-1);
}
/**
- * Return true if this WebView has a forward history item.
- * @return True iff this Webview has a forward history item.
+ * See {@link WebView#canGoForward()}
*/
+ @Override
public boolean canGoForward() {
checkThread();
WebBackForwardList l = mCallbackProxy.getBackForwardList();
@@ -2730,19 +2615,18 @@ public final class WebViewClassic implements WebViewProvider, WebViewProvider.Sc
}
/**
- * Go forward in the history of this WebView.
+ * See {@link WebView#goForward()}
*/
+ @Override
public void goForward() {
checkThread();
goBackOrForwardImpl(1);
}
/**
- * Return true if the page can go back or forward the given
- * number of steps.
- * @param steps The negative or positive number of steps to move the
- * history.
+ * See {@link WebView#canGoBackOrForward(int)}
*/
+ @Override
public boolean canGoBackOrForward(int steps) {
checkThread();
WebBackForwardList l = mCallbackProxy.getBackForwardList();
@@ -2757,12 +2641,9 @@ public final class WebViewClassic implements WebViewProvider, WebViewProvider.Sc
}
/**
- * Go to the history item that is the number of steps away from
- * the current item. Steps is negative if backward and positive
- * if forward.
- * @param steps The number of steps to take back or forward in the back
- * forward list.
+ * See {@link WebView#goBackOrForward(int)}
*/
+ @Override
public void goBackOrForward(int steps) {
checkThread();
goBackOrForwardImpl(steps);
@@ -2781,8 +2662,9 @@ public final class WebViewClassic implements WebViewProvider, WebViewProvider.Sc
}
/**
- * Returns true if private browsing is enabled in this WebView.
+ * See {@link WebView#isPrivateBrowsingEnabled()}
*/
+ @Override
public boolean isPrivateBrowsingEnabled() {
checkThread();
return getSettings().isPrivateBrowsingEnabled();
@@ -2802,10 +2684,9 @@ public final class WebViewClassic implements WebViewProvider, WebViewProvider.Sc
}
/**
- * Scroll the contents of the view up by half the view size
- * @param top true to jump to the top of the page
- * @return true if the page was scrolled
+ * See {@link WebView#pageUp(boolean)}
*/
+ @Override
public boolean pageUp(boolean top) {
checkThread();
if (mNativeClass == 0) {
@@ -2828,10 +2709,9 @@ public final class WebViewClassic implements WebViewProvider, WebViewProvider.Sc
}
/**
- * Scroll the contents of the view down by half the page size
- * @param bottom true to jump to bottom of page
- * @return true if the page was scrolled
+ * See {@link WebView#pageDown(boolean)}
*/
+ @Override
public boolean pageDown(boolean bottom) {
checkThread();
if (mNativeClass == 0) {
@@ -2853,9 +2733,9 @@ public final class WebViewClassic implements WebViewProvider, WebViewProvider.Sc
}
/**
- * Clear the view so that onDraw() will draw nothing but white background,
- * and onMeasure() will return 0 if MeasureSpec is not MeasureSpec.EXACTLY
+ * See {@link WebView#clearView()}
*/
+ @Override
public void clearView() {
checkThread();
mContentWidth = 0;
@@ -2865,14 +2745,9 @@ public final class WebViewClassic implements WebViewProvider, WebViewProvider.Sc
}
/**
- * Return a new picture that captures the current display of the webview.
- * This is a copy of the display, and will be unaffected if the webview
- * later loads a different URL.
- *
- * @return a picture containing the current contents of the view. Note this
- * picture is of the entire document, and is not restricted to the
- * bounds of the view.
+ * See {@link WebView#capturePicture()}
*/
+ @Override
public Picture capturePicture() {
checkThread();
if (mNativeClass == 0) return null;
@@ -2882,9 +2757,9 @@ public final class WebViewClassic implements WebViewProvider, WebViewProvider.Sc
}
/**
- * Return the current scale of the WebView
- * @return The current scale.
+ * See {@link WebView#getScale()}
*/
+ @Override
public float getScale() {
checkThread();
return mZoomManager.getScale();
@@ -2900,25 +2775,18 @@ public final class WebViewClassic implements WebViewProvider, WebViewProvider.Sc
}
/**
- * Set the initial scale for the WebView. 0 means default. If
- * {@link WebSettings#getUseWideViewPort()} is true, it zooms out all the
- * way. Otherwise it starts with 100%. If initial scale is greater than 0,
- * WebView starts with this value as initial scale.
- * Please note that unlike the scale properties in the viewport meta tag,
- * this method doesn't take the screen density into account.
- *
- * @param scaleInPercent The initial scale in percent.
+ * See {@link WebView#setInitialScale(int)}
*/
+ @Override
public void setInitialScale(int scaleInPercent) {
checkThread();
mZoomManager.setInitialScaleInPercent(scaleInPercent);
}
/**
- * Invoke the graphical zoom picker widget for this WebView. This will
- * result in the zoom widget appearing on the screen to control the zoom
- * level of this WebView.
+ * See {@link WebView#invokeZoomPicker()}
*/
+ @Override
public void invokeZoomPicker() {
checkThread();
if (!getSettings().supportZoom()) {
@@ -2930,23 +2798,9 @@ public final class WebViewClassic implements WebViewProvider, WebViewProvider.Sc
}
/**
- * Return a HitTestResult based on the current cursor node. If a HTML::a tag
- * is found and the anchor has a non-JavaScript url, the HitTestResult type
- * is set to SRC_ANCHOR_TYPE and the url is set in the "extra" field. If the
- * anchor does not have a url or if it is a JavaScript url, the type will
- * be UNKNOWN_TYPE and the url has to be retrieved through
- * {@link #requestFocusNodeHref} asynchronously. If a HTML::img tag is
- * found, the HitTestResult type is set to IMAGE_TYPE and the url is set in
- * the "extra" field. A type of
- * SRC_IMAGE_ANCHOR_TYPE indicates an anchor with a url that has an image as
- * a child node. If a phone number is found, the HitTestResult type is set
- * to PHONE_TYPE and the phone number is set in the "extra" field of
- * HitTestResult. If a map address is found, the HitTestResult type is set
- * to GEO_TYPE and the address is set in the "extra" field of HitTestResult.
- * If an email address is found, the HitTestResult type is set to EMAIL_TYPE
- * and the email is set in the "extra" field of HitTestResult. Otherwise,
- * HitTestResult type is set to UNKNOWN_TYPE.
+ * See {@link WebView#getHitTestResult()}
*/
+ @Override
public HitTestResult getHitTestResult() {
checkThread();
return mInitialHitTestResult;
@@ -2980,19 +2834,9 @@ public final class WebViewClassic implements WebViewProvider, WebViewProvider.Sc
}
/**
- * Request the anchor or image element URL at the last tapped point.
- * If hrefMsg is null, this method returns immediately and does not
- * dispatch hrefMsg to its target. If the tapped point hits an image,
- * an anchor, or an image in an anchor, the message associates
- * strings in named keys in its data. The value paired with the key
- * may be an empty string.
- *
- * @param hrefMsg This message will be dispatched with the result of the
- * request. The message data contains three keys:
- * - "url" returns the anchor's href attribute.
- * - "title" returns the anchor's text.
- * - "src" returns the image's src attribute.
+ * See {@link WebView#requestFocusNodeHref(Message)}
*/
+ @Override
public void requestFocusNodeHref(Message hrefMsg) {
checkThread();
if (hrefMsg == null) {
@@ -3013,12 +2857,9 @@ public final class WebViewClassic implements WebViewProvider, WebViewProvider.Sc
}
/**
- * Request the url of the image last touched by the user. msg will be sent
- * to its target with a String representing the url as its object.
- *
- * @param msg This message will be dispatched with the result of the request
- * as the data member with "url" as key. The result can be null.
+ * See {@link WebView#requestImageRef(Message)}
*/
+ @Override
public void requestImageRef(Message msg) {
checkThread();
if (0 == mNativeClass) return; // client isn't initialized
@@ -3521,11 +3362,9 @@ public final class WebViewClassic implements WebViewProvider, WebViewProvider.Sc
}
/**
- * Get the url for the current page. This is not always the same as the url
- * passed to WebViewClient.onPageStarted because although the load for
- * that url has begun, the current page may not have changed.
- * @return The url for the current page.
+ * See {@link WebView#getUrl()}
*/
+ @Override
public String getUrl() {
checkThread();
WebHistoryItem h = mCallbackProxy.getBackForwardList().getCurrentItem();
@@ -3533,13 +3372,9 @@ public final class WebViewClassic implements WebViewProvider, WebViewProvider.Sc
}
/**
- * Get the original url for the current page. This is not always the same
- * as the url passed to WebViewClient.onPageStarted because although the
- * load for that url has begun, the current page may not have changed.
- * Also, there may have been redirects resulting in a different url to that
- * originally requested.
- * @return The url that was originally requested for the current page.
+ * See {@link WebView#getOriginalUrl()}
*/
+ @Override
public String getOriginalUrl() {
checkThread();
WebHistoryItem h = mCallbackProxy.getBackForwardList().getCurrentItem();
@@ -3547,10 +3382,9 @@ public final class WebViewClassic implements WebViewProvider, WebViewProvider.Sc
}
/**
- * Get the title for the current page. This is the title of the current page
- * until WebViewClient.onReceivedTitle is called.
- * @return The title for the current page.
+ * See {@link WebView#getTitle()}
*/
+ @Override
public String getTitle() {
checkThread();
WebHistoryItem h = mCallbackProxy.getBackForwardList().getCurrentItem();
@@ -3558,10 +3392,9 @@ public final class WebViewClassic implements WebViewProvider, WebViewProvider.Sc
}
/**
- * Get the favicon for the current page. This is the favicon of the current
- * page until WebViewClient.onReceivedIcon is called.
- * @return The favicon for the current page.
+ * See {@link WebView#getFavicon()}
*/
+ @Override
public Bitmap getFavicon() {
checkThread();
WebHistoryItem h = mCallbackProxy.getBackForwardList().getCurrentItem();
@@ -3569,37 +3402,36 @@ public final class WebViewClassic implements WebViewProvider, WebViewProvider.Sc
}
/**
- * Get the touch icon url for the apple-touch-icon <link> element, or
- * a URL on this site's server pointing to the standard location of a
- * touch icon.
- * @hide
+ * See {@link WebView#getTouchIconUrl()}
*/
+ @Override
public String getTouchIconUrl() {
WebHistoryItem h = mCallbackProxy.getBackForwardList().getCurrentItem();
return h != null ? h.getTouchIconUrl() : null;
}
/**
- * Get the progress for the current page.
- * @return The progress for the current page between 0 and 100.
+ * See {@link WebView#getProgress()}
*/
+ @Override
public int getProgress() {
checkThread();
return mCallbackProxy.getProgress();
}
/**
- * @return the height of the HTML content.
+ * See {@link WebView#getContentHeight()}
*/
+ @Override
public int getContentHeight() {
checkThread();
return mContentHeight;
}
/**
- * @return the width of the HTML content.
- * @hide
+ * See {@link WebView#getContentWidth()}
*/
+ @Override
public int getContentWidth() {
return mContentWidth;
}
@@ -3612,32 +3444,27 @@ public final class WebViewClassic implements WebViewProvider, WebViewProvider.Sc
}
/**
- * Pause all layout, parsing, and JavaScript timers for all webviews. This
- * is a global requests, not restricted to just this webview. This can be
- * useful if the application has been paused.
+ * See {@link WebView#pauseTimers()}
*/
+ @Override
public void pauseTimers() {
checkThread();
mWebViewCore.sendMessage(EventHub.PAUSE_TIMERS);
}
/**
- * Resume all layout, parsing, and JavaScript timers for all webviews.
- * This will resume dispatching all timers.
+ * See {@link WebView#resumeTimers()}
*/
+ @Override
public void resumeTimers() {
checkThread();
mWebViewCore.sendMessage(EventHub.RESUME_TIMERS);
}
/**
- * Call this to pause any extra processing associated with this WebView and
- * its associated DOM, plugins, JavaScript etc. For example, if the WebView
- * is taken offscreen, this could be called to reduce unnecessary CPU or
- * network traffic. When the WebView is again "active", call onResume().
- *
- * Note that this differs from pauseTimers(), which affects all WebViews.
+ * See {@link WebView#onPause()}
*/
+ @Override
public void onPause() {
checkThread();
if (!mIsPaused) {
@@ -3674,8 +3501,9 @@ public final class WebViewClassic implements WebViewProvider, WebViewProvider.Sc
}
/**
- * Call this to resume a WebView after a previous call to onPause().
+ * See {@link WebView#onResume()}
*/
+ @Override
public void onResume() {
checkThread();
if (mIsPaused) {
@@ -3697,29 +3525,26 @@ public final class WebViewClassic implements WebViewProvider, WebViewProvider.Sc
}
/**
- * Returns true if the view is paused, meaning onPause() was called. Calling
- * onResume() sets the paused state back to false.
- * @hide
+ * See {@link WebView#isPaused()}
*/
+ @Override
public boolean isPaused() {
return mIsPaused;
}
/**
- * Call this to inform the view that memory is low so that it can
- * free any available memory.
+ * See {@link WebView#freeMemory()}
*/
+ @Override
public void freeMemory() {
checkThread();
mWebViewCore.sendMessage(EventHub.FREE_MEMORY);
}
/**
- * Clear the resource cache. Note that the cache is per-application, so
- * this will clear the cache for all WebViews used.
- *
- * @param includeDiskFiles If false, only the RAM cache is cleared.
+ * See {@link WebView#clearCache(boolean)}
*/
+ @Override
public void clearCache(boolean includeDiskFiles) {
checkThread();
// Note: this really needs to be a static method as it clears cache for all
@@ -3730,17 +3555,18 @@ public final class WebViewClassic implements WebViewProvider, WebViewProvider.Sc
}
/**
- * Make sure that clearing the form data removes the adapter from the
- * currently focused textfield if there is one.
+ * See {@link WebView#clearFormData()}
*/
+ @Override
public void clearFormData() {
checkThread();
// TODO: Implement b/6083041
}
/**
- * Tell the WebView to clear its internal back/forward list.
+ * See {@link WebView#clearHistory()}
*/
+ @Override
public void clearHistory() {
checkThread();
mCallbackProxy.getBackForwardList().setClearPending();
@@ -3748,46 +3574,37 @@ public final class WebViewClassic implements WebViewProvider, WebViewProvider.Sc
}
/**
- * Clear the SSL preferences table stored in response to proceeding with SSL
- * certificate errors.
+ * See {@link WebView#clearSslPreferences()}
*/
+ @Override
public void clearSslPreferences() {
checkThread();
mWebViewCore.sendMessage(EventHub.CLEAR_SSL_PREF_TABLE);
}
/**
- * Return the WebBackForwardList for this WebView. This contains the
- * back/forward list for use in querying each item in the history stack.
- * This is a copy of the private WebBackForwardList so it contains only a
- * snapshot of the current state. Multiple calls to this method may return
- * different objects. The object returned from this method will not be
- * updated to reflect any new state.
+ * See {@link WebView#copyBackForwardList()}
*/
+ @Override
public WebBackForwardList copyBackForwardList() {
checkThread();
return mCallbackProxy.getBackForwardList().clone();
}
- /*
- * Highlight and scroll to the next occurance of String in findAll.
- * Wraps the page infinitely, and scrolls. Must be called after
- * calling findAll.
- *
- * @param forward Direction to search.
+ /**
+ * See {@link WebView#findNext(boolean)}
*/
+ @Override
public void findNext(boolean forward) {
checkThread();
if (0 == mNativeClass) return; // client isn't initialized
mWebViewCore.sendMessage(EventHub.FIND_NEXT, forward ? 1 : 0);
}
- /*
- * Find all instances of find on the page and highlight them.
- * @param find String to find.
- * @return int The number of occurances of the String "find"
- * that were found.
+ /**
+ * See {@link WebView#findAll(String)}
*/
+ @Override
public int findAll(String find) {
return findAllBody(find, false);
}
@@ -3935,9 +3752,10 @@ public final class WebViewClassic implements WebViewProvider, WebViewProvider.Sc
return WebViewCore.nativeFindAddress(addr, caseInsensitive);
}
- /*
- * Clear the highlighting surrounding text matches created by findAll.
+ /**
+ * See {@link WebView#clearMatches()}
*/
+ @Override
public void clearMatches() {
checkThread();
if (mNativeClass == 0)
@@ -3965,11 +3783,9 @@ public final class WebViewClassic implements WebViewProvider, WebViewProvider.Sc
}
/**
- * Query the document to see if it contains any image references. The
- * message object will be dispatched with arg1 being set to 1 if images
- * were found and 0 if the document does not reference any images.
- * @param response The message that will be dispatched with the result.
+ * See {@link WebView#documentHasImages(Message)}
*/
+ @Override
public void documentHasImages(Message response) {
checkThread();
if (response == null) {
@@ -4081,6 +3897,7 @@ public final class WebViewClassic implements WebViewProvider, WebViewProvider.Sc
// helper to pin the scrollTo parameters (already in view coordinates)
// returns true if the scroll was changed
private boolean pinScrollTo(int x, int y, boolean animate, int animationDuration) {
+ abortAnimation();
x = pinLocX(x);
y = pinLocY(y);
int dx = x - getScrollX();
@@ -4089,7 +3906,6 @@ public final class WebViewClassic implements WebViewProvider, WebViewProvider.Sc
if ((dx | dy) == 0) {
return false;
}
- abortAnimation();
if (animate) {
// Log.d(LOGTAG, "startScroll: " + dx + " " + dy);
mScroller.startScroll(getScrollX(), getScrollY(), dx, dy,
@@ -4361,9 +4177,9 @@ public final class WebViewClassic implements WebViewProvider, WebViewProvider.Sc
// is used in the view system.
return;
}
- int vx = contentToViewX(cx);
- int vy = contentToViewY(cy);
- pinScrollTo(vx, vy, true, 0);
+ int vx = contentToViewDimension(cx - mScrollOffset.x);
+ int vy = contentToViewDimension(cy - mScrollOffset.y);
+ pinScrollBy(vx, vy, true, 0);
}
/**
@@ -4396,10 +4212,9 @@ public final class WebViewClassic implements WebViewProvider, WebViewProvider.Sc
}
/**
- * Set the WebViewClient that will receive various notifications and
- * requests. This will replace the current handler.
- * @param client An implementation of WebViewClient.
+ * See {@link WebView#setWebViewClient(WebViewClient)}
*/
+ @Override
public void setWebViewClient(WebViewClient client) {
checkThread();
mCallbackProxy.setWebViewClient(client);
@@ -4416,22 +4231,18 @@ public final class WebViewClassic implements WebViewProvider, WebViewProvider.Sc
}
/**
- * Register the interface to be used when content can not be handled by
- * the rendering engine, and should be downloaded instead. This will replace
- * the current handler.
- * @param listener An implementation of DownloadListener.
+ * See {@link WebView#setDownloadListener(DownloadListener)}
*/
+ @Override
public void setDownloadListener(DownloadListener listener) {
checkThread();
mCallbackProxy.setDownloadListener(listener);
}
/**
- * Set the chrome handler. This is an implementation of WebChromeClient for
- * use in handling JavaScript dialogs, favicons, titles, and the progress.
- * This will replace the current handler.
- * @param client An implementation of WebChromeClient.
+ * See {@link WebView#setWebChromeClient(WebChromeClient)}
*/
+ @Override
public void setWebChromeClient(WebChromeClient client) {
checkThread();
mCallbackProxy.setWebChromeClient(client);
@@ -4467,11 +4278,9 @@ public final class WebViewClassic implements WebViewProvider, WebViewProvider.Sc
}
/**
- * Set the Picture listener. This is an interface used to receive
- * notifications of a new Picture.
- * @param listener An implementation of WebView.PictureListener.
- * @deprecated This method is now obsolete.
+ * See {@link WebView#setPictureListener(PictureListener)}
*/
+ @Override
@Deprecated
public void setPictureListener(PictureListener listener) {
checkThread();
@@ -4495,31 +4304,9 @@ public final class WebViewClassic implements WebViewProvider, WebViewProvider.Sc
}
/**
- * This method injects the supplied Java object into the WebView. The
- * object is injected into the JavaScript context of the main frame, using
- * the supplied name. This allows the Java object to be accessed from
- * JavaScript. Note that that injected objects will not appear in
- * JavaScript until the page is next (re)loaded. For example:
- * <pre> webView.addJavascriptInterface(new Object(), "injectedObject");
- * webView.loadData("<!DOCTYPE html><title></title>", "text/html", null);
- * webView.loadUrl("javascript:alert(injectedObject.toString())");</pre>
- * <p><strong>IMPORTANT:</strong>
- * <ul>
- * <li> addJavascriptInterface() can be used to allow JavaScript to control
- * the host application. This is a powerful feature, but also presents a
- * security risk. Use of this method in a WebView containing untrusted
- * content could allow an attacker to manipulate the host application in
- * unintended ways, executing Java code with the permissions of the host
- * application. Use extreme care when using this method in a WebView which
- * could contain untrusted content.
- * <li> JavaScript interacts with Java object on a private, background
- * thread of the WebView. Care is therefore required to maintain thread
- * safety.</li>
- * </ul></p>
- * @param object The Java object to inject into the WebView's JavaScript
- * context. Null values are ignored.
- * @param name The name used to expose the instance in JavaScript.
+ * See {@link WebView#addJavascriptInterface(Object, String)}
*/
+ @Override
public void addJavascriptInterface(Object object, String name) {
checkThread();
if (object == null) {
@@ -4532,9 +4319,9 @@ public final class WebViewClassic implements WebViewProvider, WebViewProvider.Sc
}
/**
- * Removes a previously added JavaScript interface with the given name.
- * @param interfaceName The name of the interface to remove.
+ * See {@link WebView#removeJavascriptInterface(String)}
*/
+ @Override
public void removeJavascriptInterface(String interfaceName) {
checkThread();
if (mWebViewCore != null) {
@@ -4545,33 +4332,28 @@ public final class WebViewClassic implements WebViewProvider, WebViewProvider.Sc
}
/**
- * Return the WebSettings object used to control the settings for this
- * WebView.
- * @return A WebSettings object that can be used to control this WebView's
- * settings.
+ * See {@link WebView#getSettings()}
+ * Note this returns WebSettingsClassic, a sub-class of WebSettings, which can be used
+ * to access extension APIs.
*/
+ @Override
public WebSettingsClassic getSettings() {
checkThread();
return (mWebViewCore != null) ? mWebViewCore.getSettings() : null;
}
- /**
- * Return the list of currently loaded plugins.
- * @return The list of currently loaded plugins.
- *
- * @hide
- * @deprecated This was used for Gears, which has been deprecated.
- */
+ /**
+ * See {@link WebView#getPluginList()}
+ */
@Deprecated
public static synchronized PluginList getPluginList() {
checkThread();
return new PluginList();
}
- /**
- * @hide
- * @deprecated This was used for Gears, which has been deprecated.
- */
+ /**
+ * See {@link WebView#refreshPlugins(boolean)}
+ */
@Deprecated
public void refreshPlugins(boolean reloadOpenPages) {
checkThread();
@@ -4925,9 +4707,7 @@ public final class WebViewClassic implements WebViewProvider, WebViewProvider.Sc
if (layer == 0 || isPictureAfterFirstLayout) {
mWebViewCore.resumeWebKitDraw();
} else if (queueFull) {
- // temporarily disable webkit draw throttling
- // TODO: re-enable
- // mWebViewCore.pauseWebKitDraw();
+ mWebViewCore.pauseWebKitDraw();
}
if (mHTML5VideoViewProxy != null) {
@@ -5309,8 +5089,6 @@ public final class WebViewClassic implements WebViewProvider, WebViewProvider.Sc
if (keyCode >= KeyEvent.KEYCODE_DPAD_UP
&& keyCode <= KeyEvent.KEYCODE_DPAD_RIGHT) {
switchOutDrawHistory();
- letPageHandleNavKey(keyCode, event.getEventTime(), true, event.getMetaState());
- return true;
}
if (isEnterActionKey(keyCode)) {
@@ -5342,7 +5120,7 @@ public final class WebViewClassic implements WebViewProvider, WebViewProvider.Sc
}
// pass the key to DOM
- mWebViewCore.sendMessage(EventHub.KEY_DOWN, event);
+ sendKeyEvent(event);
// return true as DOM handles the key
return true;
}
@@ -5405,12 +5183,6 @@ public final class WebViewClassic implements WebViewProvider, WebViewProvider.Sc
}
}
- if (keyCode >= KeyEvent.KEYCODE_DPAD_UP
- && keyCode <= KeyEvent.KEYCODE_DPAD_RIGHT) {
- letPageHandleNavKey(keyCode, event.getEventTime(), false, event.getMetaState());
- return true;
- }
-
if (isEnterActionKey(keyCode)) {
// remove the long press message first
mPrivateHandler.removeMessages(LONG_PRESS_CENTER);
@@ -5424,7 +5196,7 @@ public final class WebViewClassic implements WebViewProvider, WebViewProvider.Sc
}
// pass the key to DOM
- mWebViewCore.sendMessage(EventHub.KEY_UP, event);
+ sendKeyEvent(event);
// return true as DOM handles the key
return true;
}
@@ -5511,10 +5283,9 @@ public final class WebViewClassic implements WebViewProvider, WebViewProvider.Sc
}
/**
- * Use this method to put the WebView into text selection mode.
- * Do not rely on this functionality; it will be deprecated in the future.
- * @deprecated This method is now obsolete.
+ * See {@link WebView#emulateShiftHeld()}
*/
+ @Override
@Deprecated
public void emulateShiftHeld() {
checkThread();
@@ -5757,6 +5528,9 @@ public final class WebViewClassic implements WebViewProvider, WebViewProvider.Sc
setFocusControllerActive(false);
mKeysPressed.clear();
}
+ if (!mTouchHighlightRegion.isEmpty()) {
+ mWebView.invalidate(mTouchHighlightRegion.getBounds());
+ }
}
void setGLRectViewport() {
@@ -5968,6 +5742,10 @@ public final class WebViewClassic implements WebViewProvider, WebViewProvider.Sc
return false;
}
+ if (!mWebView.isFocused()) {
+ mWebView.requestFocus();
+ }
+
if (DebugFlags.WEB_VIEW) {
Log.v(LOGTAG, ev + " at " + ev.getEventTime()
+ " mTouchMode=" + mTouchMode
@@ -6357,7 +6135,6 @@ public final class WebViewClassic implements WebViewProvider, WebViewProvider.Sc
break;
}
case MotionEvent.ACTION_UP: {
- if (!mWebView.isFocused()) mWebView.requestFocus();
// pass the touch events from UI thread to WebCore thread
if (shouldForwardTouchEvent()) {
TouchEventData ted = new TouchEventData();
@@ -6956,9 +6733,7 @@ public final class WebViewClassic implements WebViewProvider, WebViewProvider.Sc
case KeyEvent.KEYCODE_DPAD_LEFT:
return SoundEffectConstants.NAVIGATION_LEFT;
}
- throw new IllegalArgumentException("keyCode must be one of " +
- "{KEYCODE_DPAD_UP, KEYCODE_DPAD_RIGHT, KEYCODE_DPAD_DOWN, " +
- "KEYCODE_DPAD_LEFT}.");
+ return 0;
}
private void doTrackball(long time, int metaState) {
@@ -7181,18 +6956,9 @@ public final class WebViewClassic implements WebViewProvider, WebViewProvider.Sc
}
/**
- * Returns a view containing zoom controls i.e. +/- buttons. The caller is
- * in charge of installing this view to the view hierarchy. This view will
- * become visible when the user starts scrolling via touch and fade away if
- * the user does not interact with it.
- * <p/>
- * API version 3 introduces a built-in zoom mechanism that is shown
- * automatically by the MapView. This is the preferred approach for
- * showing the zoom UI.
- *
- * @deprecated The built-in zoom mechanism is preferred, see
- * {@link WebSettings#setBuiltInZoomControls(boolean)}.
+ * See {@link WebView#getZoomControls()}
*/
+ @Override
@Deprecated
public View getZoomControls() {
checkThread();
@@ -7220,34 +6986,36 @@ public final class WebViewClassic implements WebViewProvider, WebViewProvider.Sc
}
/**
- * @return TRUE if the WebView can be zoomed in.
+ * See {@link WebView#canZoomIn()}
*/
+ @Override
public boolean canZoomIn() {
checkThread();
return mZoomManager.canZoomIn();
}
/**
- * @return TRUE if the WebView can be zoomed out.
+ * See {@link WebView#canZoomOut()}
*/
+ @Override
public boolean canZoomOut() {
checkThread();
return mZoomManager.canZoomOut();
}
/**
- * Perform zoom in in the webview
- * @return TRUE if zoom in succeeds. FALSE if no zoom changes.
+ * See {@link WebView#zoomIn()}
*/
+ @Override
public boolean zoomIn() {
checkThread();
return mZoomManager.zoomIn();
}
/**
- * Perform zoom out in the webview
- * @return TRUE if zoom out succeeds. FALSE if no zoom changes.
+ * See {@link WebView#zoomOut()}
*/
+ @Override
public boolean zoomOut() {
checkThread();
return mZoomManager.zoomOut();
@@ -7471,7 +7239,6 @@ public final class WebViewClassic implements WebViewProvider, WebViewProvider.Sc
viewToContentX(getScrollX() + getWidth()
- mWebView.getVerticalScrollbarWidth()),
viewToContentY(getScrollY() + getViewHeightWithTitle()));
- content = nativeSubtractLayers(content);
int screenTop = contentToViewY(content.top);
int screenBottom = contentToViewY(content.bottom);
int height = screenBottom - screenTop;
@@ -8232,8 +7999,12 @@ public final class WebViewClassic implements WebViewProvider, WebViewProvider.Sc
case FORM_DID_BLUR:
// TODO: Figure out if this is needed for something (b/6111763)
break;
- case UNHANDLED_NAV_KEY:
- // TODO: Support this (b/6109044)
+ case TAKE_FOCUS:
+ int direction = msg.arg1;
+ View focusSearch = mWebView.focusSearch(direction);
+ if (focusSearch != null && focusSearch != mWebView) {
+ focusSearch.requestFocus();
+ }
break;
case CLEAR_TEXT_ENTRY:
hideSoftKeyboard();
@@ -8527,9 +8298,9 @@ public final class WebViewClassic implements WebViewProvider, WebViewProvider.Sc
return false;
}
if (mFocusedNode.mHasFocus && !mWebView.isInTouchMode()) {
- return !mFocusedNode.mEditable;
+ return mDrawCursorRing && !mFocusedNode.mEditable;
}
- if (mInitialHitTestResult.getType() == HitTestResult.UNKNOWN_TYPE) {
+ if (mFocusedNode.mHasFocus && mFocusedNode.mEditable) {
return false;
}
long delay = System.currentTimeMillis() - mTouchHighlightRequested;
@@ -9129,14 +8900,10 @@ public final class WebViewClassic implements WebViewProvider, WebViewProvider.Sc
*/
private void letPageHandleNavKey(int keyCode, long time, boolean down, int metaState) {
int keyEventAction;
- int eventHubAction;
if (down) {
keyEventAction = KeyEvent.ACTION_DOWN;
- eventHubAction = EventHub.KEY_DOWN;
- mWebView.playSoundEffect(keyCodeToSoundsEffect(keyCode));
} else {
keyEventAction = KeyEvent.ACTION_UP;
- eventHubAction = EventHub.KEY_UP;
}
KeyEvent event = new KeyEvent(time, time, keyEventAction, keyCode,
@@ -9144,7 +8911,41 @@ public final class WebViewClassic implements WebViewProvider, WebViewProvider.Sc
| (metaState & KeyEvent.META_ALT_ON)
| (metaState & KeyEvent.META_SYM_ON)
, KeyCharacterMap.VIRTUAL_KEYBOARD, 0, 0);
- mWebViewCore.sendMessage(eventHubAction, event);
+ sendKeyEvent(event);
+ }
+
+ private void sendKeyEvent(KeyEvent event) {
+ int direction = 0;
+ switch (event.getKeyCode()) {
+ case KeyEvent.KEYCODE_DPAD_DOWN:
+ direction = View.FOCUS_DOWN;
+ break;
+ case KeyEvent.KEYCODE_DPAD_UP:
+ direction = View.FOCUS_UP;
+ break;
+ case KeyEvent.KEYCODE_DPAD_LEFT:
+ direction = View.FOCUS_LEFT;
+ break;
+ case KeyEvent.KEYCODE_DPAD_RIGHT:
+ direction = View.FOCUS_RIGHT;
+ break;
+ case KeyEvent.KEYCODE_TAB:
+ direction = event.isShiftPressed() ? View.FOCUS_BACKWARD : View.FOCUS_FORWARD;
+ break;
+ }
+ if (direction != 0 && mWebView.focusSearch(direction) == null) {
+ // Can't take focus in that direction
+ direction = 0;
+ }
+ int eventHubAction = EventHub.KEY_UP;
+ if (event.getAction() == KeyEvent.ACTION_DOWN) {
+ eventHubAction = EventHub.KEY_DOWN;
+ int sound = keyCodeToSoundsEffect(event.getKeyCode());
+ if (sound != 0) {
+ mWebView.playSoundEffect(sound);
+ }
+ }
+ mWebViewCore.sendMessage(eventHubAction, direction, event);
}
/**
@@ -9158,9 +8959,7 @@ public final class WebViewClassic implements WebViewProvider, WebViewProvider.Sc
}
/**
- * Set the background color. It's white by default. Pass
- * zero to make the view transparent.
- * @param color the ARGB color described by Color.java
+ * See {@link WebView#setBackgroundColor(int)}
*/
@Override
public void setBackgroundColor(int color) {
@@ -9169,8 +8968,9 @@ public final class WebViewClassic implements WebViewProvider, WebViewProvider.Sc
}
/**
- * @deprecated This method is now obsolete.
+ * See {@link WebView#debugDump()}
*/
+ @Override
@Deprecated
public void debugDump() {
}
@@ -9334,7 +9134,6 @@ public final class WebViewClassic implements WebViewProvider, WebViewProvider.Sc
private native void nativeCopyBaseContentToPicture(Picture pict);
private native boolean nativeHasContent();
private native void nativeStopGL();
- private native Rect nativeSubtractLayers(Rect content);
private native void nativeDiscardAllTextures();
private native void nativeTileProfilingStart();
private native float nativeTileProfilingStop();
diff --git a/core/java/android/webkit/WebViewClient.java b/core/java/android/webkit/WebViewClient.java
index 81de356..0c34037 100644
--- a/core/java/android/webkit/WebViewClient.java
+++ b/core/java/android/webkit/WebViewClient.java
@@ -204,7 +204,7 @@ public class WebViewClient {
/**
* Notify the host application that an SSL error occurred while loading a
- * resource, but the WebView but chose to proceed anyway based on a
+ * resource, but the WebView chose to proceed anyway based on a
* decision retained from a previous response to onReceivedSslError().
* @hide
*/
@@ -220,7 +220,7 @@ public class WebViewClient {
* default behavior is to cancel, returning no client certificate.
*
* @param view The WebView that is initiating the callback.
- * @param handler An ClientCertRequestHandler object that will
+ * @param handler A ClientCertRequestHandler object that will
* handle the user's response.
* @param host_and_port The host and port of the requesting server.
*
@@ -266,7 +266,7 @@ public class WebViewClient {
* Notify the host application that a key was not handled by the WebView.
* Except system keys, WebView always consumes the keys in the normal flow
* or if shouldOverrideKeyEvent returns true. This is called asynchronously
- * from where the key is dispatched. It gives the host application an chance
+ * from where the key is dispatched. It gives the host application a chance
* to handle the unhandled key events.
*
* @param view The WebView that is initiating the callback.
diff --git a/core/java/android/webkit/WebViewCore.java b/core/java/android/webkit/WebViewCore.java
index 65356f5..de30755 100644
--- a/core/java/android/webkit/WebViewCore.java
+++ b/core/java/android/webkit/WebViewCore.java
@@ -139,6 +139,8 @@ public final class WebViewCore {
private int mHighMemoryUsageThresholdMb;
private int mHighUsageDeltaMb;
+ private int mChromeCanFocusDirection;
+
// The thread name used to identify the WebCore thread and for use in
// debugging other classes that require operation within the WebCore thread.
/* package */ static final String THREAD_NAME = "WebViewCoreThread";
@@ -344,6 +346,58 @@ public final class WebViewCore {
}
/**
+ * Called by JNI to advance focus to the next view.
+ */
+ private void chromeTakeFocus(int webkitDirection) {
+ if (mWebView == null) return;
+ Message m = mWebView.mPrivateHandler.obtainMessage(
+ WebViewClassic.TAKE_FOCUS);
+ m.arg1 = mapDirection(webkitDirection);
+ m.sendToTarget();
+ }
+
+ /**
+ * Called by JNI to see if we can take focus in the given direction.
+ */
+ private boolean chromeCanTakeFocus(int webkitDirection) {
+ int direction = mapDirection(webkitDirection);
+ return direction == mChromeCanFocusDirection && direction != 0;
+ }
+
+ /**
+ * Maps a Webkit focus direction to a framework one
+ */
+ private int mapDirection(int webkitDirection) {
+ /*
+ * This is WebKit's FocusDirection enum (from FocusDirection.h)
+ enum FocusDirection {
+ FocusDirectionNone = 0,
+ FocusDirectionForward,
+ FocusDirectionBackward,
+ FocusDirectionUp,
+ FocusDirectionDown,
+ FocusDirectionLeft,
+ FocusDirectionRight
+ };
+ */
+ switch (webkitDirection) {
+ case 1:
+ return View.FOCUS_FORWARD;
+ case 2:
+ return View.FOCUS_BACKWARD;
+ case 3:
+ return View.FOCUS_UP;
+ case 4:
+ return View.FOCUS_DOWN;
+ case 5:
+ return View.FOCUS_LEFT;
+ case 6:
+ return View.FOCUS_RIGHT;
+ }
+ return 0;
+ }
+
+ /**
* Called by JNI. Open a file chooser to upload a file.
* @param acceptType The value of the 'accept' attribute of the
* input tag associated with this file picker.
@@ -1190,6 +1244,23 @@ public final class WebViewCore {
+ " arg1=" + msg.arg1 + " arg2=" + msg.arg2
+ " obj=" + msg.obj);
}
+ switch (msg.what) {
+ case PAUSE_TIMERS:
+ mSavedPriority = Process.getThreadPriority(mTid);
+ Process.setThreadPriority(mTid,
+ Process.THREAD_PRIORITY_BACKGROUND);
+ pauseTimers();
+ if (mNativeClass != 0) {
+ nativeCloseIdleConnections(mNativeClass);
+ }
+ return;
+
+ case RESUME_TIMERS:
+ Process.setThreadPriority(mTid, mSavedPriority);
+ resumeTimers();
+ return;
+ }
+
if (mWebView == null || mNativeClass == 0) {
if (DebugFlags.WEB_VIEW_CORE) {
Log.w(LOGTAG, "Rejecting message " + msg.what
@@ -1198,8 +1269,6 @@ public final class WebViewCore {
return;
}
if (mDestroying == true
- && msg.what != EventHub.RESUME_TIMERS
- && msg.what != EventHub.PAUSE_TIMERS
&& msg.what != EventHub.DESTROY) {
if (DebugFlags.WEB_VIEW_CORE) {
Log.v(LOGTAG, "Rejecting message " + msg.what
@@ -1311,11 +1380,11 @@ public final class WebViewCore {
break;
case KEY_DOWN:
- key((KeyEvent) msg.obj, true);
+ key((KeyEvent) msg.obj, msg.arg1, true);
break;
case KEY_UP:
- key((KeyEvent) msg.obj, false);
+ key((KeyEvent) msg.obj, msg.arg1, false);
break;
case KEY_PRESS:
@@ -1365,18 +1434,6 @@ public final class WebViewCore {
restoreState(msg.arg1);
break;
- case PAUSE_TIMERS:
- mSavedPriority = Process.getThreadPriority(mTid);
- Process.setThreadPriority(mTid,
- Process.THREAD_PRIORITY_BACKGROUND);
- pauseTimers();
- nativeCloseIdleConnections(mNativeClass);
- break;
-
- case RESUME_TIMERS:
- Process.setThreadPriority(mTid, mSavedPriority);
- resumeTimers();
- break;
case ON_PAUSE:
nativePause(mNativeClass);
@@ -1907,12 +1964,10 @@ public final class WebViewCore {
*/
void destroy() {
synchronized (mEventHub) {
- // Do not call removeMessages as then we risk removing PAUSE_TIMERS
- // or RESUME_TIMERS messages, which we must still handle as they
- // are per process. DESTROY will instead trigger a white list in
- // mEventHub, skipping any remaining messages in the queue
+ // send DESTROY to front of queue
+ // PAUSE/RESUME timers will still be processed even if they get handled later
mEventHub.mDestroying = true;
- mEventHub.sendMessage(
+ mEventHub.sendMessageAtFrontOfQueue(
Message.obtain(null, EventHub.DESTROY));
mEventHub.blockMessages();
}
@@ -1950,11 +2005,12 @@ public final class WebViewCore {
return mBrowserFrame.saveWebArchive(filename, autoname);
}
- private void key(KeyEvent evt, boolean isDown) {
+ private void key(KeyEvent evt, int canTakeFocusDirection, boolean isDown) {
if (DebugFlags.WEB_VIEW_CORE) {
Log.v(LOGTAG, "CORE key at " + System.currentTimeMillis() + ", "
+ evt);
}
+ mChromeCanFocusDirection = canTakeFocusDirection;
int keyCode = evt.getKeyCode();
int unicodeChar = evt.getUnicodeChar();
@@ -1964,18 +2020,18 @@ public final class WebViewCore {
unicodeChar = evt.getCharacters().codePointAt(0);
}
- if (!nativeKey(mNativeClass, keyCode, unicodeChar, evt.getRepeatCount(),
+ boolean handled = nativeKey(mNativeClass, keyCode, unicodeChar, evt.getRepeatCount(),
evt.isShiftPressed(), evt.isAltPressed(),
- evt.isSymPressed(), isDown) && keyCode != KeyEvent.KEYCODE_ENTER) {
+ evt.isSymPressed(), isDown);
+ mChromeCanFocusDirection = 0;
+ if (!handled && keyCode != KeyEvent.KEYCODE_ENTER) {
if (keyCode >= KeyEvent.KEYCODE_DPAD_UP
&& keyCode <= KeyEvent.KEYCODE_DPAD_RIGHT) {
- if (DebugFlags.WEB_VIEW_CORE) {
- Log.v(LOGTAG, "key: arrow unused by page: " + keyCode);
- }
- if (mWebView != null && evt.isDown()) {
- Message.obtain(mWebView.mPrivateHandler,
- WebViewClassic.UNHANDLED_NAV_KEY, keyCode,
- 0).sendToTarget();
+ if (canTakeFocusDirection != 0 && isDown) {
+ Message m = mWebView.mPrivateHandler.obtainMessage(
+ WebViewClassic.TAKE_FOCUS);
+ m.arg1 = canTakeFocusDirection;
+ m.sendToTarget();
}
return;
}
diff --git a/core/java/android/widget/CheckedTextView.java b/core/java/android/widget/CheckedTextView.java
index 603cea1..233d892 100644
--- a/core/java/android/widget/CheckedTextView.java
+++ b/core/java/android/widget/CheckedTextView.java
@@ -161,6 +161,12 @@ public class CheckedTextView extends TextView implements Checkable {
}
@Override
+ public void setPaddingRelative(int start, int top, int end, int bottom) {
+ super.setPaddingRelative(start, top, end, bottom);
+ mBasePadding = getPaddingEnd();
+ }
+
+ @Override
protected void onDraw(Canvas canvas) {
super.onDraw(canvas);
@@ -221,16 +227,6 @@ public class CheckedTextView extends TextView implements Checkable {
}
@Override
- public void onPopulateAccessibilityEvent(AccessibilityEvent event) {
- super.onPopulateAccessibilityEvent(event);
- if (isChecked()) {
- event.getText().add(mContext.getString(R.string.radiobutton_selected));
- } else {
- event.getText().add(mContext.getString(R.string.radiobutton_not_selected));
- }
- }
-
- @Override
public void onInitializeAccessibilityNodeInfo(AccessibilityNodeInfo info) {
super.onInitializeAccessibilityNodeInfo(info);
info.setClassName(CheckedTextView.class.getName());
diff --git a/core/java/android/widget/ExpandableListView.java b/core/java/android/widget/ExpandableListView.java
index badfaa7..c2d8bda 100644
--- a/core/java/android/widget/ExpandableListView.java
+++ b/core/java/android/widget/ExpandableListView.java
@@ -740,7 +740,7 @@ public class ExpandableListView extends ListView {
/**
* Converts a flat list position (the raw position of an item (child or group)
- * in the list) to an group and/or child position (represented in a
+ * in the list) to a group and/or child position (represented in a
* packed position). This is useful in situations where the caller needs to
* use the underlying {@link ListView}'s methods. Use
* {@link ExpandableListView#getPackedPositionType} ,
diff --git a/core/java/android/widget/HorizontalScrollView.java b/core/java/android/widget/HorizontalScrollView.java
index 0b4ebf4..0db6ef2 100644
--- a/core/java/android/widget/HorizontalScrollView.java
+++ b/core/java/android/widget/HorizontalScrollView.java
@@ -1389,7 +1389,7 @@ public class HorizontalScrollView extends FrameLayout {
}
mChildToScrollTo = null;
- // Calling this with the present values causes it to re-clam them
+ // Calling this with the present values causes it to re-claim them
scrollTo(mScrollX, mScrollY);
}
@@ -1412,7 +1412,7 @@ public class HorizontalScrollView extends FrameLayout {
}
/**
- * Return true if child is an descendant of parent, (or equal to the parent).
+ * Return true if child is a descendant of parent, (or equal to the parent).
*/
private boolean isViewDescendantOf(View child, View parent) {
if (child == parent) {
@@ -1427,7 +1427,7 @@ public class HorizontalScrollView extends FrameLayout {
* Fling the scroll view
*
* @param velocityX The initial velocity in the X direction. Positive
- * numbers mean that the finger/curor is moving down the screen,
+ * numbers mean that the finger/cursor is moving down the screen,
* which means we want to scroll towards the left.
*/
public void fling(int velocityX) {
diff --git a/core/java/android/widget/ImageView.java b/core/java/android/widget/ImageView.java
index 07ae93b..3001ea1 100644
--- a/core/java/android/widget/ImageView.java
+++ b/core/java/android/widget/ImageView.java
@@ -193,9 +193,6 @@ public class ImageView extends View {
}
}
- /**
- * @hide
- */
@Override
public int getResolvedLayoutDirection(Drawable dr) {
return (dr == mDrawable) ?
diff --git a/core/java/android/widget/ListPopupWindow.java b/core/java/android/widget/ListPopupWindow.java
index 5c97593..1d966b3 100644
--- a/core/java/android/widget/ListPopupWindow.java
+++ b/core/java/android/widget/ListPopupWindow.java
@@ -1017,7 +1017,7 @@ public class ListPopupWindow {
View hintView = mPromptView;
if (hintView != null) {
- // if an hint has been specified, we accomodate more space for it and
+ // if a hint has been specified, we accomodate more space for it and
// add a text view in the drop down menu, at the bottom of the list
LinearLayout hintContainer = new LinearLayout(context);
hintContainer.setOrientation(LinearLayout.VERTICAL);
@@ -1080,6 +1080,8 @@ public class ListPopupWindow {
if (!mDropDownVerticalOffsetSet) {
mDropDownVerticalOffset = -mTempRect.top;
}
+ } else {
+ mTempRect.setEmpty();
}
// Max height available on the screen for a popup.
@@ -1092,7 +1094,25 @@ public class ListPopupWindow {
return maxHeight + padding;
}
- final int listContent = mDropDownList.measureHeightOfChildren(MeasureSpec.UNSPECIFIED,
+ final int childWidthSpec;
+ switch (mDropDownWidth) {
+ case ViewGroup.LayoutParams.WRAP_CONTENT:
+ childWidthSpec = MeasureSpec.makeMeasureSpec(
+ mContext.getResources().getDisplayMetrics().widthPixels -
+ (mTempRect.left + mTempRect.right),
+ MeasureSpec.AT_MOST);
+ break;
+ case ViewGroup.LayoutParams.MATCH_PARENT:
+ childWidthSpec = MeasureSpec.makeMeasureSpec(
+ mContext.getResources().getDisplayMetrics().widthPixels -
+ (mTempRect.left + mTempRect.right),
+ MeasureSpec.EXACTLY);
+ break;
+ default:
+ childWidthSpec = MeasureSpec.makeMeasureSpec(mDropDownWidth, MeasureSpec.EXACTLY);
+ break;
+ }
+ final int listContent = mDropDownList.measureHeightOfChildren(childWidthSpec,
0, ListView.NO_POSITION, maxHeight - otherHeights, -1);
// add padding only if the list has items in it, that way we don't show
// the popup if it is not needed
diff --git a/core/java/android/widget/ProgressBar.java b/core/java/android/widget/ProgressBar.java
index e298acb..3bc4f7f 100644
--- a/core/java/android/widget/ProgressBar.java
+++ b/core/java/android/widget/ProgressBar.java
@@ -910,9 +910,6 @@ public class ProgressBar extends View {
}
}
- /**
- * @hide
- */
@Override
public int getResolvedLayoutDirection(Drawable who) {
return (who == mProgressDrawable || who == mIndeterminateDrawable) ?
diff --git a/core/java/android/widget/RadioButton.java b/core/java/android/widget/RadioButton.java
index b6dac3e..b1bb1c0 100644
--- a/core/java/android/widget/RadioButton.java
+++ b/core/java/android/widget/RadioButton.java
@@ -78,16 +78,6 @@ public class RadioButton extends CompoundButton {
}
@Override
- public void onPopulateAccessibilityEvent(AccessibilityEvent event) {
- super.onPopulateAccessibilityEvent(event);
- if (isChecked()) {
- event.getText().add(mContext.getString(R.string.radiobutton_selected));
- } else {
- event.getText().add(mContext.getString(R.string.radiobutton_not_selected));
- }
- }
-
- @Override
public void onInitializeAccessibilityEvent(AccessibilityEvent event) {
super.onInitializeAccessibilityEvent(event);
event.setClassName(RadioButton.class.getName());
diff --git a/core/java/android/widget/ScrollView.java b/core/java/android/widget/ScrollView.java
index 3ffc0fe..25dd438 100644
--- a/core/java/android/widget/ScrollView.java
+++ b/core/java/android/widget/ScrollView.java
@@ -1412,7 +1412,7 @@ public class ScrollView extends FrameLayout {
}
mChildToScrollTo = null;
- // Calling this with the present values causes it to re-clam them
+ // Calling this with the present values causes it to re-claim them
scrollTo(mScrollX, mScrollY);
}
@@ -1436,7 +1436,7 @@ public class ScrollView extends FrameLayout {
}
/**
- * Return true if child is an descendant of parent, (or equal to the parent).
+ * Return true if child is a descendant of parent, (or equal to the parent).
*/
private boolean isViewDescendantOf(View child, View parent) {
if (child == parent) {
diff --git a/core/java/android/widget/SimpleAdapter.java b/core/java/android/widget/SimpleAdapter.java
index 4b17a92..98bcfff 100644
--- a/core/java/android/widget/SimpleAdapter.java
+++ b/core/java/android/widget/SimpleAdapter.java
@@ -268,7 +268,7 @@ public class SimpleAdapter extends BaseAdapter implements Filterable {
/**
* Called by bindView() to set the text for a TextView but only if
* there is no existing ViewBinder or if the existing ViewBinder cannot
- * handle binding to an TextView.
+ * handle binding to a TextView.
*
* @param v TextView to receive text
* @param text the text to be set for the TextView
diff --git a/core/java/android/widget/SimpleCursorAdapter.java b/core/java/android/widget/SimpleCursorAdapter.java
index c5c6c69..f74a314 100644
--- a/core/java/android/widget/SimpleCursorAdapter.java
+++ b/core/java/android/widget/SimpleCursorAdapter.java
@@ -216,7 +216,7 @@ public class SimpleCursorAdapter extends ResourceCursorAdapter {
/**
* Called by bindView() to set the text for a TextView but only if
* there is no existing ViewBinder or if the existing ViewBinder cannot
- * handle binding to an TextView.
+ * handle binding to a TextView.
*
* Intended to be overridden by Adapters that need to filter strings
* retrieved from the database.
diff --git a/core/java/android/widget/SimpleCursorTreeAdapter.java b/core/java/android/widget/SimpleCursorTreeAdapter.java
index a033542..6babf3e 100644
--- a/core/java/android/widget/SimpleCursorTreeAdapter.java
+++ b/core/java/android/widget/SimpleCursorTreeAdapter.java
@@ -283,7 +283,7 @@ public abstract class SimpleCursorTreeAdapter extends ResourceCursorTreeAdapter
/**
* Called by bindView() to set the text for a TextView but only if
* there is no existing ViewBinder or if the existing ViewBinder cannot
- * handle binding to an TextView.
+ * handle binding to a TextView.
*
* Intended to be overridden by Adapters that need to filter strings
* retrieved from the database.
diff --git a/core/java/android/widget/SpellChecker.java b/core/java/android/widget/SpellChecker.java
index df2996c..9afaee3 100644
--- a/core/java/android/widget/SpellChecker.java
+++ b/core/java/android/widget/SpellChecker.java
@@ -290,7 +290,7 @@ public class SpellChecker implements SpellCheckerSessionListener {
private SpellCheckSpan onGetSuggestionsInternal(
SuggestionsInfo suggestionsInfo, int offset, int length) {
- if (suggestionsInfo.getCookie() != mCookie) {
+ if (suggestionsInfo == null || suggestionsInfo.getCookie() != mCookie) {
return null;
}
final Editable editable = (Editable) mTextView.getText();
@@ -335,9 +335,15 @@ public class SpellChecker implements SpellCheckerSessionListener {
for (int i = 0; i < results.length; ++i) {
final SentenceSuggestionsInfo ssi = results[i];
+ if (ssi == null) {
+ continue;
+ }
SpellCheckSpan spellCheckSpan = null;
for (int j = 0; j < ssi.getSuggestionsCount(); ++j) {
final SuggestionsInfo suggestionsInfo = ssi.getSuggestionsInfoAt(j);
+ if (suggestionsInfo == null) {
+ continue;
+ }
final int offset = ssi.getOffsetAt(j);
final int length = ssi.getLengthAt(j);
final SpellCheckSpan scs = onGetSuggestionsInternal(
@@ -488,11 +494,15 @@ public class SpellChecker implements SpellCheckerSessionListener {
editable.removeSpan(mRange);
return;
}
+ // Stop spell checking when there are no characters in the range.
+ if (wordEnd < start) {
+ return;
+ }
wordStart = regionEnd;
// TODO: Find the start position of the sentence.
// Set span with the context
- final int spellCheckStart = Math.min(
- start, Math.max(wordStart, regionEnd - WORD_ITERATOR_INTERVAL));
+ final int spellCheckStart = Math.max(
+ 0, Math.min(wordStart, regionEnd - WORD_ITERATOR_INTERVAL));
if (regionEnd <= spellCheckStart) {
return;
}
diff --git a/core/java/android/widget/Spinner.java b/core/java/android/widget/Spinner.java
index 89c506f..aef8a34 100644
--- a/core/java/android/widget/Spinner.java
+++ b/core/java/android/widget/Spinner.java
@@ -26,7 +26,7 @@ import android.database.DataSetObserver;
import android.graphics.Rect;
import android.graphics.drawable.Drawable;
import android.util.AttributeSet;
-import android.util.DisplayMetrics;
+import android.util.Log;
import android.view.Gravity;
import android.view.View;
import android.view.ViewGroup;
@@ -203,6 +203,130 @@ public class Spinner extends AbsSpinner implements OnClickListener {
}
}
+ /**
+ * Set the background drawable for the spinner's popup window of choices.
+ * Only valid in {@link #MODE_DROPDOWN}; this method is a no-op in other modes.
+ *
+ * @param background Background drawable
+ *
+ * @attr ref android.R.styleable#Spinner_popupBackground
+ */
+ public void setPopupBackgroundDrawable(Drawable background) {
+ if (!(mPopup instanceof DropdownPopup)) {
+ Log.e(TAG, "setPopupBackgroundDrawable: incompatible spinner mode; ignoring...");
+ return;
+ }
+ ((DropdownPopup) mPopup).setBackgroundDrawable(background);
+ }
+
+ /**
+ * Set the background drawable for the spinner's popup window of choices.
+ * Only valid in {@link #MODE_DROPDOWN}; this method is a no-op in other modes.
+ *
+ * @param resId Resource ID of a background drawable
+ *
+ * @attr ref android.R.styleable#Spinner_popupBackground
+ */
+ public void setPopupBackgroundResource(int resId) {
+ setPopupBackgroundDrawable(getContext().getResources().getDrawable(resId));
+ }
+
+ /**
+ * Get the background drawable for the spinner's popup window of choices.
+ * Only valid in {@link #MODE_DROPDOWN}; other modes will return null.
+ *
+ * @return background Background drawable
+ *
+ * @attr ref android.R.styleable#Spinner_popupBackground
+ */
+ public Drawable getPopupBackground() {
+ return mPopup.getBackground();
+ }
+
+ /**
+ * Set a vertical offset in pixels for the spinner's popup window of choices.
+ * Only valid in {@link #MODE_DROPDOWN}; this method is a no-op in other modes.
+ *
+ * @param pixels Vertical offset in pixels
+ *
+ * @attr ref android.R.styleable#Spinner_dropDownVerticalOffset
+ */
+ public void setDropDownVerticalOffset(int pixels) {
+ mPopup.setVerticalOffset(pixels);
+ }
+
+ /**
+ * Get the configured vertical offset in pixels for the spinner's popup window of choices.
+ * Only valid in {@link #MODE_DROPDOWN}; other modes will return 0.
+ *
+ * @return Vertical offset in pixels
+ *
+ * @attr ref android.R.styleable#Spinner_dropDownVerticalOffset
+ */
+ public int getDropDownVerticalOffset() {
+ return mPopup.getVerticalOffset();
+ }
+
+ /**
+ * Set a horizontal offset in pixels for the spinner's popup window of choices.
+ * Only valid in {@link #MODE_DROPDOWN}; this method is a no-op in other modes.
+ *
+ * @param pixels Horizontal offset in pixels
+ *
+ * @attr ref android.R.styleable#Spinner_dropDownHorizontalOffset
+ */
+ public void setDropDownHorizontalOffset(int pixels) {
+ mPopup.setHorizontalOffset(pixels);
+ }
+
+ /**
+ * Get the configured horizontal offset in pixels for the spinner's popup window of choices.
+ * Only valid in {@link #MODE_DROPDOWN}; other modes will return 0.
+ *
+ * @return Horizontal offset in pixels
+ *
+ * @attr ref android.R.styleable#Spinner_dropDownHorizontalOffset
+ */
+ public int getDropDownHorizontalOffset() {
+ return mPopup.getHorizontalOffset();
+ }
+
+ /**
+ * Set the width of the spinner's popup window of choices in pixels. This value
+ * may also be set to {@link android.view.ViewGroup.LayoutParams#MATCH_PARENT}
+ * to match the width of the Spinner itself, or
+ * {@link android.view.ViewGroup.LayoutParams#WRAP_CONTENT} to wrap to the measured size
+ * of contained dropdown list items.
+ *
+ * <p>Only valid in {@link #MODE_DROPDOWN}; this method is a no-op in other modes.</p>
+ *
+ * @param pixels Width in pixels, WRAP_CONTENT, or MATCH_PARENT
+ *
+ * @attr ref android.R.styleable#Spinner_dropDownWidth
+ */
+ public void setDropDownWidth(int pixels) {
+ if (!(mPopup instanceof DropdownPopup)) {
+ Log.e(TAG, "Cannot set dropdown width for MODE_DIALOG, ignoring");
+ return;
+ }
+ mDropDownWidth = pixels;
+ }
+
+ /**
+ * Get the configured width of the spinner's popup window of choices in pixels.
+ * The returned value may also be {@link android.view.ViewGroup.LayoutParams#MATCH_PARENT}
+ * meaning the popup window will match the width of the Spinner itself, or
+ * {@link android.view.ViewGroup.LayoutParams#WRAP_CONTENT} to wrap to the measured size
+ * of contained dropdown list items.
+ *
+ * @return Width in pixels, WRAP_CONTENT, or MATCH_PARENT
+ *
+ * @attr ref android.R.styleable#Spinner_dropDownWidth
+ */
+ public int getDropDownWidth() {
+ return mDropDownWidth;
+ }
+
@Override
public void setEnabled(boolean enabled) {
super.setEnabled(enabled);
@@ -232,6 +356,16 @@ public class Spinner extends AbsSpinner implements OnClickListener {
}
}
+ /**
+ * Describes how the selected item view is positioned. The default is determined by the
+ * current theme.
+ *
+ * @return A {@link android.view.Gravity Gravity} value
+ */
+ public int getGravity() {
+ return mGravity;
+ }
+
@Override
public void setAdapter(SpinnerAdapter adapter) {
super.setAdapter(adapter);
@@ -676,6 +810,13 @@ public class Spinner extends AbsSpinner implements OnClickListener {
*/
public void setPromptText(CharSequence hintText);
public CharSequence getHintText();
+
+ public void setBackgroundDrawable(Drawable bg);
+ public void setVerticalOffset(int px);
+ public void setHorizontalOffset(int px);
+ public Drawable getBackground();
+ public int getVerticalOffset();
+ public int getHorizontalOffset();
}
private class DialogPopup implements SpinnerPopup, DialogInterface.OnClickListener {
@@ -720,6 +861,36 @@ public class Spinner extends AbsSpinner implements OnClickListener {
}
dismiss();
}
+
+ @Override
+ public void setBackgroundDrawable(Drawable bg) {
+ Log.e(TAG, "Cannot set popup background for MODE_DIALOG, ignoring");
+ }
+
+ @Override
+ public void setVerticalOffset(int px) {
+ Log.e(TAG, "Cannot set vertical offset for MODE_DIALOG, ignoring");
+ }
+
+ @Override
+ public void setHorizontalOffset(int px) {
+ Log.e(TAG, "Cannot set horizontal offset for MODE_DIALOG, ignoring");
+ }
+
+ @Override
+ public Drawable getBackground() {
+ return null;
+ }
+
+ @Override
+ public int getVerticalOffset() {
+ return 0;
+ }
+
+ @Override
+ public int getHorizontalOffset() {
+ return 0;
+ }
}
private class DropdownPopup extends ListPopupWindow implements SpinnerPopup {
diff --git a/core/java/android/widget/Switch.java b/core/java/android/widget/Switch.java
index 334b9c4..a897cc3 100644
--- a/core/java/android/widget/Switch.java
+++ b/core/java/android/widget/Switch.java
@@ -169,6 +169,8 @@ public class Switch extends CompoundButton {
/**
* Sets the switch text color, size, style, hint color, and highlight color
* from the specified TextAppearance resource.
+ *
+ * @attr ref android.R.styleable#Switch_switchTextAppearance
*/
public void setSwitchTextAppearance(Context context, int resid) {
TypedArray appearance =
@@ -274,7 +276,151 @@ public class Switch extends CompoundButton {
}
/**
+ * Set the amount of horizontal padding between the switch and the associated text.
+ *
+ * @param pixels Amount of padding in pixels
+ *
+ * @attr ref android.R.styleable#Switch_switchPadding
+ */
+ public void setSwitchPadding(int pixels) {
+ mSwitchPadding = pixels;
+ requestLayout();
+ }
+
+ /**
+ * Get the amount of horizontal padding between the switch and the associated text.
+ *
+ * @return Amount of padding in pixels
+ *
+ * @attr ref android.R.styleable#Switch_switchPadding
+ */
+ public int getSwitchPadding() {
+ return mSwitchPadding;
+ }
+
+ /**
+ * Set the minimum width of the switch in pixels. The switch's width will be the maximum
+ * of this value and its measured width as determined by the switch drawables and text used.
+ *
+ * @param pixels Minimum width of the switch in pixels
+ *
+ * @attr ref android.R.styleable#Switch_switchMinWidth
+ */
+ public void setSwitchMinWidth(int pixels) {
+ mSwitchMinWidth = pixels;
+ requestLayout();
+ }
+
+ /**
+ * Get the minimum width of the switch in pixels. The switch's width will be the maximum
+ * of this value and its measured width as determined by the switch drawables and text used.
+ *
+ * @return Minimum width of the switch in pixels
+ *
+ * @attr ref android.R.styleable#Switch_switchMinWidth
+ */
+ public int getSwitchMinWidth() {
+ return mSwitchMinWidth;
+ }
+
+ /**
+ * Set the horizontal padding around the text drawn on the switch itself.
+ *
+ * @param pixels Horizontal padding for switch thumb text in pixels
+ *
+ * @attr ref android.R.styleable#Switch_thumbTextPadding
+ */
+ public void setThumbTextPadding(int pixels) {
+ mThumbTextPadding = pixels;
+ requestLayout();
+ }
+
+ /**
+ * Get the horizontal padding around the text drawn on the switch itself.
+ *
+ * @return Horizontal padding for switch thumb text in pixels
+ *
+ * @attr ref android.R.styleable#Switch_thumbTextPadding
+ */
+ public int getThumbTextPadding() {
+ return mThumbTextPadding;
+ }
+
+ /**
+ * Set the drawable used for the track that the switch slides within.
+ *
+ * @param track Track drawable
+ *
+ * @attr ref android.R.styleable#Switch_track
+ */
+ public void setTrackDrawable(Drawable track) {
+ mTrackDrawable = track;
+ requestLayout();
+ }
+
+ /**
+ * Set the drawable used for the track that the switch slides within.
+ *
+ * @param resId Resource ID of a track drawable
+ *
+ * @attr ref android.R.styleable#Switch_track
+ */
+ public void setTrackResource(int resId) {
+ setTrackDrawable(getContext().getResources().getDrawable(resId));
+ }
+
+ /**
+ * Get the drawable used for the track that the switch slides within.
+ *
+ * @return Track drawable
+ *
+ * @attr ref android.R.styleable#Switch_track
+ */
+ public Drawable getTrackDrawable() {
+ return mTrackDrawable;
+ }
+
+ /**
+ * Set the drawable used for the switch "thumb" - the piece that the user
+ * can physically touch and drag along the track.
+ *
+ * @param thumb Thumb drawable
+ *
+ * @attr ref android.R.styleable#Switch_thumb
+ */
+ public void setThumbDrawable(Drawable thumb) {
+ mThumbDrawable = thumb;
+ requestLayout();
+ }
+
+ /**
+ * Set the drawable used for the switch "thumb" - the piece that the user
+ * can physically touch and drag along the track.
+ *
+ * @param resId Resource ID of a thumb drawable
+ *
+ * @attr ref android.R.styleable#Switch_thumb
+ */
+ public void setThumbResource(int resId) {
+ setThumbDrawable(getContext().getResources().getDrawable(resId));
+ }
+
+ /**
+ * Get the drawable used for the switch "thumb" - the piece that the user
+ * can physically touch and drag along the track.
+ *
+ * @return Thumb drawable
+ *
+ * @attr ref android.R.styleable#Switch_thumb
+ */
+ public Drawable getThumbDrawable() {
+ return mThumbDrawable;
+ }
+
+ /**
* Returns the text displayed when the button is in the checked state.
+ *
+ * @attr ref android.R.styleable#Switch_textOn
*/
public CharSequence getTextOn() {
return mTextOn;
@@ -282,6 +428,8 @@ public class Switch extends CompoundButton {
/**
* Sets the text displayed when the button is in the checked state.
+ *
+ * @attr ref android.R.styleable#Switch_textOn
*/
public void setTextOn(CharSequence textOn) {
mTextOn = textOn;
@@ -290,6 +438,8 @@ public class Switch extends CompoundButton {
/**
* Returns the text displayed when the button is not in the checked state.
+ *
+ * @attr ref android.R.styleable#Switch_textOff
*/
public CharSequence getTextOff() {
return mTextOff;
@@ -297,6 +447,8 @@ public class Switch extends CompoundButton {
/**
* Sets the text displayed when the button is not in the checked state.
+ *
+ * @attr ref android.R.styleable#Switch_textOff
*/
public void setTextOff(CharSequence textOff) {
mTextOff = textOff;
@@ -367,17 +519,8 @@ public class Switch extends CompoundButton {
@Override
public void onPopulateAccessibilityEvent(AccessibilityEvent event) {
super.onPopulateAccessibilityEvent(event);
- if (isChecked()) {
- CharSequence text = mOnLayout.getText();
- if (TextUtils.isEmpty(text)) {
- text = mContext.getString(R.string.switch_on);
- }
- event.getText().add(text);
- } else {
- CharSequence text = mOffLayout.getText();
- if (TextUtils.isEmpty(text)) {
- text = mContext.getString(R.string.switch_off);
- }
+ CharSequence text = isChecked() ? mOnLayout.getText() : mOffLayout.getText();
+ if (!TextUtils.isEmpty(text)) {
event.getText().add(text);
}
}
diff --git a/core/java/android/widget/TextView.java b/core/java/android/widget/TextView.java
index 0639e6b..b8db848 100644
--- a/core/java/android/widget/TextView.java
+++ b/core/java/android/widget/TextView.java
@@ -216,6 +216,8 @@ import java.util.Locale;
* @attr ref android.R.styleable#TextView_drawableBottom
* @attr ref android.R.styleable#TextView_drawableRight
* @attr ref android.R.styleable#TextView_drawableLeft
+ * @attr ref android.R.styleable#TextView_drawableStart
+ * @attr ref android.R.styleable#TextView_drawableEnd
* @attr ref android.R.styleable#TextView_drawablePadding
* @attr ref android.R.styleable#TextView_lineSpacingExtra
* @attr ref android.R.styleable#TextView_lineSpacingMultiplier
@@ -1520,8 +1522,6 @@ public class TextView extends View implements ViewTreeObserver.OnPreDrawListener
/**
* Returns the start padding of the view, plus space for the start
* Drawable if any.
- *
- * @hide
*/
public int getCompoundPaddingStart() {
resolveDrawables();
@@ -1537,8 +1537,6 @@ public class TextView extends View implements ViewTreeObserver.OnPreDrawListener
/**
* Returns the end padding of the view, plus space for the end
* Drawable if any.
- *
- * @hide
*/
public int getCompoundPaddingEnd() {
resolveDrawables();
@@ -1636,8 +1634,6 @@ public class TextView extends View implements ViewTreeObserver.OnPreDrawListener
/**
* Returns the total start padding of the view, including the start
* Drawable if any.
- *
- * @hide
*/
public int getTotalPaddingStart() {
return getCompoundPaddingStart();
@@ -1646,8 +1642,6 @@ public class TextView extends View implements ViewTreeObserver.OnPreDrawListener
/**
* Returns the total end padding of the view, including the end
* Drawable if any.
- *
- * @hide
*/
public int getTotalPaddingEnd() {
return getCompoundPaddingEnd();
@@ -1849,8 +1843,6 @@ public class TextView extends View implements ViewTreeObserver.OnPreDrawListener
* @attr ref android.R.styleable#TextView_drawableTop
* @attr ref android.R.styleable#TextView_drawableEnd
* @attr ref android.R.styleable#TextView_drawableBottom
- *
- * @hide
*/
public void setCompoundDrawablesRelative(Drawable start, Drawable top,
Drawable end, Drawable bottom) {
@@ -1972,8 +1964,6 @@ public class TextView extends View implements ViewTreeObserver.OnPreDrawListener
* @attr ref android.R.styleable#TextView_drawableTop
* @attr ref android.R.styleable#TextView_drawableEnd
* @attr ref android.R.styleable#TextView_drawableBottom
- *
- * @hide
*/
public void setCompoundDrawablesRelativeWithIntrinsicBounds(int start, int top, int end,
int bottom) {
@@ -1996,8 +1986,6 @@ public class TextView extends View implements ViewTreeObserver.OnPreDrawListener
* @attr ref android.R.styleable#TextView_drawableTop
* @attr ref android.R.styleable#TextView_drawableEnd
* @attr ref android.R.styleable#TextView_drawableBottom
- *
- * @hide
*/
public void setCompoundDrawablesRelativeWithIntrinsicBounds(Drawable start, Drawable top,
Drawable end, Drawable bottom) {
@@ -2034,8 +2022,6 @@ public class TextView extends View implements ViewTreeObserver.OnPreDrawListener
/**
* Returns drawables for the start, top, end, and bottom borders.
- *
- * @hide
*/
public Drawable[] getCompoundDrawablesRelative() {
final Drawables dr = mDrawables;
@@ -2093,6 +2079,20 @@ public class TextView extends View implements ViewTreeObserver.OnPreDrawListener
invalidate();
}
+ @Override
+ public void setPaddingRelative(int start, int top, int end, int bottom) {
+ if (start != getPaddingStart() ||
+ end != getPaddingEnd() ||
+ top != mPaddingTop ||
+ bottom != mPaddingBottom) {
+ nullLayouts();
+ }
+
+ // the super call will requestLayout()
+ super.setPaddingRelative(start, top, end, bottom);
+ invalidate();
+ }
+
/**
* Gets the autolink mask of the text. See {@link
* android.text.util.Linkify#ALL Linkify.ALL} and peers for
@@ -4280,6 +4280,12 @@ public class TextView extends View implements ViewTreeObserver.OnPreDrawListener
}
@Override
+ public void onScreenStateChanged(int screenState) {
+ super.onScreenStateChanged(screenState);
+ if (mEditor != null) getEditor().onScreenStateChanged(screenState);
+ }
+
+ @Override
protected boolean isPaddingOffsetRequired() {
return mShadowRadius != 0 || mDrawables != null;
}
@@ -4390,9 +4396,6 @@ public class TextView extends View implements ViewTreeObserver.OnPreDrawListener
}
}
- /**
- * @hide
- */
@Override
public int getResolvedLayoutDirection(Drawable who) {
if (who == null) return View.LAYOUT_DIRECTION_LTR;
@@ -11397,6 +11400,30 @@ public class TextView extends View implements ViewTreeObserver.OnPreDrawListener
hideControllers();
}
+ void onScreenStateChanged(int screenState) {
+ switch (screenState) {
+ case SCREEN_STATE_ON:
+ resumeBlink();
+ break;
+ case SCREEN_STATE_OFF:
+ suspendBlink();
+ break;
+ }
+ }
+
+ private void suspendBlink() {
+ if (mBlink != null) {
+ mBlink.cancel();
+ }
+ }
+
+ private void resumeBlink() {
+ if (mBlink != null) {
+ mBlink.uncancel();
+ makeBlink();
+ }
+ }
+
void adjustInputType(boolean password, boolean passwordInputType,
boolean webPasswordInputType, boolean numberPasswordInputType) {
// mInputType has been set from inputType, possibly modified by mInputMethod.
diff --git a/core/java/android/widget/ToggleButton.java b/core/java/android/widget/ToggleButton.java
index a0edafe..4beee96 100644
--- a/core/java/android/widget/ToggleButton.java
+++ b/core/java/android/widget/ToggleButton.java
@@ -154,16 +154,6 @@ public class ToggleButton extends CompoundButton {
}
@Override
- public void onPopulateAccessibilityEvent(AccessibilityEvent event) {
- super.onPopulateAccessibilityEvent(event);
- if (isChecked()) {
- event.getText().add(mContext.getString(R.string.togglebutton_pressed));
- } else {
- event.getText().add(mContext.getString(R.string.togglebutton_not_pressed));
- }
- }
-
- @Override
public void onInitializeAccessibilityEvent(AccessibilityEvent event) {
super.onInitializeAccessibilityEvent(event);
event.setClassName(ToggleButton.class.getName());
diff --git a/core/java/com/android/internal/os/ZygoteInit.java b/core/java/com/android/internal/os/ZygoteInit.java
index 6a99a2b..998c037 100644
--- a/core/java/com/android/internal/os/ZygoteInit.java
+++ b/core/java/com/android/internal/os/ZygoteInit.java
@@ -348,6 +348,7 @@ public class ZygoteInit {
TypedArray ar = mResources.obtainTypedArray(
com.android.internal.R.array.preloaded_drawables);
int N = preloadDrawables(runtime, ar);
+ ar.recycle();
Log.i(TAG, "...preloaded " + N + " resources in "
+ (SystemClock.uptimeMillis()-startTime) + "ms.");
@@ -355,6 +356,7 @@ public class ZygoteInit {
ar = mResources.obtainTypedArray(
com.android.internal.R.array.preloaded_color_state_lists);
N = preloadColorStateLists(runtime, ar);
+ ar.recycle();
Log.i(TAG, "...preloaded " + N + " resources in "
+ (SystemClock.uptimeMillis()-startTime) + "ms.");
}
diff --git a/core/java/com/android/internal/util/AsyncService.java b/core/java/com/android/internal/util/AsyncService.java
index 54d3c42..e39a2bf 100644
--- a/core/java/com/android/internal/util/AsyncService.java
+++ b/core/java/com/android/internal/util/AsyncService.java
@@ -106,7 +106,7 @@ abstract public class AsyncService extends Service {
/**
* Called when service is destroyed. After returning the
- * service is dead an no more processing should be expected
+ * service is dead and no more processing should be expected
* to occur.
*/
@Override
diff --git a/core/java/com/android/internal/util/StateMachine.java b/core/java/com/android/internal/util/StateMachine.java
index 61c0c8e..da189f1 100644
--- a/core/java/com/android/internal/util/StateMachine.java
+++ b/core/java/com/android/internal/util/StateMachine.java
@@ -1195,7 +1195,7 @@ public class StateMachine {
}
/**
- * Constructor creates an StateMachine using the looper.
+ * Constructor creates a StateMachine using the looper.
*
* @param name of the state machine
*/
diff --git a/core/java/com/android/internal/view/BaseIWindow.java b/core/java/com/android/internal/view/BaseIWindow.java
index b227700..15d11d8 100644
--- a/core/java/com/android/internal/view/BaseIWindow.java
+++ b/core/java/com/android/internal/view/BaseIWindow.java
@@ -49,6 +49,9 @@ public class BaseIWindow extends IWindow.Stub {
public void dispatchGetNewSurface() {
}
+ public void dispatchScreenState(boolean on) {
+ }
+
public void windowFocusChanged(boolean hasFocus, boolean touchEnabled) {
}
diff --git a/core/java/com/android/internal/view/menu/ActionMenuItemView.java b/core/java/com/android/internal/view/menu/ActionMenuItemView.java
index a10d241..d5c2018 100644
--- a/core/java/com/android/internal/view/menu/ActionMenuItemView.java
+++ b/core/java/com/android/internal/view/menu/ActionMenuItemView.java
@@ -27,28 +27,26 @@ import android.view.Gravity;
import android.view.MotionEvent;
import android.view.View;
import android.view.accessibility.AccessibilityEvent;
-import android.widget.Button;
-import android.widget.ImageButton;
-import android.widget.LinearLayout;
+import android.widget.TextView;
import android.widget.Toast;
/**
* @hide
*/
-public class ActionMenuItemView extends LinearLayout
+public class ActionMenuItemView extends TextView
implements MenuView.ItemView, View.OnClickListener, View.OnLongClickListener,
ActionMenuView.ActionMenuChildView {
private static final String TAG = "ActionMenuItemView";
private MenuItemImpl mItemData;
private CharSequence mTitle;
+ private Drawable mIcon;
private MenuBuilder.ItemInvoker mItemInvoker;
- private ImageButton mImageButton;
- private Button mTextButton;
private boolean mAllowTextWithIcon;
private boolean mExpandedFormat;
private int mMinWidth;
+ private int mSavedPaddingLeft;
public ActionMenuItemView(Context context) {
this(context, null);
@@ -68,17 +66,12 @@ public class ActionMenuItemView extends LinearLayout
mMinWidth = a.getDimensionPixelSize(
com.android.internal.R.styleable.ActionMenuItemView_minWidth, 0);
a.recycle();
- }
- @Override
- public void onFinishInflate() {
- mImageButton = (ImageButton) findViewById(com.android.internal.R.id.imageButton);
- mTextButton = (Button) findViewById(com.android.internal.R.id.textButton);
- mImageButton.setOnClickListener(this);
- mTextButton.setOnClickListener(this);
- mImageButton.setOnLongClickListener(this);
setOnClickListener(this);
setOnLongClickListener(this);
+
+ // Save the inflated padding for later, we'll need it.
+ mSavedPaddingLeft = getPaddingLeft();
}
public MenuItemImpl getItemData() {
@@ -96,13 +89,6 @@ public class ActionMenuItemView extends LinearLayout
setEnabled(itemData.isEnabled());
}
- @Override
- public void setEnabled(boolean enabled) {
- super.setEnabled(enabled);
- mImageButton.setEnabled(enabled);
- mTextButton.setEnabled(enabled);
- }
-
public void onClick(View v) {
if (mItemInvoker != null) {
mItemInvoker.invokeItem(mItemData);
@@ -135,26 +121,22 @@ public class ActionMenuItemView extends LinearLayout
}
private void updateTextButtonVisibility() {
- boolean visible = !TextUtils.isEmpty(mTextButton.getText());
- visible &= mImageButton.getDrawable() == null ||
+ boolean visible = !TextUtils.isEmpty(mTitle);
+ visible &= mIcon == null ||
(mItemData.showsTextAsAction() && (mAllowTextWithIcon || mExpandedFormat));
- mTextButton.setVisibility(visible ? VISIBLE : GONE);
+ setText(visible ? mTitle : null);
}
public void setIcon(Drawable icon) {
- mImageButton.setImageDrawable(icon);
- if (icon != null) {
- mImageButton.setVisibility(VISIBLE);
- } else {
- mImageButton.setVisibility(GONE);
- }
+ mIcon = icon;
+ setCompoundDrawablesWithIntrinsicBounds(icon, null, null, null);
updateTextButtonVisibility();
}
public boolean hasText() {
- return mTextButton.getVisibility() != GONE;
+ return !TextUtils.isEmpty(getText());
}
public void setShortcut(boolean showShortcut, char shortcutKey) {
@@ -164,8 +146,6 @@ public class ActionMenuItemView extends LinearLayout
public void setTitle(CharSequence title) {
mTitle = title;
- mTextButton.setText(mTitle);
-
setContentDescription(mTitle);
updateTextButtonVisibility();
}
@@ -236,12 +216,17 @@ public class ActionMenuItemView extends LinearLayout
@Override
protected void onMeasure(int widthMeasureSpec, int heightMeasureSpec) {
+ final boolean textVisible = hasText();
+ if (textVisible) {
+ setPadding(mSavedPaddingLeft, getPaddingTop(), getPaddingRight(), getPaddingBottom());
+ }
+
super.onMeasure(widthMeasureSpec, heightMeasureSpec);
final int widthMode = MeasureSpec.getMode(widthMeasureSpec);
- final int specSize = MeasureSpec.getSize(widthMeasureSpec);
+ final int widthSize = MeasureSpec.getSize(widthMeasureSpec);
final int oldMeasuredWidth = getMeasuredWidth();
- final int targetWidth = widthMode == MeasureSpec.AT_MOST ? Math.min(specSize, mMinWidth)
+ final int targetWidth = widthMode == MeasureSpec.AT_MOST ? Math.min(widthSize, mMinWidth)
: mMinWidth;
if (widthMode != MeasureSpec.EXACTLY && mMinWidth > 0 && oldMeasuredWidth < targetWidth) {
@@ -249,5 +234,13 @@ public class ActionMenuItemView extends LinearLayout
super.onMeasure(MeasureSpec.makeMeasureSpec(targetWidth, MeasureSpec.EXACTLY),
heightMeasureSpec);
}
+
+ if (!textVisible && mIcon != null) {
+ // TextView won't center compound drawables in both dimensions without
+ // a little coercion. Pad in to center the icon after we've measured.
+ final int w = getMeasuredWidth();
+ final int dw = mIcon.getIntrinsicWidth();
+ setPadding((w - dw) / 2, getPaddingTop(), getPaddingRight(), getPaddingBottom());
+ }
}
}
diff --git a/core/java/com/android/internal/view/menu/ActionMenuPresenter.java b/core/java/com/android/internal/view/menu/ActionMenuPresenter.java
index 530809b..dca45a9 100644
--- a/core/java/com/android/internal/view/menu/ActionMenuPresenter.java
+++ b/core/java/com/android/internal/view/menu/ActionMenuPresenter.java
@@ -116,9 +116,9 @@ public class ActionMenuPresenter extends BaseMenuPresenter
if (!mMaxItemsSet) {
mMaxItems = mContext.getResources().getInteger(
com.android.internal.R.integer.max_action_buttons);
- if (mMenu != null) {
- mMenu.onItemsChanged(true);
- }
+ }
+ if (mMenu != null) {
+ mMenu.onItemsChanged(true);
}
}
diff --git a/core/java/com/android/internal/view/menu/ActionMenuView.java b/core/java/com/android/internal/view/menu/ActionMenuView.java
index 8d8c72c..e00fe9f 100644
--- a/core/java/com/android/internal/view/menu/ActionMenuView.java
+++ b/core/java/com/android/internal/view/menu/ActionMenuView.java
@@ -96,6 +96,13 @@ public class ActionMenuView extends LinearLayout implements MenuBuilder.ItemInvo
if (mFormatItems) {
onMeasureExactFormat(widthMeasureSpec, heightMeasureSpec);
} else {
+ // Previous measurement at exact format may have set margins - reset them.
+ final int childCount = getChildCount();
+ for (int i = 0; i < childCount; i++) {
+ final View child = getChildAt(i);
+ final LayoutParams lp = (LayoutParams) child.getLayoutParams();
+ lp.leftMargin = lp.rightMargin = 0;
+ }
super.onMeasure(widthMeasureSpec, heightMeasureSpec);
}
}
diff --git a/core/java/com/android/internal/widget/ActionBarView.java b/core/java/com/android/internal/widget/ActionBarView.java
index 2f325bf..8c05459 100644
--- a/core/java/com/android/internal/widget/ActionBarView.java
+++ b/core/java/com/android/internal/widget/ActionBarView.java
@@ -324,13 +324,31 @@ public class ActionBarView extends AbsActionBarView {
if (mSplitView != null) {
mSplitView.addView(mMenuView);
}
+ mMenuView.getLayoutParams().width = LayoutParams.MATCH_PARENT;
} else {
addView(mMenuView);
+ mMenuView.getLayoutParams().width = LayoutParams.WRAP_CONTENT;
}
+ mMenuView.requestLayout();
}
if (mSplitView != null) {
mSplitView.setVisibility(splitActionBar ? VISIBLE : GONE);
}
+
+ if (mActionMenuPresenter != null) {
+ if (!splitActionBar) {
+ mActionMenuPresenter.setExpandedActionViewsExclusive(
+ getResources().getBoolean(
+ com.android.internal.R.bool.action_bar_expanded_action_views_exclusive));
+ } else {
+ mActionMenuPresenter.setExpandedActionViewsExclusive(false);
+ // Allow full screen width in split mode.
+ mActionMenuPresenter.setWidthLimit(
+ getContext().getResources().getDisplayMetrics().widthPixels, true);
+ // No limit to the item count; use whatever will fit.
+ mActionMenuPresenter.setItemLimit(Integer.MAX_VALUE);
+ }
+ }
super.setSplitActionBar(splitActionBar);
}
}
diff --git a/core/java/com/android/internal/widget/SlidingTab.java b/core/java/com/android/internal/widget/SlidingTab.java
index 3865510..f535a08 100644
--- a/core/java/com/android/internal/widget/SlidingTab.java
+++ b/core/java/com/android/internal/widget/SlidingTab.java
@@ -416,7 +416,7 @@ public class SlidingTab extends ViewGroup {
}
/**
- * Start animating the slider. Note we need two animations since an ValueAnimator
+ * Start animating the slider. Note we need two animations since a ValueAnimator
* keeps internal state of the invalidation region which is just the view being animated.
*
* @param anim1