From 259447bc6490f4ec7e04eb5d8bfa1b041650ed53 Mon Sep 17 00:00:00 2001 From: Omari Stephens Date: Wed, 12 Aug 2009 00:30:59 -0700 Subject: [PATCH 1/8] Wrap up the stress test into a single script make test_restore.sh return a value signifying success or failure --- tests/backup/backup_stress_test.sh | 64 ++++++++++++++++++++++++++++++ tests/backup/test_restore.sh | 4 +- 2 files changed, 67 insertions(+), 1 deletion(-) create mode 100755 tests/backup/backup_stress_test.sh diff --git a/tests/backup/backup_stress_test.sh b/tests/backup/backup_stress_test.sh new file mode 100755 index 0000000000000..81555079cb3ae --- /dev/null +++ b/tests/backup/backup_stress_test.sh @@ -0,0 +1,64 @@ +#!/bin/bash + +# Copyright (C) 2009 The Android Open Source Project +# +# Licensed under the Apache License, Version 2.0 (the "License"); +# you may not use this file except in compliance with the License. +# You may obtain a copy of the License at +# +# http://www.apache.org/licenses/LICENSE-2.0 +# +# Unless required by applicable law or agreed to in writing, software +# distributed under the License is distributed on an "AS IS" BASIS, +# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +# See the License for the specific language governing permissions and +# limitations under the License. + +iterations=150 +failures=0 +i=0 +LOGDIR="$HOME/backup_tests" +LOGFILE="$LOGDIR/backup_stress.`date +%s`.log" +export BUGREPORT_DIR="$LOGDIR/bugreports" + +# make sure that we have a place to put logs and bugreports +mkdir -p $LOGDIR $BUGREPORT_DIR + +echo "logfile is $LOGFILE" + +(while (sleep 10); do + failed=0 + + echo + echo "Iteration $i at `date`" + echo + + ./test_backup.sh "$@" 2>&1 + + sleep 10 + echo "Restore at `date`" + echo + + ./test_restore.sh "$@" 2>&1 || failed=1 + + if [ "$failed" -ne 0 ]; then + failures=$(($failures+1)) + # Long and verbose so it sticks out + echo "FAILED iteration $i of $iterations; $failures failures so far" + echo "FAILED iteration $i of $iterations; $failures failures so far" > /dev/stderr + else + printf "Iteration %d:\tPASS; remaining: %d\n" $i $(($iterations - $i - 1)) + printf "Iteration %d:\tPASS; remaining: %d\n" $i $(($iterations - $i - 1)) > /dev/stderr + fi + + echo "End $i at `date`" + + i=$(($i+1)) + if [ $i -eq $iterations ]; then + echo "DONE: $iterations iterations with $failures failures." + echo "DONE: $iterations iterations with $failures failures." > /dev/stderr + [ "$failures" -eq 0 ] && exit 0 + exit 1 + fi +done) > "$LOGFILE" + diff --git a/tests/backup/test_restore.sh b/tests/backup/test_restore.sh index 4506c16e71452..46b46e4759083 100755 --- a/tests/backup/test_restore.sh +++ b/tests/backup/test_restore.sh @@ -18,7 +18,7 @@ #export DRY_RUN="echo" source test_backup_common.sh -BUGREPORT_DIR="$HOME/backup/bugreports" +[ -z "$BUGREPORT_DIR" ] && BUGREPORT_DIR="$HOME/backup/bugreports" function check_file { @@ -107,3 +107,5 @@ echo echo "Last 3 timestamps in 3.txt:" a shell cat /data/data/com.android.backuptest/files/3.txt | tail -n 3 +exit $need_bug + From 3940f2df36fbe6a712c88655fc78244977ebd0ab Mon Sep 17 00:00:00 2001 From: Owen Lin Date: Thu, 13 Aug 2009 15:21:16 +0800 Subject: [PATCH 2/8] Log more information to help us debug which ListView has IllegalStateException in monkey tests. (bug 1995826) --- core/java/android/widget/ListView.java | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/core/java/android/widget/ListView.java b/core/java/android/widget/ListView.java index 02a137ddff0a1..993b7cbcdb457 100644 --- a/core/java/android/widget/ListView.java +++ b/core/java/android/widget/ListView.java @@ -1428,7 +1428,8 @@ public class ListView extends AbsListView { throw new IllegalStateException("The content of the adapter has changed but " + "ListView did not receive a notification. Make sure the content of " + "your adapter is not modified from a background thread, but only " - + "from the UI thread."); + + "from the UI thread. [in ListView(" + getId() + ", " + getClass() + + ") with Adapter(" + mAdapter.getClass() + ")]"); } setSelectedPositionInt(mNextSelectedPosition); From 0196225690be075a48c463d71b26a7b6291f39d3 Mon Sep 17 00:00:00 2001 From: Mike Lockwood Date: Thu, 13 Aug 2009 16:05:38 -0400 Subject: [PATCH 3/8] PowerManager: Fix problem waking screen from negative proximity sensor events. Fixes bug b/2048591 Signed-off-by: Mike Lockwood --- .../java/com/android/server/PowerManagerService.java | 10 +++++++++- 1 file changed, 9 insertions(+), 1 deletion(-) diff --git a/services/java/com/android/server/PowerManagerService.java b/services/java/com/android/server/PowerManagerService.java index 447e9faea7a89..1249289ca27df 100644 --- a/services/java/com/android/server/PowerManagerService.java +++ b/services/java/com/android/server/PowerManagerService.java @@ -2054,7 +2054,15 @@ class PowerManagerService extends IPowerManager.Stub if (event.values[0] == 0.0) { goToSleep(milliseconds); } else { - userActivity(milliseconds, false); + // proximity sensor negative events user activity. + // temporarily set mUserActivityAllowed to true so this will work + // even when the keyguard is on. + synchronized (mLocks) { + boolean savedActivityAllowed = mUserActivityAllowed; + mUserActivityAllowed = true; + userActivity(milliseconds, false); + mUserActivityAllowed = savedActivityAllowed; + } } } From 94272625d4486ab4dcd14ef96357008d373db51b Mon Sep 17 00:00:00 2001 From: Dianne Hackborn Date: Thu, 13 Aug 2009 10:20:21 -0700 Subject: [PATCH 4/8] Always have a wallpaper service running. --- core/res/AndroidManifest.xml | 3 - core/res/res/values/themes.xml | 1 + .../java/com/android/server/SystemServer.java | 5 +- .../server/WallpaperManagerService.java | 115 ++++++++++++------ .../android/server/WindowManagerService.java | 1 + 5 files changed, 86 insertions(+), 39 deletions(-) diff --git a/core/res/AndroidManifest.xml b/core/res/AndroidManifest.xml index d55f188eb53cc..608c1ff6ee0d2 100644 --- a/core/res/AndroidManifest.xml +++ b/core/res/AndroidManifest.xml @@ -1164,9 +1164,6 @@ - - - diff --git a/core/res/res/values/themes.xml b/core/res/res/values/themes.xml index 1e3a4a88ef13f..bfdce1e25328a 100644 --- a/core/res/res/values/themes.xml +++ b/core/res/res/values/themes.xml @@ -112,6 +112,7 @@ false false @android:drawable/title_bar_shadow + false @android:style/WindowTitle 25dip @android:style/WindowTitleBackground diff --git a/services/java/com/android/server/SystemServer.java b/services/java/com/android/server/SystemServer.java index 8919ccc24e08f..ad8e8921751dc 100644 --- a/services/java/com/android/server/SystemServer.java +++ b/services/java/com/android/server/SystemServer.java @@ -196,6 +196,7 @@ class ServerThread extends Thread { InputMethodManagerService imm = null; AppWidgetService appWidget = null; NotificationManagerService notification = null; + WallpaperManagerService wallpaper = null; if (factoryTest != SystemServer.FACTORY_TEST_LOW_LEVEL) { try { @@ -302,7 +303,8 @@ class ServerThread extends Thread { try { Log.i(TAG, "Starting Wallpaper Service"); - ServiceManager.addService(Context.WALLPAPER_SERVICE, new WallpaperManagerService(context)); + wallpaper = new WallpaperManagerService(context); + ServiceManager.addService(Context.WALLPAPER_SERVICE, wallpaper); } catch (Throwable e) { Log.e(TAG, "Failure starting Wallpaper Service", e); } @@ -381,6 +383,7 @@ class ServerThread extends Thread { } catch (RemoteException e) { } + if (wallpaper != null) wallpaper.systemReady(); battery.systemReady(); Watchdog.getInstance().start(); diff --git a/services/java/com/android/server/WallpaperManagerService.java b/services/java/com/android/server/WallpaperManagerService.java index 06565c74432c9..9fe4eee053e36 100644 --- a/services/java/com/android/server/WallpaperManagerService.java +++ b/services/java/com/android/server/WallpaperManagerService.java @@ -59,6 +59,7 @@ import org.xmlpull.v1.XmlPullParser; import org.xmlpull.v1.XmlPullParserException; import org.xmlpull.v1.XmlSerializer; +import com.android.internal.service.wallpaper.ImageWallpaper; import com.android.internal.util.FastXmlSerializer; class WallpaperManagerService extends IWallpaperManager.Stub { @@ -172,12 +173,29 @@ class WallpaperManagerService extends IWallpaperManager.Stub { mWallpaperObserver.stopWatching(); } + public void systemReady() { + synchronized (mLock) { + try { + bindWallpaperComponentLocked(mWallpaperComponent); + } catch (RuntimeException e) { + Log.w(TAG, "Failure starting previous wallpaper", e); + try { + bindWallpaperComponentLocked(null); + } catch (RuntimeException e2) { + Log.w(TAG, "Failure starting default wallpaper", e2); + clearWallpaperComponentLocked(); + } + } + } + } + public void clearWallpaper() { synchronized (mLock) { File f = WALLPAPER_FILE; if (f.exists()) { f.delete(); } + bindWallpaperComponentLocked(null); } } @@ -231,7 +249,7 @@ class WallpaperManagerService extends IWallpaperManager.Stub { synchronized (mLock) { ParcelFileDescriptor pfd = updateWallpaperBitmapLocked(name); if (pfd != null) { - clearWallpaperComponentLocked(); + bindWallpaperComponentLocked(null); saveSettingsLocked(); } return pfd; @@ -256,16 +274,45 @@ class WallpaperManagerService extends IWallpaperManager.Stub { synchronized (mLock) { final long ident = Binder.clearCallingIdentity(); try { - ServiceInfo si = mContext.getPackageManager().getServiceInfo(name, - PackageManager.GET_META_DATA | PackageManager.GET_PERMISSIONS); - if (!android.Manifest.permission.BIND_WALLPAPER.equals(si.permission)) { - throw new SecurityException("Selected service does not require " - + android.Manifest.permission.BIND_WALLPAPER - + ": " + name); + bindWallpaperComponentLocked(name); + } finally { + Binder.restoreCallingIdentity(ident); + } + } + } + + void bindWallpaperComponentLocked(ComponentName name) { + // Has the component changed? + if (mWallpaperConnection != null) { + if (mWallpaperComponent == null) { + if (name == null) { + // Still using default wallpaper. + return; } - + } else if (mWallpaperComponent.equals(name)) { + // Changing to same wallpaper. + return; + } + } + + try { + ComponentName realName = name; + if (realName == null) { + // The default component is our static image wallpaper. + realName = new ComponentName("android", + ImageWallpaper.class.getName()); + } + ServiceInfo si = mContext.getPackageManager().getServiceInfo(realName, + PackageManager.GET_META_DATA | PackageManager.GET_PERMISSIONS); + if (!android.Manifest.permission.BIND_WALLPAPER.equals(si.permission)) { + throw new SecurityException("Selected service does not require " + + android.Manifest.permission.BIND_WALLPAPER + + ": " + realName); + } + + Intent intent = new Intent(WallpaperService.SERVICE_INTERFACE); + if (name != null) { // Make sure the selected service is actually a wallpaper service. - Intent intent = new Intent(WallpaperService.SERVICE_INTERFACE); List ris = mContext.getPackageManager() .queryIntentServices(intent, 0); for (int i=0; i Date: Thu, 13 Aug 2009 14:55:02 -0700 Subject: [PATCH 7/8] - changed removeAccount() to be handled by the AccountAuthenticator and to return a boolean - changed the IAccountManager API to use oneway IPCs - changed the AccountManager to not have to start a thread for any of the calls (versus every call) --- api/current.xml | 604 ++++--------- .../AbstractAccountAuthenticator.java | 25 + core/java/android/accounts/Account.java | 24 +- .../java/android/accounts/AccountManager.java | 809 +++++++----------- ...lback.java => AccountManagerCallback.java} | 6 +- .../accounts/AccountManagerFuture.java | 65 ++ .../accounts/AccountManagerService.java | 364 ++++---- .../accounts/ChooseAccountActivity.java | 6 +- core/java/android/accounts/Constants.java | 1 + core/java/android/accounts/Future1.java | 46 - core/java/android/accounts/Future2.java | 57 -- .../android/accounts/Future2Callback.java | 20 - .../GrantCredentialsPermissionActivity.java | 4 +- .../accounts/IAccountAuthenticator.aidl | 5 + .../android/accounts/IAccountManager.aidl | 9 +- .../AbstractSyncableContentProvider.java | 6 +- .../android/content/AbstractTableMerger.java | 6 +- core/java/android/content/SyncManager.java | 16 +- .../SyncStateContentProviderHelper.java | 8 +- .../android/content/SyncStorageEngine.java | 4 +- core/java/android/provider/Calendar.java | 2 +- .../android/provider/SubscribedFeeds.java | 8 +- .../android/provider/SyncStateContract.java | 10 +- .../SubscribedFeedsIntentService.java | 8 +- .../accounts/AccountManagerServiceTest.java | 37 +- .../content/AbstractTableMergerTest.java | 8 +- 26 files changed, 872 insertions(+), 1286 deletions(-) rename core/java/android/accounts/{Future1Callback.java => AccountManagerCallback.java} (87%) create mode 100644 core/java/android/accounts/AccountManagerFuture.java delete mode 100644 core/java/android/accounts/Future1.java delete mode 100644 core/java/android/accounts/Future2.java delete mode 100644 core/java/android/accounts/Future2Callback.java diff --git a/api/current.xml b/api/current.xml index d57198473ab18..ee8d2eb865116 100644 --- a/api/current.xml +++ b/api/current.xml @@ -13463,6 +13463,23 @@ + + + + + + + + - - - + - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - + - + - + @@ -14160,7 +13963,7 @@ - - - - - - - - - - + - + - + @@ -14276,13 +14071,13 @@ - + - - - - - - - - - - - - - - - - - - - - - - + + @@ -14401,7 +14176,7 @@ - - - - - - - - - - - - - + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - + + + + + + + + CREATOR = new Creator() { @@ -79,6 +79,6 @@ public class Account implements Parcelable { }; public String toString() { - return "Account {name=" + mName + ", type=" + mType + "}"; + return "Account {name=" + name + ", type=" + type + "}"; } } diff --git a/core/java/android/accounts/AccountManager.java b/core/java/android/accounts/AccountManager.java index 502abbb312f29..9f70534fb81ab 100644 --- a/core/java/android/accounts/AccountManager.java +++ b/core/java/android/accounts/AccountManager.java @@ -26,8 +26,6 @@ import android.os.Handler; import android.os.Looper; import android.os.RemoteException; import android.os.Parcelable; -import android.util.Config; -import android.util.Log; import java.io.IOException; import java.util.concurrent.Callable; @@ -80,258 +78,132 @@ public class AccountManager { return (AccountManager) context.getSystemService(Context.ACCOUNT_SERVICE); } - public String blockingGetPassword(Account account) { - ensureNotOnMainThread(); + public String getPassword(final Account account) { try { return mService.getPassword(account); } catch (RemoteException e) { - // if this happens the entire runtime will restart + // will never happen throw new RuntimeException(e); } } - public Future1 getPassword(final Future1Callback callback, - final Account account, final Handler handler) { - return startAsFuture(callback, handler, new Callable() { - public String call() throws Exception { - return blockingGetPassword(account); - } - }); - } - - public String blockingGetUserData(Account account, String key) { - ensureNotOnMainThread(); + public String getUserData(final Account account, final String key) { try { return mService.getUserData(account, key); } catch (RemoteException e) { - // if this happens the entire runtime will restart + // will never happen throw new RuntimeException(e); } } - public Future1 getUserData(Future1Callback callback, - final Account account, final String key, Handler handler) { - return startAsFuture(callback, handler, new Callable() { - public String call() throws Exception { - return blockingGetUserData(account, key); - } - }); - } - - public AuthenticatorDescription[] blockingGetAuthenticatorTypes() { - ensureNotOnMainThread(); + public AuthenticatorDescription[] getAuthenticatorTypes() { try { return mService.getAuthenticatorTypes(); } catch (RemoteException e) { - // if this happens the entire runtime will restart + // will never happen throw new RuntimeException(e); } } - public Future1 getAuthenticatorTypes( - Future1Callback callback, Handler handler) { - return startAsFuture(callback, handler, new Callable() { - public AuthenticatorDescription[] call() throws Exception { - return blockingGetAuthenticatorTypes(); - } - }); - } - - public Account[] blockingGetAccounts() { - ensureNotOnMainThread(); + public Account[] getAccounts() { try { - return mService.getAccounts(); + return mService.getAccounts(null); } catch (RemoteException e) { - // if this happens the entire runtime will restart + // won't ever happen throw new RuntimeException(e); } } - public Account[] blockingGetAccountsByType(String accountType) { - ensureNotOnMainThread(); + public Account[] getAccountsByType(String type) { try { - return mService.getAccountsByType(accountType); + return mService.getAccounts(type); } catch (RemoteException e) { - // if this happens the entire runtime will restart + // won't ever happen throw new RuntimeException(e); } } - public Future1 getAccounts(Future1Callback callback, Handler handler) { - return startAsFuture(callback, handler, new Callable() { - public Account[] call() throws Exception { - return blockingGetAccounts(); - } - }); - } - - public Future1 getAccountsByType(Future1Callback callback, - final String type, Handler handler) { - return startAsFuture(callback, handler, new Callable() { - public Account[] call() throws Exception { - return blockingGetAccountsByType(type); - } - }); - } - - public boolean blockingAddAccountExplicitly(Account account, String password, Bundle extras) { - ensureNotOnMainThread(); + public boolean addAccountExplicitly(Account account, String password, Bundle extras) { try { return mService.addAccount(account, password, extras); } catch (RemoteException e) { - // if this happens the entire runtime will restart + // won't ever happen throw new RuntimeException(e); } } - public Future1 addAccountExplicitly(final Future1Callback callback, - final Account account, final String password, final Bundle extras, - final Handler handler) { - return startAsFuture(callback, handler, new Callable() { - public Boolean call() throws Exception { - return blockingAddAccountExplicitly(account, password, extras); + public AccountManagerFuture removeAccount(final Account account, + AccountManagerCallback callback, Handler handler) { + return new Future2Task(handler, callback) { + public void doWork() throws RemoteException { + mService.removeAccount(mResponse, account); } - }); - } - - public void blockingRemoveAccount(Account account) { - ensureNotOnMainThread(); - try { - mService.removeAccount(account); - } catch (RemoteException e) { - // if this happens the entire runtime will restart - } - } - - public Future1 removeAccount(Future1Callback callback, final Account account, - final Handler handler) { - return startAsFuture(callback, handler, new Callable() { - public Void call() throws Exception { - blockingRemoveAccount(account); - return null; + public Boolean bundleToResult(Bundle bundle) throws AuthenticatorException { + if (!bundle.containsKey(Constants.BOOLEAN_RESULT_KEY)) { + throw new AuthenticatorException("no result in response"); + } + return bundle.getBoolean(Constants.BOOLEAN_RESULT_KEY); } - }); + }.start(); } - public void blockingInvalidateAuthToken(String accountType, String authToken) { - ensureNotOnMainThread(); + public void invalidateAuthToken(final String accountType, final String authToken) { try { mService.invalidateAuthToken(accountType, authToken); } catch (RemoteException e) { - // if this happens the entire runtime will restart - } - } - - public Future1 invalidateAuthToken(Future1Callback callback, - final String accountType, final String authToken, final Handler handler) { - return startAsFuture(callback, handler, new Callable() { - public Void call() throws Exception { - blockingInvalidateAuthToken(accountType, authToken); - return null; - } - }); - } - - public String blockingPeekAuthToken(Account account, String authTokenType) { - ensureNotOnMainThread(); - try { - return mService.peekAuthToken(account, authTokenType); - } catch (RemoteException e) { - // if this happens the entire runtime will restart + // won't ever happen throw new RuntimeException(e); } } - public Future1 peekAuthToken(Future1Callback callback, - final Account account, final String authTokenType, final Handler handler) { - return startAsFuture(callback, handler, new Callable() { - public String call() throws Exception { - return blockingPeekAuthToken(account, authTokenType); - } - }); + public String peekAuthToken(final Account account, final String authTokenType) { + try { + return mService.peekAuthToken(account, authTokenType); + } catch (RemoteException e) { + // won't ever happen + throw new RuntimeException(e); + } } - public void blockingSetPassword(Account account, String password) { - ensureNotOnMainThread(); + public void setPassword(final Account account, final String password) { try { mService.setPassword(account, password); } catch (RemoteException e) { - // if this happens the entire runtime will restart + // won't ever happen + throw new RuntimeException(e); } } - public Future1 setPassword(Future1Callback callback, - final Account account, final String password, final Handler handler) { - return startAsFuture(callback, handler, new Callable() { - public Void call() throws Exception { - blockingSetPassword(account, password); - return null; - } - }); - } - - public void blockingClearPassword(Account account) { - ensureNotOnMainThread(); + public void clearPassword(final Account account) { try { mService.clearPassword(account); } catch (RemoteException e) { - // if this happens the entire runtime will restart + // won't ever happen + throw new RuntimeException(e); } } - public Future1 clearPassword(final Future1Callback callback, final Account account, - final Handler handler) { - return startAsFuture(callback, handler, new Callable() { - public Void call() throws Exception { - blockingClearPassword(account); - return null; - } - }); - } - - public void blockingSetUserData(Account account, String key, String value) { - ensureNotOnMainThread(); + public void setUserData(final Account account, final String key, final String value) { try { mService.setUserData(account, key, value); } catch (RemoteException e) { - // if this happens the entire runtime will restart + // won't ever happen + throw new RuntimeException(e); } } - public Future1 setUserData(Future1Callback callback, - final Account account, final String key, final String value, final Handler handler) { - return startAsFuture(callback, handler, new Callable() { - public Void call() throws Exception { - blockingSetUserData(account, key, value); - return null; - } - }); - } - - public void blockingSetAuthToken(Account account, String authTokenType, String authToken) { - ensureNotOnMainThread(); + public void setAuthToken(Account account, final String authTokenType, final String authToken) { try { mService.setAuthToken(account, authTokenType, authToken); } catch (RemoteException e) { - // if this happens the entire runtime will restart + // won't ever happen + throw new RuntimeException(e); } } - public Future1 setAuthToken(Future1Callback callback, - final Account account, final String authTokenType, final String authToken, - final Handler handler) { - return startAsFuture(callback, handler, new Callable() { - public Void call() throws Exception { - blockingSetAuthToken(account, authTokenType, authToken); - return null; - } - }); - } - public String blockingGetAuthToken(Account account, String authTokenType, boolean notifyAuthFailure) throws OperationCanceledException, IOException, AuthenticatorException { - ensureNotOnMainThread(); Bundle bundle = getAuthToken(account, authTokenType, notifyAuthFailure, null /* callback */, null /* handler */).getResult(); return bundle.getString(Constants.AUTHTOKEN_KEY); @@ -349,9 +221,9 @@ public class AccountManager { * @param loginOptions * @param activity the activity to launch the login intent, if necessary, and to which */ - public Future2 getAuthToken( + public AccountManagerFuture getAuthToken( final Account account, final String authTokenType, final Bundle loginOptions, - final Activity activity, Future2Callback callback, Handler handler) { + final Activity activity, AccountManagerCallback callback, Handler handler) { if (activity == null) throw new IllegalArgumentException("activity is null"); if (authTokenType == null) throw new IllegalArgumentException("authTokenType is null"); return new AmsTask(activity, handler, callback) { @@ -363,9 +235,9 @@ public class AccountManager { }.start(); } - public Future2 getAuthToken( + public AccountManagerFuture getAuthToken( final Account account, final String authTokenType, final boolean notifyAuthFailure, - Future2Callback callback, Handler handler) { + AccountManagerCallback callback, Handler handler) { if (account == null) throw new IllegalArgumentException("account is null"); if (authTokenType == null) throw new IllegalArgumentException("authTokenType is null"); return new AmsTask(null, handler, callback) { @@ -376,10 +248,10 @@ public class AccountManager { }.start(); } - public Future2 addAccount(final String accountType, + public AccountManagerFuture addAccount(final String accountType, final String authTokenType, final String[] requiredFeatures, final Bundle addAccountOptions, - final Activity activity, Future2Callback callback, Handler handler) { + final Activity activity, AccountManagerCallback callback, Handler handler) { return new AmsTask(activity, handler, callback) { public void doWork() throws RemoteException { mService.addAcount(mResponse, accountType, authTokenType, @@ -389,44 +261,45 @@ public class AccountManager { } /** @deprecated use {@link #confirmCredentials} instead */ - public Future1 confirmPassword(final Account account, final String password, - Future1Callback callback, Handler handler) { - return new AMSTaskBoolean(handler, callback) { + public AccountManagerFuture confirmPassword(final Account account, final String password, + AccountManagerCallback callback, Handler handler) { + return new Future2Task(handler, callback) { public void doWork() throws RemoteException { - mService.confirmPassword(response, account, password); + mService.confirmPassword(mResponse, account, password); } - }; - } - - public Account[] blockingGetAccountsWithTypeAndFeatures(String type, String[] features) - throws AuthenticatorException, IOException, OperationCanceledException { - Future2 future = getAccountsWithTypeAndFeatures(type, features, - null /* callback */, null /* handler */); - Bundle result = future.getResult(); - Parcelable[] accountsTemp = result.getParcelableArray(Constants.ACCOUNTS_KEY); - if (accountsTemp == null) { - throw new AuthenticatorException("accounts should not be null"); - } - Account[] accounts = new Account[accountsTemp.length]; - for (int i = 0; i < accountsTemp.length; i++) { - accounts[i] = (Account) accountsTemp[i]; - } - return accounts; - } - - public Future2 getAccountsWithTypeAndFeatures( - final String type, final String[] features, - Future2Callback callback, Handler handler) { - if (type == null) throw new IllegalArgumentException("type is null"); - return new AmsTask(null /* activity */, handler, callback) { - public void doWork() throws RemoteException { - mService.getAccountsByTypeAndFeatures(mResponse, type, features); + public Boolean bundleToResult(Bundle bundle) throws AuthenticatorException { + if (!bundle.containsKey(Constants.BOOLEAN_RESULT_KEY)) { + throw new AuthenticatorException("no result in response"); + } + return bundle.getBoolean(Constants.BOOLEAN_RESULT_KEY); } }.start(); } - public Future2 confirmCredentials(final Account account, final Activity activity, - final Future2Callback callback, + public AccountManagerFuture getAccountsByTypeAndFeatures( + final String type, final String[] features, + AccountManagerCallback callback, Handler handler) { + if (type == null) throw new IllegalArgumentException("type is null"); + return new Future2Task(handler, callback) { + public void doWork() throws RemoteException { + mService.getAccountsByFeatures(mResponse, type, features); + } + public Account[] bundleToResult(Bundle bundle) throws AuthenticatorException { + if (!bundle.containsKey(Constants.ACCOUNTS_KEY)) { + throw new AuthenticatorException("no result in response"); + } + final Parcelable[] parcelables = bundle.getParcelableArray(Constants.ACCOUNTS_KEY); + Account[] descs = new Account[parcelables.length]; + for (int i = 0; i < parcelables.length; i++) { + descs[i] = (Account) parcelables[i]; + } + return descs; + } + }.start(); + } + + public AccountManagerFuture confirmCredentials(final Account account, final Activity activity, + final AccountManagerCallback callback, final Handler handler) { return new AmsTask(activity, handler, callback) { public void doWork() throws RemoteException { @@ -435,9 +308,9 @@ public class AccountManager { }.start(); } - public Future2 updateCredentials(final Account account, final String authTokenType, + public AccountManagerFuture updateCredentials(final Account account, final String authTokenType, final Bundle loginOptions, final Activity activity, - final Future2Callback callback, + final AccountManagerCallback callback, final Handler handler) { return new AmsTask(activity, handler, callback) { public void doWork() throws RemoteException { @@ -447,8 +320,8 @@ public class AccountManager { }.start(); } - public Future2 editProperties(final String accountType, final Activity activity, - final Future2Callback callback, + public AccountManagerFuture editProperties(final String accountType, final Activity activity, + final AccountManagerCallback callback, final Handler handler) { return new AmsTask(activity, handler, callback) { public void doWork() throws RemoteException { @@ -471,8 +344,8 @@ public class AccountManager { } } - private void postToHandler(Handler handler, final Future2Callback callback, - final Future2 future) { + private void postToHandler(Handler handler, final AccountManagerCallback callback, + final AccountManagerFuture future) { handler = handler == null ? mMainHandler : handler; handler.post(new Runnable() { public void run() { @@ -483,87 +356,24 @@ public class AccountManager { private void postToHandler(Handler handler, final OnAccountsUpdatedListener listener, final Account[] accounts) { - handler = handler == null ? mMainHandler : handler; + final Account[] accountsCopy = new Account[accounts.length]; + // send a copy to make sure that one doesn't + // change what another sees + System.arraycopy(accounts, 0, accountsCopy, 0, accountsCopy.length); + handler = (handler == null) ? mMainHandler : handler; handler.post(new Runnable() { public void run() { - listener.onAccountsUpdated(accounts); + listener.onAccountsUpdated(accountsCopy); } }); } - private void postToHandler(Handler handler, final Future1Callback callback, - final Future1 future) { - handler = handler == null ? mMainHandler : handler; - handler.post(new Runnable() { - public void run() { - callback.run(future); - } - }); - } - - private Future1 startAsFuture(Future1Callback callback, Handler handler, - Callable callable) { - final FutureTaskWithCallback task = - new FutureTaskWithCallback(callback, callable, handler); - new Thread(task).start(); - return task; - } - - private class FutureTaskWithCallback extends FutureTask implements Future1 { - final Future1Callback mCallback; - final Handler mHandler; - - public FutureTaskWithCallback(Future1Callback callback, Callable callable, - Handler handler) { - super(callable); - mCallback = callback; - mHandler = handler; - } - - protected void done() { - if (mCallback != null) { - postToHandler(mHandler, mCallback, this); - } - } - - public V internalGetResult(Long timeout, TimeUnit unit) throws OperationCanceledException { - try { - if (timeout == null) { - return get(); - } else { - return get(timeout, unit); - } - } catch (InterruptedException e) { - // we will cancel the task below - } catch (CancellationException e) { - // we will cancel the task below - } catch (TimeoutException e) { - // we will cancel the task below - } catch (ExecutionException e) { - // this should never happen - throw new IllegalStateException(e.getCause()); - } finally { - cancel(true /* interruptIfRunning */); - } - throw new OperationCanceledException(); - } - - public V getResult() throws OperationCanceledException { - return internalGetResult(null, null); - } - - public V getResult(long timeout, TimeUnit unit) throws OperationCanceledException { - return internalGetResult(null, null); - } - } - - private abstract class AmsTask extends FutureTask implements Future2 { + private abstract class AmsTask extends FutureTask implements AccountManagerFuture { final IAccountManagerResponse mResponse; final Handler mHandler; - final Future2Callback mCallback; + final AccountManagerCallback mCallback; final Activity mActivity; - final Thread mThread; - public AmsTask(Activity activity, Handler handler, Future2Callback callback) { + public AmsTask(Activity activity, Handler handler, AccountManagerCallback callback) { super(new Callable() { public Bundle call() throws Exception { throw new IllegalStateException("this should never be called"); @@ -574,19 +384,14 @@ public class AccountManager { mCallback = callback; mActivity = activity; mResponse = new Response(); - mThread = new Thread(new Runnable() { - public void run() { - try { - doWork(); - } catch (RemoteException e) { - // never happens - } - } - }, "AmsTask"); } - public final Future2 start() { - mThread.start(); + public final AccountManagerFuture start() { + try { + doWork(); + } catch (RemoteException e) { + setException(e); + } return this; } @@ -594,6 +399,7 @@ public class AccountManager { private Bundle internalGetResult(Long timeout, TimeUnit unit) throws OperationCanceledException, IOException, AuthenticatorException { + ensureNotOnMainThread(); try { if (timeout == null) { return get(); @@ -676,92 +482,50 @@ public class AccountManager { } - private abstract class AMSTaskBoolean extends FutureTask implements Future1 { - final IAccountManagerResponse response; + private abstract class BaseFutureTask extends FutureTask { + final public IAccountManagerResponse mResponse; final Handler mHandler; - final Future1Callback mCallback; - public AMSTaskBoolean(Handler handler, Future1Callback callback) { - super(new Callable() { - public Boolean call() throws Exception { + + public BaseFutureTask(Handler handler) { + super(new Callable() { + public T call() throws Exception { throw new IllegalStateException("this should never be called"); } }); - mHandler = handler; - mCallback = callback; - response = new Response(); - - new Thread(new Runnable() { - public void run() { - try { - doWork(); - } catch (RemoteException e) { - // never happens - } - } - }).start(); + mResponse = new Response(); } public abstract void doWork() throws RemoteException; + public abstract T bundleToResult(Bundle bundle) throws AuthenticatorException; - protected void done() { - if (mCallback != null) { - postToHandler(mHandler, mCallback, this); - } + protected void postRunnableToHandler(Runnable runnable) { + Handler handler = (mHandler == null) ? mMainHandler : mHandler; + handler.post(runnable); } - private Boolean internalGetResult(Long timeout, TimeUnit unit) { + protected void startTask() { try { - if (timeout == null) { - return get(); - } else { - return get(timeout, unit); - } - } catch (InterruptedException e) { - // fall through and cancel - } catch (TimeoutException e) { - // fall through and cancel - } catch (CancellationException e) { - return false; - } catch (ExecutionException e) { - final Throwable cause = e.getCause(); - if (cause instanceof IOException) { - return false; - } else if (cause instanceof UnsupportedOperationException) { - return false; - } else if (cause instanceof AuthenticatorException) { - return false; - } else if (cause instanceof RuntimeException) { - throw (RuntimeException) cause; - } else if (cause instanceof Error) { - throw (Error) cause; - } else { - throw new IllegalStateException(cause); - } - } finally { - cancel(true /* interrupt if running */); + doWork(); + } catch (RemoteException e) { + setException(e); } - return false; } - public Boolean getResult() throws OperationCanceledException { - return internalGetResult(null, null); - } - - public Boolean getResult(long timeout, TimeUnit unit) throws OperationCanceledException { - return internalGetResult(timeout, unit); - } - - private class Response extends IAccountManagerResponse.Stub { + protected class Response extends IAccountManagerResponse.Stub { public void onResult(Bundle bundle) { try { - if (bundle.containsKey(Constants.BOOLEAN_RESULT_KEY)) { - set(bundle.getBoolean(Constants.BOOLEAN_RESULT_KEY)); + T result = bundleToResult(bundle); + if (result == null) { return; } + set(result); + return; } catch (ClassCastException e) { // we will set the exception below + } catch (AuthenticatorException e) { + // we will set the exception below } onError(Constants.ERROR_CODE_INVALID_RESPONSE, "no result in response"); } @@ -774,6 +538,76 @@ public class AccountManager { setException(convertErrorToException(code, message)); } } + } + + private abstract class Future2Task + extends BaseFutureTask implements AccountManagerFuture { + final AccountManagerCallback mCallback; + public Future2Task(Handler handler, AccountManagerCallback callback) { + super(handler); + mCallback = callback; + } + + protected void done() { + if (mCallback != null) { + postRunnableToHandler(new Runnable() { + public void run() { + mCallback.run(Future2Task.this); + } + }); + } + } + + public Future2Task start() { + startTask(); + return this; + } + + private T internalGetResult(Long timeout, TimeUnit unit) + throws OperationCanceledException, IOException, AuthenticatorException { + ensureNotOnMainThread(); + try { + if (timeout == null) { + return get(); + } else { + return get(timeout, unit); + } + } catch (InterruptedException e) { + // fall through and cancel + } catch (TimeoutException e) { + // fall through and cancel + } catch (CancellationException e) { + // fall through and cancel + } catch (ExecutionException e) { + final Throwable cause = e.getCause(); + if (cause instanceof IOException) { + throw (IOException) cause; + } else if (cause instanceof UnsupportedOperationException) { + throw new AuthenticatorException(cause); + } else if (cause instanceof AuthenticatorException) { + throw (AuthenticatorException) cause; + } else if (cause instanceof RuntimeException) { + throw (RuntimeException) cause; + } else if (cause instanceof Error) { + throw (Error) cause; + } else { + throw new IllegalStateException(cause); + } + } finally { + cancel(true /* interrupt if running */); + } + throw new OperationCanceledException(); + } + + public T getResult() + throws OperationCanceledException, IOException, AuthenticatorException { + return internalGetResult(null, null); + } + + public T getResult(long timeout, TimeUnit unit) + throws OperationCanceledException, IOException, AuthenticatorException { + return internalGetResult(timeout, unit); + } } @@ -797,11 +631,12 @@ public class AccountManager { return new AuthenticatorException(message); } - private class GetAuthTokenByTypeAndFeaturesTask extends AmsTask implements Future2Callback { + private class GetAuthTokenByTypeAndFeaturesTask + extends AmsTask implements AccountManagerCallback { GetAuthTokenByTypeAndFeaturesTask(final String accountType, final String authTokenType, final String[] features, Activity activityForPrompting, final Bundle addAccountOptions, final Bundle loginOptions, - Future2Callback callback, Handler handler) { + AccountManagerCallback callback, Handler handler) { super(activityForPrompting, handler, callback); if (accountType == null) throw new IllegalArgumentException("account type is null"); mAccountType = accountType; @@ -811,101 +646,100 @@ public class AccountManager { mLoginOptions = loginOptions; mMyCallback = this; } - volatile Future2 mFuture = null; + volatile AccountManagerFuture mFuture = null; final String mAccountType; final String mAuthTokenType; final String[] mFeatures; final Bundle mAddAccountOptions; final Bundle mLoginOptions; - final Future2Callback mMyCallback; + final AccountManagerCallback mMyCallback; public void doWork() throws RemoteException { - getAccountsWithTypeAndFeatures(mAccountType, mFeatures, new Future2Callback() { - public void run(Future2 future) { - Bundle getAccountsResult; - try { - getAccountsResult = future.getResult(); - } catch (OperationCanceledException e) { - setException(e); - return; - } catch (IOException e) { - setException(e); - return; - } catch (AuthenticatorException e) { - setException(e); - return; - } - - Parcelable[] accounts = - getAccountsResult.getParcelableArray(Constants.ACCOUNTS_KEY); - if (accounts.length == 0) { - if (mActivity != null) { - // no accounts, add one now. pretend that the user directly - // made this request - mFuture = addAccount(mAccountType, mAuthTokenType, mFeatures, - mAddAccountOptions, mActivity, mMyCallback, mHandler); - } else { - // send result since we can't prompt to add an account - Bundle result = new Bundle(); - result.putString(Constants.ACCOUNT_NAME_KEY, null); - result.putString(Constants.ACCOUNT_TYPE_KEY, null); - result.putString(Constants.AUTHTOKEN_KEY, null); + getAccountsByTypeAndFeatures(mAccountType, mFeatures, + new AccountManagerCallback() { + public void run(AccountManagerFuture future) { + Account[] accounts; try { - mResponse.onResult(result); - } catch (RemoteException e) { - // this will never happen + accounts = future.getResult(); + } catch (OperationCanceledException e) { + setException(e); + return; + } catch (IOException e) { + setException(e); + return; + } catch (AuthenticatorException e) { + setException(e); + return; } - // we are done - } - } else if (accounts.length == 1) { - // have a single account, return an authtoken for it - if (mActivity == null) { - mFuture = getAuthToken((Account) accounts[0], mAuthTokenType, - false /* notifyAuthFailure */, mMyCallback, mHandler); - } else { - mFuture = getAuthToken((Account) accounts[0], - mAuthTokenType, mLoginOptions, - mActivity, mMyCallback, mHandler); - } - } else { - if (mActivity != null) { - IAccountManagerResponse chooseResponse = - new IAccountManagerResponse.Stub() { - public void onResult(Bundle value) throws RemoteException { - Account account = new Account( - value.getString(Constants.ACCOUNT_NAME_KEY), - value.getString(Constants.ACCOUNT_TYPE_KEY)); - mFuture = getAuthToken(account, mAuthTokenType, mLoginOptions, + + if (accounts.length == 0) { + if (mActivity != null) { + // no accounts, add one now. pretend that the user directly + // made this request + mFuture = addAccount(mAccountType, mAuthTokenType, mFeatures, + mAddAccountOptions, mActivity, mMyCallback, mHandler); + } else { + // send result since we can't prompt to add an account + Bundle result = new Bundle(); + result.putString(Constants.ACCOUNT_NAME_KEY, null); + result.putString(Constants.ACCOUNT_TYPE_KEY, null); + result.putString(Constants.AUTHTOKEN_KEY, null); + try { + mResponse.onResult(result); + } catch (RemoteException e) { + // this will never happen + } + // we are done + } + } else if (accounts.length == 1) { + // have a single account, return an authtoken for it + if (mActivity == null) { + mFuture = getAuthToken(accounts[0], mAuthTokenType, + false /* notifyAuthFailure */, mMyCallback, mHandler); + } else { + mFuture = getAuthToken(accounts[0], + mAuthTokenType, mLoginOptions, mActivity, mMyCallback, mHandler); } + } else { + if (mActivity != null) { + IAccountManagerResponse chooseResponse = + new IAccountManagerResponse.Stub() { + public void onResult(Bundle value) throws RemoteException { + Account account = new Account( + value.getString(Constants.ACCOUNT_NAME_KEY), + value.getString(Constants.ACCOUNT_TYPE_KEY)); + mFuture = getAuthToken(account, mAuthTokenType, mLoginOptions, + mActivity, mMyCallback, mHandler); + } - public void onError(int errorCode, String errorMessage) - throws RemoteException { - mResponse.onError(errorCode, errorMessage); + public void onError(int errorCode, String errorMessage) + throws RemoteException { + mResponse.onError(errorCode, errorMessage); + } + }; + // have many accounts, launch the chooser + Intent intent = new Intent(); + intent.setClassName("android", + "android.accounts.ChooseAccountActivity"); + intent.putExtra(Constants.ACCOUNTS_KEY, accounts); + intent.putExtra(Constants.ACCOUNT_MANAGER_RESPONSE_KEY, + new AccountManagerResponse(chooseResponse)); + mActivity.startActivity(intent); + // the result will arrive via the IAccountManagerResponse + } else { + // send result since we can't prompt to select an account + Bundle result = new Bundle(); + result.putString(Constants.ACCOUNTS_KEY, null); + try { + mResponse.onResult(result); + } catch (RemoteException e) { + // this will never happen + } + // we are done } - }; - // have many accounts, launch the chooser - Intent intent = new Intent(); - intent.setClassName("android", - "android.accounts.ChooseAccountActivity"); - intent.putExtra(Constants.ACCOUNTS_KEY, accounts); - intent.putExtra(Constants.ACCOUNT_MANAGER_RESPONSE_KEY, - new AccountManagerResponse(chooseResponse)); - mActivity.startActivity(intent); - // the result will arrive via the IAccountManagerResponse - } else { - // send result since we can't prompt to select an account - Bundle result = new Bundle(); - result.putString(Constants.ACCOUNTS_KEY, null); - try { - mResponse.onResult(result); - } catch (RemoteException e) { - // this will never happen } - // we are done - } - } - }}, mHandler); + }}, mHandler); } @@ -915,7 +749,7 @@ public class AccountManager { // or to cause this to be canceled if mFuture isn't set. // Once this is done then getAuthTokenByFeatures can be changed to return a Future2. - public void run(Future2 future) { + public void run(AccountManagerFuture future) { try { set(future.get()); } catch (InterruptedException e) { @@ -932,7 +766,7 @@ public class AccountManager { final String accountType, final String authTokenType, final String[] features, final Activity activityForPrompting, final Bundle addAccountOptions, final Bundle loginOptions, - final Future2Callback callback, final Handler handler) { + final AccountManagerCallback callback, final Handler handler) { if (accountType == null) throw new IllegalArgumentException("account type is null"); if (authTokenType == null) throw new IllegalArgumentException("authTokenType is null"); new GetAuthTokenByTypeAndFeaturesTask(accountType, authTokenType, features, @@ -942,13 +776,6 @@ public class AccountManager { private final HashMap mAccountsUpdatedListeners = Maps.newHashMap(); - // These variable are only used from the LOGIN_ACCOUNTS_CHANGED_ACTION BroadcastReceiver - // and its getAccounts() callback which are both invoked only on the main thread. As a - // result we don't need to protect against concurrent accesses and any changes are guaranteed - // to be visible when used. Basically, these two variables are thread-confined. - private Future1 mAccountsLookupFuture = null; - private boolean mAccountLookupPending = false; - /** * BroadcastReceiver that listens for the LOGIN_ACCOUNTS_CHANGED_ACTION intent * so that it can read the updated list of accounts and send them to the listener @@ -956,58 +783,14 @@ public class AccountManager { */ private final BroadcastReceiver mAccountsChangedBroadcastReceiver = new BroadcastReceiver() { public void onReceive(final Context context, final Intent intent) { - if (mAccountsLookupFuture != null) { - // an accounts lookup is already in progress, - // don't bother starting another request - mAccountLookupPending = true; - return; - } - // initiate a read of the accounts - mAccountsLookupFuture = getAccounts(new Future1Callback() { - public void run(Future1 future) { - // clear the future so that future receives will try the lookup again - mAccountsLookupFuture = null; - - // get the accounts array - Account[] accounts; - try { - accounts = future.getResult(); - } catch (OperationCanceledException e) { - // this should never happen, but if it does pretend we got another - // accounts changed broadcast - if (Config.LOGD) { - Log.d(TAG, "the accounts lookup for listener notifications was " - + "canceled, try again by simulating the receipt of " - + "a LOGIN_ACCOUNTS_CHANGED_ACTION broadcast"); - } - onReceive(context, intent); - return; - } - - // send the result to the listeners - synchronized (mAccountsUpdatedListeners) { - for (Map.Entry entry : - mAccountsUpdatedListeners.entrySet()) { - Account[] accountsCopy = new Account[accounts.length]; - // send the listeners a copy to make sure that one doesn't - // change what another sees - System.arraycopy(accounts, 0, accountsCopy, 0, accountsCopy.length); - postToHandler(entry.getValue(), entry.getKey(), accountsCopy); - } - } - - // If mAccountLookupPending was set when the account lookup finished it - // means that we had previously ignored a LOGIN_ACCOUNTS_CHANGED_ACTION - // intent because a lookup was already in progress. Now that we are done - // with this lookup and notification pretend that another intent - // was received by calling onReceive() directly. - if (mAccountLookupPending) { - mAccountLookupPending = false; - onReceive(context, intent); - return; - } + final Account[] accounts = getAccounts(); + // send the result to the listeners + synchronized (mAccountsUpdatedListeners) { + for (Map.Entry entry : + mAccountsUpdatedListeners.entrySet()) { + postToHandler(entry.getValue(), entry.getKey(), accounts); } - }, mMainHandler); + } } }; @@ -1045,15 +828,7 @@ public class AccountManager { } if (updateImmediately) { - getAccounts(new Future1Callback() { - public void run(Future1 future) { - try { - listener.onAccountsUpdated(future.getResult()); - } catch (OperationCanceledException e) { - // ignore - } - } - }, handler); + postToHandler(handler, listener, getAccounts()); } } diff --git a/core/java/android/accounts/Future1Callback.java b/core/java/android/accounts/AccountManagerCallback.java similarity index 87% rename from core/java/android/accounts/Future1Callback.java rename to core/java/android/accounts/AccountManagerCallback.java index 886671ba6b36b..4aa7169088c53 100644 --- a/core/java/android/accounts/Future1Callback.java +++ b/core/java/android/accounts/AccountManagerCallback.java @@ -15,6 +15,6 @@ */ package android.accounts; -public interface Future1Callback { - void run(Future1 future); -} +public interface AccountManagerCallback { + void run(AccountManagerFuture future); +} \ No newline at end of file diff --git a/core/java/android/accounts/AccountManagerFuture.java b/core/java/android/accounts/AccountManagerFuture.java new file mode 100644 index 0000000000000..99393987462ea --- /dev/null +++ b/core/java/android/accounts/AccountManagerFuture.java @@ -0,0 +1,65 @@ +/* + * Copyright (C) 2009 The Android Open Source Project + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ +package android.accounts; + +import java.util.concurrent.Future; +import java.util.concurrent.TimeUnit; +import java.util.concurrent.ExecutionException; +import java.util.concurrent.TimeoutException; +import java.io.IOException; + +/** + * An extension of {@link java.util.concurrent.Future} that provides wrappers for {@link #get()} + * that handle the various + * exceptions that {@link #get()} may return and rethrows them as exceptions specific to + * {@link android.accounts.AccountManager}. + */ +public interface AccountManagerFuture extends Future { + /** + * Wrapper for {@link java.util.concurrent.Future#get()}. If the get() throws + * {@link InterruptedException} then the + * {@link AccountManagerFuture} is canceled and + * {@link android.accounts.OperationCanceledException} is thrown. + * @return the {@link android.os.Bundle} that is returned by get() + * @throws android.accounts.OperationCanceledException if get() throws the unchecked + * CancellationException + * or if the Future was interrupted. + */ + V getResult() throws OperationCanceledException, IOException, AuthenticatorException; + + /** + * Wrapper for {@link java.util.concurrent.Future#get()}. If the get() throws + * {@link InterruptedException} then the + * {@link AccountManagerFuture} is canceled and + * {@link android.accounts.OperationCanceledException} is thrown. + * @param timeout the maximum time to wait + * @param unit the time unit of the timeout argument + * @return the {@link android.os.Bundle} that is returned by + * {@link java.util.concurrent.Future#get()} + * @throws android.accounts.OperationCanceledException if get() throws the unchecked + * {@link java.util.concurrent.CancellationException} or if the {@link AccountManagerFuture} + * was interrupted. + */ + V getResult(long timeout, TimeUnit unit) + throws OperationCanceledException, IOException, AuthenticatorException; + + @Deprecated + V get() throws InterruptedException, ExecutionException; + + @Deprecated + V get(long timeout, TimeUnit unit) + throws InterruptedException, ExecutionException, TimeoutException; +} \ No newline at end of file diff --git a/core/java/android/accounts/AccountManagerService.java b/core/java/android/accounts/AccountManagerService.java index 0c941bea3837d..140c814394f61 100644 --- a/core/java/android/accounts/AccountManagerService.java +++ b/core/java/android/accounts/AccountManagerService.java @@ -214,10 +214,48 @@ public class AccountManagerService extends IAccountManager.Stub { long identityToken = clearCallingIdentity(); try { - SQLiteDatabase db = mOpenHelper.getReadableDatabase(); - Cursor cursor = db.query(TABLE_ACCOUNTS, new String[]{ACCOUNTS_PASSWORD}, - ACCOUNTS_NAME + "=? AND " + ACCOUNTS_TYPE+ "=?", - new String[]{account.mName, account.mType}, null, null, null); + return readPasswordFromDatabase(account); + } finally { + restoreCallingIdentity(identityToken); + } + } + + private String readPasswordFromDatabase(Account account) { + SQLiteDatabase db = mOpenHelper.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); + try { + if (cursor.moveToNext()) { + return cursor.getString(0); + } + return null; + } finally { + cursor.close(); + } + } + + public String getUserData(Account account, String key) { + checkAuthenticateAccountsPermission(account); + long identityToken = clearCallingIdentity(); + try { + return readUserDataFromDatabase(account, key); + } finally { + restoreCallingIdentity(identityToken); + } + } + + private String readUserDataFromDatabase(Account account, String key) { + SQLiteDatabase db = mOpenHelper.getReadableDatabase(); + db.beginTransaction(); + try { + long accountId = getAccountId(db, account); + if (accountId < 0) { + return null; + } + Cursor cursor = db.query(TABLE_EXTRAS, new String[]{EXTRAS_VALUE}, + EXTRAS_ACCOUNTS_ID + "=" + accountId + " AND " + EXTRAS_KEY + "=?", + new String[]{key}, null, null, null); try { if (cursor.moveToNext()) { return cursor.getString(0); @@ -227,37 +265,7 @@ public class AccountManagerService extends IAccountManager.Stub { cursor.close(); } } finally { - restoreCallingIdentity(identityToken); - } - } - - public String getUserData(Account account, String key) { - checkAuthenticateAccountsPermission(account); - long identityToken = clearCallingIdentity(); - try { - SQLiteDatabase db = mOpenHelper.getReadableDatabase(); - db.beginTransaction(); - try { - long accountId = getAccountId(db, account); - if (accountId < 0) { - return null; - } - Cursor cursor = db.query(TABLE_EXTRAS, new String[]{EXTRAS_VALUE}, - EXTRAS_ACCOUNTS_ID + "=" + accountId + " AND " + EXTRAS_KEY + "=?", - new String[]{key}, null, null, null); - try { - if (cursor.moveToNext()) { - return cursor.getString(0); - } - return null; - } finally { - cursor.close(); - } - } finally { - db.endTransaction(); - } - } finally { - restoreCallingIdentity(identityToken); + db.endTransaction(); } } @@ -280,39 +288,23 @@ public class AccountManagerService extends IAccountManager.Stub { } } - public Account[] getAccounts() { - checkReadAccountsPermission(); - long identityToken = clearCallingIdentity(); - try { - return getAccountsByType(null); - } finally { - restoreCallingIdentity(identityToken); - } - } - public Account[] getAccountsByType(String accountType) { - checkReadAccountsPermission(); - long identityToken = clearCallingIdentity(); - try { - SQLiteDatabase db = mOpenHelper.getReadableDatabase(); + SQLiteDatabase db = mOpenHelper.getReadableDatabase(); - final String selection = accountType == null ? null : (ACCOUNTS_TYPE + "=?"); - final String[] selectionArgs = accountType == null ? null : new String[]{accountType}; - Cursor cursor = db.query(TABLE_ACCOUNTS, ACCOUNT_NAME_TYPE_PROJECTION, - selection, selectionArgs, null, null, null); - try { - int i = 0; - Account[] accounts = new Account[cursor.getCount()]; - while (cursor.moveToNext()) { - accounts[i] = new Account(cursor.getString(1), cursor.getString(2)); - i++; - } - return accounts; - } finally { - cursor.close(); + final String selection = accountType == null ? null : (ACCOUNTS_TYPE + "=?"); + final String[] selectionArgs = accountType == null ? null : new String[]{accountType}; + Cursor cursor = db.query(TABLE_ACCOUNTS, ACCOUNT_NAME_TYPE_PROJECTION, + selection, selectionArgs, null, null, null); + try { + int i = 0; + Account[] accounts = new Account[cursor.getCount()]; + while (cursor.moveToNext()) { + accounts[i] = new Account(cursor.getString(1), cursor.getString(2)); + i++; } + return accounts; } finally { - restoreCallingIdentity(identityToken); + cursor.close(); } } @@ -322,43 +314,47 @@ public class AccountManagerService extends IAccountManager.Stub { // fails if the account already exists long identityToken = clearCallingIdentity(); try { - SQLiteDatabase db = mOpenHelper.getWritableDatabase(); - db.beginTransaction(); - try { - long numMatches = DatabaseUtils.longForQuery(db, - "select count(*) from " + TABLE_ACCOUNTS - + " WHERE " + ACCOUNTS_NAME + "=? AND " + ACCOUNTS_TYPE+ "=?", - new String[]{account.mName, account.mType}); - if (numMatches > 0) { - return false; - } - ContentValues values = new ContentValues(); - values.put(ACCOUNTS_NAME, account.mName); - values.put(ACCOUNTS_TYPE, account.mType); - values.put(ACCOUNTS_PASSWORD, password); - long accountId = db.insert(TABLE_ACCOUNTS, ACCOUNTS_NAME, values); - if (accountId < 0) { - return false; - } - if (extras != null) { - for (String key : extras.keySet()) { - final String value = extras.getString(key); - if (insertExtra(db, accountId, key, value) < 0) { - return false; - } - } - } - db.setTransactionSuccessful(); - sendAccountsChangedBroadcast(); - return true; - } finally { - db.endTransaction(); - } + return insertAccountIntoDatabase(account, password, extras); } finally { restoreCallingIdentity(identityToken); } } + private boolean insertAccountIntoDatabase(Account account, String password, Bundle extras) { + SQLiteDatabase db = mOpenHelper.getWritableDatabase(); + db.beginTransaction(); + try { + long numMatches = DatabaseUtils.longForQuery(db, + "select count(*) from " + TABLE_ACCOUNTS + + " WHERE " + ACCOUNTS_NAME + "=? AND " + ACCOUNTS_TYPE+ "=?", + new String[]{account.name, account.type}); + if (numMatches > 0) { + return false; + } + ContentValues values = new ContentValues(); + values.put(ACCOUNTS_NAME, account.name); + values.put(ACCOUNTS_TYPE, account.type); + values.put(ACCOUNTS_PASSWORD, password); + long accountId = db.insert(TABLE_ACCOUNTS, ACCOUNTS_NAME, values); + if (accountId < 0) { + return false; + } + if (extras != null) { + for (String key : extras.keySet()) { + final String value = extras.getString(key); + if (insertExtra(db, accountId, key, value) < 0) { + return false; + } + } + } + db.setTransactionSuccessful(); + sendAccountsChangedBroadcast(); + return true; + } finally { + db.endTransaction(); + } + } + private long insertExtra(SQLiteDatabase db, long accountId, String key, String value) { ContentValues values = new ContentValues(); values.put(EXTRAS_KEY, key); @@ -367,19 +363,61 @@ public class AccountManagerService extends IAccountManager.Stub { return db.insert(TABLE_EXTRAS, EXTRAS_KEY, values); } - public void removeAccount(Account account) { + public void removeAccount(IAccountManagerResponse response, Account account) { checkManageAccountsPermission(); long identityToken = clearCallingIdentity(); try { - final SQLiteDatabase db = mOpenHelper.getWritableDatabase(); - db.delete(TABLE_ACCOUNTS, ACCOUNTS_NAME + "=? AND " + ACCOUNTS_TYPE+ "=?", - new String[]{account.mName, account.mType}); - sendAccountsChangedBroadcast(); + new RemoveAccountSession(response, account).bind(); } finally { restoreCallingIdentity(identityToken); } } + private class RemoveAccountSession extends Session { + final Account mAccount; + public RemoveAccountSession(IAccountManagerResponse response, Account account) { + super(response, account.type, false /* expectActivityLaunch */); + mAccount = account; + } + + protected String toDebugString(long now) { + return super.toDebugString(now) + ", removeAccount" + + ", account " + mAccount; + } + + public void run() throws RemoteException { + mAuthenticator.getAccountRemovalAllowed(this, mAccount); + } + + public void onResult(Bundle result) { + if (result != null && result.containsKey(Constants.BOOLEAN_RESULT_KEY) + && !result.containsKey(Constants.INTENT_KEY)) { + final boolean removalAllowed = result.getBoolean(Constants.BOOLEAN_RESULT_KEY); + if (removalAllowed) { + removeAccount(mAccount); + } + IAccountManagerResponse response = getResponseAndClose(); + if (response != null) { + Bundle result2 = new Bundle(); + result2.putBoolean(Constants.BOOLEAN_RESULT_KEY, removalAllowed); + try { + response.onResult(result2); + } catch (RemoteException e) { + // ignore + } + } + } + super.onResult(result); + } + } + + private void removeAccount(Account account) { + final SQLiteDatabase db = mOpenHelper.getWritableDatabase(); + db.delete(TABLE_ACCOUNTS, ACCOUNTS_NAME + "=? AND " + ACCOUNTS_TYPE+ "=?", + new String[]{account.name, account.type}); + sendAccountsChangedBroadcast(); + } + public void invalidateAuthToken(String accountType, String authToken) { checkManageAccountsPermission(); long identityToken = clearCallingIdentity(); @@ -398,6 +436,9 @@ public class AccountManagerService extends IAccountManager.Stub { } private void invalidateAuthToken(SQLiteDatabase db, String accountType, String authToken) { + if (authToken == null || accountType == null) { + return; + } Cursor cursor = db.rawQuery( "SELECT " + TABLE_AUTHTOKENS + "." + AUTHTOKENS_ID + ", " + TABLE_ACCOUNTS + "." + ACCOUNTS_NAME @@ -488,7 +529,7 @@ public class AccountManagerService extends IAccountManager.Stub { values.put(ACCOUNTS_PASSWORD, password); mOpenHelper.getWritableDatabase().update(TABLE_ACCOUNTS, values, ACCOUNTS_NAME + "=? AND " + ACCOUNTS_TYPE+ "=?", - new String[]{account.mName, account.mType}); + new String[]{account.name, account.type}); sendAccountsChangedBroadcast(); } finally { restoreCallingIdentity(identityToken); @@ -509,40 +550,58 @@ public class AccountManagerService extends IAccountManager.Stub { } } + private void sendResult(IAccountManagerResponse response, Bundle bundle) { + if (response != null) { + try { + response.onResult(bundle); + } catch (RemoteException e) { + // if the caller is dead then there is no one to care about remote + // exceptions + if (Log.isLoggable(TAG, Log.VERBOSE)) { + Log.v(TAG, "failure while notifying response", e); + } + } + } + } + public void setUserData(Account account, String key, String value) { checkAuthenticateAccountsPermission(account); long identityToken = clearCallingIdentity(); try { - SQLiteDatabase db = mOpenHelper.getWritableDatabase(); - db.beginTransaction(); - try { - long accountId = getAccountId(db, account); - if (accountId < 0) { - return; - } - long extrasId = getExtrasId(db, accountId, key); - if (extrasId < 0 ) { - extrasId = insertExtra(db, accountId, key, value); - if (extrasId < 0) { - return; - } - } else { - ContentValues values = new ContentValues(); - values.put(EXTRAS_VALUE, value); - if (1 != db.update(TABLE_EXTRAS, values, EXTRAS_ID + "=" + extrasId, null)) { - return; - } - - } - db.setTransactionSuccessful(); - } finally { - db.endTransaction(); - } + writeUserdataIntoDatabase(account, key, value); } finally { restoreCallingIdentity(identityToken); } } + private void writeUserdataIntoDatabase(Account account, String key, String value) { + SQLiteDatabase db = mOpenHelper.getWritableDatabase(); + db.beginTransaction(); + try { + long accountId = getAccountId(db, account); + if (accountId < 0) { + return; + } + long extrasId = getExtrasId(db, accountId, key); + if (extrasId < 0 ) { + extrasId = insertExtra(db, accountId, key, value); + if (extrasId < 0) { + return; + } + } else { + ContentValues values = new ContentValues(); + values.put(EXTRAS_VALUE, value); + if (1 != db.update(TABLE_EXTRAS, values, EXTRAS_ID + "=" + extrasId, null)) { + return; + } + + } + db.setTransactionSuccessful(); + } finally { + db.endTransaction(); + } + } + private void onResult(IAccountManagerResponse response, Bundle result) { try { response.onResult(result); @@ -571,14 +630,14 @@ public class AccountManagerService extends IAccountManager.Stub { if (authToken != null) { Bundle result = new Bundle(); result.putString(Constants.AUTHTOKEN_KEY, authToken); - result.putString(Constants.ACCOUNT_NAME_KEY, account.mName); - result.putString(Constants.ACCOUNT_TYPE_KEY, account.mType); + result.putString(Constants.ACCOUNT_NAME_KEY, account.name); + result.putString(Constants.ACCOUNT_TYPE_KEY, account.type); onResult(response, result); return; } } - new Session(response, account.mType, expectActivityLaunch) { + new Session(response, account.type, expectActivityLaunch) { protected String toDebugString(long now) { if (loginOptions != null) loginOptions.keySet(); return super.toDebugString(now) + ", getAuthToken" @@ -651,7 +710,7 @@ public class AccountManagerService extends IAccountManager.Stub { mContext.getText(R.string.permission_request_notification_subtitle); n.setLatestEventInfo(mContext, mContext.getText(R.string.permission_request_notification_title), - String.format(subtitleFormatString.toString(), account.mName), + String.format(subtitleFormatString.toString(), account.name), PendingIntent.getActivity(mContext, 0, intent, PendingIntent.FLAG_CANCEL_CURRENT)); ((NotificationManager) mContext.getSystemService(Context.NOTIFICATION_SERVICE)) .notify(getCredentialPermissionNotificationId(account, authTokenType, uid), n); @@ -661,9 +720,9 @@ public class AccountManagerService extends IAccountManager.Stub { AccountAuthenticatorResponse response, String authTokenType, String authTokenLabel) { RegisteredServicesCache.ServiceInfo serviceInfo = mAuthenticatorCache.getServiceInfo( - AuthenticatorDescription.newKey(account.mType)); + AuthenticatorDescription.newKey(account.type)); if (serviceInfo == null) { - throw new IllegalArgumentException("unknown account type: " + account.mType); + throw new IllegalArgumentException("unknown account type: " + account.type); } final Context authContext; @@ -671,7 +730,7 @@ public class AccountManagerService extends IAccountManager.Stub { authContext = mContext.createPackageContext( serviceInfo.type.packageName, 0); } catch (PackageManager.NameNotFoundException e) { - throw new IllegalArgumentException("unknown account type: " + account.mType); + throw new IllegalArgumentException("unknown account type: " + account.type); } Intent intent = new Intent(mContext, GrantCredentialsPermissionActivity.class); @@ -750,7 +809,7 @@ public class AccountManagerService extends IAccountManager.Stub { checkManageAccountsPermission(); long identityToken = clearCallingIdentity(); try { - new Session(response, account.mType, expectActivityLaunch) { + new Session(response, account.type, expectActivityLaunch) { public void run() throws RemoteException { mAuthenticator.confirmCredentials(this, account); } @@ -769,7 +828,7 @@ public class AccountManagerService extends IAccountManager.Stub { checkManageAccountsPermission(); long identityToken = clearCallingIdentity(); try { - new Session(response, account.mType, false /* expectActivityLaunch */) { + new Session(response, account.type, false /* expectActivityLaunch */) { public void run() throws RemoteException { mAuthenticator.confirmPassword(this, account, password); } @@ -789,7 +848,7 @@ public class AccountManagerService extends IAccountManager.Stub { checkManageAccountsPermission(); long identityToken = clearCallingIdentity(); try { - new Session(response, account.mType, expectActivityLaunch) { + new Session(response, account.type, expectActivityLaunch) { public void run() throws RemoteException { mAuthenticator.updateCredentials(this, account, authTokenType, loginOptions); } @@ -898,10 +957,21 @@ public class AccountManagerService extends IAccountManager.Stub { + ", " + (mFeatures != null ? TextUtils.join(",", mFeatures) : null); } } - public void getAccountsByTypeAndFeatures(IAccountManagerResponse response, + + public Account[] getAccounts(String type) { + checkReadAccountsPermission(); + long identityToken = clearCallingIdentity(); + try { + return getAccountsByType(type); + } finally { + restoreCallingIdentity(identityToken); + } + } + + public void getAccountsByFeatures(IAccountManagerResponse response, String type, String[] features) { checkReadAccountsPermission(); - if (type == null) { + if (features != null && type == null) { if (response != null) { try { response.onError(Constants.ERROR_CODE_BAD_ARGUMENTS, "type is null"); @@ -913,6 +983,10 @@ public class AccountManagerService extends IAccountManager.Stub { } long identityToken = clearCallingIdentity(); try { + if (features == null || features.length == 0) { + getAccountsByType(type); + return; + } new GetAccountsByTypeAndFeatureSession(response, type, features).bind(); } finally { restoreCallingIdentity(identityToken); @@ -925,7 +999,7 @@ public class AccountManagerService extends IAccountManager.Stub { private long getAccountId(SQLiteDatabase db, Account account) { Cursor cursor = db.query(TABLE_ACCOUNTS, new String[]{ACCOUNTS_ID}, - "name=? AND type=?", new String[]{account.mName, account.mType}, null, null, null); + "name=? AND type=?", new String[]{account.name, account.type}, null, null, null); try { if (cursor.moveToNext()) { return cursor.getLong(0); @@ -1401,7 +1475,7 @@ public class AccountManagerService extends IAccountManager.Stub { } private boolean permissionIsGranted(Account account, String authTokenType, int callerUid) { - final boolean fromAuthenticator = hasAuthenticatorUid(account.mType, callerUid); + final boolean fromAuthenticator = hasAuthenticatorUid(account.type, callerUid); final boolean hasExplicitGrants = hasExplicitlyGrantedPermission(account, authTokenType); if (Log.isLoggable(TAG, Log.VERBOSE)) { Log.v(TAG, "checkGrantsOrCallingUidAgainstAuthenticator: caller uid " @@ -1416,7 +1490,9 @@ public class AccountManagerService extends IAccountManager.Stub { for (RegisteredServicesCache.ServiceInfo serviceInfo : mAuthenticatorCache.getAllServices()) { if (serviceInfo.type.type.equals(accountType)) { - return serviceInfo.uid == callingUid; + return (serviceInfo.uid == callingUid) || + (mContext.getPackageManager().checkSignatures(serviceInfo.uid, callingUid) + == PackageManager.SIGNATURE_MATCH); } } return false; @@ -1428,7 +1504,7 @@ public class AccountManagerService extends IAccountManager.Stub { } SQLiteDatabase db = mOpenHelper.getReadableDatabase(); String[] args = {String.valueOf(Binder.getCallingUid()), authTokenType, - account.mName, account.mType}; + account.name, account.type}; final boolean permissionGranted = DatabaseUtils.longForQuery(db, COUNT_OF_MATCHING_GRANTS, args) != 0; if (!permissionGranted && isDebuggableMonkeyBuild) { @@ -1444,7 +1520,7 @@ public class AccountManagerService extends IAccountManager.Stub { private void checkCallingUidAgainstAuthenticator(Account account) { final int uid = Binder.getCallingUid(); - if (!hasAuthenticatorUid(account.mType, uid)) { + if (!hasAuthenticatorUid(account.type, uid)) { String msg = "caller uid " + uid + " is different than the authenticator's uid"; Log.w(TAG, msg); throw new SecurityException(msg); diff --git a/core/java/android/accounts/ChooseAccountActivity.java b/core/java/android/accounts/ChooseAccountActivity.java index 83377f352f0b1..bd6f2054e668d 100644 --- a/core/java/android/accounts/ChooseAccountActivity.java +++ b/core/java/android/accounts/ChooseAccountActivity.java @@ -45,7 +45,7 @@ public class ChooseAccountActivity extends ListActivity { String[] mAccountNames = new String[mAccounts.length]; for (int i = 0; i < mAccounts.length; i++) { - mAccountNames[i] = ((Account) mAccounts[i]).mName; + mAccountNames[i] = ((Account) mAccounts[i]).name; } // Use an existing ListAdapter that will map an array @@ -59,8 +59,8 @@ public class ChooseAccountActivity extends ListActivity { Account account = (Account) mAccounts[position]; Log.d(TAG, "selected account " + account); Bundle bundle = new Bundle(); - bundle.putString(Constants.ACCOUNT_NAME_KEY, account.mName); - bundle.putString(Constants.ACCOUNT_TYPE_KEY, account.mType); + bundle.putString(Constants.ACCOUNT_NAME_KEY, account.name); + bundle.putString(Constants.ACCOUNT_TYPE_KEY, account.type); mResult = bundle; finish(); } diff --git a/core/java/android/accounts/Constants.java b/core/java/android/accounts/Constants.java index da8173f5f47b7..8736f41216e8a 100644 --- a/core/java/android/accounts/Constants.java +++ b/core/java/android/accounts/Constants.java @@ -31,6 +31,7 @@ public class Constants { public static final String AUTHENTICATOR_TYPES_KEY = "authenticator_types"; public static final String USERDATA_KEY = "userdata"; public static final String AUTHTOKEN_KEY = "authtoken"; + public static final String PASSWORD_KEY = "password"; public static final String ACCOUNT_NAME_KEY = "authAccount"; public static final String ACCOUNT_TYPE_KEY = "accountType"; public static final String ERROR_CODE_KEY = "errorCode"; diff --git a/core/java/android/accounts/Future1.java b/core/java/android/accounts/Future1.java deleted file mode 100644 index 386cb6ec2ad06..0000000000000 --- a/core/java/android/accounts/Future1.java +++ /dev/null @@ -1,46 +0,0 @@ -/* - * Copyright (C) 2009 The Android Open Source Project - * - * Licensed under the Apache License, Version 2.0 (the "License"); - * you may not use this file except in compliance with the License. - * You may obtain a copy of the License at - * - * http://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, - * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - * See the License for the specific language governing permissions and - * limitations under the License. - */ -package android.accounts; - -import java.util.concurrent.Future; -import java.util.concurrent.TimeUnit; - -/** - * An extension of {@link Future} that provides wrappers for {@link #get()} that handle the various - * exceptions that {@link #get()} may return and rethrows them as exceptions specific to - * {@link AccountManager}. - */ -public interface Future1 extends Future { - /** - * Wrapper for {@link Future#get()}. If the get() throws {@link InterruptedException} then the - * {@link Future1} is canceled and {@link OperationCanceledException} is thrown. - * @return the {@link android.os.Bundle} that is returned by get() - * @throws OperationCanceledException if get() throws the unchecked CancellationException - * or if the Future was interrupted. - */ - V getResult() throws OperationCanceledException; - - /** - * Wrapper for {@link Future#get()}. If the get() throws {@link InterruptedException} then the - * {@link Future1} is canceled and {@link OperationCanceledException} is thrown. - * @param timeout the maximum time to wait - * @param unit the time unit of the timeout argument - * @return the {@link android.os.Bundle} that is returned by {@link Future#get()} - * @throws OperationCanceledException if get() throws the unchecked - * {@link java.util.concurrent.CancellationException} or if the {@link Future1} was interrupted. - */ - V getResult(long timeout, TimeUnit unit) throws OperationCanceledException; -} \ No newline at end of file diff --git a/core/java/android/accounts/Future2.java b/core/java/android/accounts/Future2.java deleted file mode 100644 index b2ea84f493060..0000000000000 --- a/core/java/android/accounts/Future2.java +++ /dev/null @@ -1,57 +0,0 @@ -/* - * Copyright (C) 2009 The Android Open Source Project - * - * Licensed under the Apache License, Version 2.0 (the "License"); - * you may not use this file except in compliance with the License. - * You may obtain a copy of the License at - * - * http://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, - * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - * See the License for the specific language governing permissions and - * limitations under the License. - */ -package android.accounts; - -import android.os.Bundle; - -import java.io.IOException; -import java.util.concurrent.Future; -import java.util.concurrent.TimeUnit; - -/** - * An extension of {@link Future} that provides wrappers for {@link #get()} that handle the various - * exceptions that {@link #get()} may return and rethrows them as exceptions specific to - * {@link AccountManager}. - */ -public interface Future2 extends Future { - /** - * Wrapper for {@link Future#get()}. If the get() throws {@link InterruptedException} then the - * {@link Future2} is canceled and {@link OperationCanceledException} is thrown. - * @return the {@link android.os.Bundle} that is returned by {@link Future#get()} - * @throws OperationCanceledException if get() throws the unchecked - * {@link java.util.concurrent.CancellationException} or if the {@link Future2} was interrupted. - * @throws IOException if the request was unable to complete due to a network error - * @throws AuthenticatorException if there was an error communicating with the - * {@link AbstractAccountAuthenticator}. - */ - Bundle getResult() - throws OperationCanceledException, IOException, AuthenticatorException; - - /** - * Wrapper for {@link Future#get()}. If the get() throws {@link InterruptedException} then the - * {@link Future2} is canceled and {@link OperationCanceledException} is thrown. - * @param timeout the maximum time to wait - * @param unit the time unit of the timeout argument - * @return the {@link android.os.Bundle} that is returned by {@link Future#get()} - * @throws OperationCanceledException if get() throws the unchecked - * {@link java.util.concurrent.CancellationException} or if the {@link Future2} was interrupted. - * @throws IOException if the request was unable to complete due to a network error - * @throws AuthenticatorException if there was an error communicating with the - * {@link AbstractAccountAuthenticator}. - */ - Bundle getResult(long timeout, TimeUnit unit) - throws OperationCanceledException, IOException, AuthenticatorException; -} diff --git a/core/java/android/accounts/Future2Callback.java b/core/java/android/accounts/Future2Callback.java deleted file mode 100644 index 7ef0c94741d09..0000000000000 --- a/core/java/android/accounts/Future2Callback.java +++ /dev/null @@ -1,20 +0,0 @@ -/* - * Copyright (C) 2009 The Android Open Source Project - * - * Licensed under the Apache License, Version 2.0 (the "License"); - * you may not use this file except in compliance with the License. - * You may obtain a copy of the License at - * - * http://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, - * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - * See the License for the specific language governing permissions and - * limitations under the License. - */ -package android.accounts; - -public interface Future2Callback { - void run(Future2 future); -} \ No newline at end of file diff --git a/core/java/android/accounts/GrantCredentialsPermissionActivity.java b/core/java/android/accounts/GrantCredentialsPermissionActivity.java index f92d43f68e9bc..e06afb4d5948d 100644 --- a/core/java/android/accounts/GrantCredentialsPermissionActivity.java +++ b/core/java/android/accounts/GrantCredentialsPermissionActivity.java @@ -63,12 +63,12 @@ public class GrantCredentialsPermissionActivity extends Activity implements View CharSequence grantCredentialsPermissionFormat = getResources().getText( R.string.grant_credentials_permission_message_desc); messageView.setText(String.format(grantCredentialsPermissionFormat.toString(), - mAccount.mName, accountTypeLabel)); + mAccount.name, accountTypeLabel)); } else { CharSequence grantCredentialsPermissionFormat = getResources().getText( R.string.grant_credentials_permission_message_with_authtokenlabel_desc); messageView.setText(String.format(grantCredentialsPermissionFormat.toString(), - authTokenLabel, mAccount.mName, accountTypeLabel)); + authTokenLabel, mAccount.name, accountTypeLabel)); } String[] packageLabels = new String[packages.length]; diff --git a/core/java/android/accounts/IAccountAuthenticator.aidl b/core/java/android/accounts/IAccountAuthenticator.aidl index 7d4de392b6ef6..48f053c2ef649 100644 --- a/core/java/android/accounts/IAccountAuthenticator.aidl +++ b/core/java/android/accounts/IAccountAuthenticator.aidl @@ -70,4 +70,9 @@ oneway interface IAccountAuthenticator { */ void hasFeatures(in IAccountAuthenticatorResponse response, in Account account, in String[] features); + + /** + * Gets whether or not the account is allowed to be removed. + */ + void getAccountRemovalAllowed(in IAccountAuthenticatorResponse response, in Account account); } diff --git a/core/java/android/accounts/IAccountManager.aidl b/core/java/android/accounts/IAccountManager.aidl index 15ab4e822c505..411952b2664a4 100644 --- a/core/java/android/accounts/IAccountManager.aidl +++ b/core/java/android/accounts/IAccountManager.aidl @@ -21,6 +21,7 @@ import android.accounts.Account; import android.accounts.AuthenticatorDescription; import android.os.Bundle; + /** * Central application service that provides account management. * @hide @@ -29,10 +30,10 @@ interface IAccountManager { String getPassword(in Account account); String getUserData(in Account account, String key); AuthenticatorDescription[] getAuthenticatorTypes(); - Account[] getAccounts(); - Account[] getAccountsByType(String accountType); + Account[] getAccounts(String accountType); + void getAccountsByFeatures(in IAccountManagerResponse response, String accountType, in String[] features); boolean addAccount(in Account account, String password, in Bundle extras); - void removeAccount(in Account account); + void removeAccount(in IAccountManagerResponse response, in Account account); void invalidateAuthToken(String accountType, String authToken); String peekAuthToken(in Account account, String authTokenType); void setAuthToken(in Account account, String authTokenType, String authToken); @@ -52,8 +53,6 @@ interface IAccountManager { boolean expectActivityLaunch); void confirmCredentials(in IAccountManagerResponse response, in Account account, boolean expectActivityLaunch); - void getAccountsByTypeAndFeatures(in IAccountManagerResponse response, String accountType, - in String[] features); /* * @deprecated diff --git a/core/java/android/content/AbstractSyncableContentProvider.java b/core/java/android/content/AbstractSyncableContentProvider.java index 218f501ae92d2..808f30ce9bb1c 100644 --- a/core/java/android/content/AbstractSyncableContentProvider.java +++ b/core/java/android/content/AbstractSyncableContentProvider.java @@ -170,7 +170,7 @@ public abstract class AbstractSyncableContentProvider extends SyncableContentPro // AbstractGDataSyncAdapter, which will put acore into a crash loop ArrayList gaiaAccounts = new ArrayList(); for (Account acct: accounts) { - if (acct.mType.equals("com.google.GAIA")) { + if (acct.type.equals("com.google.GAIA")) { gaiaAccounts.add(acct); } } @@ -693,7 +693,7 @@ public abstract class AbstractSyncableContentProvider extends SyncableContentPro if (!accounts.containsKey(account)) { int numDeleted; numDeleted = db.delete(table, "_sync_account=? AND _sync_account_type=?", - new String[]{account.mName, account.mType}); + new String[]{account.name, account.type}); if (Config.LOGV) { Log.v(TAG, "deleted " + numDeleted + " records from table " + table @@ -726,7 +726,7 @@ public abstract class AbstractSyncableContentProvider extends SyncableContentPro // remove the data in the synced tables for (String table : tables) { db.delete(table, SYNC_ACCOUNT_WHERE_CLAUSE, - new String[]{account.mName, account.mType}); + new String[]{account.name, account.type}); } db.setTransactionSuccessful(); } finally { diff --git a/core/java/android/content/AbstractTableMerger.java b/core/java/android/content/AbstractTableMerger.java index a3daa01e8317f..9545fd7f1a74b 100644 --- a/core/java/android/content/AbstractTableMerger.java +++ b/core/java/android/content/AbstractTableMerger.java @@ -174,7 +174,7 @@ public abstract class AbstractTableMerger Cursor diffsCursor = null; try { // load the local database entries, so we can merge them with the server - final String[] accountSelectionArgs = new String[]{account.mName, account.mType}; + final String[] accountSelectionArgs = new String[]{account.name, account.type}; localCursor = mDb.query(mTable, syncDirtyProjection, SELECT_MARKED, accountSelectionArgs, null, null, mTable + "." + _SYNC_ID); @@ -487,7 +487,7 @@ public abstract class AbstractTableMerger try { if (deleteBySyncId) { selectionArgs = new String[]{diffsCursor.getString(serverSyncIdColumn), - account.mName, account.mType}; + account.name, account.type}; c = mDb.query(mTable, new String[]{BaseColumns._ID}, SELECT_BY_SYNC_ID_AND_ACCOUNT, selectionArgs, null, null, null); } else { @@ -534,7 +534,7 @@ public abstract class AbstractTableMerger SyncableContentProvider clientDiffs = mergeResult.tempContentProvider; if (Log.isLoggable(TAG, Log.VERBOSE)) Log.v(TAG, "generating client updates"); - final String[] accountSelectionArgs = new String[]{account.mName, account.mType}; + final String[] accountSelectionArgs = new String[]{account.name, account.type}; // Generate the client updates and insertions // Create a cursor for dirty records diff --git a/core/java/android/content/SyncManager.java b/core/java/android/content/SyncManager.java index d54e26091d24b..f25639496186f 100644 --- a/core/java/android/content/SyncManager.java +++ b/core/java/android/content/SyncManager.java @@ -596,7 +596,7 @@ class SyncManager implements OnAccountsUpdatedListener { for (String authority : syncableAuthorities) { for (Account account : accounts) { - if (mSyncAdapters.getServiceInfo(new SyncAdapterType(authority, account.mType)) + if (mSyncAdapters.getServiceInfo(new SyncAdapterType(authority, account.type)) != null) { scheduleSyncOperation( new SyncOperation(account, source, authority, extras, delay)); @@ -1094,8 +1094,8 @@ class SyncManager implements OnAccountsUpdatedListener { for (int i=0; i syncAdapterInfo = mSyncAdapters.getServiceInfo(syncAdapterType); if (syncAdapterInfo == null) { diff --git a/core/java/android/content/SyncStateContentProviderHelper.java b/core/java/android/content/SyncStateContentProviderHelper.java index dc728eccf3d43..64bbe25cefb0e 100644 --- a/core/java/android/content/SyncStateContentProviderHelper.java +++ b/core/java/android/content/SyncStateContentProviderHelper.java @@ -172,7 +172,7 @@ public class SyncStateContentProviderHelper { */ public void copySyncState(SQLiteDatabase dbSrc, SQLiteDatabase dbDest, Account account) { - final String[] whereArgs = new String[]{account.mName, account.mType}; + final String[] whereArgs = new String[]{account.name, account.type}; Cursor c = dbSrc.query(SYNC_STATE_TABLE, new String[]{"_sync_account", "_sync_account_type", "data"}, ACCOUNT_WHERE, whereArgs, null, null, null); @@ -209,7 +209,7 @@ public class SyncStateContentProviderHelper { public void discardSyncData(SQLiteDatabase db, Account account) { if (account != null) { - db.delete(SYNC_STATE_TABLE, ACCOUNT_WHERE, new String[]{account.mName, account.mType}); + db.delete(SYNC_STATE_TABLE, ACCOUNT_WHERE, new String[]{account.name, account.type}); } else { db.delete(SYNC_STATE_TABLE, null, null); } @@ -220,7 +220,7 @@ public class SyncStateContentProviderHelper { */ public byte[] readSyncDataBytes(SQLiteDatabase db, Account account) { Cursor c = db.query(SYNC_STATE_TABLE, null, ACCOUNT_WHERE, - new String[]{account.mName, account.mType}, null, null, null); + new String[]{account.name, account.type}, null, null, null); try { if (c.moveToFirst()) { return c.getBlob(c.getColumnIndexOrThrow("data")); @@ -238,6 +238,6 @@ public class SyncStateContentProviderHelper { ContentValues values = new ContentValues(); values.put("data", data); db.update(SYNC_STATE_TABLE, values, ACCOUNT_WHERE, - new String[]{account.mName, account.mType}); + new String[]{account.name, account.type}); } } diff --git a/core/java/android/content/SyncStorageEngine.java b/core/java/android/content/SyncStorageEngine.java index 8cc06426c5997..b3f9bbbb520f9 100644 --- a/core/java/android/content/SyncStorageEngine.java +++ b/core/java/android/content/SyncStorageEngine.java @@ -1127,8 +1127,8 @@ public class SyncStorageEngine extends Handler { AuthorityInfo authority = mAuthorities.get(i); out.startTag(null, "authority"); out.attribute(null, "id", Integer.toString(authority.ident)); - out.attribute(null, "account", authority.account.mName); - out.attribute(null, "type", authority.account.mType); + out.attribute(null, "account", authority.account.name); + out.attribute(null, "type", authority.account.type); out.attribute(null, "authority", authority.authority); if (!authority.enabled) { out.attribute(null, "enabled", "false"); diff --git a/core/java/android/provider/Calendar.java b/core/java/android/provider/Calendar.java index 489d936dab86c..fab3f3d034f7f 100644 --- a/core/java/android/provider/Calendar.java +++ b/core/java/android/provider/Calendar.java @@ -174,7 +174,7 @@ public final class Calendar { return Calendar.Calendars.delete(cr, Calendar.Calendars._SYNC_ACCOUNT + "=? AND " + Calendar.Calendars._SYNC_ACCOUNT_TYPE + "=?", - new String[] {account.mName, account.mType}); + new String[] {account.name, account.type}); } /** diff --git a/core/java/android/provider/SubscribedFeeds.java b/core/java/android/provider/SubscribedFeeds.java index f94b4427bd283..8e9f4021c934f 100644 --- a/core/java/android/provider/SubscribedFeeds.java +++ b/core/java/android/provider/SubscribedFeeds.java @@ -119,8 +119,8 @@ public class SubscribedFeeds { String authority, String service) { ContentValues values = new ContentValues(); values.put(SubscribedFeeds.Feeds.FEED, feed); - values.put(SubscribedFeeds.Feeds._SYNC_ACCOUNT, account.mName); - values.put(SubscribedFeeds.Feeds._SYNC_ACCOUNT_TYPE, account.mType); + values.put(SubscribedFeeds.Feeds._SYNC_ACCOUNT, account.name); + values.put(SubscribedFeeds.Feeds._SYNC_ACCOUNT_TYPE, account.type); values.put(SubscribedFeeds.Feeds.AUTHORITY, authority); values.put(SubscribedFeeds.Feeds.SERVICE, service); return resolver.insert(SubscribedFeeds.Feeds.CONTENT_URI, values); @@ -134,7 +134,7 @@ public class SubscribedFeeds { where.append(" AND " + SubscribedFeeds.Feeds.FEED + "=?"); where.append(" AND " + SubscribedFeeds.Feeds.AUTHORITY + "=?"); return resolver.delete(SubscribedFeeds.Feeds.CONTENT_URI, - where.toString(), new String[] {account.mName, account.mType, feed, authority}); + where.toString(), new String[] {account.name, account.type, feed, authority}); } public static int deleteFeeds(ContentResolver resolver, @@ -144,7 +144,7 @@ public class SubscribedFeeds { where.append(" AND " + SubscribedFeeds.Feeds._SYNC_ACCOUNT_TYPE + "=?"); where.append(" AND " + SubscribedFeeds.Feeds.AUTHORITY + "=?"); return resolver.delete(SubscribedFeeds.Feeds.CONTENT_URI, - where.toString(), new String[] {account.mName, account.mType, authority}); + where.toString(), new String[] {account.name, account.type, authority}); } /** diff --git a/core/java/android/provider/SyncStateContract.java b/core/java/android/provider/SyncStateContract.java index 7927e286ebca2..5c93af046321c 100644 --- a/core/java/android/provider/SyncStateContract.java +++ b/core/java/android/provider/SyncStateContract.java @@ -71,7 +71,7 @@ public class SyncStateContract { public static byte[] get(ContentProviderClient provider, Uri uri, Account account) throws RemoteException { Cursor c = provider.query(uri, DATA_PROJECTION, SELECT_BY_ACCOUNT, - new String[]{account.mName, account.mType}, null); + new String[]{account.name, account.type}, null); try { if (c.moveToNext()) { return c.getBlob(c.getColumnIndexOrThrow(Columns.DATA)); @@ -96,8 +96,8 @@ public class SyncStateContract { Account account, byte[] data) throws RemoteException { ContentValues values = new ContentValues(); values.put(Columns.DATA, data); - values.put(Columns.ACCOUNT_NAME, account.mName); - values.put(Columns.ACCOUNT_TYPE, account.mType); + values.put(Columns.ACCOUNT_NAME, account.name); + values.put(Columns.ACCOUNT_TYPE, account.type); provider.insert(uri, values); } @@ -116,8 +116,8 @@ public class SyncStateContract { values.put(Columns.DATA, data); return ContentProviderOperation .newInsert(uri) - .withValue(Columns.ACCOUNT_NAME, account.mName) - .withValue(Columns.ACCOUNT_TYPE, account.mType) + .withValue(Columns.ACCOUNT_NAME, account.name) + .withValue(Columns.ACCOUNT_TYPE, account.type) .withValues(values) .build(); } diff --git a/packages/SubscribedFeedsProvider/src/com/android/providers/subscribedfeeds/SubscribedFeedsIntentService.java b/packages/SubscribedFeedsProvider/src/com/android/providers/subscribedfeeds/SubscribedFeedsIntentService.java index 3cd2cc4a00cc4..b854f869aba53 100644 --- a/packages/SubscribedFeedsProvider/src/com/android/providers/subscribedfeeds/SubscribedFeedsIntentService.java +++ b/packages/SubscribedFeedsProvider/src/com/android/providers/subscribedfeeds/SubscribedFeedsIntentService.java @@ -113,7 +113,7 @@ public class SubscribedFeedsIntentService extends IntentService { // TODO(fredq) fix the hardcoded type final Account account = new Account(accountName, "com.google.GAIA"); c = context.getContentResolver().query(SubscribedFeeds.Feeds.CONTENT_URI, - null, where, new String[]{account.mName, account.mType, feed}, null); + null, where, new String[]{account.name, account.type, feed}, null); if (c.getCount() == 0) { Log.w(TAG, "received tickle for non-existent feed: " + "account " + accountName + ", feed " + feed); @@ -171,12 +171,12 @@ public class SubscribedFeedsIntentService extends IntentService { try { ContentValues values = new ContentValues(); for (Account account : accounts) { - values.put(SyncConstValue._SYNC_ACCOUNT, account.mName); - values.put(SyncConstValue._SYNC_ACCOUNT_TYPE, account.mType); + values.put(SyncConstValue._SYNC_ACCOUNT, account.name); + values.put(SyncConstValue._SYNC_ACCOUNT_TYPE, account.type); contentResolver.update(SubscribedFeeds.Feeds.CONTENT_URI, values, SubscribedFeeds.Feeds._SYNC_ACCOUNT + "=? AND " + SubscribedFeeds.Feeds._SYNC_ACCOUNT_TYPE + "=?", - new String[] {account.mName, account.mType}); + new String[] {account.name, account.type}); } } catch (SQLiteFullException e) { Log.w(TAG, "disk full while trying to mark the feeds as dirty, skipping"); diff --git a/tests/AndroidTests/src/com/android/unit_tests/accounts/AccountManagerServiceTest.java b/tests/AndroidTests/src/com/android/unit_tests/accounts/AccountManagerServiceTest.java index 6b8e1f0bc7bc4..1e4f161d9f60c 100644 --- a/tests/AndroidTests/src/com/android/unit_tests/accounts/AccountManagerServiceTest.java +++ b/tests/AndroidTests/src/com/android/unit_tests/accounts/AccountManagerServiceTest.java @@ -28,7 +28,6 @@ import android.os.Bundle; import java.util.Arrays; import java.util.Comparator; -import java.util.Map; public class AccountManagerServiceTest extends AndroidTestCase { @Override @@ -48,9 +47,9 @@ public class AccountManagerServiceTest extends AndroidTestCase { if (object1 == object2) return 0; if (object1 == null) return 1; if (object2 == null) return -1; - int result = object1.mType.compareTo(object2.mType); + int result = object1.type.compareTo(object2.type); if (result != 0) return result; - return object1.mName.compareTo(object2.mName); + return object1.name.compareTo(object2.name); } } @@ -62,16 +61,14 @@ public class AccountManagerServiceTest extends AndroidTestCase { Account a12 = new Account("account1", "type2"); Account a22 = new Account("account2", "type2"); Account a32 = new Account("account3", "type2"); - assertTrue(ams.addAccount(a11, "p11", null)); - assertTrue(ams.addAccount(a12, "p12", null)); - assertTrue(ams.addAccount(a21, "p21", null)); - assertTrue(ams.addAccount(a22, "p22", null)); - assertTrue(ams.addAccount(a31, "p31", null)); - assertTrue(ams.addAccount(a32, "p32", null)); + ams.addAccount(a11, "p11", null); + ams.addAccount(a12, "p12", null); + ams.addAccount(a21, "p21", null); + ams.addAccount(a22, "p22", null); + ams.addAccount(a31, "p31", null); + ams.addAccount(a32, "p32", null); - assertFalse("duplicate account insertion should fail", ams.addAccount(a32, "p", null)); - - Account[] accounts = ams.getAccounts(); + Account[] accounts = ams.getAccounts(null); Arrays.sort(accounts, new AccountSorter()); assertEquals(6, accounts.length); assertEquals(a11, accounts[0]); @@ -88,7 +85,7 @@ public class AccountManagerServiceTest extends AndroidTestCase { assertEquals(a21, accounts[1]); assertEquals(a31, accounts[2]); - ams.removeAccount(a21); + ams.removeAccount(null, a21); accounts = ams.getAccountsByType("type1" ); Arrays.sort(accounts, new AccountSorter()); @@ -101,8 +98,8 @@ public class AccountManagerServiceTest extends AndroidTestCase { AccountManagerService ams = new AccountManagerService(getContext()); Account a11 = new Account("account1", "type1"); Account a12 = new Account("account1", "type2"); - assertTrue(ams.addAccount(a11, "p11", null)); - assertTrue(ams.addAccount(a12, "p12", null)); + ams.addAccount(a11, "p11", null); + ams.addAccount(a12, "p12", null); assertEquals("p11", ams.getPassword(a11)); assertEquals("p12", ams.getPassword(a12)); @@ -125,8 +122,8 @@ public class AccountManagerServiceTest extends AndroidTestCase { u12.putString("a", "a_a12"); u12.putString("b", "b_a12"); u12.putString("c", "c_a12"); - assertTrue(ams.addAccount(a11, "p11", u11)); - assertTrue(ams.addAccount(a12, "p12", u12)); + ams.addAccount(a11, "p11", u11); + ams.addAccount(a12, "p12", u12); assertEquals("a_a11", ams.getUserData(a11, "a")); assertEquals("b_a11", ams.getUserData(a11, "b")); @@ -149,8 +146,8 @@ public class AccountManagerServiceTest extends AndroidTestCase { AccountManagerService ams = new AccountManagerService(getContext()); Account a11 = new Account("account1", "type1"); Account a12 = new Account("account1", "type2"); - assertTrue(ams.addAccount(a11, "p11", null)); - assertTrue(ams.addAccount(a12, "p12", null)); + ams.addAccount(a11, "p11", null); + ams.addAccount(a12, "p12", null); ams.setAuthToken(a11, "att1", "a11_att1"); ams.setAuthToken(a11, "att2", "a11_att2"); @@ -167,7 +164,7 @@ public class AccountManagerServiceTest extends AndroidTestCase { assertEquals("a12_att3", ams.peekAuthToken(a12, "att3")); ams.setAuthToken(a11, "att3", "a11_att3b"); - ams.invalidateAuthToken(a12.mType, "a12_att2"); + ams.invalidateAuthToken(a12.type, "a12_att2"); assertEquals("a11_att1", ams.peekAuthToken(a11, "att1")); assertEquals("a11_att2", ams.peekAuthToken(a11, "att2")); diff --git a/tests/FrameworkTest/tests/src/android/content/AbstractTableMergerTest.java b/tests/FrameworkTest/tests/src/android/content/AbstractTableMergerTest.java index 42c1e789b9c31..a8af7f83a16d9 100644 --- a/tests/FrameworkTest/tests/src/android/content/AbstractTableMergerTest.java +++ b/tests/FrameworkTest/tests/src/android/content/AbstractTableMergerTest.java @@ -74,8 +74,8 @@ public class AbstractTableMergerTest extends AndroidTestCase { if (syncVersion != null) values.put("_sync_version", syncVersion); if (syncId != null) values.put("_sync_id", syncId); if (syncAccount != null) { - values.put("_sync_account", syncAccount.mName); - values.put("_sync_account_type", syncAccount.mType); + values.put("_sync_account", syncAccount.name); + values.put("_sync_account_type", syncAccount.type); } values.put("_sync_local_id", syncLocalId); values.put("_sync_dirty", 0); @@ -88,8 +88,8 @@ public class AbstractTableMergerTest extends AndroidTestCase { if (syncVersion != null) values.put("_sync_version", syncVersion); if (syncId != null) values.put("_sync_id", syncId); if (syncAccount != null) { - values.put("_sync_account", syncAccount.mName); - values.put("_sync_account_type", syncAccount.mType); + values.put("_sync_account", syncAccount.name); + values.put("_sync_account_type", syncAccount.type); } if (syncLocalId != null) values.put("_sync_local_id", syncLocalId); return values; From 25a5d3db3ff06b9952395832308bc3b48913c4ee Mon Sep 17 00:00:00 2001 From: Robert Greenwalt Date: Thu, 13 Aug 2009 15:59:51 -0700 Subject: [PATCH 8/8] Fix CDMA to startup/shutdown based on apn en/disab Fixes MMS during wifi. Also fixes CDMA for ConnectivityManager change. --- .../android/net/MobileDataStateTracker.java | 14 +- .../telephony/DataConnectionTracker.java | 198 ++++++++++++++++-- .../android/internal/telephony/PhoneBase.java | 68 ++++++ .../internal/telephony/cdma/CDMAPhone.java | 56 ----- .../cdma/CdmaDataConnectionTracker.java | 127 ++++------- .../internal/telephony/gsm/GSMPhone.java | 67 ------ .../gsm/GsmDataConnectionTracker.java | 194 ++--------------- 7 files changed, 310 insertions(+), 414 deletions(-) diff --git a/core/java/android/net/MobileDataStateTracker.java b/core/java/android/net/MobileDataStateTracker.java index 6b00900ee090e..da1115dcb2455 100644 --- a/core/java/android/net/MobileDataStateTracker.java +++ b/core/java/android/net/MobileDataStateTracker.java @@ -49,7 +49,6 @@ public class MobileDataStateTracker extends NetworkStateTracker { private String mApnType; private boolean mEnabled; - private boolean mTeardownRequested; /** * Create a new MobileDataStateTracker @@ -66,7 +65,6 @@ public class MobileDataStateTracker extends NetworkStateTracker { TelephonyManager.getDefault().getNetworkTypeName()); mApnType = apnType; mPhoneService = null; - mTeardownRequested = false; if(netType == ConnectivityManager.TYPE_MOBILE) { mEnabled = true; } else { @@ -81,7 +79,9 @@ public class MobileDataStateTracker extends NetworkStateTracker { "net.eth0.dns3", "net.eth0.dns4", "net.gprs.dns1", - "net.gprs.dns2"}; + "net.gprs.dns2", + "net.ppp0.dns1", + "net.ppp0.dns2"}; } @@ -160,9 +160,9 @@ public class MobileDataStateTracker extends NetworkStateTracker { switch (state) { case DISCONNECTED: - if(mTeardownRequested) { + if(isTeardownRequested()) { mEnabled = false; - mTeardownRequested = false; + setTeardownRequested(false); } setDetailedState(DetailedState.DISCONNECTED, reason, apnName); @@ -277,7 +277,7 @@ public class MobileDataStateTracker extends NetworkStateTracker { */ @Override public boolean teardown() { - mTeardownRequested = true; + setTeardownRequested(true); return (setEnableApn(mApnType, false) != Phone.APN_REQUEST_FAILED); } @@ -286,7 +286,7 @@ public class MobileDataStateTracker extends NetworkStateTracker { */ public boolean reconnect() { mEnabled = true; - mTeardownRequested = false; + setTeardownRequested(false); mEnabled = (setEnableApn(mApnType, true) != Phone.APN_REQUEST_FAILED); return mEnabled; diff --git a/telephony/java/com/android/internal/telephony/DataConnectionTracker.java b/telephony/java/com/android/internal/telephony/DataConnectionTracker.java index fa0ffcd2d2d64..29e89b57e0a04 100644 --- a/telephony/java/com/android/internal/telephony/DataConnectionTracker.java +++ b/telephony/java/com/android/internal/telephony/DataConnectionTracker.java @@ -24,14 +24,18 @@ import android.os.Message; import android.os.RemoteException; import android.provider.Settings; import android.provider.Settings.SettingNotFoundException; +import android.text.TextUtils; import android.util.Log; +import java.util.ArrayList; + /** * {@hide} * */ public abstract class DataConnectionTracker extends Handler { - private static final boolean DBG = true; + protected static final boolean DBG = true; + protected final String LOG_TAG = "DataConnectionTracker"; /** * IDLE: ready to start data connection setup, default state @@ -98,6 +102,20 @@ public abstract class DataConnectionTracker extends Handler { //***** Constants + protected static final int APN_INVALID_ID = -1; + protected static final int APN_DEFAULT_ID = 0; + protected static final int APN_MMS_ID = 1; + protected static final int APN_SUPL_ID = 2; + protected static final int APN_DUN_ID = 3; + protected static final int APN_HIPRI_ID = 4; + protected static final int APN_NUM_TYPES = 5; + + protected boolean[] dataEnabled = new boolean[APN_NUM_TYPES]; + protected int enabledCount = 0; + + /* Currently requested APN type */ + protected String mRequestedApnType = Phone.APN_TYPE_DEFAULT; + /** Retry configuration: A doubling of retry times from 5secs to 30minutes */ protected static final String DEFAULT_DATA_RETRY_CONFIG = "default_randomization=2000," + "5000,10000,20000,40000,80000:5000,160000:5000," @@ -167,6 +185,8 @@ public abstract class DataConnectionTracker extends Handler { this.phone = phone; } + public abstract void dispose(); + public Activity getActivity() { return activity; } @@ -301,24 +321,18 @@ public abstract class DataConnectionTracker extends Handler { * @return {@code false} if data connectivity has been explicitly disabled, * {@code true} otherwise. */ - public abstract boolean getDataEnabled(); + public boolean getDataEnabled() { + return dataEnabled[APN_DEFAULT_ID]; + } /** * Report on whether data connectivity is enabled * @return {@code false} if data connectivity has been explicitly disabled, * {@code true} otherwise. */ - public abstract boolean getAnyDataEnabled(); - - /** - * Prevent mobile data connections from being established, - * or once again allow mobile data connections. If the state - * toggles, then either tear down or set up data, as - * appropriate to match the new state. - * @param enable indicates whether to enable ({@code true}) or disable ({@code false}) data - * @return {@code true} if the operation succeeded - */ - public abstract boolean setDataEnabled(boolean enable); + public boolean getAnyDataEnabled() { + return (enabledCount != 0); + } protected abstract void startNetStatPoll(); @@ -327,4 +341,162 @@ public abstract class DataConnectionTracker extends Handler { protected abstract void restartRadio(); protected abstract void log(String s); + + protected int apnTypeToId(String type) { + if (TextUtils.equals(type, Phone.APN_TYPE_DEFAULT)) { + return APN_DEFAULT_ID; + } else if (TextUtils.equals(type, Phone.APN_TYPE_MMS)) { + return APN_MMS_ID; + } else if (TextUtils.equals(type, Phone.APN_TYPE_SUPL)) { + return APN_SUPL_ID; + } else if (TextUtils.equals(type, Phone.APN_TYPE_DUN)) { + return APN_DUN_ID; + } else if (TextUtils.equals(type, Phone.APN_TYPE_HIPRI)) { + return APN_HIPRI_ID; + } else { + return APN_INVALID_ID; + } + } + + protected abstract boolean isApnTypeActive(String type); + + protected abstract boolean isApnTypeAvailable(String type); + + protected abstract String[] getActiveApnTypes(); + + protected abstract String getActiveApnString(); + + public abstract ArrayList getAllDataConnections(); + + protected abstract String getInterfaceName(String apnType); + + protected abstract String getIpAddress(String apnType); + + protected abstract String getGateway(String apnType); + + protected abstract String[] getDnsServers(String apnType); + + protected abstract void setState(State s); + + protected boolean isEnabled(int id) { + if (id != APN_INVALID_ID) { + return dataEnabled[id]; + } + return false; + } + + /** + * Ensure that we are connected to an APN of the specified type. + * @param type the APN type (currently the only valid values + * are {@link Phone#APN_TYPE_MMS} and {@link Phone#APN_TYPE_SUPL}) + * @return the result of the operation. Success is indicated by + * a return value of either {@code Phone.APN_ALREADY_ACTIVE} or + * {@code Phone.APN_REQUEST_STARTED}. In the latter case, a broadcast + * will be sent by the ConnectivityManager when a connection to + * the APN has been established. + */ + public int enableApnType(String type) { + int id = apnTypeToId(type); + if (id == APN_INVALID_ID) { + return Phone.APN_REQUEST_FAILED; + } + + // If already active, return + if(DBG) Log.d(LOG_TAG, "enableApnType("+type+"), isApnTypeActive = " + + isApnTypeActive(type) + " and state = " + state); + + if (isApnTypeActive(type)) { + if (state == State.INITING) return Phone.APN_REQUEST_STARTED; + else if (state == State.CONNECTED) return Phone.APN_ALREADY_ACTIVE; + } + + if (!isApnTypeAvailable(type)) { + return Phone.APN_TYPE_NOT_AVAILABLE; + } + + setEnabled(id, true); + mRequestedApnType = type; + sendMessage(obtainMessage(EVENT_ENABLE_NEW_APN)); + return Phone.APN_REQUEST_STARTED; + } + + /** + * The APN of the specified type is no longer needed. Ensure that if + * use of the default APN has not been explicitly disabled, we are connected + * to the default APN. + * @param type the APN type. The only valid values are currently + * {@link Phone#APN_TYPE_MMS} and {@link Phone#APN_TYPE_SUPL}. + * @return + */ + public int disableApnType(String type) { + if (DBG) Log.d(LOG_TAG, "disableApnType("+type+")"); + int id = apnTypeToId(type); + if (id == APN_INVALID_ID) { + return Phone.APN_REQUEST_FAILED; + } + if (isEnabled(id)) { + setEnabled(id, false); + if (isApnTypeActive(Phone.APN_TYPE_DEFAULT)) { + mRequestedApnType = Phone.APN_TYPE_DEFAULT; + if (dataEnabled[APN_DEFAULT_ID]) { + return Phone.APN_ALREADY_ACTIVE; + } else { + return Phone.APN_REQUEST_STARTED; + } + } else { + return Phone.APN_REQUEST_STARTED; + } + } else { + return Phone.APN_REQUEST_FAILED; + } + } + + protected synchronized void setEnabled(int id, boolean enable) { + if (DBG) Log.d(LOG_TAG, "setEnabled(" + id + ", " + enable + ')'); + if (dataEnabled[id] != enable) { + dataEnabled[id] = enable; + + if (enable) { + enabledCount++; + } else { + enabledCount--; + } + + if (enabledCount == 0) { + setPrivateDataEnabled(false); + } else if (enabledCount == 1) { + setPrivateDataEnabled(true); + } + } + } + + /** + * Prevent mobile data connections from being established, + * or once again allow mobile data connections. If the state + * toggles, then either tear down or set up data, as + * appropriate to match the new state. + *

This operation only affects the default APN, and if the same APN is + * currently being used for MMS traffic, the teardown will not happen + * even when {@code enable} is {@code false}.

+ * @param enable indicates whether to enable ({@code true}) or disable ({@code false}) data + * @return {@code true} if the operation succeeded + */ + public boolean setDataEnabled(boolean enable) { + if (DBG) Log.d(LOG_TAG, "setDataEnabled("+enable+")"); + setEnabled(APN_DEFAULT_ID, enable); + return true; + } + + private void setPrivateDataEnabled(boolean enable) { + if (DBG) Log.d(LOG_TAG, "setPrivateDataEnabled("+enable+")"); + if (enable) { + sendMessage(obtainMessage(EVENT_TRY_SETUP_DATA)); + } else { + Message msg = obtainMessage(EVENT_CLEAN_UP_CONNECTION); + msg.arg1 = 1; // tearDown is true + msg.obj = Phone.REASON_DATA_DISABLED; + sendMessage(msg); + } + } + } diff --git a/telephony/java/com/android/internal/telephony/PhoneBase.java b/telephony/java/com/android/internal/telephony/PhoneBase.java index b2795270ae507..6f4aef9a52040 100644 --- a/telephony/java/com/android/internal/telephony/PhoneBase.java +++ b/telephony/java/com/android/internal/telephony/PhoneBase.java @@ -109,6 +109,7 @@ public abstract class PhoneBase implements Phone { public CommandsInterface mCM; protected IccFileHandler mIccFileHandler; boolean mDnsCheckDisabled = false; + public DataConnectionTracker mDataConnection; /** * Set a system property, unless we're in unit test mode @@ -824,4 +825,71 @@ public abstract class PhoneBase implements Phone { // This function should be overridden by the class CDMAPhone. Not implemented in GSMPhone. Log.e(LOG_TAG, "Error! This function should never be executed, inactive CDMAPhone."); } + + public String getInterfaceName(String apnType) { + return mDataConnection.getInterfaceName(apnType); + } + + public String getIpAddress(String apnType) { + return mDataConnection.getIpAddress(apnType); + } + + public boolean isDataConnectivityEnabled() { + return mDataConnection.getDataEnabled(); + } + + public String getGateway(String apnType) { + return mDataConnection.getGateway(apnType); + } + + public String[] getDnsServers(String apnType) { + return mDataConnection.getDnsServers(apnType); + } + + public String[] getActiveApnTypes() { + return mDataConnection.getActiveApnTypes(); + } + + public String getActiveApn() { + return mDataConnection.getActiveApnString(); + } + + public int enableApnType(String type) { + return mDataConnection.enableApnType(type); + } + + public int disableApnType(String type) { + return mDataConnection.disableApnType(type); + } + + /** + * simulateDataConnection + * + * simulates various data connection states. This messes with + * DataConnectionTracker's internal states, but doesn't actually change + * the underlying radio connection states. + * + * @param state Phone.DataState enum. + */ + public void simulateDataConnection(Phone.DataState state) { + DataConnectionTracker.State dcState; + + switch (state) { + case CONNECTED: + dcState = DataConnectionTracker.State.CONNECTED; + break; + case SUSPENDED: + dcState = DataConnectionTracker.State.CONNECTED; + break; + case DISCONNECTED: + dcState = DataConnectionTracker.State.FAILED; + break; + default: + dcState = DataConnectionTracker.State.CONNECTING; + break; + } + + mDataConnection.setState(dcState); + notifyDataConnection(null); + } } diff --git a/telephony/java/com/android/internal/telephony/cdma/CDMAPhone.java b/telephony/java/com/android/internal/telephony/cdma/CDMAPhone.java index 522fa1dde641c..f0c0ea21f17ee 100755 --- a/telephony/java/com/android/internal/telephony/cdma/CDMAPhone.java +++ b/telephony/java/com/android/internal/telephony/cdma/CDMAPhone.java @@ -95,7 +95,6 @@ public class CDMAPhone extends PhoneBase { CdmaCallTracker mCT; CdmaSMSDispatcher mSMS; CdmaServiceStateTracker mSST; - CdmaDataConnectionTracker mDataConnection; RuimFileHandler mRuimFileHandler; RuimRecords mRuimRecords; RuimCard mRuimCard; @@ -369,42 +368,11 @@ public class CDMAPhone extends PhoneBase { return mCT.backgroundCall; } - public String getGateway(String apnType) { - return mDataConnection.getGateway(); - } - public boolean handleInCallMmiCommands(String dialString) { Log.e(LOG_TAG, "method handleInCallMmiCommands is NOT supported in CDMA!"); return false; } - public int enableApnType(String type) { - // This request is mainly used to enable MMS APN - // In CDMA there is no need to enable/disable a different APN for MMS - Log.d(LOG_TAG, "Request to enableApnType("+type+")"); - if (TextUtils.equals(type, Phone.APN_TYPE_MMS)) { - return Phone.APN_ALREADY_ACTIVE; - } else { - return Phone.APN_REQUEST_FAILED; - } - } - - public int disableApnType(String type) { - // This request is mainly used to disable MMS APN - // In CDMA there is no need to enable/disable a different APN for MMS - Log.d(LOG_TAG, "Request to disableApnType("+type+")"); - if (TextUtils.equals(type, Phone.APN_TYPE_MMS)) { - return Phone.APN_REQUEST_STARTED; - } else { - return Phone.APN_REQUEST_FAILED; - } - } - - public String getActiveApn() { - Log.d(LOG_TAG, "Request to getActiveApn()"); - return null; - } - public void setNetworkSelectionModeAutomatic(Message response) { Log.e(LOG_TAG, "method setNetworkSelectionModeAutomatic is NOT supported in CDMA!"); @@ -481,10 +449,6 @@ public class CDMAPhone extends PhoneBase { return false; } - public String getInterfaceName(String apnType) { - return mDataConnection.getInterfaceName(); - } - public CellLocation getCellLocation() { return mSST.cellLoc; } @@ -512,10 +476,6 @@ public class CDMAPhone extends PhoneBase { return false; } - public boolean isDataConnectivityEnabled() { - return mDataConnection.getDataEnabled(); - } - public boolean isDataConnectivityPossible() { boolean noData = mDataConnection.getDataEnabled() && getDataConnectionState() == DataState.DISCONNECTED; @@ -528,10 +488,6 @@ public class CDMAPhone extends PhoneBase { Log.e(LOG_TAG, "setLine1Number: not possible in CDMA"); } - public String[] getDnsServers(String apnType) { - return mDataConnection.getDnsServers(); - } - public IccCard getIccCard() { return mRuimCard; } @@ -584,10 +540,6 @@ public class CDMAPhone extends PhoneBase { mCT.unregisterForCallWaiting(h); } - public String getIpAddress(String apnType) { - return mDataConnection.getIpAddress(); - } - public void getNeighboringCids(Message response) { /* @@ -701,14 +653,6 @@ public class CDMAPhone extends PhoneBase { Log.e(LOG_TAG, "getAvailableNetworks: not possible in CDMA"); } - public String[] getActiveApnTypes() { - String[] result; - Log.d(LOG_TAG, "Request to getActiveApn()"); - result = new String[1]; - result[0] = Phone.APN_TYPE_DEFAULT; - return result; - } - public void setOutgoingCallerIdDisplay(int commandInterfaceCLIRMode, Message onComplete) { Log.e(LOG_TAG, "setOutgoingCallerIdDisplay: not possible in CDMA"); } diff --git a/telephony/java/com/android/internal/telephony/cdma/CdmaDataConnectionTracker.java b/telephony/java/com/android/internal/telephony/cdma/CdmaDataConnectionTracker.java index 8bae483215f48..d02d33e25ea21 100644 --- a/telephony/java/com/android/internal/telephony/cdma/CdmaDataConnectionTracker.java +++ b/telephony/java/com/android/internal/telephony/cdma/CdmaDataConnectionTracker.java @@ -57,8 +57,7 @@ import java.util.ArrayList; * {@hide} */ public final class CdmaDataConnectionTracker extends DataConnectionTracker { - private static final String LOG_TAG = "CDMA"; - private static final boolean DBG = true; + protected final String LOG_TAG = "CDMA"; private CDMAPhone mCdmaPhone; @@ -77,12 +76,6 @@ public final class CdmaDataConnectionTracker extends DataConnectionTracker { /** Currently active CdmaDataConnection */ private CdmaDataConnection mActiveDataConnection; - /** Defined cdma connection profiles */ - private static final int EXTERNAL_NETWORK_DEFAULT_ID = 0; - private static final int EXTERNAL_NETWORK_NUM_TYPES = 1; - - private boolean[] dataEnabled = new boolean[EXTERNAL_NETWORK_NUM_TYPES]; - /** * Pool size of CdmaDataConnection objects. */ @@ -101,6 +94,11 @@ public final class CdmaDataConnectionTracker extends DataConnectionTracker { private static final int DATA_CONNECTION_ACTIVE_PH_LINK_DOWN = 1; private static final int DATA_CONNECTION_ACTIVE_PH_LINK_UP = 2; + private static final String[] mSupportedApnTypes = { + Phone.APN_TYPE_DEFAULT, + Phone.APN_TYPE_MMS, + Phone.APN_TYPE_HIPRI }; + // Possibly promoate to base class, the only difference is // the INTENT_RECONNECT_ALARM action is a different string. // Do consider technology changes if it is promoted. @@ -182,9 +180,12 @@ public final class CdmaDataConnectionTracker extends DataConnectionTracker { // and 2) whether the RIL will setup the baseband to auto-PS attach. SharedPreferences sp = PreferenceManager.getDefaultSharedPreferences(phone.getContext()); - dataEnabled[EXTERNAL_NETWORK_DEFAULT_ID] = + dataEnabled[APN_DEFAULT_ID] = !sp.getBoolean(CDMAPhone.DATA_DISABLED_ON_BOOT_KEY, false); - noAutoAttach = !dataEnabled[EXTERNAL_NETWORK_DEFAULT_ID]; + if (dataEnabled[APN_DEFAULT_ID]) { + enabledCount++; + } + noAutoAttach = !dataEnabled[APN_DEFAULT_ID]; if (!mRetryMgr.configure(SystemProperties.get("ro.cdma.data_retry_config"))) { if (!mRetryMgr.configure(DEFAULT_DATA_RETRY_CONFIG)) { @@ -219,7 +220,7 @@ public final class CdmaDataConnectionTracker extends DataConnectionTracker { if(DBG) Log.d(LOG_TAG, "CdmaDataConnectionTracker finalized"); } - void setState(State s) { + protected void setState(State s) { if (DBG) log ("setState: " + s); if (state != s) { if (s == State.INITING) { // request Data connection context @@ -236,39 +237,33 @@ public final class CdmaDataConnectionTracker extends DataConnectionTracker { state = s; } - public int enableApnType(String type) { - // This request is mainly used to enable MMS APN - // In CDMA there is no need to enable/disable a different APN for MMS - Log.d(LOG_TAG, "Request to enableApnType("+type+")"); - if (TextUtils.equals(type, Phone.APN_TYPE_MMS)) { - return Phone.APN_ALREADY_ACTIVE; - } else if (TextUtils.equals(type, Phone.APN_TYPE_SUPL)) { - Log.w(LOG_TAG, "Phone.APN_TYPE_SUPL not enabled for CDMA"); - return Phone.APN_REQUEST_FAILED; - } else { - return Phone.APN_REQUEST_FAILED; + @Override + protected boolean isApnTypeActive(String type) { + return (isApnTypeAvailable(type) && + mCdmaPhone.mSST.getCurrentCdmaDataConnectionState() == + ServiceState.STATE_IN_SERVICE); + } + + @Override + protected boolean isApnTypeAvailable(String type) { + for (String s : mSupportedApnTypes) { + if (TextUtils.equals(type, s)) { + return true; + } } + return false; } - public int disableApnType(String type) { - // This request is mainly used to disable MMS APN - // In CDMA there is no need to enable/disable a different APN for MMS - Log.d(LOG_TAG, "Request to disableApnType("+type+")"); - if (TextUtils.equals(type, Phone.APN_TYPE_MMS)) { - return Phone.APN_REQUEST_STARTED; - } else { - return Phone.APN_REQUEST_FAILED; + protected String[] getActiveApnTypes() { + if (mCdmaPhone.mSST.getCurrentCdmaDataConnectionState() == + ServiceState.STATE_IN_SERVICE) { + return mSupportedApnTypes.clone(); } + return new String[0]; } - private boolean isEnabled(int cdmaDataProfile) { - return dataEnabled[cdmaDataProfile]; - } - - private void setEnabled(int cdmaDataProfile, boolean enable) { - Log.d(LOG_TAG, "setEnabled(" + cdmaDataProfile + ", " + enable + ')'); - dataEnabled[cdmaDataProfile] = enable; - Log.d(LOG_TAG, "dataEnabled[DEFAULT_PROFILE]=" + dataEnabled[EXTERNAL_NETWORK_DEFAULT_ID]); + protected String getActiveApnString() { + return null; } /** @@ -294,54 +289,6 @@ public final class CdmaDataConnectionTracker extends DataConnectionTracker { return true; } - /** - * Prevent mobile data connections from being established, - * or once again allow mobile data connections. If the state - * toggles, then either tear down or set up data, as - * appropriate to match the new state. - *

This operation only affects the default connection - * @param enable indicates whether to enable ({@code true}) or disable ({@code false}) data - * @return {@code true} if the operation succeeded - */ - public boolean setDataEnabled(boolean enable) { - - boolean isEnabled = isEnabled(EXTERNAL_NETWORK_DEFAULT_ID); - - Log.d(LOG_TAG, "setDataEnabled("+enable+") isEnabled=" + isEnabled); - if (!isEnabled && enable) { - setEnabled(EXTERNAL_NETWORK_DEFAULT_ID, true); - sendMessage(obtainMessage(EVENT_TRY_SETUP_DATA)); - } else if (!enable) { - setEnabled(EXTERNAL_NETWORK_DEFAULT_ID, false); - Message msg = obtainMessage(EVENT_CLEAN_UP_CONNECTION); - msg.arg1 = 1; // tearDown is true - msg.obj = Phone.REASON_DATA_DISABLED; - sendMessage(msg); - } - return true; - } - - /** - * Report the current state of data connectivity (enabled or disabled) - * @return {@code false} if data connectivity has been explicitly disabled, - * {@code true} otherwise. - */ - public boolean getDataEnabled() { - return dataEnabled[EXTERNAL_NETWORK_DEFAULT_ID]; - } - - /** - * Report on whether data connectivity is enabled - * @return {@code false} if data connectivity has been explicitly disabled, - * {@code true} otherwise. - */ - public boolean getAnyDataEnabled() { - for (int i=0; i < EXTERNAL_NETWORK_NUM_TYPES; i++) { - if (isEnabled(i)) return true; - } - return false; - } - private boolean isDataAllowed() { boolean roaming = phone.getServiceState().getRoaming(); return getAnyDataEnabled() && (!roaming || getDataOnRoamingEnabled()); @@ -921,28 +868,28 @@ public final class CdmaDataConnectionTracker extends DataConnectionTracker { } } - String getInterfaceName() { + protected String getInterfaceName(String apnType) { if (mActiveDataConnection != null) { return mActiveDataConnection.getInterface(); } return null; } - protected String getIpAddress() { + protected String getIpAddress(String apnType) { if (mActiveDataConnection != null) { return mActiveDataConnection.getIpAddress(); } return null; } - String getGateway() { + protected String getGateway(String apnType) { if (mActiveDataConnection != null) { return mActiveDataConnection.getGatewayAddress(); } return null; } - protected String[] getDnsServers() { + protected String[] getDnsServers(String apnType) { if (mActiveDataConnection != null) { return mActiveDataConnection.getDnsServers(); } diff --git a/telephony/java/com/android/internal/telephony/gsm/GSMPhone.java b/telephony/java/com/android/internal/telephony/gsm/GSMPhone.java index 94d43441ff617..bb04a43bab10c 100755 --- a/telephony/java/com/android/internal/telephony/gsm/GSMPhone.java +++ b/telephony/java/com/android/internal/telephony/gsm/GSMPhone.java @@ -102,7 +102,6 @@ public class GSMPhone extends PhoneBase { GsmCallTracker mCT; GsmServiceStateTracker mSST; GsmSMSDispatcher mSMS; - GsmDataConnectionTracker mDataConnection; SIMRecords mSIMRecords; SimCard mSimCard; StkService mStkService; @@ -280,14 +279,6 @@ public class GSMPhone extends PhoneBase { return "GSM"; } - public String[] getActiveApnTypes() { - return mDataConnection.getActiveApnTypes(); - } - - public String getActiveApn() { - return mDataConnection.getActiveApnString(); - } - public SignalStrength getSignalStrength() { return mSST.mSignalStrength; } @@ -1145,38 +1136,10 @@ public class GSMPhone extends PhoneBase { return mDataConnection.setDataEnabled(true); } - public int enableApnType(String type) { - return mDataConnection.enableApnType(type); - } - - public int disableApnType(String type) { - return mDataConnection.disableApnType(type); - } - public boolean disableDataConnectivity() { return mDataConnection.setDataEnabled(false); } - public String getInterfaceName(String apnType) { - return mDataConnection.getInterfaceName(apnType); - } - - public String getIpAddress(String apnType) { - return mDataConnection.getIpAddress(apnType); - } - - public String getGateway(String apnType) { - return mDataConnection.getGateway(apnType); - } - - public String[] getDnsServers(String apnType) { - return mDataConnection.getDnsServers(apnType); - } - - public boolean isDataConnectivityEnabled() { - return mDataConnection.getDataEnabled(); - } - /** * The only circumstances under which we report that data connectivity is not * possible are @@ -1543,36 +1506,6 @@ public class GSMPhone extends PhoneBase { } } } - /** - * simulateDataConnection - * - * simulates various data connection states. This messes with - * DataConnectionTracker's internal states, but doesn't actually change - * the underlying radio connection states. - * - * @param state Phone.DataState enum. - */ - public void simulateDataConnection(Phone.DataState state) { - DataConnectionTracker.State dcState; - - switch (state) { - case CONNECTED: - dcState = DataConnectionTracker.State.CONNECTED; - break; - case SUSPENDED: - dcState = DataConnectionTracker.State.CONNECTED; - break; - case DISCONNECTED: - dcState = DataConnectionTracker.State.FAILED; - break; - default: - dcState = DataConnectionTracker.State.CONNECTING; - break; - } - - mDataConnection.setState(dcState); - notifyDataConnection(null); - } /** * Retrieves the PhoneSubInfo of the GSMPhone diff --git a/telephony/java/com/android/internal/telephony/gsm/GsmDataConnectionTracker.java b/telephony/java/com/android/internal/telephony/gsm/GsmDataConnectionTracker.java index 34d303965fe33..3aee26e8ccef2 100644 --- a/telephony/java/com/android/internal/telephony/gsm/GsmDataConnectionTracker.java +++ b/telephony/java/com/android/internal/telephony/gsm/GsmDataConnectionTracker.java @@ -63,8 +63,7 @@ import java.util.ArrayList; * {@hide} */ public final class GsmDataConnectionTracker extends DataConnectionTracker { - private static final String LOG_TAG = "GSM"; - private static final boolean DBG = true; + protected final String LOG_TAG = "GSM"; private GSMPhone mGsmPhone; /** @@ -119,31 +118,17 @@ public final class GsmDataConnectionTracker extends DataConnectionTracker { private ApnSetting preferredApn = null; + /* Currently active APN */ + protected ApnSetting mActiveApn; + /** * pdpList holds all the PDP connection, i.e. IP Link in GPRS */ private ArrayList pdpList; - /** Currently requested APN type */ - private String mRequestedApnType = Phone.APN_TYPE_DEFAULT; - - /** Currently active APN */ - private ApnSetting mActiveApn; - /** Currently active PdpConnection */ private PdpConnection mActivePdp; - private static int APN_INVALID_ID = -1; - private static int APN_DEFAULT_ID = 0; - private static int APN_MMS_ID = 1; - private static int APN_SUPL_ID = 2; - private static int APN_DUN_ID = 3; - private static int APN_HIPRI_ID = 4; - private static int APN_NUM_TYPES = 5; - - private boolean[] dataEnabled = new boolean[APN_NUM_TYPES]; - private int enabledCount = 0; - /** Is packet service restricted by network */ private boolean mIsPsRestricted = false; @@ -293,7 +278,7 @@ public final class GsmDataConnectionTracker extends DataConnectionTracker { if(DBG) Log.d(LOG_TAG, "GsmDataConnectionTracker finalized"); } - void setState(State s) { + protected void setState(State s) { if (DBG) log ("setState: " + s); if (state != s) { if (s == State.INITING) { // request PDP context @@ -317,7 +302,7 @@ public final class GsmDataConnectionTracker extends DataConnectionTracker { } } - String[] getActiveApnTypes() { + public String[] getActiveApnTypes() { String[] result; if (mActiveApn != null) { result = mActiveApn.types; @@ -336,87 +321,6 @@ public final class GsmDataConnectionTracker extends DataConnectionTracker { return result; } - protected int apnTypeToId(String type) { - if (TextUtils.equals(type, Phone.APN_TYPE_DEFAULT)) { - return APN_DEFAULT_ID; - } else if (TextUtils.equals(type, Phone.APN_TYPE_MMS)) { - return APN_MMS_ID; - } else if (TextUtils.equals(type, Phone.APN_TYPE_SUPL)) { - return APN_SUPL_ID; - } else if (TextUtils.equals(type, Phone.APN_TYPE_DUN)) { - return APN_DUN_ID; - } else if (TextUtils.equals(type, Phone.APN_TYPE_HIPRI)) { - return APN_HIPRI_ID; - } else { - return APN_INVALID_ID; - } - } - - /** - * Ensure that we are connected to an APN of the specified type. - * @param type the APN type (currently the only valid values - * are {@link Phone#APN_TYPE_MMS} and {@link Phone#APN_TYPE_SUPL}) - * @return the result of the operation. Success is indicated by - * a return value of either {@code Phone.APN_ALREADY_ACTIVE} or - * {@code Phone.APN_REQUEST_STARTED}. In the latter case, a broadcast - * will be sent by the ConnectivityManager when a connection to - * the APN has been established. - */ - protected int enableApnType(String type) { - int id = apnTypeToId(type); - if (id == APN_INVALID_ID) { - return Phone.APN_REQUEST_FAILED; - } - - // If already active, return - if(DBG) Log.d(LOG_TAG, "enableApnType("+type+"), isApnTypeActive = " - + isApnTypeActive(type) + " and state = " + state); - if (isApnTypeActive(type)) { - if (state == State.INITING) return Phone.APN_REQUEST_STARTED; - else if (state == State.CONNECTED) return Phone.APN_ALREADY_ACTIVE; - } - - if (!isApnTypeAvailable(type)) { - return Phone.APN_TYPE_NOT_AVAILABLE; - } - - setEnabled(id, true); - mRequestedApnType = type; - sendMessage(obtainMessage(EVENT_ENABLE_NEW_APN)); - return Phone.APN_REQUEST_STARTED; - } - - /** - * The APN of the specified type is no longer needed. Ensure that if - * use of the default APN has not been explicitly disabled, we are connected - * to the default APN. - * @param type the APN type. The only valid values are currently - * {@link Phone#APN_TYPE_MMS} and {@link Phone#APN_TYPE_SUPL}. - * @return - */ - protected int disableApnType(String type) { - if (DBG) Log.d(LOG_TAG, "disableApnType("+type+")"); - int id = apnTypeToId(type); - if (id == APN_INVALID_ID) { - return Phone.APN_REQUEST_FAILED; - } - if (isEnabled(id)) { - setEnabled(id, false); - if (isApnTypeActive(Phone.APN_TYPE_DEFAULT)) { - mRequestedApnType = Phone.APN_TYPE_DEFAULT; - if (dataEnabled[APN_DEFAULT_ID]) { - return Phone.APN_ALREADY_ACTIVE; - } else { - return Phone.APN_REQUEST_STARTED; - } - } else { - return Phone.APN_REQUEST_STARTED; - } - } else { - return Phone.APN_REQUEST_FAILED; - } - } - /** * The data connection is expected to be setup while device * 1. has sim card @@ -439,12 +343,14 @@ public final class GsmDataConnectionTracker extends DataConnectionTracker { return true; } - private boolean isApnTypeActive(String type) { - // TODO: to support simultaneous, mActiveApn can be a List instead. + @Override + protected boolean isApnTypeActive(String type) { + // TODO: support simultaneous with List instead return mActiveApn != null && mActiveApn.canHandleType(type); } - private boolean isApnTypeAvailable(String type) { + @Override + protected boolean isApnTypeAvailable(String type) { if (allApns != null) { for (ApnSetting apn : allApns) { if (apn.canHandleType(type)) { @@ -455,80 +361,6 @@ public final class GsmDataConnectionTracker extends DataConnectionTracker { return false; } - private boolean isEnabled(int id) { - if (id != APN_INVALID_ID) { - return dataEnabled[id]; - } - return false; - } - - private void setEnabled(int id, boolean enable) { - if (DBG) Log.d(LOG_TAG, "setEnabled(" + id + ", " + enable + ')'); - if (dataEnabled[id] != enable) { - dataEnabled[id] = enable; - - if (enable) { - enabledCount++; - } else { - enabledCount--; - } - - if (enabledCount == 0) { - setPrivateDataEnabled(false); - } else if (enabledCount == 1) { - setPrivateDataEnabled(true); - } - } - } - - /** - * Prevent mobile data connections from being established, - * or once again allow mobile data connections. If the state - * toggles, then either tear down or set up data, as - * appropriate to match the new state. - *

This operation only affects the default APN, and if the same APN is - * currently being used for MMS traffic, the teardown will not happen - * even when {@code enable} is {@code false}.

- * @param enable indicates whether to enable ({@code true}) or disable ({@code false}) data - * @return {@code true} if the operation succeeded - */ - public boolean setDataEnabled(boolean enable) { - if (DBG) Log.d(LOG_TAG, "setDataEnabled("+enable+")"); - setEnabled(APN_DEFAULT_ID, enable); - return true; - } - - private void setPrivateDataEnabled(boolean enable) { - if (DBG) Log.d(LOG_TAG, "setPrivateDataEnabled("+enable+")"); - if (enable) { - sendMessage(obtainMessage(EVENT_TRY_SETUP_DATA)); - } else { - Message msg = obtainMessage(EVENT_CLEAN_UP_CONNECTION); - msg.arg1 = 1; // tearDown is true - msg.obj = Phone.REASON_DATA_DISABLED; - sendMessage(msg); - } - } - - /** - * Report the current state of data connectivity (enabled or disabled) for - * the default APN. - * @return {@code false} if data connectivity has been explicitly disabled, - * {@code true} otherwise. - */ - public boolean getDataEnabled() { - return dataEnabled[APN_DEFAULT_ID]; - } - - /** - * Report on whether data connectivity is enabled for any APN. - * @return {@code false} if data connectivity has been explicitly disabled, - * {@code true} otherwise. - */ - public boolean getAnyDataEnabled() { - return (enabledCount != 0); - } - /** * Formerly this method was ArrayList getAllPdps() */ @@ -744,7 +576,7 @@ public final class GsmDataConnectionTracker extends DataConnectionTracker { return true; } - String getInterfaceName(String apnType) { + protected String getInterfaceName(String apnType) { if (mActivePdp != null && (apnType == null || mActiveApn.canHandleType(apnType))) { return mActivePdp.getInterface(); @@ -760,7 +592,7 @@ public final class GsmDataConnectionTracker extends DataConnectionTracker { return null; } - String getGateway(String apnType) { + public String getGateway(String apnType) { if (mActivePdp != null && (apnType == null || mActiveApn.canHandleType(apnType))) { return mActivePdp.getGatewayAddress();