summaryrefslogtreecommitdiffstats
diff options
context:
space:
mode:
-rw-r--r--api/17.txt4
-rw-r--r--api/current.txt4
-rw-r--r--cmds/am/src/com/android/commands/am/Am.java4
-rw-r--r--core/java/android/accounts/AccountAuthenticatorCache.java7
-rw-r--r--core/java/android/accounts/AccountManagerService.java187
-rw-r--r--core/java/android/accounts/IAccountAuthenticatorCache.java14
-rw-r--r--core/java/android/app/ActivityManager.java2
-rw-r--r--core/java/android/app/ActivityManagerNative.java6
-rw-r--r--core/java/android/app/ContextImpl.java14
-rw-r--r--core/java/android/app/IActivityManager.java2
-rwxr-xr-xcore/java/android/bluetooth/BluetoothA2dp.java13
-rwxr-xr-xcore/java/android/bluetooth/BluetoothHeadset.java23
-rw-r--r--core/java/android/bluetooth/BluetoothHealth.java11
-rwxr-xr-xcore/java/android/bluetooth/BluetoothInputDevice.java19
-rw-r--r--core/java/android/bluetooth/BluetoothPan.java13
-rwxr-xr-xcore/java/android/bluetooth/BluetoothPbap.java13
-rw-r--r--core/java/android/bluetooth/BluetoothSocket.java37
-rw-r--r--core/java/android/bluetooth/BluetoothTetheringDataTracker.java20
-rw-r--r--core/java/android/content/ContentService.java5
-rw-r--r--core/java/android/content/SyncManager.java244
-rw-r--r--core/java/android/content/SyncQueue.java42
-rw-r--r--core/java/android/content/pm/RegisteredServicesCache.java247
-rw-r--r--core/java/android/content/pm/RegisteredServicesCacheListener.java4
-rw-r--r--core/java/android/os/UserManager.java35
-rw-r--r--core/java/android/provider/Settings.java13
-rw-r--r--core/java/android/service/dreams/DreamService.java2
-rw-r--r--core/java/android/view/View.java18
-rw-r--r--core/java/android/widget/LinearLayout.java6
-rw-r--r--core/java/com/android/internal/view/menu/ActionMenuPresenter.java3
-rw-r--r--core/java/com/android/internal/widget/LockPatternUtils.java10
-rwxr-xr-xcore/res/res/values/strings.xml2
-rw-r--r--core/tests/coretests/src/android/accounts/AccountManagerServiceTest.java18
-rw-r--r--docs/html/distribute/googleplay/quality/core.jd5
-rw-r--r--docs/html/distribute/googleplay/quality/tablet.jd3
-rw-r--r--docs/html/distribute/googleplay/spotlight/tablets.jd13
-rw-r--r--docs/html/guide/google/gcm/adv.jd7
-rw-r--r--docs/html/guide/google/gcm/gcm.jd15
-rw-r--r--docs/html/guide/google/gcm/gs.jd2
-rw-r--r--graphics/java/android/graphics/Bitmap.java5
-rw-r--r--media/java/android/media/MediaRouter.java9
-rwxr-xr-xmedia/tests/MediaFrameworkTest/src/com/android/mediaframeworktest/MediaFrameworkPerfTestRunner.java8
-rw-r--r--media/tests/MediaFrameworkTest/src/com/android/mediaframeworktest/performance/MediaPlayerPerformance.java71
-rw-r--r--packages/SettingsProvider/src/com/android/providers/settings/SettingsBackupAgent.java121
-rw-r--r--packages/SystemUI/AndroidManifest.xml1
-rw-r--r--packages/SystemUI/res/values/strings.xml2
-rw-r--r--packages/SystemUI/src/com/android/systemui/statusbar/phone/QuickSettings.java50
-rwxr-xr-xpolicy/src/com/android/internal/policy/impl/PhoneWindowManager.java3
-rw-r--r--policy/src/com/android/internal/policy/impl/keyguard/KeyguardHostView.java3
-rw-r--r--policy/src/com/android/internal/policy/impl/keyguard/KeyguardSecurityModel.java3
-rw-r--r--policy/src/com/android/internal/policy/impl/keyguard/KeyguardSelectorView.java12
-rw-r--r--policy/src/com/android/internal/policy/impl/keyguard/KeyguardUpdateMonitor.java11
-rwxr-xr-xservices/java/com/android/server/BluetoothManagerService.java463
-rwxr-xr-xservices/java/com/android/server/NotificationManagerService.java21
-rw-r--r--services/java/com/android/server/WiredAccessoryManager.java2
-rw-r--r--services/java/com/android/server/accessibility/AccessibilityManagerService.java16
-rw-r--r--services/java/com/android/server/accessibility/TouchExplorer.java1
-rw-r--r--services/java/com/android/server/am/ActiveServices.java31
-rw-r--r--services/java/com/android/server/am/ActivityManagerService.java119
-rw-r--r--services/java/com/android/server/am/BroadcastQueue.java50
-rw-r--r--services/java/com/android/server/am/BroadcastRecord.java10
-rw-r--r--services/java/com/android/server/power/PowerManagerService.java57
-rwxr-xr-xservices/java/com/android/server/wm/WindowManagerService.java16
-rw-r--r--services/jni/com_android_server_power_PowerManagerService.cpp14
-rw-r--r--wifi/java/android/net/wifi/WifiWatchdogStateMachine.java20
64 files changed, 1433 insertions, 773 deletions
diff --git a/api/17.txt b/api/17.txt
index f8ad4d9..9af3b49 100644
--- a/api/17.txt
+++ b/api/17.txt
@@ -16620,6 +16620,8 @@ package android.os {
method public android.os.UserHandle getUserForSerialNumber(long);
method public java.lang.String getUserName();
method public boolean isUserAGoat();
+ method public boolean isUserRunning(android.os.UserHandle);
+ method public boolean isUserRunningOrStopping(android.os.UserHandle);
}
public abstract class Vibrator {
@@ -18903,7 +18905,7 @@ package android.provider {
field public static final deprecated java.lang.String INSTALL_NON_MARKET_APPS = "install_non_market_apps";
field public static final java.lang.String LOCATION_PROVIDERS_ALLOWED = "location_providers_allowed";
field public static final java.lang.String LOCK_PATTERN_ENABLED = "lock_pattern_autolock";
- field public static final java.lang.String LOCK_PATTERN_TACTILE_FEEDBACK_ENABLED = "lock_pattern_tactile_feedback_enabled";
+ field public static final deprecated java.lang.String LOCK_PATTERN_TACTILE_FEEDBACK_ENABLED = "lock_pattern_tactile_feedback_enabled";
field public static final java.lang.String LOCK_PATTERN_VISIBLE = "lock_pattern_visible_pattern";
field public static final deprecated java.lang.String LOGGING_ID = "logging_id";
field public static final deprecated java.lang.String NETWORK_PREFERENCE = "network_preference";
diff --git a/api/current.txt b/api/current.txt
index f8ad4d9..9af3b49 100644
--- a/api/current.txt
+++ b/api/current.txt
@@ -16620,6 +16620,8 @@ package android.os {
method public android.os.UserHandle getUserForSerialNumber(long);
method public java.lang.String getUserName();
method public boolean isUserAGoat();
+ method public boolean isUserRunning(android.os.UserHandle);
+ method public boolean isUserRunningOrStopping(android.os.UserHandle);
}
public abstract class Vibrator {
@@ -18903,7 +18905,7 @@ package android.provider {
field public static final deprecated java.lang.String INSTALL_NON_MARKET_APPS = "install_non_market_apps";
field public static final java.lang.String LOCATION_PROVIDERS_ALLOWED = "location_providers_allowed";
field public static final java.lang.String LOCK_PATTERN_ENABLED = "lock_pattern_autolock";
- field public static final java.lang.String LOCK_PATTERN_TACTILE_FEEDBACK_ENABLED = "lock_pattern_tactile_feedback_enabled";
+ field public static final deprecated java.lang.String LOCK_PATTERN_TACTILE_FEEDBACK_ENABLED = "lock_pattern_tactile_feedback_enabled";
field public static final java.lang.String LOCK_PATTERN_VISIBLE = "lock_pattern_visible_pattern";
field public static final deprecated java.lang.String LOGGING_ID = "logging_id";
field public static final deprecated java.lang.String NETWORK_PREFERENCE = "network_preference";
diff --git a/cmds/am/src/com/android/commands/am/Am.java b/cmds/am/src/com/android/commands/am/Am.java
index 3df88bb..add7a23 100644
--- a/cmds/am/src/com/android/commands/am/Am.java
+++ b/cmds/am/src/com/android/commands/am/Am.java
@@ -415,6 +415,10 @@ public class Am {
ComponentName cn = mAm.startService(null, intent, intent.getType(), mUserId);
if (cn == null) {
System.err.println("Error: Not found; no service started.");
+ } else if (cn.getPackageName().equals("!")) {
+ System.err.println("Error: Requires permission " + cn.getClassName());
+ } else if (cn.getPackageName().equals("!!")) {
+ System.err.println("Error: " + cn.getClassName());
}
}
diff --git a/core/java/android/accounts/AccountAuthenticatorCache.java b/core/java/android/accounts/AccountAuthenticatorCache.java
index 7214c50..f937cde 100644
--- a/core/java/android/accounts/AccountAuthenticatorCache.java
+++ b/core/java/android/accounts/AccountAuthenticatorCache.java
@@ -16,17 +16,18 @@
package android.accounts;
+import android.content.Context;
import android.content.pm.PackageManager;
import android.content.pm.RegisteredServicesCache;
import android.content.pm.XmlSerializerAndParser;
import android.content.res.Resources;
import android.content.res.TypedArray;
-import android.content.Context;
-import android.util.AttributeSet;
import android.text.TextUtils;
+import android.util.AttributeSet;
+
import org.xmlpull.v1.XmlPullParser;
-import org.xmlpull.v1.XmlSerializer;
import org.xmlpull.v1.XmlPullParserException;
+import org.xmlpull.v1.XmlSerializer;
import java.io.IOException;
diff --git a/core/java/android/accounts/AccountManagerService.java b/core/java/android/accounts/AccountManagerService.java
index fc569e0..5cde65c 100644
--- a/core/java/android/accounts/AccountManagerService.java
+++ b/core/java/android/accounts/AccountManagerService.java
@@ -18,7 +18,7 @@ package android.accounts;
import android.Manifest;
import android.app.ActivityManager;
-import android.app.AppGlobals;
+import android.app.ActivityManagerNative;
import android.app.Notification;
import android.app.NotificationManager;
import android.app.PendingIntent;
@@ -32,10 +32,10 @@ import android.content.ServiceConnection;
import android.content.pm.ApplicationInfo;
import android.content.pm.PackageInfo;
import android.content.pm.PackageManager;
+import android.content.pm.PackageManager.NameNotFoundException;
import android.content.pm.RegisteredServicesCache;
import android.content.pm.RegisteredServicesCacheListener;
import android.content.pm.UserInfo;
-import android.content.res.Resources;
import android.database.Cursor;
import android.database.DatabaseUtils;
import android.database.sqlite.SQLiteDatabase;
@@ -55,10 +55,13 @@ import android.os.UserManager;
import android.text.TextUtils;
import android.util.Log;
import android.util.Pair;
+import android.util.Slog;
import android.util.SparseArray;
import com.android.internal.R;
import com.android.internal.util.IndentingPrintWriter;
+import com.google.android.collect.Lists;
+import com.google.android.collect.Sets;
import java.io.File;
import java.io.FileDescriptor;
@@ -67,6 +70,7 @@ import java.util.ArrayList;
import java.util.Arrays;
import java.util.Collection;
import java.util.HashMap;
+import java.util.HashSet;
import java.util.LinkedHashMap;
import java.util.List;
import java.util.Map;
@@ -243,8 +247,7 @@ public class AccountManagerService
}
public void systemReady() {
- mAuthenticatorCache.generateServicesMap();
- initUser(0);
+ initUser(UserHandle.USER_OWNER);
}
private UserManager getUserManager() {
@@ -261,7 +264,7 @@ public class AccountManagerService
accounts = new UserAccounts(mContext, userId);
mUsers.append(userId, accounts);
purgeOldGrants(accounts);
- validateAccountsAndPopulateCache(accounts);
+ validateAccountsInternal(accounts, true /* invalidateAuthenticatorCache */);
}
return accounts;
}
@@ -299,7 +302,34 @@ public class AccountManagerService
}
}
- private void validateAccountsAndPopulateCache(UserAccounts accounts) {
+ /**
+ * Validate internal set of accounts against installed authenticators for
+ * given user. Clears cached authenticators before validating.
+ */
+ public void validateAccounts(int userId) {
+ final UserAccounts accounts = getUserAccounts(userId);
+
+ // Invalidate user-specific cache to make sure we catch any
+ // removed authenticators.
+ validateAccountsInternal(accounts, true /* invalidateAuthenticatorCache */);
+ }
+
+ /**
+ * Validate internal set of accounts against installed authenticators for
+ * given user. Clear cached authenticators before validating when requested.
+ */
+ private void validateAccountsInternal(
+ UserAccounts accounts, boolean invalidateAuthenticatorCache) {
+ if (invalidateAuthenticatorCache) {
+ mAuthenticatorCache.invalidateCache(accounts.userId);
+ }
+
+ final HashSet<AuthenticatorDescription> knownAuth = Sets.newHashSet();
+ for (RegisteredServicesCache.ServiceInfo<AuthenticatorDescription> service :
+ mAuthenticatorCache.getAllServices(accounts.userId)) {
+ knownAuth.add(service.type);
+ }
+
synchronized (accounts.cacheLock) {
final SQLiteDatabase db = accounts.openHelper.getWritableDatabase();
boolean accountDeleted = false;
@@ -314,9 +344,9 @@ public class AccountManagerService
final long accountId = cursor.getLong(0);
final String accountType = cursor.getString(1);
final String accountName = cursor.getString(2);
- if (mAuthenticatorCache.getServiceInfo(
- AuthenticatorDescription.newKey(accountType)) == null) {
- Log.d(TAG, "deleting account " + accountName + " because type "
+
+ if (!knownAuth.contains(AuthenticatorDescription.newKey(accountType))) {
+ Slog.w(TAG, "deleting account " + accountName + " because type "
+ accountType + " no longer has a registered authenticator");
db.delete(TABLE_ACCOUNTS, ACCOUNTS_ID + "=" + accountId, null);
accountDeleted = true;
@@ -390,20 +420,10 @@ public class AccountManagerService
}
}
- private List<UserInfo> getAllUsers() {
- return getUserManager().getUsers();
- }
-
- public void onServiceChanged(AuthenticatorDescription desc, boolean removed) {
- // Validate accounts for all users
- List<UserInfo> users = getAllUsers();
- if (users == null) {
- validateAccountsAndPopulateCache(getUserAccountsForCaller());
- } else {
- for (UserInfo user : users) {
- validateAccountsAndPopulateCache(getUserAccounts(user.id));
- }
- }
+ @Override
+ public void onServiceChanged(AuthenticatorDescription desc, int userId, boolean removed) {
+ Slog.d(TAG, "onServiceChanged() for userId " + userId);
+ validateAccountsInternal(getUserAccounts(userId), false /* invalidateAuthenticatorCache */);
}
public String getPassword(Account account) {
@@ -470,10 +490,11 @@ public class AccountManagerService
+ "caller's uid " + Binder.getCallingUid()
+ ", pid " + Binder.getCallingPid());
}
- long identityToken = clearCallingIdentity();
+ final int userId = UserHandle.getCallingUserId();
+ final long identityToken = clearCallingIdentity();
try {
Collection<AccountAuthenticatorCache.ServiceInfo<AuthenticatorDescription>>
- authenticatorCollection = mAuthenticatorCache.getAllServices();
+ authenticatorCollection = mAuthenticatorCache.getAllServices(userId);
AuthenticatorDescription[] types =
new AuthenticatorDescription[authenticatorCollection.size()];
int i = 0;
@@ -1055,9 +1076,9 @@ public class AccountManagerService
if (authTokenType == null) throw new IllegalArgumentException("authTokenType is null");
checkBinderPermission(Manifest.permission.USE_CREDENTIALS);
final UserAccounts accounts = getUserAccountsForCaller();
- AccountAuthenticatorCache.ServiceInfo<AuthenticatorDescription> authenticatorInfo =
- mAuthenticatorCache.getServiceInfo(
- AuthenticatorDescription.newKey(account.type));
+ final RegisteredServicesCache.ServiceInfo<AuthenticatorDescription> authenticatorInfo;
+ authenticatorInfo = mAuthenticatorCache.getServiceInfo(
+ AuthenticatorDescription.newKey(account.type), accounts.userId);
final boolean customTokens =
authenticatorInfo != null && authenticatorInfo.type.customTokens;
@@ -1074,7 +1095,7 @@ public class AccountManagerService
if (notifyOnAuthFailure) {
loginOptions.putBoolean(AccountManager.KEY_NOTIFY_ON_FAILURE, true);
}
-
+
long identityToken = clearCallingIdentity();
try {
// if the caller has permission, do the peek. otherwise go the more expensive
@@ -1183,28 +1204,6 @@ public class AccountManagerService
account, authTokenType, uid), n, user);
}
- String getAccountLabel(String accountType) {
- RegisteredServicesCache.ServiceInfo<AuthenticatorDescription> serviceInfo =
- mAuthenticatorCache.getServiceInfo(
- AuthenticatorDescription.newKey(accountType));
- if (serviceInfo == null) {
- throw new IllegalArgumentException("unknown account type: " + accountType);
- }
-
- final Context authContext;
- try {
- authContext = mContext.createPackageContext(
- serviceInfo.type.packageName, 0);
- } catch (PackageManager.NameNotFoundException e) {
- throw new IllegalArgumentException("unknown account type: " + accountType);
- }
- try {
- return authContext.getString(serviceInfo.type.labelId);
- } catch (Resources.NotFoundException e) {
- throw new IllegalArgumentException("unknown account type: " + accountType);
- }
- }
-
private Intent newGrantCredentialsPermissionIntent(Account account, int uid,
AccountAuthenticatorResponse response, String authTokenType, String authTokenLabel) {
@@ -1506,28 +1505,48 @@ public class AccountManagerService
}
/**
- * Returns all the accounts qualified by user.
+ * Returns accounts for all running users.
+ *
* @hide
*/
+ public AccountAndUser[] getRunningAccounts() {
+ final int[] runningUserIds;
+ try {
+ runningUserIds = ActivityManagerNative.getDefault().getRunningUserIds();
+ } catch (RemoteException e) {
+ // Running in system_server; should never happen
+ throw new RuntimeException(e);
+ }
+ return getAccounts(runningUserIds);
+ }
+
+ /** {@hide} */
public AccountAndUser[] getAllAccounts() {
- ArrayList<AccountAndUser> allAccounts = new ArrayList<AccountAndUser>();
- List<UserInfo> users = getAllUsers();
- if (users == null) return new AccountAndUser[0];
+ final List<UserInfo> users = getUserManager().getUsers();
+ final int[] userIds = new int[users.size()];
+ for (int i = 0; i < userIds.length; i++) {
+ userIds[i] = users.get(i).id;
+ }
+ return getAccounts(userIds);
+ }
- synchronized(mUsers) {
- for (UserInfo user : users) {
- UserAccounts userAccounts = getUserAccounts(user.id);
+ private AccountAndUser[] getAccounts(int[] userIds) {
+ final ArrayList<AccountAndUser> runningAccounts = Lists.newArrayList();
+ synchronized (mUsers) {
+ for (int userId : userIds) {
+ UserAccounts userAccounts = getUserAccounts(userId);
if (userAccounts == null) continue;
synchronized (userAccounts.cacheLock) {
Account[] accounts = getAccountsFromCacheLocked(userAccounts, null);
for (int a = 0; a < accounts.length; a++) {
- allAccounts.add(new AccountAndUser(accounts[a], user.id));
+ runningAccounts.add(new AccountAndUser(accounts[a], userId));
}
}
}
}
- AccountAndUser[] accountsArray = new AccountAndUser[allAccounts.size()];
- return allAccounts.toArray(accountsArray);
+
+ AccountAndUser[] accountsArray = new AccountAndUser[runningAccounts.size()];
+ return runningAccounts.toArray(accountsArray);
}
public Account[] getAccounts(String type) {
@@ -1836,9 +1855,9 @@ public class AccountManagerService
* if no authenticator or the bind fails then return false, otherwise return true
*/
private boolean bindToAuthenticator(String authenticatorType) {
- AccountAuthenticatorCache.ServiceInfo<AuthenticatorDescription> authenticatorInfo =
- mAuthenticatorCache.getServiceInfo(
- AuthenticatorDescription.newKey(authenticatorType));
+ final AccountAuthenticatorCache.ServiceInfo<AuthenticatorDescription> authenticatorInfo;
+ authenticatorInfo = mAuthenticatorCache.getServiceInfo(
+ AuthenticatorDescription.newKey(authenticatorType), mAccounts.userId);
if (authenticatorInfo == null) {
if (Log.isLoggable(TAG, Log.VERBOSE)) {
Log.v(TAG, "there is no authenticator for " + authenticatorType
@@ -2024,6 +2043,7 @@ public class AccountManagerService
return false;
}
+ @Override
protected void dump(FileDescriptor fd, PrintWriter fout, String[] args) {
if (mContext.checkCallingOrSelfPermission(android.Manifest.permission.DUMP)
!= PackageManager.PERMISSION_GRANTED) {
@@ -2033,17 +2053,15 @@ public class AccountManagerService
return;
}
final boolean isCheckinRequest = scanArgs(args, "--checkin") || scanArgs(args, "-c");
+ final IndentingPrintWriter ipw = new IndentingPrintWriter(fout, " ");
- fout = new IndentingPrintWriter(fout, " ");
- int size = mUsers.size();
- for (int i = 0; i < size; i++) {
- fout.println("User " + mUsers.keyAt(i) + ":");
- ((IndentingPrintWriter) fout).increaseIndent();
- dumpUser(mUsers.valueAt(i), fd, fout, args, isCheckinRequest);
- ((IndentingPrintWriter) fout).decreaseIndent();
- if (i < size - 1) {
- fout.println();
- }
+ final List<UserInfo> users = getUserManager().getUsers();
+ for (UserInfo user : users) {
+ ipw.println("User " + user + ":");
+ ipw.increaseIndent();
+ dumpUser(getUserAccounts(user.id), fd, ipw, args, isCheckinRequest);
+ ipw.println();
+ ipw.decreaseIndent();
}
}
@@ -2083,7 +2101,7 @@ public class AccountManagerService
}
fout.println();
- mAuthenticatorCache.dump(fd, fout, args);
+ mAuthenticatorCache.dump(fd, fout, args, userAccounts.userId);
}
}
}
@@ -2154,11 +2172,21 @@ public class AccountManagerService
throw new SecurityException(msg);
}
- private boolean inSystemImage(int callerUid) {
- String[] packages = mPackageManager.getPackagesForUid(callerUid);
+ private boolean inSystemImage(int callingUid) {
+ final int callingUserId = UserHandle.getUserId(callingUid);
+
+ final PackageManager userPackageManager;
+ try {
+ userPackageManager = mContext.createPackageContextAsUser(
+ "android", 0, new UserHandle(callingUserId)).getPackageManager();
+ } catch (NameNotFoundException e) {
+ return false;
+ }
+
+ String[] packages = userPackageManager.getPackagesForUid(callingUid);
for (String name : packages) {
try {
- PackageInfo packageInfo = mPackageManager.getPackageInfo(name, 0 /* flags */);
+ PackageInfo packageInfo = userPackageManager.getPackageInfo(name, 0 /* flags */);
if (packageInfo != null
&& (packageInfo.applicationInfo.flags & ApplicationInfo.FLAG_SYSTEM) != 0) {
return true;
@@ -2186,8 +2214,9 @@ public class AccountManagerService
}
private boolean hasAuthenticatorUid(String accountType, int callingUid) {
+ final int callingUserId = UserHandle.getUserId(callingUid);
for (RegisteredServicesCache.ServiceInfo<AuthenticatorDescription> serviceInfo :
- mAuthenticatorCache.getAllServices()) {
+ mAuthenticatorCache.getAllServices(callingUserId)) {
if (serviceInfo.type.type.equals(accountType)) {
return (serviceInfo.uid == callingUid) ||
(mPackageManager.checkSignatures(serviceInfo.uid, callingUid)
diff --git a/core/java/android/accounts/IAccountAuthenticatorCache.java b/core/java/android/accounts/IAccountAuthenticatorCache.java
index 20dd585..06c2106 100644
--- a/core/java/android/accounts/IAccountAuthenticatorCache.java
+++ b/core/java/android/accounts/IAccountAuthenticatorCache.java
@@ -39,18 +39,19 @@ public interface IAccountAuthenticatorCache {
* matches the account type or null if none is present
*/
RegisteredServicesCache.ServiceInfo<AuthenticatorDescription> getServiceInfo(
- AuthenticatorDescription type);
+ AuthenticatorDescription type, int userId);
/**
* @return A copy of a Collection of all the current Authenticators.
*/
- Collection<RegisteredServicesCache.ServiceInfo<AuthenticatorDescription>> getAllServices();
+ Collection<RegisteredServicesCache.ServiceInfo<AuthenticatorDescription>> getAllServices(
+ int userId);
/**
* Dumps the state of the cache. See
* {@link android.os.Binder#dump(java.io.FileDescriptor, java.io.PrintWriter, String[])}
*/
- void dump(FileDescriptor fd, PrintWriter fout, String[] args);
+ void dump(FileDescriptor fd, PrintWriter fout, String[] args, int userId);
/**
* Sets a listener that will be notified whenever the authenticator set changes
@@ -61,8 +62,5 @@ public interface IAccountAuthenticatorCache {
void setListener(RegisteredServicesCacheListener<AuthenticatorDescription> listener,
Handler handler);
- /**
- * Refreshes the authenticator cache.
- */
- void generateServicesMap();
-} \ No newline at end of file
+ void invalidateCache(int userId);
+}
diff --git a/core/java/android/app/ActivityManager.java b/core/java/android/app/ActivityManager.java
index 0eda6b4..594be68 100644
--- a/core/java/android/app/ActivityManager.java
+++ b/core/java/android/app/ActivityManager.java
@@ -1981,7 +1981,7 @@ public class ActivityManager {
*/
public boolean isUserRunning(int userid) {
try {
- return ActivityManagerNative.getDefault().isUserRunning(userid);
+ return ActivityManagerNative.getDefault().isUserRunning(userid, false);
} catch (RemoteException e) {
return false;
}
diff --git a/core/java/android/app/ActivityManagerNative.java b/core/java/android/app/ActivityManagerNative.java
index bb62c9e..7492629 100644
--- a/core/java/android/app/ActivityManagerNative.java
+++ b/core/java/android/app/ActivityManagerNative.java
@@ -1608,7 +1608,8 @@ public abstract class ActivityManagerNative extends Binder implements IActivityM
case IS_USER_RUNNING_TRANSACTION: {
data.enforceInterface(IActivityManager.descriptor);
int userid = data.readInt();
- boolean result = isUserRunning(userid);
+ boolean orStopping = data.readInt() != 0;
+ boolean result = isUserRunning(userid, orStopping);
reply.writeNoException();
reply.writeInt(result ? 1 : 0);
return true;
@@ -3865,11 +3866,12 @@ class ActivityManagerProxy implements IActivityManager
return userInfo;
}
- public boolean isUserRunning(int userid) throws RemoteException {
+ public boolean isUserRunning(int userid, boolean orStopping) throws RemoteException {
Parcel data = Parcel.obtain();
Parcel reply = Parcel.obtain();
data.writeInterfaceToken(IActivityManager.descriptor);
data.writeInt(userid);
+ data.writeInt(orStopping ? 1 : 0);
mRemote.transact(IS_USER_RUNNING_TRANSACTION, data, reply, 0);
reply.readException();
boolean result = reply.readInt() != 0;
diff --git a/core/java/android/app/ContextImpl.java b/core/java/android/app/ContextImpl.java
index c324da9..3e1e358 100644
--- a/core/java/android/app/ContextImpl.java
+++ b/core/java/android/app/ContextImpl.java
@@ -1352,10 +1352,16 @@ class ContextImpl extends Context {
ComponentName cn = ActivityManagerNative.getDefault().startService(
mMainThread.getApplicationThread(), service,
service.resolveTypeIfNeeded(getContentResolver()), user.getIdentifier());
- if (cn != null && cn.getPackageName().equals("!")) {
- throw new SecurityException(
- "Not allowed to start service " + service
- + " without permission " + cn.getClassName());
+ if (cn != null) {
+ if (cn.getPackageName().equals("!")) {
+ throw new SecurityException(
+ "Not allowed to start service " + service
+ + " without permission " + cn.getClassName());
+ } else if (cn.getPackageName().equals("!!")) {
+ throw new SecurityException(
+ "Unable to start service " + service
+ + ": " + cn.getClassName());
+ }
}
return cn;
} catch (RemoteException e) {
diff --git a/core/java/android/app/IActivityManager.java b/core/java/android/app/IActivityManager.java
index da844ef..97250e9 100644
--- a/core/java/android/app/IActivityManager.java
+++ b/core/java/android/app/IActivityManager.java
@@ -326,7 +326,7 @@ public interface IActivityManager extends IInterface {
public boolean switchUser(int userid) throws RemoteException;
public int stopUser(int userid, IStopUserCallback callback) throws RemoteException;
public UserInfo getCurrentUser() throws RemoteException;
- public boolean isUserRunning(int userid) throws RemoteException;
+ public boolean isUserRunning(int userid, boolean orStopping) throws RemoteException;
public int[] getRunningUserIds() throws RemoteException;
public boolean removeSubTask(int taskId, int subTaskIndex) throws RemoteException;
diff --git a/core/java/android/bluetooth/BluetoothA2dp.java b/core/java/android/bluetooth/BluetoothA2dp.java
index 6e2278d..6fdf3b4 100755
--- a/core/java/android/bluetooth/BluetoothA2dp.java
+++ b/core/java/android/bluetooth/BluetoothA2dp.java
@@ -45,6 +45,7 @@ import java.util.List;
public final class BluetoothA2dp implements BluetoothProfile {
private static final String TAG = "BluetoothA2dp";
private static final boolean DBG = true;
+ private static final boolean VDBG = false;
/**
* Intent used to broadcast the change in connection state of the A2DP
@@ -113,7 +114,7 @@ public final class BluetoothA2dp implements BluetoothProfile {
public void onBluetoothStateChange(boolean up) {
if (DBG) Log.d(TAG, "onBluetoothStateChange: up=" + up);
if (!up) {
- if (DBG) Log.d(TAG,"Unbinding service...");
+ if (VDBG) Log.d(TAG,"Unbinding service...");
synchronized (mConnection) {
try {
mService = null;
@@ -126,7 +127,7 @@ public final class BluetoothA2dp implements BluetoothProfile {
synchronized (mConnection) {
try {
if (mService == null) {
- if (DBG) Log.d(TAG,"Binding service...");
+ if (VDBG) Log.d(TAG,"Binding service...");
if (!mContext.bindService(new Intent(IBluetoothA2dp.class.getName()), mConnection, 0)) {
Log.e(TAG, "Could not bind to Bluetooth A2DP Service");
}
@@ -269,7 +270,7 @@ public final class BluetoothA2dp implements BluetoothProfile {
* {@inheritDoc}
*/
public List<BluetoothDevice> getConnectedDevices() {
- if (DBG) log("getConnectedDevices()");
+ if (VDBG) log("getConnectedDevices()");
if (mService != null && isEnabled()) {
try {
return mService.getConnectedDevices();
@@ -286,7 +287,7 @@ public final class BluetoothA2dp implements BluetoothProfile {
* {@inheritDoc}
*/
public List<BluetoothDevice> getDevicesMatchingConnectionStates(int[] states) {
- if (DBG) log("getDevicesMatchingStates()");
+ if (VDBG) log("getDevicesMatchingStates()");
if (mService != null && isEnabled()) {
try {
return mService.getDevicesMatchingConnectionStates(states);
@@ -303,7 +304,7 @@ public final class BluetoothA2dp implements BluetoothProfile {
* {@inheritDoc}
*/
public int getConnectionState(BluetoothDevice device) {
- if (DBG) log("getState(" + device + ")");
+ if (VDBG) log("getState(" + device + ")");
if (mService != null && isEnabled()
&& isValidDevice(device)) {
try {
@@ -365,7 +366,7 @@ public final class BluetoothA2dp implements BluetoothProfile {
* @hide
*/
public int getPriority(BluetoothDevice device) {
- if (DBG) log("getPriority(" + device + ")");
+ if (VDBG) log("getPriority(" + device + ")");
if (mService != null && isEnabled()
&& isValidDevice(device)) {
try {
diff --git a/core/java/android/bluetooth/BluetoothHeadset.java b/core/java/android/bluetooth/BluetoothHeadset.java
index 541b69f..793d798 100755
--- a/core/java/android/bluetooth/BluetoothHeadset.java
+++ b/core/java/android/bluetooth/BluetoothHeadset.java
@@ -46,6 +46,7 @@ import java.util.List;
public final class BluetoothHeadset implements BluetoothProfile {
private static final String TAG = "BluetoothHeadset";
private static final boolean DBG = true;
+ private static final boolean VDBG = false;
/**
* Intent used to broadcast the change in connection state of the Headset
@@ -226,7 +227,7 @@ public final class BluetoothHeadset implements BluetoothProfile {
public void onBluetoothStateChange(boolean up) {
if (DBG) Log.d(TAG, "onBluetoothStateChange: up=" + up);
if (!up) {
- if (DBG) Log.d(TAG,"Unbinding service...");
+ if (VDBG) Log.d(TAG,"Unbinding service...");
synchronized (mConnection) {
try {
mService = null;
@@ -239,7 +240,7 @@ public final class BluetoothHeadset implements BluetoothProfile {
synchronized (mConnection) {
try {
if (mService == null) {
- if (DBG) Log.d(TAG,"Binding service...");
+ if (VDBG) Log.d(TAG,"Binding service...");
if (!mContext.bindService(new Intent(IBluetoothHeadset.class.getName()), mConnection, 0)) {
Log.e(TAG, "Could not bind to Bluetooth Headset Service");
}
@@ -281,7 +282,7 @@ public final class BluetoothHeadset implements BluetoothProfile {
* are ok.
*/
/*package*/ void close() {
- if (DBG) log("close()");
+ if (VDBG) log("close()");
IBluetoothManager mgr = mAdapter.getBluetoothManager();
if (mgr != null) {
@@ -387,7 +388,7 @@ public final class BluetoothHeadset implements BluetoothProfile {
* {@inheritDoc}
*/
public List<BluetoothDevice> getConnectedDevices() {
- if (DBG) log("getConnectedDevices()");
+ if (VDBG) log("getConnectedDevices()");
if (mService != null && isEnabled()) {
try {
return mService.getConnectedDevices();
@@ -404,7 +405,7 @@ public final class BluetoothHeadset implements BluetoothProfile {
* {@inheritDoc}
*/
public List<BluetoothDevice> getDevicesMatchingConnectionStates(int[] states) {
- if (DBG) log("getDevicesMatchingStates()");
+ if (VDBG) log("getDevicesMatchingStates()");
if (mService != null && isEnabled()) {
try {
return mService.getDevicesMatchingConnectionStates(states);
@@ -421,7 +422,7 @@ public final class BluetoothHeadset implements BluetoothProfile {
* {@inheritDoc}
*/
public int getConnectionState(BluetoothDevice device) {
- if (DBG) log("getConnectionState(" + device + ")");
+ if (VDBG) log("getConnectionState(" + device + ")");
if (mService != null && isEnabled() &&
isValidDevice(device)) {
try {
@@ -483,7 +484,7 @@ public final class BluetoothHeadset implements BluetoothProfile {
* @hide
*/
public int getPriority(BluetoothDevice device) {
- if (DBG) log("getPriority(" + device + ")");
+ if (VDBG) log("getPriority(" + device + ")");
if (mService != null && isEnabled() &&
isValidDevice(device)) {
try {
@@ -566,7 +567,7 @@ public final class BluetoothHeadset implements BluetoothProfile {
* false otherwise or on error
*/
public boolean isAudioConnected(BluetoothDevice device) {
- if (DBG) log("isAudioConnected()");
+ if (VDBG) log("isAudioConnected()");
if (mService != null && isEnabled() &&
isValidDevice(device)) {
try {
@@ -594,7 +595,7 @@ public final class BluetoothHeadset implements BluetoothProfile {
* @hide
*/
public int getBatteryUsageHint(BluetoothDevice device) {
- if (DBG) log("getBatteryUsageHint()");
+ if (VDBG) log("getBatteryUsageHint()");
if (mService != null && isEnabled() &&
isValidDevice(device)) {
try {
@@ -661,7 +662,7 @@ public final class BluetoothHeadset implements BluetoothProfile {
* @hide
*/
public int getAudioState(BluetoothDevice device) {
- if (DBG) log("getAudioState");
+ if (VDBG) log("getAudioState");
if (mService != null && !isDisabled()) {
try {
return mService.getAudioState(device);
@@ -683,7 +684,7 @@ public final class BluetoothHeadset implements BluetoothProfile {
* @hide
*/
public boolean isAudioOn() {
- if (DBG) log("isAudioOn()");
+ if (VDBG) log("isAudioOn()");
if (mService != null && isEnabled()) {
try {
return mService.isAudioOn();
diff --git a/core/java/android/bluetooth/BluetoothHealth.java b/core/java/android/bluetooth/BluetoothHealth.java
index 4a0bc7e..cb23662 100644
--- a/core/java/android/bluetooth/BluetoothHealth.java
+++ b/core/java/android/bluetooth/BluetoothHealth.java
@@ -58,6 +58,7 @@ import java.util.List;
public final class BluetoothHealth implements BluetoothProfile {
private static final String TAG = "BluetoothHealth";
private static final boolean DBG = true;
+ private static final boolean VDBG = false;
/**
* Health Profile Source Role - the health device.
@@ -102,7 +103,7 @@ public final class BluetoothHealth implements BluetoothProfile {
public void onBluetoothStateChange(boolean up) {
if (DBG) Log.d(TAG, "onBluetoothStateChange: up=" + up);
if (!up) {
- if (DBG) Log.d(TAG,"Unbinding service...");
+ if (VDBG) Log.d(TAG,"Unbinding service...");
synchronized (mConnection) {
try {
mService = null;
@@ -115,7 +116,7 @@ public final class BluetoothHealth implements BluetoothProfile {
synchronized (mConnection) {
try {
if (mService == null) {
- if (DBG) Log.d(TAG,"Binding service...");
+ if (VDBG) Log.d(TAG,"Binding service...");
if (!mContext.bindService(new Intent(IBluetoothHealth.class.getName()), mConnection, 0)) {
Log.e(TAG, "Could not bind to Bluetooth Health Service");
}
@@ -148,7 +149,7 @@ public final class BluetoothHealth implements BluetoothProfile {
BluetoothHealthCallback callback) {
if (!isEnabled() || name == null) return false;
- if (DBG) log("registerSinkApplication(" + name + ":" + dataType + ")");
+ if (VDBG) log("registerSinkApplication(" + name + ":" + dataType + ")");
return registerAppConfiguration(name, dataType, SINK_ROLE,
CHANNEL_TYPE_ANY, callback);
}
@@ -174,7 +175,7 @@ public final class BluetoothHealth implements BluetoothProfile {
boolean result = false;
if (!isEnabled() || !checkAppParam(name, role, channelType, callback)) return result;
- if (DBG) log("registerApplication(" + name + ":" + dataType + ")");
+ if (VDBG) log("registerApplication(" + name + ":" + dataType + ")");
BluetoothHealthCallbackWrapper wrapper = new BluetoothHealthCallbackWrapper(callback);
BluetoothHealthAppConfiguration config =
new BluetoothHealthAppConfiguration(name, dataType, role, channelType);
@@ -488,7 +489,7 @@ public final class BluetoothHealth implements BluetoothProfile {
}
/*package*/ void close() {
- if (DBG) log("close()");
+ if (VDBG) log("close()");
IBluetoothManager mgr = mAdapter.getBluetoothManager();
if (mgr != null) {
try {
diff --git a/core/java/android/bluetooth/BluetoothInputDevice.java b/core/java/android/bluetooth/BluetoothInputDevice.java
index bff966d..db7e424 100755
--- a/core/java/android/bluetooth/BluetoothInputDevice.java
+++ b/core/java/android/bluetooth/BluetoothInputDevice.java
@@ -45,6 +45,7 @@ import java.util.List;
public final class BluetoothInputDevice implements BluetoothProfile {
private static final String TAG = "BluetoothInputDevice";
private static final boolean DBG = true;
+ private static final boolean VDBG = false;
/**
* Intent used to broadcast the change in connection state of the Input
@@ -191,7 +192,7 @@ public final class BluetoothInputDevice implements BluetoothProfile {
public void onBluetoothStateChange(boolean up) {
if (DBG) Log.d(TAG, "onBluetoothStateChange: up=" + up);
if (!up) {
- if (DBG) Log.d(TAG,"Unbinding service...");
+ if (VDBG) Log.d(TAG,"Unbinding service...");
synchronized (mConnection) {
try {
mService = null;
@@ -204,7 +205,7 @@ public final class BluetoothInputDevice implements BluetoothProfile {
synchronized (mConnection) {
try {
if (mService == null) {
- if (DBG) Log.d(TAG,"Binding service...");
+ if (VDBG) Log.d(TAG,"Binding service...");
if (!mContext.bindService(new Intent(IBluetoothInputDevice.class.getName()), mConnection, 0)) {
Log.e(TAG, "Could not bind to Bluetooth HID Service");
}
@@ -243,7 +244,7 @@ public final class BluetoothInputDevice implements BluetoothProfile {
}
/*package*/ void close() {
- if (DBG) log("close()");
+ if (VDBG) log("close()");
IBluetoothManager mgr = mAdapter.getBluetoothManager();
if (mgr != null) {
try {
@@ -344,7 +345,7 @@ public final class BluetoothInputDevice implements BluetoothProfile {
* {@inheritDoc}
*/
public List<BluetoothDevice> getConnectedDevices() {
- if (DBG) log("getConnectedDevices()");
+ if (VDBG) log("getConnectedDevices()");
if (mService != null && isEnabled()) {
try {
return mService.getConnectedDevices();
@@ -361,7 +362,7 @@ public final class BluetoothInputDevice implements BluetoothProfile {
* {@inheritDoc}
*/
public List<BluetoothDevice> getDevicesMatchingConnectionStates(int[] states) {
- if (DBG) log("getDevicesMatchingStates()");
+ if (VDBG) log("getDevicesMatchingStates()");
if (mService != null && isEnabled()) {
try {
return mService.getDevicesMatchingConnectionStates(states);
@@ -378,7 +379,7 @@ public final class BluetoothInputDevice implements BluetoothProfile {
* {@inheritDoc}
*/
public int getConnectionState(BluetoothDevice device) {
- if (DBG) log("getState(" + device + ")");
+ if (VDBG) log("getState(" + device + ")");
if (mService != null && isEnabled() && isValidDevice(device)) {
try {
return mService.getConnectionState(device);
@@ -438,7 +439,7 @@ public final class BluetoothInputDevice implements BluetoothProfile {
* @hide
*/
public int getPriority(BluetoothDevice device) {
- if (DBG) log("getPriority(" + device + ")");
+ if (VDBG) log("getPriority(" + device + ")");
if (mService != null && isEnabled() && isValidDevice(device)) {
try {
return mService.getPriority(device);
@@ -519,7 +520,7 @@ public final class BluetoothInputDevice implements BluetoothProfile {
* @hide
*/
public boolean getProtocolMode(BluetoothDevice device) {
- if (DBG) log("getProtocolMode(" + device + ")");
+ if (VDBG) log("getProtocolMode(" + device + ")");
if (mService != null && isEnabled() && isValidDevice(device)) {
try {
return mService.getProtocolMode(device);
@@ -570,7 +571,7 @@ public final class BluetoothInputDevice implements BluetoothProfile {
* @hide
*/
public boolean getReport(BluetoothDevice device, byte reportType, byte reportId, int bufferSize) {
- if (DBG) log("getReport(" + device + "), reportType=" + reportType + " reportId=" + reportId + "bufferSize=" + bufferSize);
+ if (VDBG) log("getReport(" + device + "), reportType=" + reportType + " reportId=" + reportId + "bufferSize=" + bufferSize);
if (mService != null && isEnabled() && isValidDevice(device)) {
try {
return mService.getReport(device, reportType, reportId, bufferSize);
diff --git a/core/java/android/bluetooth/BluetoothPan.java b/core/java/android/bluetooth/BluetoothPan.java
index cae7a73..e25ec86 100644
--- a/core/java/android/bluetooth/BluetoothPan.java
+++ b/core/java/android/bluetooth/BluetoothPan.java
@@ -44,6 +44,7 @@ import java.util.List;
public final class BluetoothPan implements BluetoothProfile {
private static final String TAG = "BluetoothPan";
private static final boolean DBG = true;
+ private static final boolean VDBG = false;
/**
* Intent used to broadcast the change in connection state of the Pan
@@ -145,7 +146,7 @@ public final class BluetoothPan implements BluetoothProfile {
}
/*package*/ void close() {
- if (DBG) log("close()");
+ if (VDBG) log("close()");
if (mConnection != null) {
mContext.unbindService(mConnection);
mConnection = null;
@@ -175,7 +176,7 @@ public final class BluetoothPan implements BluetoothProfile {
}
Log.d(TAG, "BluetoothPan(), bindService called");
} else {
- if (DBG) Log.d(TAG,"Unbinding service...");
+ if (VDBG) Log.d(TAG,"Unbinding service...");
synchronized (mConnection) {
try {
mPanService = null;
@@ -266,7 +267,7 @@ public final class BluetoothPan implements BluetoothProfile {
* {@inheritDoc}
*/
public List<BluetoothDevice> getConnectedDevices() {
- if (DBG) log("getConnectedDevices()");
+ if (VDBG) log("getConnectedDevices()");
if (mPanService != null && isEnabled()) {
try {
return mPanService.getConnectedDevices();
@@ -283,7 +284,7 @@ public final class BluetoothPan implements BluetoothProfile {
* {@inheritDoc}
*/
public List<BluetoothDevice> getDevicesMatchingConnectionStates(int[] states) {
- if (DBG) log("getDevicesMatchingStates()");
+ if (VDBG) log("getDevicesMatchingStates()");
if (mPanService != null && isEnabled()) {
try {
return mPanService.getDevicesMatchingConnectionStates(states);
@@ -300,7 +301,7 @@ public final class BluetoothPan implements BluetoothProfile {
* {@inheritDoc}
*/
public int getConnectionState(BluetoothDevice device) {
- if (DBG) log("getState(" + device + ")");
+ if (VDBG) log("getState(" + device + ")");
if (mPanService != null && isEnabled()
&& isValidDevice(device)) {
try {
@@ -324,7 +325,7 @@ public final class BluetoothPan implements BluetoothProfile {
}
public boolean isTetheringOn() {
- if (DBG) log("isTetheringOn()");
+ if (VDBG) log("isTetheringOn()");
try {
return mPanService.isTetheringOn();
} catch (RemoteException e) {
diff --git a/core/java/android/bluetooth/BluetoothPbap.java b/core/java/android/bluetooth/BluetoothPbap.java
index 7de2ef6..b5280e5 100755
--- a/core/java/android/bluetooth/BluetoothPbap.java
+++ b/core/java/android/bluetooth/BluetoothPbap.java
@@ -51,7 +51,8 @@ import android.util.Log;
public class BluetoothPbap {
private static final String TAG = "BluetoothPbap";
- private static final boolean DBG = false;
+ private static final boolean DBG = true;
+ private static final boolean VDBG = false;
/** int extra for PBAP_STATE_CHANGED_ACTION */
public static final String PBAP_STATE =
@@ -114,7 +115,7 @@ public class BluetoothPbap {
public void onBluetoothStateChange(boolean up) {
if (DBG) Log.d(TAG, "onBluetoothStateChange: up=" + up);
if (!up) {
- if (DBG) Log.d(TAG,"Unbinding service...");
+ if (VDBG) Log.d(TAG,"Unbinding service...");
synchronized (mConnection) {
try {
mService = null;
@@ -127,7 +128,7 @@ public class BluetoothPbap {
synchronized (mConnection) {
try {
if (mService == null) {
- if (DBG) Log.d(TAG,"Binding service...");
+ if (VDBG) Log.d(TAG,"Binding service...");
if (!mContext.bindService(
new Intent(IBluetoothPbap.class.getName()),
mConnection, 0)) {
@@ -206,7 +207,7 @@ public class BluetoothPbap {
* object is currently not connected to the Pbap service.
*/
public int getState() {
- if (DBG) log("getState()");
+ if (VDBG) log("getState()");
if (mService != null) {
try {
return mService.getState();
@@ -225,7 +226,7 @@ public class BluetoothPbap {
* the Pbap service.
*/
public BluetoothDevice getClient() {
- if (DBG) log("getClient()");
+ if (VDBG) log("getClient()");
if (mService != null) {
try {
return mService.getClient();
@@ -243,7 +244,7 @@ public class BluetoothPbap {
* object is not currently connected to the Pbap service.
*/
public boolean isConnected(BluetoothDevice device) {
- if (DBG) log("isConnected(" + device + ")");
+ if (VDBG) log("isConnected(" + device + ")");
if (mService != null) {
try {
return mService.isConnected(device);
diff --git a/core/java/android/bluetooth/BluetoothSocket.java b/core/java/android/bluetooth/BluetoothSocket.java
index 1bc640f..aba8710 100644
--- a/core/java/android/bluetooth/BluetoothSocket.java
+++ b/core/java/android/bluetooth/BluetoothSocket.java
@@ -72,6 +72,8 @@ import java.nio.ByteBuffer;
*/
public final class BluetoothSocket implements Closeable {
private static final String TAG = "BluetoothSocket";
+ private static final boolean DBG = true;
+ private static final boolean VDBG = false;
/** @hide */
public static final int MAX_RFCOMM_CHANNEL = 30;
@@ -172,7 +174,7 @@ public final class BluetoothSocket implements Closeable {
BluetoothSocket as = new BluetoothSocket(this);
as.mSocketState = SocketState.CONNECTED;
FileDescriptor[] fds = mSocket.getAncillaryFileDescriptors();
- Log.d(TAG, "socket fd passed by stack fds: " + fds);
+ if (VDBG) Log.d(TAG, "socket fd passed by stack fds: " + fds);
if(fds == null || fds.length != 1) {
Log.e(TAG, "socket fd passed from stack failed, fds: " + fds);
throw new IOException("bt socket acept failed");
@@ -291,7 +293,7 @@ public final class BluetoothSocket implements Closeable {
mUuid, mPort, getSecurityFlags());
synchronized(this)
{
- Log.d(TAG, "connect(), SocketState: " + mSocketState + ", mPfd: " + mPfd);
+ if (DBG) Log.d(TAG, "connect(), SocketState: " + mSocketState + ", mPfd: " + mPfd);
if (mSocketState == SocketState.CLOSED) throw new IOException("socket closed");
if (mPfd == null) throw new IOException("bt socket connect failed");
FileDescriptor fd = mPfd.getFileDescriptor();
@@ -339,23 +341,24 @@ public final class BluetoothSocket implements Closeable {
// read out port number
try {
synchronized(this) {
- Log.d(TAG, "bindListen(), SocketState: " + mSocketState + ", mPfd: " + mPfd);
+ if (VDBG) Log.d(TAG, "bindListen(), SocketState: " + mSocketState + ", mPfd: " +
+ mPfd);
if(mSocketState != SocketState.INIT) return EBADFD;
if(mPfd == null) return -1;
FileDescriptor fd = mPfd.getFileDescriptor();
- Log.d(TAG, "bindListen(), new LocalSocket ");
+ if (VDBG) Log.d(TAG, "bindListen(), new LocalSocket ");
mSocket = new LocalSocket(fd);
- Log.d(TAG, "bindListen(), new LocalSocket.getInputStream() ");
+ if (VDBG) Log.d(TAG, "bindListen(), new LocalSocket.getInputStream() ");
mSocketIS = mSocket.getInputStream();
mSocketOS = mSocket.getOutputStream();
}
- Log.d(TAG, "bindListen(), readInt mSocketIS: " + mSocketIS);
+ if (VDBG) Log.d(TAG, "bindListen(), readInt mSocketIS: " + mSocketIS);
int channel = readInt(mSocketIS);
synchronized(this) {
if(mSocketState == SocketState.INIT)
mSocketState = SocketState.LISTENING;
}
- Log.d(TAG, "channel: " + channel);
+ if (VDBG) Log.d(TAG, "channel: " + channel);
if (mPort == -1) {
mPort = channel;
} // else ASSERT(mPort == channel)
@@ -385,26 +388,26 @@ public final class BluetoothSocket implements Closeable {
}
/*package*/ int available() throws IOException {
- Log.d(TAG, "available: " + mSocketIS);
+ if (VDBG) Log.d(TAG, "available: " + mSocketIS);
return mSocketIS.available();
}
/*package*/ int read(byte[] b, int offset, int length) throws IOException {
- Log.d(TAG, "read in: " + mSocketIS + " len: " + length);
+ if (VDBG) Log.d(TAG, "read in: " + mSocketIS + " len: " + length);
int ret = mSocketIS.read(b, offset, length);
if(ret < 0)
throw new IOException("bt socket closed, read return: " + ret);
- Log.d(TAG, "read out: " + mSocketIS + " ret: " + ret);
+ if (VDBG) Log.d(TAG, "read out: " + mSocketIS + " ret: " + ret);
return ret;
}
/*package*/ int write(byte[] b, int offset, int length) throws IOException {
- Log.d(TAG, "write: " + mSocketOS + " length: " + length);
+ if (VDBG) Log.d(TAG, "write: " + mSocketOS + " length: " + length);
mSocketOS.write(b, offset, length);
// There is no good way to confirm since the entire process is asynchronous anyway
- Log.d(TAG, "write out: " + mSocketOS + " length: " + length);
+ if (VDBG) Log.d(TAG, "write out: " + mSocketOS + " length: " + length);
return length;
}
@@ -420,10 +423,10 @@ public final class BluetoothSocket implements Closeable {
if(mSocketState == SocketState.CLOSED)
return;
mSocketState = SocketState.CLOSED;
- Log.d(TAG, "close() this: " + this + ", channel: " + mPort + ", mSocketIS: " + mSocketIS +
+ if (VDBG) Log.d(TAG, "close() this: " + this + ", channel: " + mPort + ", mSocketIS: " + mSocketIS +
", mSocketOS: " + mSocketOS + "mSocket: " + mSocket);
if(mSocket != null) {
- Log.d(TAG, "Closing mSocket: " + mSocket);
+ if (VDBG) Log.d(TAG, "Closing mSocket: " + mSocket);
mSocket.shutdownInput();
mSocket.shutdownOutput();
mSocket.close();
@@ -449,7 +452,7 @@ public final class BluetoothSocket implements Closeable {
private String waitSocketSignal(InputStream is) throws IOException {
byte [] sig = new byte[SOCK_SIGNAL_SIZE];
int ret = readAll(is, sig);
- Log.d(TAG, "waitSocketSignal read 16 bytes signal ret: " + ret);
+ if (VDBG) Log.d(TAG, "waitSocketSignal read 16 bytes signal ret: " + ret);
ByteBuffer bb = ByteBuffer.wrap(sig);
bb.order(ByteOrder.nativeOrder());
int size = bb.getShort();
@@ -458,7 +461,7 @@ public final class BluetoothSocket implements Closeable {
int channel = bb.getInt();
int status = bb.getInt();
String RemoteAddr = convertAddr(addr);
- Log.d(TAG, "waitSocketSignal: sig size: " + size + ", remote addr: "
+ if (VDBG) Log.d(TAG, "waitSocketSignal: sig size: " + size + ", remote addr: "
+ RemoteAddr + ", channel: " + channel + ", status: " + status);
if(status != 0)
throw new IOException("Connection failure, status: " + status);
@@ -481,7 +484,7 @@ public final class BluetoothSocket implements Closeable {
private int readInt(InputStream is) throws IOException {
byte[] ibytes = new byte[4];
int ret = readAll(is, ibytes);
- Log.d(TAG, "inputStream.read ret: " + ret);
+ if (VDBG) Log.d(TAG, "inputStream.read ret: " + ret);
ByteBuffer bb = ByteBuffer.wrap(ibytes);
bb.order(ByteOrder.nativeOrder());
return bb.getInt();
diff --git a/core/java/android/bluetooth/BluetoothTetheringDataTracker.java b/core/java/android/bluetooth/BluetoothTetheringDataTracker.java
index 30406e9..063e5a8 100644
--- a/core/java/android/bluetooth/BluetoothTetheringDataTracker.java
+++ b/core/java/android/bluetooth/BluetoothTetheringDataTracker.java
@@ -51,6 +51,8 @@ import java.util.concurrent.atomic.AtomicInteger;
public class BluetoothTetheringDataTracker implements NetworkStateTracker {
private static final String NETWORKTYPE = "BLUETOOTH_TETHER";
private static final String TAG = "BluetoothTethering";
+ private static final boolean DBG = true;
+ private static final boolean VDBG = false;
private AtomicBoolean mTeardownRequested = new AtomicBoolean(false);
private AtomicBoolean mPrivateDnsRouteSet = new AtomicBoolean(false);
@@ -99,10 +101,10 @@ public class BluetoothTetheringDataTracker implements NetworkStateTracker {
* Begin monitoring connectivity
*/
public void startMonitoring(Context context, Handler target) {
- Log.d(TAG, "startMonitoring: target: " + target);
+ if (DBG) Log.d(TAG, "startMonitoring: target: " + target);
mContext = context;
mCsHandler = target;
- Log.d(TAG, "startMonitoring: mCsHandler: " + mCsHandler);
+ if (VDBG) Log.d(TAG, "startMonitoring: mCsHandler: " + mCsHandler);
BluetoothAdapter adapter = BluetoothAdapter.getDefaultAdapter();
if (adapter != null) {
adapter.getProfileProxy(mContext, mProfileServiceListener, BluetoothProfile.PAN);
@@ -310,31 +312,31 @@ public class BluetoothTetheringDataTracker implements NetworkStateTracker {
}
public synchronized void startReverseTether(String iface) {
mIface = iface;
- Log.d(TAG, "startReverseTether mCsHandler: " + mCsHandler);
+ if (DBG) Log.d(TAG, "startReverseTether mCsHandler: " + mCsHandler);
mDhcpThread = new Thread(new Runnable() {
public void run() {
//TODO(): Add callbacks for failure and success case.
//Currently this thread runs independently.
- Log.d(TAG, "startReverseTether mCsHandler: " + mCsHandler);
+ if (DBG) Log.d(TAG, "startReverseTether mCsHandler: " + mCsHandler);
String DhcpResultName = "dhcp." + mIface + ".result";;
String result = "";
- Log.d(TAG, "waiting for change of sys prop dhcp result: " + DhcpResultName);
+ if (VDBG) Log.d(TAG, "waiting for change of sys prop dhcp result: " + DhcpResultName);
for(int i = 0; i < 30*5; i++) {
try { Thread.sleep(200); } catch (InterruptedException ie) { return;}
result = SystemProperties.get(DhcpResultName);
- Log.d(TAG, "read " + DhcpResultName + ": " + result);
+ if (VDBG) Log.d(TAG, "read " + DhcpResultName + ": " + result);
if(result.equals("failed")) {
Log.e(TAG, "startReverseTether, failed to start dhcp service");
return;
}
if(result.equals("ok")) {
- Log.d(TAG, "startReverseTether, dhcp resut: " + result);
+ if (VDBG) Log.d(TAG, "startReverseTether, dhcp resut: " + result);
if(readLinkProperty(mIface)) {
mNetworkInfo.setIsAvailable(true);
mNetworkInfo.setDetailedState(DetailedState.CONNECTED, null, null);
- Log.d(TAG, "startReverseTether mCsHandler: " + mCsHandler);
+ if (VDBG) Log.d(TAG, "startReverseTether mCsHandler: " + mCsHandler);
if(mCsHandler != null) {
Message msg = mCsHandler.obtainMessage(EVENT_CONFIGURATION_CHANGED, mNetworkInfo);
msg.sendToTarget();
@@ -346,7 +348,7 @@ public class BluetoothTetheringDataTracker implements NetworkStateTracker {
return;
}
}
- Log.d(TAG, "startReverseTether, dhcp failed, resut: " + result);
+ Log.e(TAG, "startReverseTether, dhcp failed, resut: " + result);
}
});
mDhcpThread.start();
diff --git a/core/java/android/content/ContentService.java b/core/java/android/content/ContentService.java
index 0f6488a..4512e82 100644
--- a/core/java/android/content/ContentService.java
+++ b/core/java/android/content/ContentService.java
@@ -345,10 +345,11 @@ public final class ContentService extends IContentService.Stub {
public SyncAdapterType[] getSyncAdapterTypes() {
// This makes it so that future permission checks will be in the context of this
// process rather than the caller's process. We will restore this before returning.
- long identityToken = clearCallingIdentity();
+ final int userId = UserHandle.getCallingUserId();
+ final long identityToken = clearCallingIdentity();
try {
SyncManager syncManager = getSyncManager();
- return syncManager.getSyncAdapterTypes();
+ return syncManager.getSyncAdapterTypes(userId);
} finally {
restoreCallingIdentity(identityToken);
}
diff --git a/core/java/android/content/SyncManager.java b/core/java/android/content/SyncManager.java
index 564a804..93c9526 100644
--- a/core/java/android/content/SyncManager.java
+++ b/core/java/android/content/SyncManager.java
@@ -20,8 +20,8 @@ import android.accounts.Account;
import android.accounts.AccountAndUser;
import android.accounts.AccountManager;
import android.accounts.AccountManagerService;
-import android.accounts.OnAccountsUpdateListener;
import android.app.ActivityManager;
+import android.app.ActivityManagerNative;
import android.app.AlarmManager;
import android.app.Notification;
import android.app.NotificationManager;
@@ -58,6 +58,7 @@ import android.util.Log;
import android.util.Pair;
import com.android.internal.R;
+import com.android.internal.util.IndentingPrintWriter;
import com.google.android.collect.Lists;
import com.google.android.collect.Maps;
import com.google.android.collect.Sets;
@@ -81,7 +82,7 @@ import java.util.concurrent.CountDownLatch;
/**
* @hide
*/
-public class SyncManager implements OnAccountsUpdateListener {
+public class SyncManager {
private static final String TAG = "SyncManager";
/** Delay a sync due to local changes this long. In milliseconds */
@@ -141,7 +142,8 @@ public class SyncManager implements OnAccountsUpdateListener {
private static final AccountAndUser[] INITIAL_ACCOUNTS_ARRAY = new AccountAndUser[0];
- private volatile AccountAndUser[] mAccounts = INITIAL_ACCOUNTS_ARRAY;
+ // TODO: add better locking around mRunningAccounts
+ private volatile AccountAndUser[] mRunningAccounts = INITIAL_ACCOUNTS_ARRAY;
volatile private PowerManager.WakeLock mHandleAlarmWakeLock;
volatile private PowerManager.WakeLock mSyncManagerWakeLock;
@@ -205,7 +207,10 @@ public class SyncManager implements OnAccountsUpdateListener {
private BroadcastReceiver mAccountsUpdatedReceiver = new BroadcastReceiver() {
public void onReceive(Context context, Intent intent) {
- onAccountsUpdated(null);
+ updateRunningAccounts();
+
+ // Kick off sync for everyone, since this was a radical account change
+ scheduleSync(null, UserHandle.USER_ALL, null, null, 0 /* no delay */, false);
}
};
@@ -242,33 +247,14 @@ public class SyncManager implements OnAccountsUpdateListener {
return found;
}
- public void onAccountsUpdated(Account[] accounts) {
- // remember if this was the first time this was called after an update
- final boolean justBootedUp = mAccounts == INITIAL_ACCOUNTS_ARRAY;
-
- List<UserInfo> users = getAllUsers();
- if (users == null) return;
-
- int count = 0;
-
- // Get accounts from AccountManager for all the users on the system
- // TODO: Limit this to active users, when such a concept exists.
- AccountAndUser[] allAccounts = AccountManagerService.getSingleton().getAllAccounts();
- for (UserInfo user : users) {
- if (mBootCompleted) {
- Account[] accountsForUser =
- AccountManagerService.getSingleton().getAccounts(user.id);
- mSyncStorageEngine.doDatabaseCleanup(accountsForUser, user.id);
- }
- }
-
- mAccounts = allAccounts;
+ public void updateRunningAccounts() {
+ mRunningAccounts = AccountManagerService.getSingleton().getRunningAccounts();
for (ActiveSyncContext currentSyncContext : mActiveSyncContexts) {
- if (!containsAccountAndUser(allAccounts,
+ if (!containsAccountAndUser(mRunningAccounts,
currentSyncContext.mSyncOperation.account,
currentSyncContext.mSyncOperation.userId)) {
- Log.d(TAG, "canceling sync since the account has been removed");
+ Log.d(TAG, "canceling sync since the account is no longer running");
sendSyncFinishedOrCanceledMessage(currentSyncContext,
null /* no result since this is a cancel */);
}
@@ -277,26 +263,6 @@ public class SyncManager implements OnAccountsUpdateListener {
// we must do this since we don't bother scheduling alarms when
// the accounts are not set yet
sendCheckAlarmsMessage();
-
- if (allAccounts.length > 0) {
- // If this is the first time this was called after a bootup then
- // the accounts haven't really changed, instead they were just loaded
- // from the AccountManager. Otherwise at least one of the accounts
- // has a change.
- //
- // If there was a real account change then force a sync of all accounts.
- // This is a bit of overkill, but at least it will end up retrying syncs
- // that failed due to an authentication failure and thus will recover if the
- // account change was a password update.
- //
- // If this was the bootup case then don't sync everything, instead only
- // sync those that have an unknown syncable state, which will give them
- // a chance to set their syncable state.
-
- boolean onlyThoseWithUnkownSyncableState = justBootedUp;
- scheduleSync(null, UserHandle.USER_ALL, null, null, 0 /* no delay */,
- onlyThoseWithUnkownSyncableState);
- }
}
private BroadcastReceiver mConnectivityIntentReceiver =
@@ -336,19 +302,18 @@ public class SyncManager implements OnAccountsUpdateListener {
@Override
public void onReceive(Context context, Intent intent) {
String action = intent.getAction();
- final int userId = intent.getIntExtra(Intent.EXTRA_USER_HANDLE, -1);
+ final int userId = intent.getIntExtra(Intent.EXTRA_USER_HANDLE, UserHandle.USER_NULL);
+ if (userId == UserHandle.USER_NULL) return;
+
if (Intent.ACTION_USER_REMOVED.equals(action)) {
- Log.i(TAG, "User removed - cleanup: u" + userId);
- onUserRemoved(intent);
- } else if (Intent.ACTION_USER_STARTED.equals(action)) {
- Log.i(TAG, "User started - check alarms: u" + userId);
- sendCheckAlarmsMessage();
- } else if (Intent.ACTION_USER_STOPPED.equals(action)) {
- Log.i(TAG, "User stopped - stop syncs: u" + userId);
- cancelActiveSync(
- null /* any account */,
- userId,
- null /* any authority */);
+ Log.i(TAG, "User removed: u" + userId);
+ onUserRemoved(userId);
+ } else if (Intent.ACTION_USER_STARTING.equals(action)) {
+ Log.i(TAG, "User starting: u" + userId);
+ onUserStarting(userId);
+ } else if (Intent.ACTION_USER_STOPPING.equals(action)) {
+ Log.i(TAG, "User stopping: u" + userId);
+ onUserStopping(userId);
}
}
};
@@ -390,7 +355,8 @@ public class SyncManager implements OnAccountsUpdateListener {
mSyncHandler = new SyncHandler(syncThread.getLooper());
mSyncAdapters.setListener(new RegisteredServicesCacheListener<SyncAdapterType>() {
- public void onServiceChanged(SyncAdapterType type, boolean removed) {
+ @Override
+ public void onServiceChanged(SyncAdapterType type, int userId, boolean removed) {
if (!removed) {
scheduleSync(null, UserHandle.USER_ALL, type.authority, null, 0 /* no delay */,
false /* onlyThoseWithUnkownSyncableState */);
@@ -422,7 +388,8 @@ public class SyncManager implements OnAccountsUpdateListener {
intentFilter = new IntentFilter();
intentFilter.addAction(Intent.ACTION_USER_REMOVED);
- intentFilter.addAction(Intent.ACTION_USER_STARTED);
+ intentFilter.addAction(Intent.ACTION_USER_STARTING);
+ intentFilter.addAction(Intent.ACTION_USER_STOPPING);
mContext.registerReceiverAsUser(
mUserIntentReceiver, UserHandle.ALL, intentFilter, null, null);
@@ -467,8 +434,9 @@ public class SyncManager implements OnAccountsUpdateListener {
UserHandle.ALL,
new IntentFilter(AccountManager.LOGIN_ACCOUNTS_CHANGED_ACTION),
null, null);
+
// do this synchronously to ensure we have the accounts before this call returns
- onAccountsUpdated(null);
+ onUserStarting(UserHandle.USER_OWNER);
}
// Pick a random second in a day to seed all periodic syncs
@@ -548,7 +516,7 @@ public class SyncManager implements OnAccountsUpdateListener {
} else {
// if the accounts aren't configured yet then we can't support an account-less
// sync request
- accounts = mAccounts;
+ accounts = mRunningAccounts;
if (accounts.length == 0) {
if (isLoggable) {
Log.v(TAG, "scheduleSync: no accounts configured, dropping");
@@ -579,32 +547,33 @@ public class SyncManager implements OnAccountsUpdateListener {
source = SyncStorageEngine.SOURCE_SERVER;
}
- // Compile a list of authorities that have sync adapters.
- // For each authority sync each account that matches a sync adapter.
- final HashSet<String> syncableAuthorities = new HashSet<String>();
- for (RegisteredServicesCache.ServiceInfo<SyncAdapterType> syncAdapter :
- mSyncAdapters.getAllServices()) {
- syncableAuthorities.add(syncAdapter.type.authority);
- }
+ for (AccountAndUser account : accounts) {
+ // Compile a list of authorities that have sync adapters.
+ // For each authority sync each account that matches a sync adapter.
+ final HashSet<String> syncableAuthorities = new HashSet<String>();
+ for (RegisteredServicesCache.ServiceInfo<SyncAdapterType> syncAdapter :
+ mSyncAdapters.getAllServices(account.userId)) {
+ syncableAuthorities.add(syncAdapter.type.authority);
+ }
- // if the url was specified then replace the list of authorities with just this authority
- // or clear it if this authority isn't syncable
- if (requestedAuthority != null) {
- final boolean hasSyncAdapter = syncableAuthorities.contains(requestedAuthority);
- syncableAuthorities.clear();
- if (hasSyncAdapter) syncableAuthorities.add(requestedAuthority);
- }
+ // if the url was specified then replace the list of authorities
+ // with just this authority or clear it if this authority isn't
+ // syncable
+ if (requestedAuthority != null) {
+ final boolean hasSyncAdapter = syncableAuthorities.contains(requestedAuthority);
+ syncableAuthorities.clear();
+ if (hasSyncAdapter) syncableAuthorities.add(requestedAuthority);
+ }
- for (String authority : syncableAuthorities) {
- for (AccountAndUser account : accounts) {
+ for (String authority : syncableAuthorities) {
int isSyncable = mSyncStorageEngine.getIsSyncable(account.account, account.userId,
authority);
if (isSyncable == 0) {
continue;
}
- final RegisteredServicesCache.ServiceInfo<SyncAdapterType> syncAdapterInfo =
- mSyncAdapters.getServiceInfo(
- SyncAdapterType.newKey(authority, account.account.type));
+ final RegisteredServicesCache.ServiceInfo<SyncAdapterType> syncAdapterInfo;
+ syncAdapterInfo = mSyncAdapters.getServiceInfo(
+ SyncAdapterType.newKey(authority, account.account.type), account.userId);
if (syncAdapterInfo == null) {
continue;
}
@@ -681,10 +650,9 @@ public class SyncManager implements OnAccountsUpdateListener {
false /* onlyThoseWithUnkownSyncableState */);
}
- public SyncAdapterType[] getSyncAdapterTypes() {
- final Collection<RegisteredServicesCache.ServiceInfo<SyncAdapterType>>
- serviceInfos =
- mSyncAdapters.getAllServices();
+ public SyncAdapterType[] getSyncAdapterTypes(int userId) {
+ final Collection<RegisteredServicesCache.ServiceInfo<SyncAdapterType>> serviceInfos;
+ serviceInfos = mSyncAdapters.getAllServices(userId);
SyncAdapterType[] types = new SyncAdapterType[serviceInfos.size()];
int i = 0;
for (RegisteredServicesCache.ServiceInfo<SyncAdapterType> serviceInfo : serviceInfos) {
@@ -920,16 +888,42 @@ public class SyncManager implements OnAccountsUpdateListener {
}
}
- private void onUserRemoved(Intent intent) {
- int userId = intent.getIntExtra(Intent.EXTRA_USER_HANDLE, -1);
- if (userId == -1) return;
- removeUser(userId);
+ private void onUserStarting(int userId) {
+ // Make sure that accounts we're about to use are valid
+ AccountManagerService.getSingleton().validateAccounts(userId);
+
+ mSyncAdapters.invalidateCache(userId);
+
+ updateRunningAccounts();
+
+ final Account[] accounts = AccountManagerService.getSingleton().getAccounts(userId);
+ mSyncStorageEngine.doDatabaseCleanup(accounts, userId);
+
+ mSyncQueue.addPendingOperations(userId);
+
+ // Schedule sync for any accounts under started user
+ for (Account account : accounts) {
+ scheduleSync(account, userId, null, null, 0 /* no delay */,
+ true /* onlyThoseWithUnknownSyncableState */);
+ }
+
+ sendCheckAlarmsMessage();
+ }
+
+ private void onUserStopping(int userId) {
+ updateRunningAccounts();
+
+ cancelActiveSync(
+ null /* any account */,
+ userId,
+ null /* any authority */);
}
- private void removeUser(int userId) {
+ private void onUserRemoved(int userId) {
+ updateRunningAccounts();
+
// Clean up the storage engine database
mSyncStorageEngine.doDatabaseCleanup(new Account[0], userId);
- onAccountsUpdated(null);
synchronized (mSyncQueue) {
mSyncQueue.removeUser(userId);
}
@@ -1062,14 +1056,10 @@ public class SyncManager implements OnAccountsUpdateListener {
}
protected void dump(FileDescriptor fd, PrintWriter pw) {
- dumpSyncState(pw);
- dumpSyncHistory(pw);
-
- pw.println();
- pw.println("SyncAdapters:");
- for (RegisteredServicesCache.ServiceInfo info : mSyncAdapters.getAllServices()) {
- pw.println(" " + info);
- }
+ final IndentingPrintWriter ipw = new IndentingPrintWriter(pw, " ");
+ dumpSyncState(ipw);
+ dumpSyncHistory(ipw);
+ dumpSyncAdapters(ipw);
}
static String formatTime(long time) {
@@ -1085,13 +1075,13 @@ public class SyncManager implements OnAccountsUpdateListener {
if (users != null) {
for (UserInfo user : users) {
pw.print("u" + user.id + "="
- + mSyncStorageEngine.getMasterSyncAutomatically(user.id));
+ + mSyncStorageEngine.getMasterSyncAutomatically(user.id) + " ");
}
pw.println();
}
pw.print("memory low: "); pw.println(mStorageIsLow);
- final AccountAndUser[] accounts = mAccounts;
+ final AccountAndUser[] accounts = AccountManagerService.getSingleton().getAllAccounts();
pw.print("accounts: ");
if (accounts != INITIAL_ACCOUNTS_ARRAY) {
@@ -1153,7 +1143,7 @@ public class SyncManager implements OnAccountsUpdateListener {
pw.print(" "); pw.print(account.account.type);
pw.println(":");
for (RegisteredServicesCache.ServiceInfo<SyncAdapterType> syncAdapterType :
- mSyncAdapters.getAllServices()) {
+ mSyncAdapters.getAllServices(account.userId)) {
if (!syncAdapterType.type.accountType.equals(account.account.type)) {
continue;
}
@@ -1530,6 +1520,23 @@ public class SyncManager implements OnAccountsUpdateListener {
}
}
+ private void dumpSyncAdapters(IndentingPrintWriter pw) {
+ pw.println();
+ final List<UserInfo> users = getAllUsers();
+ if (users != null) {
+ for (UserInfo user : users) {
+ pw.println("Sync adapters for " + user + ":");
+ pw.increaseIndent();
+ for (RegisteredServicesCache.ServiceInfo<?> info :
+ mSyncAdapters.getAllServices(user.id)) {
+ pw.println(info);
+ }
+ pw.decreaseIndent();
+ pw.println();
+ }
+ }
+ }
+
private static class AuthoritySyncStats {
String name;
long elapsedTime;
@@ -1613,18 +1620,10 @@ public class SyncManager implements OnAccountsUpdateListener {
Maps.newHashMap();
private volatile CountDownLatch mReadyToRunLatch = new CountDownLatch(1);
+
public void onBootCompleted() {
mBootCompleted = true;
- // TODO: Handle bootcompleted event for specific user. Now let's just iterate through
- // all the users.
- List<UserInfo> users = getAllUsers();
- if (users != null) {
- for (UserInfo user : users) {
- mSyncStorageEngine.doDatabaseCleanup(
- AccountManagerService.getSingleton().getAccounts(user.id),
- user.id);
- }
- }
+
if (mReadyToRunLatch != null) {
mReadyToRunLatch.countDown();
}
@@ -1814,7 +1813,7 @@ public class SyncManager implements OnAccountsUpdateListener {
return earliestFuturePollTime;
}
- AccountAndUser[] accounts = mAccounts;
+ AccountAndUser[] accounts = mRunningAccounts;
final long nowAbsolute = System.currentTimeMillis();
final long shiftedNowAbsolute = (0 < nowAbsolute - mSyncRandomOffsetMillis)
@@ -1869,9 +1868,10 @@ public class SyncManager implements OnAccountsUpdateListener {
// Sync now
final Pair<Long, Long> backoff = mSyncStorageEngine.getBackoff(
info.account, info.userId, info.authority);
- final RegisteredServicesCache.ServiceInfo<SyncAdapterType> syncAdapterInfo =
- mSyncAdapters.getServiceInfo(
- SyncAdapterType.newKey(info.authority, info.account.type));
+ final RegisteredServicesCache.ServiceInfo<SyncAdapterType> syncAdapterInfo;
+ syncAdapterInfo = mSyncAdapters.getServiceInfo(
+ SyncAdapterType.newKey(info.authority, info.account.type),
+ info.userId);
if (syncAdapterInfo == null) {
continue;
}
@@ -1927,7 +1927,7 @@ public class SyncManager implements OnAccountsUpdateListener {
// If the accounts aren't known yet then we aren't ready to run. We will be kicked
// when the account lookup request does complete.
- AccountAndUser[] accounts = mAccounts;
+ AccountAndUser[] accounts = mRunningAccounts;
if (accounts == INITIAL_ACCOUNTS_ARRAY) {
if (isLoggable) {
Log.v(TAG, "maybeStartNextSync: accounts not known, skipping");
@@ -1998,7 +1998,7 @@ public class SyncManager implements OnAccountsUpdateListener {
final RegisteredServicesCache.ServiceInfo<SyncAdapterType> syncAdapterInfo;
syncAdapterInfo = mSyncAdapters.getServiceInfo(
- SyncAdapterType.newKey(op.authority, op.account.type));
+ SyncAdapterType.newKey(op.authority, op.account.type), op.userId);
// only proceed if network is connected for requesting UID
final boolean uidNetworkConnected;
@@ -2030,7 +2030,7 @@ public class SyncManager implements OnAccountsUpdateListener {
for (Integer user : removedUsers) {
// if it's still removed
if (mUserManager.getUserInfo(user) == null) {
- removeUser(user);
+ onUserRemoved(user);
}
}
}
@@ -2167,8 +2167,8 @@ public class SyncManager implements OnAccountsUpdateListener {
// connect to the sync adapter
SyncAdapterType syncAdapterType = SyncAdapterType.newKey(op.authority, op.account.type);
- RegisteredServicesCache.ServiceInfo<SyncAdapterType> syncAdapterInfo =
- mSyncAdapters.getServiceInfo(syncAdapterType);
+ final RegisteredServicesCache.ServiceInfo<SyncAdapterType> syncAdapterInfo;
+ syncAdapterInfo = mSyncAdapters.getServiceInfo(syncAdapterType, op.userId);
if (syncAdapterInfo == null) {
Log.d(TAG, "can't find a sync adapter for " + syncAdapterType
+ ", removing settings for it");
diff --git a/core/java/android/content/SyncQueue.java b/core/java/android/content/SyncQueue.java
index c18c86b..395658c 100644
--- a/core/java/android/content/SyncQueue.java
+++ b/core/java/android/content/SyncQueue.java
@@ -16,14 +16,15 @@
package android.content;
-import com.google.android.collect.Maps;
-
-import android.content.pm.RegisteredServicesCache;
+import android.accounts.Account;
+import android.content.pm.RegisteredServicesCache.ServiceInfo;
import android.os.SystemClock;
+import android.os.UserHandle;
import android.text.format.DateUtils;
-import android.util.Pair;
import android.util.Log;
-import android.accounts.Account;
+import android.util.Pair;
+
+import com.google.android.collect.Maps;
import java.util.ArrayList;
import java.util.HashMap;
@@ -36,7 +37,9 @@ import java.util.Map;
*/
public class SyncQueue {
private static final String TAG = "SyncManager";
- private SyncStorageEngine mSyncStorageEngine;
+
+ private final SyncStorageEngine mSyncStorageEngine;
+ private final SyncAdaptersCache mSyncAdapters;
// A Map of SyncOperations operationKey -> SyncOperation that is designed for
// quick lookup of an enqueued SyncOperation.
@@ -44,23 +47,28 @@ public class SyncQueue {
public SyncQueue(SyncStorageEngine syncStorageEngine, final SyncAdaptersCache syncAdapters) {
mSyncStorageEngine = syncStorageEngine;
- ArrayList<SyncStorageEngine.PendingOperation> ops
- = mSyncStorageEngine.getPendingOperations();
- final int N = ops.size();
- for (int i=0; i<N; i++) {
- SyncStorageEngine.PendingOperation op = ops.get(i);
- final Pair<Long, Long> backoff =
- syncStorageEngine.getBackoff(op.account, op.userId, op.authority);
- final RegisteredServicesCache.ServiceInfo<SyncAdapterType> syncAdapterInfo =
- syncAdapters.getServiceInfo(
- SyncAdapterType.newKey(op.authority, op.account.type));
+ mSyncAdapters = syncAdapters;
+
+ addPendingOperations(UserHandle.USER_OWNER);
+ }
+
+ public void addPendingOperations(int userId) {
+ for (SyncStorageEngine.PendingOperation op : mSyncStorageEngine.getPendingOperations()) {
+ if (op.userId != userId) continue;
+
+ final Pair<Long, Long> backoff = mSyncStorageEngine.getBackoff(
+ op.account, op.userId, op.authority);
+ final ServiceInfo<SyncAdapterType> syncAdapterInfo = mSyncAdapters.getServiceInfo(
+ SyncAdapterType.newKey(op.authority, op.account.type), op.userId);
if (syncAdapterInfo == null) {
+ Log.w(TAG, "Missing sync adapter info for authority " + op.authority + ", userId "
+ + op.userId);
continue;
}
SyncOperation syncOperation = new SyncOperation(
op.account, op.userId, op.syncSource, op.authority, op.extras, 0 /* delay */,
backoff != null ? backoff.first : 0,
- syncStorageEngine.getDelayUntilTime(op.account, op.userId, op.authority),
+ mSyncStorageEngine.getDelayUntilTime(op.account, op.userId, op.authority),
syncAdapterInfo.type.allowParallelSyncs());
syncOperation.expedited = op.expedited;
syncOperation.pendingOperation = op;
diff --git a/core/java/android/content/pm/RegisteredServicesCache.java b/core/java/android/content/pm/RegisteredServicesCache.java
index 7642670..0b91786 100644
--- a/core/java/android/content/pm/RegisteredServicesCache.java
+++ b/core/java/android/content/pm/RegisteredServicesCache.java
@@ -16,49 +16,54 @@
package android.content.pm;
-import android.content.Context;
import android.content.BroadcastReceiver;
+import android.content.ComponentName;
+import android.content.Context;
import android.content.Intent;
import android.content.IntentFilter;
-import android.content.ComponentName;
import android.content.pm.PackageManager.NameNotFoundException;
import android.content.res.Resources;
import android.content.res.XmlResourceParser;
import android.os.Environment;
import android.os.Handler;
+import android.os.UserHandle;
import android.util.AtomicFile;
-import android.util.Log;
import android.util.AttributeSet;
+import android.util.Log;
+import android.util.Slog;
+import android.util.SparseArray;
import android.util.Xml;
-import java.util.Map;
-import java.util.Collection;
-import java.util.Collections;
-import java.util.HashMap;
-import java.util.List;
-import java.util.ArrayList;
-import java.util.concurrent.atomic.AtomicReference;
-import java.io.File;
-import java.io.FileOutputStream;
-import java.io.FileDescriptor;
-import java.io.PrintWriter;
-import java.io.IOException;
-import java.io.FileInputStream;
-
import com.android.internal.util.FastXmlSerializer;
-
-import com.google.android.collect.Maps;
import com.google.android.collect.Lists;
+import com.google.android.collect.Maps;
-import org.xmlpull.v1.XmlPullParserException;
import org.xmlpull.v1.XmlPullParser;
+import org.xmlpull.v1.XmlPullParserException;
import org.xmlpull.v1.XmlSerializer;
+import java.io.File;
+import java.io.FileDescriptor;
+import java.io.FileInputStream;
+import java.io.FileOutputStream;
+import java.io.IOException;
+import java.io.PrintWriter;
+import java.util.ArrayList;
+import java.util.Collection;
+import java.util.Collections;
+import java.util.List;
+import java.util.Map;
+
/**
- * A cache of registered services. This cache
- * is built by interrogating the {@link PackageManager} and is updated as packages are added,
- * removed and changed. The services are referred to by type V and
- * are made available via the {@link #getServiceInfo} method.
+ * Cache of registered services. This cache is lazily built by interrogating
+ * {@link PackageManager} on a per-user basis. It's updated as packages are
+ * added, removed and changed. Users are responsible for calling
+ * {@link #invalidateCache(int)} when a user is started, since
+ * {@link PackageManager} broadcasts aren't sent for stopped users.
+ * <p>
+ * The services are referred to by type V and are made available via the
+ * {@link #getServiceInfo} method.
+ *
* @hide
*/
public abstract class RegisteredServicesCache<V> {
@@ -69,15 +74,29 @@ public abstract class RegisteredServicesCache<V> {
private final String mMetaDataName;
private final String mAttributesName;
private final XmlSerializerAndParser<V> mSerializerAndParser;
- private final AtomicReference<BroadcastReceiver> mReceiver;
private final Object mServicesLock = new Object();
- // synchronized on mServicesLock
- private HashMap<V, Integer> mPersistentServices;
- // synchronized on mServicesLock
- private Map<V, ServiceInfo<V>> mServices;
- // synchronized on mServicesLock
+
+ // @GuardedBy("mServicesLock")
private boolean mPersistentServicesFileDidNotExist;
+ // @GuardedBy("mServicesLock")
+ private final SparseArray<UserServices<V>> mUserServices = new SparseArray<UserServices<V>>();
+
+ private static class UserServices<V> {
+ // @GuardedBy("mServicesLock")
+ public final Map<V, Integer> persistentServices = Maps.newHashMap();
+ // @GuardedBy("mServicesLock")
+ public Map<V, ServiceInfo<V>> services = null;
+ }
+
+ private UserServices<V> findOrCreateUserLocked(int userId) {
+ UserServices<V> services = mUserServices.get(userId);
+ if (services == null) {
+ services = new UserServices<V>();
+ mUserServices.put(userId, services);
+ }
+ return services;
+ }
/**
* This file contains the list of known services. We would like to maintain this forever
@@ -102,36 +121,59 @@ public abstract class RegisteredServicesCache<V> {
File syncDir = new File(systemDir, "registered_services");
mPersistentServicesFile = new AtomicFile(new File(syncDir, interfaceName + ".xml"));
- generateServicesMap();
+ // Load persisted services from disk
+ readPersistentServicesLocked();
- final BroadcastReceiver receiver = new BroadcastReceiver() {
- @Override
- public void onReceive(Context context1, Intent intent) {
- generateServicesMap();
- }
- };
- mReceiver = new AtomicReference<BroadcastReceiver>(receiver);
IntentFilter intentFilter = new IntentFilter();
intentFilter.addAction(Intent.ACTION_PACKAGE_ADDED);
intentFilter.addAction(Intent.ACTION_PACKAGE_CHANGED);
intentFilter.addAction(Intent.ACTION_PACKAGE_REMOVED);
intentFilter.addDataScheme("package");
- mContext.registerReceiver(receiver, intentFilter);
+ mContext.registerReceiverAsUser(mPackageReceiver, UserHandle.ALL, intentFilter, null, null);
+
// Register for events related to sdcard installation.
IntentFilter sdFilter = new IntentFilter();
sdFilter.addAction(Intent.ACTION_EXTERNAL_APPLICATIONS_AVAILABLE);
sdFilter.addAction(Intent.ACTION_EXTERNAL_APPLICATIONS_UNAVAILABLE);
- mContext.registerReceiver(receiver, sdFilter);
+ mContext.registerReceiver(mExternalReceiver, sdFilter);
}
- public void dump(FileDescriptor fd, PrintWriter fout, String[] args) {
- Map<V, ServiceInfo<V>> services;
+ private final BroadcastReceiver mPackageReceiver = new BroadcastReceiver() {
+ @Override
+ public void onReceive(Context context, Intent intent) {
+ final int uid = intent.getIntExtra(Intent.EXTRA_UID, -1);
+ if (uid != -1) {
+ generateServicesMap(UserHandle.getUserId(uid));
+ }
+ }
+ };
+
+ private final BroadcastReceiver mExternalReceiver = new BroadcastReceiver() {
+ @Override
+ public void onReceive(Context context, Intent intent) {
+ // External apps can't coexist with multi-user, so scan owner
+ generateServicesMap(UserHandle.USER_OWNER);
+ }
+ };
+
+ public void invalidateCache(int userId) {
synchronized (mServicesLock) {
- services = mServices;
+ final UserServices<V> user = findOrCreateUserLocked(userId);
+ user.services = null;
}
- fout.println("RegisteredServicesCache: " + services.size() + " services");
- for (ServiceInfo info : services.values()) {
- fout.println(" " + info);
+ }
+
+ public void dump(FileDescriptor fd, PrintWriter fout, String[] args, int userId) {
+ synchronized (mServicesLock) {
+ final UserServices<V> user = findOrCreateUserLocked(userId);
+ if (user.services != null) {
+ fout.println("RegisteredServicesCache: " + user.services.size() + " services");
+ for (ServiceInfo<?> info : user.services.values()) {
+ fout.println(" " + info);
+ }
+ } else {
+ fout.println("RegisteredServicesCache: services not loaded");
+ }
}
}
@@ -151,7 +193,7 @@ public abstract class RegisteredServicesCache<V> {
}
}
- private void notifyListener(final V type, final boolean removed) {
+ private void notifyListener(final V type, final int userId, final boolean removed) {
if (Log.isLoggable(TAG, Log.VERBOSE)) {
Log.d(TAG, "notifyListener: " + type + " is " + (removed ? "removed" : "added"));
}
@@ -168,7 +210,7 @@ public abstract class RegisteredServicesCache<V> {
final RegisteredServicesCacheListener<V> listener2 = listener;
handler.post(new Runnable() {
public void run() {
- listener2.onServiceChanged(type, removed);
+ listener2.onServiceChanged(type, userId, removed);
}
});
}
@@ -200,9 +242,14 @@ public abstract class RegisteredServicesCache<V> {
* @param type the account type of the authenticator
* @return the AuthenticatorInfo that matches the account type or null if none is present
*/
- public ServiceInfo<V> getServiceInfo(V type) {
+ public ServiceInfo<V> getServiceInfo(V type, int userId) {
synchronized (mServicesLock) {
- return mServices.get(type);
+ // Find user and lazily populate cache
+ final UserServices<V> user = findOrCreateUserLocked(userId);
+ if (user.services == null) {
+ generateServicesMap(userId);
+ }
+ return user.services.get(type);
}
}
@@ -210,29 +257,15 @@ public abstract class RegisteredServicesCache<V> {
* @return a collection of {@link RegisteredServicesCache.ServiceInfo} objects for all
* registered authenticators.
*/
- public Collection<ServiceInfo<V>> getAllServices() {
+ public Collection<ServiceInfo<V>> getAllServices(int userId) {
synchronized (mServicesLock) {
- return Collections.unmodifiableCollection(mServices.values());
- }
- }
-
- /**
- * Stops the monitoring of package additions, removals and changes.
- */
- public void close() {
- final BroadcastReceiver receiver = mReceiver.getAndSet(null);
- if (receiver != null) {
- mContext.unregisterReceiver(receiver);
- }
- }
-
- @Override
- protected void finalize() throws Throwable {
- if (mReceiver.get() != null) {
- Log.e(TAG, "RegisteredServicesCache finalized without being closed");
+ // Find user and lazily populate cache
+ final UserServices<V> user = findOrCreateUserLocked(userId);
+ if (user.services == null) {
+ generateServicesMap(userId);
+ }
+ return Collections.unmodifiableCollection(user.services.values());
}
- close();
- super.finalize();
}
private boolean inSystemImage(int callerUid) {
@@ -251,11 +284,17 @@ public abstract class RegisteredServicesCache<V> {
return false;
}
- public void generateServicesMap() {
- PackageManager pm = mContext.getPackageManager();
- ArrayList<ServiceInfo<V>> serviceInfos = new ArrayList<ServiceInfo<V>>();
- List<ResolveInfo> resolveInfos = pm.queryIntentServices(new Intent(mInterfaceName),
- PackageManager.GET_META_DATA);
+ /**
+ * Populate {@link UserServices#services} by scanning installed packages for
+ * given {@link UserHandle}.
+ */
+ private void generateServicesMap(int userId) {
+ Slog.d(TAG, "generateServicesMap() for " + userId);
+
+ final PackageManager pm = mContext.getPackageManager();
+ final ArrayList<ServiceInfo<V>> serviceInfos = new ArrayList<ServiceInfo<V>>();
+ final List<ResolveInfo> resolveInfos = pm.queryIntentServicesAsUser(
+ new Intent(mInterfaceName), PackageManager.GET_META_DATA, userId);
for (ResolveInfo resolveInfo : resolveInfos) {
try {
ServiceInfo<V> info = parseServiceInfo(resolveInfo);
@@ -272,10 +311,14 @@ public abstract class RegisteredServicesCache<V> {
}
synchronized (mServicesLock) {
- if (mPersistentServices == null) {
- readPersistentServicesLocked();
+ final UserServices<V> user = findOrCreateUserLocked(userId);
+ final boolean firstScan = user.services == null;
+ if (firstScan) {
+ user.services = Maps.newHashMap();
+ } else {
+ user.services.clear();
}
- mServices = Maps.newHashMap();
+
StringBuilder changes = new StringBuilder();
for (ServiceInfo<V> info : serviceInfos) {
// four cases:
@@ -287,19 +330,19 @@ public abstract class RegisteredServicesCache<V> {
// - ignore
// - exists, the UID is different, and the new one is a system package
// - add, notify user that it was added
- Integer previousUid = mPersistentServices.get(info.type);
+ Integer previousUid = user.persistentServices.get(info.type);
if (previousUid == null) {
changes.append(" New service added: ").append(info).append("\n");
- mServices.put(info.type, info);
- mPersistentServices.put(info.type, info.uid);
- if (!mPersistentServicesFileDidNotExist) {
- notifyListener(info.type, false /* removed */);
+ user.services.put(info.type, info);
+ user.persistentServices.put(info.type, info.uid);
+ if (!(mPersistentServicesFileDidNotExist && firstScan)) {
+ notifyListener(info.type, userId, false /* removed */);
}
} else if (previousUid == info.uid) {
if (Log.isLoggable(TAG, Log.VERBOSE)) {
changes.append(" Existing service (nop): ").append(info).append("\n");
}
- mServices.put(info.type, info);
+ user.services.put(info.type, info);
} else if (inSystemImage(info.uid)
|| !containsTypeAndUid(serviceInfos, info.type, previousUid)) {
if (inSystemImage(info.uid)) {
@@ -309,9 +352,9 @@ public abstract class RegisteredServicesCache<V> {
changes.append(" Existing service replacing a removed service: ")
.append(info).append("\n");
}
- mServices.put(info.type, info);
- mPersistentServices.put(info.type, info.uid);
- notifyListener(info.type, false /* removed */);
+ user.services.put(info.type, info);
+ user.persistentServices.put(info.type, info.uid);
+ notifyListener(info.type, userId, false /* removed */);
} else {
// ignore
changes.append(" Existing service with new uid ignored: ").append(info)
@@ -320,15 +363,15 @@ public abstract class RegisteredServicesCache<V> {
}
ArrayList<V> toBeRemoved = Lists.newArrayList();
- for (V v1 : mPersistentServices.keySet()) {
+ for (V v1 : user.persistentServices.keySet()) {
if (!containsType(serviceInfos, v1)) {
toBeRemoved.add(v1);
}
}
for (V v1 : toBeRemoved) {
- mPersistentServices.remove(v1);
+ user.persistentServices.remove(v1);
changes.append(" Service removed: ").append(v1).append("\n");
- notifyListener(v1, true /* removed */);
+ notifyListener(v1, userId, true /* removed */);
}
if (changes.length() > 0) {
if (Log.isLoggable(TAG, Log.VERBOSE)) {
@@ -342,7 +385,6 @@ public abstract class RegisteredServicesCache<V> {
serviceInfos.size() + " services unchanged");
}
}
- mPersistentServicesFileDidNotExist = false;
}
}
@@ -415,7 +457,7 @@ public abstract class RegisteredServicesCache<V> {
* Read all sync status back in to the initial engine state.
*/
private void readPersistentServicesLocked() {
- mPersistentServices = Maps.newHashMap();
+ mUserServices.clear();
if (mSerializerAndParser == null) {
return;
}
@@ -444,8 +486,10 @@ public abstract class RegisteredServicesCache<V> {
break;
}
String uidString = parser.getAttributeValue(null, "uid");
- int uid = Integer.parseInt(uidString);
- mPersistentServices.put(service, uid);
+ final int uid = Integer.parseInt(uidString);
+ final int userId = UserHandle.getUserId(uid);
+ final UserServices<V> user = findOrCreateUserLocked(userId);
+ user.persistentServices.put(service, uid);
}
}
eventType = parser.next();
@@ -478,11 +522,14 @@ public abstract class RegisteredServicesCache<V> {
out.startDocument(null, true);
out.setFeature("http://xmlpull.org/v1/doc/features.html#indent-output", true);
out.startTag(null, "services");
- for (Map.Entry<V, Integer> service : mPersistentServices.entrySet()) {
- out.startTag(null, "service");
- out.attribute(null, "uid", Integer.toString(service.getValue()));
- mSerializerAndParser.writeAsXml(service.getKey(), out);
- out.endTag(null, "service");
+ for (int i = 0; i < mUserServices.size(); i++) {
+ final UserServices<V> user = mUserServices.valueAt(i);
+ for (Map.Entry<V, Integer> service : user.persistentServices.entrySet()) {
+ out.startTag(null, "service");
+ out.attribute(null, "uid", Integer.toString(service.getValue()));
+ mSerializerAndParser.writeAsXml(service.getKey(), out);
+ out.endTag(null, "service");
+ }
}
out.endTag(null, "services");
out.endDocument();
diff --git a/core/java/android/content/pm/RegisteredServicesCacheListener.java b/core/java/android/content/pm/RegisteredServicesCacheListener.java
index 7095229..df79544 100644
--- a/core/java/android/content/pm/RegisteredServicesCacheListener.java
+++ b/core/java/android/content/pm/RegisteredServicesCacheListener.java
@@ -16,8 +16,6 @@
package android.content.pm;
-import android.os.Parcelable;
-
/**
* Listener for changes to the set of registered services managed by a RegisteredServicesCache.
* @hide
@@ -28,5 +26,5 @@ public interface RegisteredServicesCacheListener<V> {
* @param type the type of registered service
* @param removed true if the service was removed
*/
- void onServiceChanged(V type, boolean removed);
+ void onServiceChanged(V type, int userId, boolean removed);
}
diff --git a/core/java/android/os/UserManager.java b/core/java/android/os/UserManager.java
index 83a0c78..2739cac 100644
--- a/core/java/android/os/UserManager.java
+++ b/core/java/android/os/UserManager.java
@@ -16,6 +16,8 @@
package android.os;
import com.android.internal.R;
+
+import android.app.ActivityManagerNative;
import android.content.Context;
import android.content.pm.UserInfo;
import android.graphics.Bitmap;
@@ -82,6 +84,39 @@ public class UserManager {
}
/**
+ * Return whether the given user is actively running. This means that
+ * the user is in the "started" state, not "stopped" -- it is currently
+ * allowed to run code through scheduled alarms, receiving broadcasts,
+ * etc. A started user may be either the current foreground user or a
+ * background user; the result here does not distinguish between the two.
+ * @param user The user to retrieve the running state for.
+ */
+ public boolean isUserRunning(UserHandle user) {
+ try {
+ return ActivityManagerNative.getDefault().isUserRunning(
+ user.getIdentifier(), false);
+ } catch (RemoteException e) {
+ return false;
+ }
+ }
+
+ /**
+ * Return whether the given user is actively running <em>or</em> stopping.
+ * This is like {@link #isUserRunning(UserHandle)}, but will also return
+ * true if the user had been running but is in the process of being stopped
+ * (but is not yet fully stopped, and still running some code).
+ * @param user The user to retrieve the running state for.
+ */
+ public boolean isUserRunningOrStopping(UserHandle user) {
+ try {
+ return ActivityManagerNative.getDefault().isUserRunning(
+ user.getIdentifier(), true);
+ } catch (RemoteException e) {
+ return false;
+ }
+ }
+
+ /**
* Returns the UserInfo object describing a specific user.
* Requires {@link android.Manifest.permission#MANAGE_USERS} permission.
* @param userHandle the user handle of the user whose information is being requested.
diff --git a/core/java/android/provider/Settings.java b/core/java/android/provider/Settings.java
index 91c6db5..00ea873 100644
--- a/core/java/android/provider/Settings.java
+++ b/core/java/android/provider/Settings.java
@@ -3197,10 +3197,16 @@ public final class Settings {
public static final String LOCK_PATTERN_VISIBLE = "lock_pattern_visible_pattern";
/**
- * Whether lock pattern will vibrate as user enters (0 = false, 1 = true)
+ * Whether lock pattern will vibrate as user enters (0 = false, 1 =
+ * true)
+ *
+ * @deprecated Starting in {@link VERSION_CODES#JELLY_BEAN_MR1} the
+ * lockscreen uses
+ * {@link Settings.System#HAPTIC_FEEDBACK_ENABLED}.
*/
- public static final String LOCK_PATTERN_TACTILE_FEEDBACK_ENABLED =
- "lock_pattern_tactile_feedback_enabled";
+ @Deprecated
+ public static final String
+ LOCK_PATTERN_TACTILE_FEEDBACK_ENABLED = "lock_pattern_tactile_feedback_enabled";
/**
* This preference allows the device to be locked given time after screen goes off,
@@ -5321,6 +5327,7 @@ public final class Settings {
ENABLE_ACCESSIBILITY_GLOBAL_GESTURE_ENABLED,
WIFI_NETWORKS_AVAILABLE_NOTIFICATION_ON,
WIFI_NETWORKS_AVAILABLE_REPEAT_DELAY,
+ WIFI_WATCHDOG_POOR_NETWORK_TEST_ENABLED,
WIFI_NUM_OPEN_NETWORKS_KEPT,
EMERGENCY_TONE,
CALL_AUTO_RETRY,
diff --git a/core/java/android/service/dreams/DreamService.java b/core/java/android/service/dreams/DreamService.java
index cb78763..f865455 100644
--- a/core/java/android/service/dreams/DreamService.java
+++ b/core/java/android/service/dreams/DreamService.java
@@ -120,7 +120,7 @@ public class DreamService extends Service implements Window.Callback {
private boolean mInteractive = false;
private boolean mLowProfile = true;
private boolean mFullscreen = false;
- private boolean mScreenBright = false;
+ private boolean mScreenBright = true;
private boolean mFinished;
private boolean mDebug = false;
diff --git a/core/java/android/view/View.java b/core/java/android/view/View.java
index ae51c1d..0d76eac 100644
--- a/core/java/android/view/View.java
+++ b/core/java/android/view/View.java
@@ -6858,12 +6858,12 @@ public class View implements Drawable.Callback, KeyEvent.Callback,
/**
* Performs the specified accessibility action on the view. For
* possible accessibility actions look at {@link AccessibilityNodeInfo}.
- * <p>
- * If an {@link AccessibilityDelegate} has been specified via calling
- * {@link #setAccessibilityDelegate(AccessibilityDelegate)} its
- * {@link AccessibilityDelegate#performAccessibilityAction(View, int, Bundle)}
- * is responsible for handling this call.
- * </p>
+ * <p>
+ * If an {@link AccessibilityDelegate} has been specified via calling
+ * {@link #setAccessibilityDelegate(AccessibilityDelegate)} its
+ * {@link AccessibilityDelegate#performAccessibilityAction(View, int, Bundle)}
+ * is responsible for handling this call.
+ * </p>
*
* @param action The action to perform.
* @param arguments Optional action arguments.
@@ -6886,12 +6886,14 @@ public class View implements Drawable.Callback, KeyEvent.Callback,
switch (action) {
case AccessibilityNodeInfo.ACTION_CLICK: {
if (isClickable()) {
- return performClick();
+ performClick();
+ return true;
}
} break;
case AccessibilityNodeInfo.ACTION_LONG_CLICK: {
if (isLongClickable()) {
- return performLongClick();
+ performLongClick();
+ return true;
}
} break;
case AccessibilityNodeInfo.ACTION_FOCUS: {
diff --git a/core/java/android/widget/LinearLayout.java b/core/java/android/widget/LinearLayout.java
index b6f0862..2f31ebd 100644
--- a/core/java/android/widget/LinearLayout.java
+++ b/core/java/android/widget/LinearLayout.java
@@ -648,6 +648,8 @@ public class LinearLayout extends ViewGroup {
int largestChildHeight = Integer.MIN_VALUE;
+ final int layoutDirection = getLayoutDirection();
+
// See how tall everyone is. Also remember max width.
for (int i = 0; i < count; ++i) {
final View child = getVirtualChildAt(i);
@@ -667,6 +669,7 @@ public class LinearLayout extends ViewGroup {
}
LinearLayout.LayoutParams lp = (LinearLayout.LayoutParams) child.getLayoutParams();
+ lp.onResolveLayoutDirection(layoutDirection);
totalWeight += lp.weight;
@@ -989,6 +992,8 @@ public class LinearLayout extends ViewGroup {
int largestChildWidth = Integer.MIN_VALUE;
+ final int layoutDirection = getLayoutDirection();
+
// See how wide everyone is. Also remember max height.
for (int i = 0; i < count; ++i) {
final View child = getVirtualChildAt(i);
@@ -1009,6 +1014,7 @@ public class LinearLayout extends ViewGroup {
final LinearLayout.LayoutParams lp = (LinearLayout.LayoutParams)
child.getLayoutParams();
+ lp.onResolveLayoutDirection(layoutDirection);
totalWeight += lp.weight;
diff --git a/core/java/com/android/internal/view/menu/ActionMenuPresenter.java b/core/java/com/android/internal/view/menu/ActionMenuPresenter.java
index 2811332..4bb6d06 100644
--- a/core/java/com/android/internal/view/menu/ActionMenuPresenter.java
+++ b/core/java/com/android/internal/view/menu/ActionMenuPresenter.java
@@ -476,6 +476,9 @@ public class ActionMenuPresenter extends BaseMenuPresenter
if (isAction) maxActions--;
item.setIsActionButton(isAction);
+ } else {
+ // Neither requires nor requests an action button.
+ item.setIsActionButton(false);
}
}
return true;
diff --git a/core/java/com/android/internal/widget/LockPatternUtils.java b/core/java/com/android/internal/widget/LockPatternUtils.java
index d14b1ee..3f40f20 100644
--- a/core/java/com/android/internal/widget/LockPatternUtils.java
+++ b/core/java/com/android/internal/widget/LockPatternUtils.java
@@ -949,14 +949,8 @@ public class LockPatternUtils {
* @return Whether tactile feedback for the pattern is enabled.
*/
public boolean isTactileFeedbackEnabled() {
- return getBoolean(Settings.Secure.LOCK_PATTERN_TACTILE_FEEDBACK_ENABLED, false);
- }
-
- /**
- * Set whether tactile feedback for the pattern is enabled.
- */
- public void setTactileFeedbackEnabled(boolean enabled) {
- setBoolean(Settings.Secure.LOCK_PATTERN_TACTILE_FEEDBACK_ENABLED, enabled);
+ return Settings.System.getIntForUser(mContentResolver,
+ Settings.System.HAPTIC_FEEDBACK_ENABLED, 1, UserHandle.USER_CURRENT) != 0;
}
/**
diff --git a/core/res/res/values/strings.xml b/core/res/res/values/strings.xml
index 1c71e64..72de22c 100755
--- a/core/res/res/values/strings.xml
+++ b/core/res/res/values/strings.xml
@@ -3936,7 +3936,7 @@
</string>
<!-- Text spoken when the user is performing a gesture that will enable accessibility. [CHAR LIMIT=none] -->
- <string name="continue_to_enable_accessibility">Keep holding down your two fingers to enable accessibility.</string>
+ <string name="continue_to_enable_accessibility">Keep holding down two fingers to enable accessibility.</string>
<!-- Text spoken when the user enabled accessibility. [CHAR LIMIT=none] -->
<string name="accessibility_enabled">Accessibility enabled.</string>
<!-- Text spoken when the user stops preforming a gesture that would enable accessibility. [CHAR LIMIT=none] -->
diff --git a/core/tests/coretests/src/android/accounts/AccountManagerServiceTest.java b/core/tests/coretests/src/android/accounts/AccountManagerServiceTest.java
index 1d7576f..84c9957 100644
--- a/core/tests/coretests/src/android/accounts/AccountManagerServiceTest.java
+++ b/core/tests/coretests/src/android/accounts/AccountManagerServiceTest.java
@@ -197,7 +197,9 @@ public class AccountManagerServiceTest extends AndroidTestCase {
mServices.add(new ServiceInfo<AuthenticatorDescription>(d2, null, 0));
}
- public ServiceInfo<AuthenticatorDescription> getServiceInfo(AuthenticatorDescription type) {
+ @Override
+ public ServiceInfo<AuthenticatorDescription> getServiceInfo(
+ AuthenticatorDescription type, int userId) {
for (ServiceInfo<AuthenticatorDescription> service : mServices) {
if (service.type.equals(type)) {
return service;
@@ -206,21 +208,25 @@ public class AccountManagerServiceTest extends AndroidTestCase {
return null;
}
- public Collection<ServiceInfo<AuthenticatorDescription>> getAllServices() {
+ @Override
+ public Collection<ServiceInfo<AuthenticatorDescription>> getAllServices(int userId) {
return mServices;
}
- public void dump(final FileDescriptor fd, final PrintWriter fout, final String[] args) {
+ @Override
+ public void dump(
+ final FileDescriptor fd, final PrintWriter fout, final String[] args, int userId) {
}
+ @Override
public void setListener(
final RegisteredServicesCacheListener<AuthenticatorDescription> listener,
final Handler handler) {
}
- @Override
- public void generateServicesMap() {
- }
+ @Override
+ public void invalidateCache(int userId) {
+ }
}
static public class MyMockContext extends MockContext {
diff --git a/docs/html/distribute/googleplay/quality/core.jd b/docs/html/distribute/googleplay/quality/core.jd
index 291550f..c1ef68c 100644
--- a/docs/html/distribute/googleplay/quality/core.jd
+++ b/docs/html/distribute/googleplay/quality/core.jd
@@ -589,8 +589,9 @@ rather, you should focus on a small number of representative devices, even using
one or two devices per form factor. </p>
<p>If you are not able to obtain actual hardware devices for testing, you should
-set up emulated devices (AVDs) to represent the most common form factors and
-hardware/software combinations. </p>
+<a href="{@docRoot}tools/devices/index.html">set up emulated devices (AVDs)</a>
+to represent the most common form factors and
+hardware/software combinations.</p>
<p>To go beyond basic testing, you can add more devices, more form factors, or
new hardware/software combinations to your test environment. You can also
diff --git a/docs/html/distribute/googleplay/quality/tablet.jd b/docs/html/distribute/googleplay/quality/tablet.jd
index f180f54..80346a7 100644
--- a/docs/html/distribute/googleplay/quality/tablet.jd
+++ b/docs/html/distribute/googleplay/quality/tablet.jd
@@ -528,7 +528,8 @@ one or two devices per form factor. The table below provides an overview of
devices you could use for testing.</p>
<p>If you are not able to obtain actual hardware devices for testing, you should
-set up emulated devices (AVDs) to represent the most common form factors and
+<a href="{@docRoot}tools/devices/index.html">set up emulated devices (AVDs)</a>
+to represent the most common form factors and
hardware/software combinations. See the table below for suggestions on the emulator
configurations to use. </p>
diff --git a/docs/html/distribute/googleplay/spotlight/tablets.jd b/docs/html/distribute/googleplay/spotlight/tablets.jd
index f968a40..ee256bc 100644
--- a/docs/html/distribute/googleplay/spotlight/tablets.jd
+++ b/docs/html/distribute/googleplay/spotlight/tablets.jd
@@ -152,12 +152,13 @@ phone apps serve as great complements to each other."</p>
</div>
<div style="line-height:1.4em;">
- <p style="margin-top:0;margin-bottom:12px;">Over a year ago, developer
-TinyCo, makers of games such as Tiny Monsters, switched to a
-simultaneous launch strategy for their products. They chose Android as one of their
-primary launch platforms because of its large installed base and global reach. They
-also knew that the growing base of Android tablet users represented a huge
-opportunity. </p>
+ <p style="margin-top:0;margin-bottom:12px;">
+
+<p>Over a year ago, app developer TinyCo, makers of a suite of games such as
+Tiny Monsters, decided to prioritize launching across multiple platforms
+effectively. They chose Android as one of their primary launch platforms because
+of its large installed base and global reach. They also knew that the growing
+base of Android tablet users represented a huge opportunity.</p>
<p>Tiny Village was their first title to take advantage of the strategy, and
it proved to be a winning one &mdash; especially in terms of Android
diff --git a/docs/html/guide/google/gcm/adv.jd b/docs/html/guide/google/gcm/adv.jd
index aa66e25..356ee1d 100644
--- a/docs/html/guide/google/gcm/adv.jd
+++ b/docs/html/guide/google/gcm/adv.jd
@@ -163,7 +163,7 @@ registerReceiver(mRetryReceiver, filter);
<p>There are two ways to unregister a device from GCM: manually and automatically.</p>
<p>An Android application can manually unregister itself by issuing a <code>com.google.android.c2dm.intent.UNREGISTER</code> intent, which is useful when the application offers a logoff feature (so it can unregister on logoff and register again on logon). See the <a href="gcm.html#unregistering">Architectural Overview</a> for more discussion of this topic. This is the sequence of events when an application unregisters itself:</p>
<ol>
- <li> The application issues a <code>com.google.android.c2dm.intent.UNREGISTER</code> intent, passing the registration ID (the application should have saved its registration ID when it received the proper <code>com.google.android.c2dm.intent.REGISTRATION</code> intent).</li>
+ <li> The application issues a <code>com.google.android.c2dm.intent.UNREGISTER</code> intent, passing the package name as an extra.</li>
<li>When the GCM server is done with the unregistration, it sends a <code>com.google.android.c2dm.intent.REGISTRATION</code> intent with the <code>unregistered</code> extra set.</li>
<li>The application then must contact the 3rd-party server so it can remove the registration ID.</li>
<li>The application should also clear its registration ID.
@@ -174,7 +174,7 @@ registerReceiver(mRetryReceiver, filter);
<li>The end user uninstalls the application.</li>
<li>The 3rd-party server sends a message to GCM server.</li>
<li>The GCM server sends the message to the device.</li>
- <li>The GCM client receives the message and queries Package Manager about whether there are broadcast receivers configured to receive it, which returns <code>false</code>.
+ <li>The GCM client receives the message and queries Package Manager about whether there are broadcast receivers configured to receive it, which returns <code>false</code>.
</li>
<li>The GCM client informs the GCM server that the application was uninstalled.</li>
<li>The GCM server marks the registration ID for deletion.</li>
@@ -183,6 +183,9 @@ registerReceiver(mRetryReceiver, filter);
<li>The 3rd-party deletes the registration ID.
</li>
</ol>
+
+<p class ="note"><strong>Note:</strong> The GCM client is the Google Cloud Messaging framework present on the device.</p>
+
<p>Note that it might take a while for the registration ID be completely removed from GCM. Thus it is possible that messages sent during step 7 above gets a valid message ID as response, even though the message will not be delivered to the device. Eventually, the registration ID will be removed and the server will get a <code>NotRegistered</code> error, without any further action being required from the 3rd-party server (this scenario happens frequently while an application is being developed and tested).</p>
<h2 id="collapsible">Send-to-Sync vs. Messages with Payload</h2>
diff --git a/docs/html/guide/google/gcm/gcm.jd b/docs/html/guide/google/gcm/gcm.jd
index c4dfecf..a47ceb9 100644
--- a/docs/html/guide/google/gcm/gcm.jd
+++ b/docs/html/guide/google/gcm/gcm.jd
@@ -773,13 +773,8 @@ the HTTP response contains a non-200 status code (such as 400, 401, or 503).</p>
<td>There was an error authenticating the sender account. <a href="#auth_error">Troubleshoot</a></td>
</tr>
<tr>
- <td>500</td>
- <td>There was an internal error in the GCM server while trying to process the request. <a href="#internal_error">Troubleshoot</a></td>
- </tr>
- <tr>
- <td>503</td>
- <td>Indicates that the server is temporarily unavailable (i.e., because of timeouts, etc ). Sender must retry later, honoring any <code>Retry-After</code> header
- included in the response. Application servers must implement exponential back-off. The GCM server took too long to process the request. <a href="#internal_error">Troubleshoot</a></td>
+ <td>5xx</td>
+ <td>Errors in the 500-599 range (such as 500 or 503) indicate that there was an internal error in the GCM server while trying to process the request, or that the server is temporarily unavailable (for example, because of timeouts). Sender must retry later, honoring any <code>Retry-After</code> header included in the response. Application servers must implement exponential back-off. <a href="#internal_error">Troubleshoot</a></td>
</tr>
</table>
@@ -935,17 +930,15 @@ all messages at the same time.</li>
Senders that cause problems risk being blacklisted.
<br />
-Happens when the HTTP status code is 503, or when the <code>error</code> field of a JSON object in the results array is <code>Unavailable</code>.
+Happens when the HTTP status code is between 501 and 599, or when the <code>error</code> field of a JSON object in the results array is <code>Unavailable</code>.
</dd>
<dt id="internal_error"><strong>Internal Server Error</strong></dt>
<dd>
The server encountered an error while trying to process the request. You
-could retry the same request (obeying the requirements listed in the <strong>Timeout</strong>
+could retry the same request (obeying the requirements listed in the <a href="#timeout">Timeout</a>
section), but if the error persists, please report the problem in the <a href="https://groups.google.com/forum/?fromgroups#!forum/android-gcm">android-gcm group</a>.
-<br />
-Senders that cause problems risk being blacklisted.
<br />
Happens when the HTTP status code is 500, or when the <code>error</code> field of a JSON
object in the results array is <code>InternalServerError</code>.
diff --git a/docs/html/guide/google/gcm/gs.jd b/docs/html/guide/google/gcm/gs.jd
index 93eb794..8d132d8 100644
--- a/docs/html/guide/google/gcm/gs.jd
+++ b/docs/html/guide/google/gcm/gs.jd
@@ -145,7 +145,9 @@ page.title=GCM: Getting Started
<li>If the value is dynamic, the service should override the <code>getSenderIds()</code> method.</li>
</ul>
+
<h4>Step 3: Write the my_app_package.GCMIntentService class</h4>
+
<p>Next write the <code>my_app_package.GCMIntentService</code> class, overriding the following callback methods (which are called by <code>GCMBroadcastReceiver</code>):<br>
</p>
<ul>
diff --git a/graphics/java/android/graphics/Bitmap.java b/graphics/java/android/graphics/Bitmap.java
index 6ba57809..4604437 100644
--- a/graphics/java/android/graphics/Bitmap.java
+++ b/graphics/java/android/graphics/Bitmap.java
@@ -413,6 +413,11 @@ public final class Bitmap implements Parcelable {
}
nativeCopyPixelsFromBuffer(mNativeBitmap, src);
+
+ // now update the buffer's position
+ int position = src.position();
+ position += bitmapBytes >> shift;
+ src.position(position);
}
/**
diff --git a/media/java/android/media/MediaRouter.java b/media/java/android/media/MediaRouter.java
index 16ad74f..8e9384ee 100644
--- a/media/java/android/media/MediaRouter.java
+++ b/media/java/android/media/MediaRouter.java
@@ -152,6 +152,8 @@ public class MediaRouter {
dispatchRouteChanged(sStatic.mDefaultAudioVideo);
}
+ final int mainType = mCurAudioRoutesInfo.mMainType;
+
boolean a2dpEnabled;
try {
a2dpEnabled = mAudioService.isBluetoothA2dpOn();
@@ -180,11 +182,10 @@ public class MediaRouter {
}
if (mBluetoothA2dpRoute != null) {
- if (mCurAudioRoutesInfo.mMainType != AudioRoutesInfo.MAIN_SPEAKER &&
- mSelectedRoute == mBluetoothA2dpRoute) {
+ if (mainType != AudioRoutesInfo.MAIN_SPEAKER &&
+ mSelectedRoute == mBluetoothA2dpRoute && !a2dpEnabled) {
selectRouteStatic(ROUTE_TYPE_LIVE_AUDIO, mDefaultAudioVideo);
- } else if (mCurAudioRoutesInfo.mMainType == AudioRoutesInfo.MAIN_SPEAKER &&
- (mSelectedRoute == mDefaultAudioVideo || mSelectedRoute == null) &&
+ } else if ((mSelectedRoute == mDefaultAudioVideo || mSelectedRoute == null) &&
a2dpEnabled) {
selectRouteStatic(ROUTE_TYPE_LIVE_AUDIO, mBluetoothA2dpRoute);
}
diff --git a/media/tests/MediaFrameworkTest/src/com/android/mediaframeworktest/MediaFrameworkPerfTestRunner.java b/media/tests/MediaFrameworkTest/src/com/android/mediaframeworktest/MediaFrameworkPerfTestRunner.java
index a6cf355..3d5905d 100755
--- a/media/tests/MediaFrameworkTest/src/com/android/mediaframeworktest/MediaFrameworkPerfTestRunner.java
+++ b/media/tests/MediaFrameworkTest/src/com/android/mediaframeworktest/MediaFrameworkPerfTestRunner.java
@@ -38,7 +38,7 @@ import android.util.Log;
public class MediaFrameworkPerfTestRunner extends InstrumentationTestRunner {
public static boolean mGetNativeHeapDump = false;
-
+ public static boolean mGetProcmem = false;
@Override
public TestSuite getAllTests() {
@@ -61,6 +61,12 @@ public class MediaFrameworkPerfTestRunner extends InstrumentationTestRunner {
if (get_heap_dump != null) {
mGetNativeHeapDump = true;
}
+
+ String get_procmem = (String) icicle.get("get_procmem");
+ if (get_procmem != null) {
+ mGetProcmem = true;
+ }
+
}
}
diff --git a/media/tests/MediaFrameworkTest/src/com/android/mediaframeworktest/performance/MediaPlayerPerformance.java b/media/tests/MediaFrameworkTest/src/com/android/mediaframeworktest/performance/MediaPlayerPerformance.java
index ccb0638..9b1098e 100644
--- a/media/tests/MediaFrameworkTest/src/com/android/mediaframeworktest/performance/MediaPlayerPerformance.java
+++ b/media/tests/MediaFrameworkTest/src/com/android/mediaframeworktest/performance/MediaPlayerPerformance.java
@@ -1,12 +1,12 @@
/*
* Copyright (C) 2008 The Android Open Source Project
- *
+ *
* Licensed under the Apache License, Version 2.0 (the "License"); you may not
* use this file except in compliance with the License. You may obtain a copy of
* the License at
- *
+ *
* http://www.apache.org/licenses/LICENSE-2.0
- *
+ *
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS, WITHOUT
* WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the
@@ -53,7 +53,7 @@ import android.media.MediaMetadataRetriever;
import com.android.mediaframeworktest.MediaProfileReader;
/**
- * Junit / Instrumentation - performance measurement for media player and
+ * Junit / Instrumentation - performance measurement for media player and
* recorder
*
* FIXME:
@@ -100,6 +100,7 @@ public class MediaPlayerPerformance extends ActivityInstrumentationTestCase2<Med
super("com.android.mediaframeworktest", MediaFrameworkTest.class);
}
+ @Override
protected void setUp() throws Exception {
super.setUp();
//Insert a 2 second before launching the test activity. This is
@@ -109,19 +110,26 @@ public class MediaPlayerPerformance extends ActivityInstrumentationTestCase2<Med
if (MediaFrameworkPerfTestRunner.mGetNativeHeapDump)
MediaTestUtil.getNativeHeapDump(this.getName() + "_before");
- mProcMemWriter = new BufferedWriter(new FileWriter
- (new File(MEDIA_PROCMEM_OUTPUT), true));
- mProcMemWriter.write(this.getName() + "\n");
- mMemWriter = new BufferedWriter(new FileWriter
- (new File(MEDIA_MEMORY_OUTPUT), true));
+ if (MediaFrameworkPerfTestRunner.mGetProcmem) {
+ mProcMemWriter = new BufferedWriter(new FileWriter
+ (new File(MEDIA_PROCMEM_OUTPUT), true));
+ mProcMemWriter.write(this.getName() + "\n");
+ mMemWriter = new BufferedWriter(new FileWriter
+ (new File(MEDIA_MEMORY_OUTPUT), true));
+ }
}
+ @Override
protected void tearDown() throws Exception {
if (MediaFrameworkPerfTestRunner.mGetNativeHeapDump)
MediaTestUtil.getNativeHeapDump(this.getName() + "_after");
- mProcMemWriter.close();
- mMemWriter.close();
+
+ if (MediaFrameworkPerfTestRunner.mGetProcmem) {
+ mMemWriter.write("\n");
+ mProcMemWriter.close();
+ mMemWriter.close();
+ }
super.tearDown();
}
@@ -157,6 +165,7 @@ public class MediaPlayerPerformance extends ActivityInstrumentationTestCase2<Med
}
private final class RawPreviewCallback implements PreviewCallback {
+ @Override
public void onPreviewFrame(byte[] rawData, Camera camera) {
mPreviewDone.open();
}
@@ -285,19 +294,21 @@ public class MediaPlayerPerformance extends ActivityInstrumentationTestCase2<Med
}
}
- public void writeProcmemInfo() throws Exception{
- String cmd = "procmem " + getMediaserverPid();
- Process p = Runtime.getRuntime().exec(cmd);
-
- InputStream inStream = p.getInputStream();
- InputStreamReader inReader = new InputStreamReader(inStream);
- BufferedReader inBuffer = new BufferedReader(inReader);
- String s;
- while ((s = inBuffer.readLine()) != null) {
- mProcMemWriter.write(s);
- mProcMemWriter.write("\n");
+ public void writeProcmemInfo() throws Exception {
+ if (MediaFrameworkPerfTestRunner.mGetProcmem) {
+ String cmd = "procmem " + getMediaserverPid();
+ Process p = Runtime.getRuntime().exec(cmd);
+
+ InputStream inStream = p.getInputStream();
+ InputStreamReader inReader = new InputStreamReader(inStream);
+ BufferedReader inBuffer = new BufferedReader(inReader);
+ String s;
+ while ((s = inBuffer.readLine()) != null) {
+ mProcMemWriter.write(s);
+ mProcMemWriter.write("\n");
+ }
+ mProcMemWriter.write("\n\n");
}
- mProcMemWriter.write("\n\n");
}
public String captureMediaserverInfo() {
@@ -368,13 +379,11 @@ public class MediaPlayerPerformance extends ActivityInstrumentationTestCase2<Med
boolean memoryResult = false;
mStartPid = getMediaserverPid();
- mMemWriter.write("H263 Video Playback Only\n");
for (int i = 0; i < NUM_STRESS_LOOP; i++) {
mediaStressPlayback(MediaNames.VIDEO_HIGHRES_H263);
getMemoryWriteToLog(i);
writeProcmemInfo();
}
- mMemWriter.write("\n");
memoryResult = validateMemoryResult(mStartPid, mStartMemory, DECODER_LIMIT);
assertTrue("H263 playback memory test", memoryResult);
}
@@ -385,13 +394,11 @@ public class MediaPlayerPerformance extends ActivityInstrumentationTestCase2<Med
boolean memoryResult = false;
mStartPid = getMediaserverPid();
- mMemWriter.write("H264 Video Playback only\n");
for (int i = 0; i < NUM_STRESS_LOOP; i++) {
mediaStressPlayback(MediaNames.VIDEO_H264_AMR);
getMemoryWriteToLog(i);
writeProcmemInfo();
}
- mMemWriter.write("\n");
memoryResult = validateMemoryResult(mStartPid, mStartMemory, DECODER_LIMIT);
assertTrue("H264 playback memory test", memoryResult);
}
@@ -402,7 +409,6 @@ public class MediaPlayerPerformance extends ActivityInstrumentationTestCase2<Med
boolean memoryResult = false;
mStartPid = getMediaserverPid();
- mMemWriter.write("H263 video record only\n");
int frameRate = MediaProfileReader.getMaxFrameRateForCodec(MediaRecorder.VideoEncoder.H263);
assertTrue("H263 video recording frame rate", frameRate != -1);
for (int i = 0; i < NUM_STRESS_LOOP; i++) {
@@ -411,7 +417,6 @@ public class MediaPlayerPerformance extends ActivityInstrumentationTestCase2<Med
getMemoryWriteToLog(i);
writeProcmemInfo();
}
- mMemWriter.write("\n");
memoryResult = validateMemoryResult(mStartPid, mStartMemory, ENCODER_LIMIT);
assertTrue("H263 record only memory test", memoryResult);
}
@@ -422,7 +427,6 @@ public class MediaPlayerPerformance extends ActivityInstrumentationTestCase2<Med
boolean memoryResult = false;
mStartPid = getMediaserverPid();
- mMemWriter.write("MPEG4 video record only\n");
int frameRate = MediaProfileReader.getMaxFrameRateForCodec(MediaRecorder.VideoEncoder.MPEG_4_SP);
assertTrue("MPEG4 video recording frame rate", frameRate != -1);
for (int i = 0; i < NUM_STRESS_LOOP; i++) {
@@ -431,7 +435,6 @@ public class MediaPlayerPerformance extends ActivityInstrumentationTestCase2<Med
getMemoryWriteToLog(i);
writeProcmemInfo();
}
- mMemWriter.write("\n");
memoryResult = validateMemoryResult(mStartPid, mStartMemory, ENCODER_LIMIT);
assertTrue("mpeg4 record only memory test", memoryResult);
}
@@ -445,14 +448,12 @@ public class MediaPlayerPerformance extends ActivityInstrumentationTestCase2<Med
mStartPid = getMediaserverPid();
int frameRate = MediaProfileReader.getMaxFrameRateForCodec(MediaRecorder.VideoEncoder.H263);
assertTrue("H263 video recording frame rate", frameRate != -1);
- mMemWriter.write("Audio and h263 video record\n");
for (int i = 0; i < NUM_STRESS_LOOP; i++) {
assertTrue(stressVideoRecord(frameRate, 352, 288, MediaRecorder.VideoEncoder.H263,
MediaRecorder.OutputFormat.MPEG_4, MediaNames.RECORDED_VIDEO_3GP, false));
getMemoryWriteToLog(i);
writeProcmemInfo();
}
- mMemWriter.write("\n");
memoryResult = validateMemoryResult(mStartPid, mStartMemory, ENCODER_LIMIT);
assertTrue("H263 audio video record memory test", memoryResult);
}
@@ -463,13 +464,11 @@ public class MediaPlayerPerformance extends ActivityInstrumentationTestCase2<Med
boolean memoryResult = false;
mStartPid = getMediaserverPid();
- mMemWriter.write("Audio record only\n");
for (int i = 0; i < NUM_STRESS_LOOP; i++) {
stressAudioRecord(MediaNames.RECORDER_OUTPUT);
getMemoryWriteToLog(i);
writeProcmemInfo();
}
- mMemWriter.write("\n");
memoryResult = validateMemoryResult(mStartPid, mStartMemory, ENCODER_LIMIT);
assertTrue("audio record only memory test", memoryResult);
}
@@ -480,13 +479,11 @@ public class MediaPlayerPerformance extends ActivityInstrumentationTestCase2<Med
boolean memoryResult = false;
mStartPid = getMediaserverPid();
- mMemWriter.write("Camera Preview Only\n");
for (int i = 0; i < NUM_STRESS_LOOP; i++) {
stressCameraPreview();
getMemoryWriteToLog(i);
writeProcmemInfo();
}
- mMemWriter.write("\n");
memoryResult = validateMemoryResult(mStartPid, mStartMemory, CAMERA_LIMIT);
assertTrue("camera preview memory test", memoryResult);
}
diff --git a/packages/SettingsProvider/src/com/android/providers/settings/SettingsBackupAgent.java b/packages/SettingsProvider/src/com/android/providers/settings/SettingsBackupAgent.java
index 0a0474c..0b85e70 100644
--- a/packages/SettingsProvider/src/com/android/providers/settings/SettingsBackupAgent.java
+++ b/packages/SettingsProvider/src/com/android/providers/settings/SettingsBackupAgent.java
@@ -26,6 +26,7 @@ import android.database.Cursor;
import android.net.Uri;
import android.net.wifi.WifiManager;
import android.os.FileUtils;
+import android.os.Handler;
import android.os.ParcelFileDescriptor;
import android.os.Process;
import android.provider.Settings;
@@ -61,12 +62,6 @@ public class SettingsBackupAgent extends BackupAgentHelper {
private static final boolean DEBUG = false;
private static final boolean DEBUG_BACKUP = DEBUG || false;
- /* Don't restore wifi config until we have new logic for parsing the
- * saved wifi config and configuring the new APs without having to
- * disable and re-enable wifi
- */
- private static final boolean NAIVE_WIFI_RESTORE = false;
-
private static final String KEY_SYSTEM = "system";
private static final String KEY_SECURE = "secure";
private static final String KEY_GLOBAL = "global";
@@ -127,10 +122,16 @@ public class SettingsBackupAgent extends BackupAgentHelper {
// stored in the full-backup tarfile as well, so should not be changed.
private static final String STAGE_FILE = "flattened-data";
+ // Delay in milliseconds between the restore operation and when we will bounce
+ // wifi in order to rewrite the supplicant config etc.
+ private static final long WIFI_BOUNCE_DELAY_MILLIS = 60 * 1000; // one minute
+
private SettingsHelper mSettingsHelper;
private WifiManager mWfm;
private static String mWifiConfigFile;
+ WifiRestoreRunnable mWifiRestore = null;
+
// Class for capturing a network definition from the wifi supplicant config file
static class Network {
String ssid = ""; // equals() and hashCode() need these to be non-null
@@ -297,6 +298,66 @@ public class SettingsBackupAgent extends BackupAgentHelper {
writeNewChecksums(stateChecksums, newState);
}
+ class WifiRestoreRunnable implements Runnable {
+ private byte[] restoredSupplicantData;
+ private byte[] restoredWifiConfigFile;
+
+ void incorporateWifiSupplicant(BackupDataInput data) {
+ restoredSupplicantData = new byte[data.getDataSize()];
+ if (restoredSupplicantData.length <= 0) return;
+ try {
+ data.readEntityData(restoredSupplicantData, 0, data.getDataSize());
+ } catch (IOException e) {
+ Log.w(TAG, "Unable to read supplicant data");
+ restoredSupplicantData = null;
+ }
+ }
+
+ void incorporateWifiConfigFile(BackupDataInput data) {
+ restoredWifiConfigFile = new byte[data.getDataSize()];
+ if (restoredWifiConfigFile.length <= 0) return;
+ try {
+ data.readEntityData(restoredWifiConfigFile, 0, data.getDataSize());
+ } catch (IOException e) {
+ Log.w(TAG, "Unable to read config file");
+ restoredWifiConfigFile = null;
+ }
+ }
+
+ @Override
+ public void run() {
+ if (restoredSupplicantData != null || restoredWifiConfigFile != null) {
+ if (DEBUG_BACKUP) {
+ Log.v(TAG, "Starting deferred restore of wifi data");
+ }
+ final int retainedWifiState = enableWifi(false);
+ if (restoredSupplicantData != null) {
+ restoreWifiSupplicant(FILE_WIFI_SUPPLICANT,
+ restoredSupplicantData, restoredSupplicantData.length);
+ FileUtils.setPermissions(FILE_WIFI_SUPPLICANT,
+ FileUtils.S_IRUSR | FileUtils.S_IWUSR |
+ FileUtils.S_IRGRP | FileUtils.S_IWGRP,
+ Process.myUid(), Process.WIFI_UID);
+ }
+ if (restoredWifiConfigFile != null) {
+ restoreFileData(mWifiConfigFile,
+ restoredWifiConfigFile, restoredWifiConfigFile.length);
+ }
+ // restore the previous WIFI state.
+ enableWifi(retainedWifiState == WifiManager.WIFI_STATE_ENABLED ||
+ retainedWifiState == WifiManager.WIFI_STATE_ENABLING);
+ }
+ }
+ }
+
+ // Instantiate the wifi-config restore runnable, scheduling it for execution
+ // a minute hence
+ void initWifiRestoreIfNecessary() {
+ if (mWifiRestore == null) {
+ mWifiRestore = new WifiRestoreRunnable();
+ }
+ }
+
@Override
public void onRestore(BackupDataInput data, int appVersionCode,
ParcelFileDescriptor newState) throws IOException {
@@ -315,26 +376,26 @@ public class SettingsBackupAgent extends BackupAgentHelper {
restoreSettings(data, Settings.Secure.CONTENT_URI, movedToGlobal);
} else if (KEY_GLOBAL.equals(key)) {
restoreSettings(data, Settings.Global.CONTENT_URI, null);
- } else if (NAIVE_WIFI_RESTORE && KEY_WIFI_SUPPLICANT.equals(key)) {
- int retainedWifiState = enableWifi(false);
- restoreWifiSupplicant(FILE_WIFI_SUPPLICANT, data);
- FileUtils.setPermissions(FILE_WIFI_SUPPLICANT,
- FileUtils.S_IRUSR | FileUtils.S_IWUSR |
- FileUtils.S_IRGRP | FileUtils.S_IWGRP,
- Process.myUid(), Process.WIFI_UID);
- // retain the previous WIFI state.
- enableWifi(retainedWifiState == WifiManager.WIFI_STATE_ENABLED ||
- retainedWifiState == WifiManager.WIFI_STATE_ENABLING);
+ } else if (KEY_WIFI_SUPPLICANT.equals(key)) {
+ initWifiRestoreIfNecessary();
+ mWifiRestore.incorporateWifiSupplicant(data);
} else if (KEY_LOCALE.equals(key)) {
byte[] localeData = new byte[size];
data.readEntityData(localeData, 0, size);
mSettingsHelper.setLocaleData(localeData, size);
- } else if (NAIVE_WIFI_RESTORE && KEY_WIFI_CONFIG.equals(key)) {
- restoreFileData(mWifiConfigFile, data);
+ } else if (KEY_WIFI_CONFIG.equals(key)) {
+ initWifiRestoreIfNecessary();
+ mWifiRestore.incorporateWifiConfigFile(data);
} else {
data.skipEntityData();
}
}
+
+ // If we have wifi data to restore, post a runnable to perform the
+ // bounce-and-update operation a little ways in the future.
+ if (mWifiRestore != null) {
+ new Handler(getMainLooper()).postDelayed(mWifiRestore, WIFI_BOUNCE_DELAY_MILLIS);
+ }
}
@Override
@@ -619,7 +680,7 @@ public class SettingsBackupAgent extends BackupAgentHelper {
getContentResolver().insert(destination, contentValues);
}
- if (DEBUG || true) {
+ if (DEBUG) {
Log.d(TAG, "Restored setting: " + destination + " : "+ key + "=" + value);
}
}
@@ -731,17 +792,6 @@ public class SettingsBackupAgent extends BackupAgentHelper {
}
- private void restoreFileData(String filename, BackupDataInput data) {
- byte[] bytes = new byte[data.getDataSize()];
- if (bytes.length <= 0) return;
- try {
- data.readEntityData(bytes, 0, data.getDataSize());
- restoreFileData(filename, bytes, bytes.length);
- } catch (IOException e) {
- Log.w(TAG, "Unable to read file data for " + filename);
- }
- }
-
private void restoreFileData(String filename, byte[] bytes, int size) {
try {
File file = new File(filename);
@@ -794,17 +844,6 @@ public class SettingsBackupAgent extends BackupAgentHelper {
}
}
- private void restoreWifiSupplicant(String filename, BackupDataInput data) {
- byte[] bytes = new byte[data.getDataSize()];
- if (bytes.length <= 0) return;
- try {
- data.readEntityData(bytes, 0, data.getDataSize());
- restoreWifiSupplicant(filename, bytes, bytes.length);
- } catch (IOException e) {
- Log.w(TAG, "Unable to read supplicant data");
- }
- }
-
private void restoreWifiSupplicant(String filename, byte[] bytes, int size) {
try {
WifiNetworkSettings supplicantImage = new WifiNetworkSettings();
diff --git a/packages/SystemUI/AndroidManifest.xml b/packages/SystemUI/AndroidManifest.xml
index 64b660f..f0e5a87 100644
--- a/packages/SystemUI/AndroidManifest.xml
+++ b/packages/SystemUI/AndroidManifest.xml
@@ -108,6 +108,7 @@
</activity>
<activity android:name=".recent.RecentsActivity"
+ android:label="@string/accessibility_desc_recent_apps"
android:theme="@android:style/Theme.Holo.Wallpaper.NoTitleBar"
android:excludeFromRecents="true"
android:launchMode="singleInstance"
diff --git a/packages/SystemUI/res/values/strings.xml b/packages/SystemUI/res/values/strings.xml
index 7ac27fe..e0b0227 100644
--- a/packages/SystemUI/res/values/strings.xml
+++ b/packages/SystemUI/res/values/strings.xml
@@ -377,6 +377,8 @@
<string name="accessibility_desc_notification_shade">Notification shade.</string>
<!-- Content description for the quick settings panel (not shown on the screen). [CHAR LIMIT=NONE] -->
<string name="accessibility_desc_quick_settings">Quick settings.</string>
+ <!-- Content description for the recent apps panel (not shown on the screen). [CHAR LIMIT=NONE] -->
+ <string name="accessibility_desc_recent_apps">Recent apps.</string>
<!-- Content description of the user tile in quick settings (not shown on the screen). [CHAR LIMIT=NONE] -->
<string name="accessibility_quick_settings_user">User <xliff:g id="user" example="John Doe">%s</xliff:g>.</string>
diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/phone/QuickSettings.java b/packages/SystemUI/src/com/android/systemui/statusbar/phone/QuickSettings.java
index e8772df..0937c46 100644
--- a/packages/SystemUI/src/com/android/systemui/statusbar/phone/QuickSettings.java
+++ b/packages/SystemUI/src/com/android/systemui/statusbar/phone/QuickSettings.java
@@ -102,7 +102,7 @@ class QuickSettings {
private int mBrightnessDialogShortTimeout;
private int mBrightnessDialogLongTimeout;
- private AsyncTask<Void, Void, Pair<String, BitmapDrawable>> mUserInfoTask;
+ private AsyncTask<Void, Void, Pair<String, Drawable>> mUserInfoTask;
private LevelListDrawable mBatteryLevels;
private LevelListDrawable mChargingBatteryLevels;
@@ -203,35 +203,43 @@ class QuickSettings {
final int userId = userInfo.id;
final Context context = currentUserContext;
- mUserInfoTask = new AsyncTask<Void, Void, Pair<String, BitmapDrawable>>() {
+ mUserInfoTask = new AsyncTask<Void, Void, Pair<String, Drawable>>() {
@Override
- protected Pair<String, BitmapDrawable> doInBackground(Void... params) {
- Cursor cursor = context.getContentResolver().query(
+ protected Pair<String, Drawable> doInBackground(Void... params) {
+ final Cursor cursor = context.getContentResolver().query(
Profile.CONTENT_URI, new String[] {Phone._ID, Phone.DISPLAY_NAME},
null, null, null);
+ final UserManager um =
+ (UserManager) mContext.getSystemService(Context.USER_SERVICE);
- if (cursor == null) {
- // Info not available. Should become available later.
- return new Pair<String, BitmapDrawable>(null, null);
+ // Fall back to the UserManager nickname if we can't read the name from the local
+ // profile below.
+ String nickName = um.getUserName();
+ String name = nickName;
+ Drawable avatar = null;
+ Bitmap rawAvatar = um.getUserIcon(userId);
+ if (rawAvatar != null) {
+ avatar = new BitmapDrawable(mContext.getResources(), rawAvatar);
+ } else {
+ avatar = mContext.getResources().getDrawable(R.drawable.ic_qs_default_user);
}
- String name = null;
- try {
- if (cursor.moveToFirst()) {
- name = cursor.getString(cursor.getColumnIndex(Phone.DISPLAY_NAME));
+ // Try and read the display name from the local profile
+ if (cursor != null) {
+ try {
+ if (cursor.moveToFirst()) {
+ name = cursor.getString(cursor.getColumnIndex(Phone.DISPLAY_NAME));
+ }
+ } finally {
+ cursor.close();
}
- } finally {
- cursor.close();
}
- final UserManager userManager =
- (UserManager) mContext.getSystemService(Context.USER_SERVICE);
- final BitmapDrawable icon = new BitmapDrawable(mContext.getResources(),
- userManager.getUserIcon(userId));
- return new Pair<String, BitmapDrawable>(name, icon);
+
+ return new Pair<String, Drawable>(name, avatar);
}
@Override
- protected void onPostExecute(Pair<String, BitmapDrawable> result) {
+ protected void onPostExecute(Pair<String, Drawable> result) {
super.onPostExecute(result);
mModel.setUserTileInfo(result.first, result.second);
mUserInfoTask = null;
@@ -305,9 +313,7 @@ class QuickSettings {
ImageView iv = (ImageView) view.findViewById(R.id.user_imageview);
TextView tv = (TextView) view.findViewById(R.id.user_textview);
tv.setText(state.label);
- if (us.avatar != null) {
- iv.setImageDrawable(us.avatar);
- }
+ iv.setImageDrawable(us.avatar);
view.setContentDescription(mContext.getString(
R.string.accessibility_quick_settings_user, state.label));
}
diff --git a/policy/src/com/android/internal/policy/impl/PhoneWindowManager.java b/policy/src/com/android/internal/policy/impl/PhoneWindowManager.java
index 9307f37..7e047fd 100755
--- a/policy/src/com/android/internal/policy/impl/PhoneWindowManager.java
+++ b/policy/src/com/android/internal/policy/impl/PhoneWindowManager.java
@@ -4302,6 +4302,9 @@ public class PhoneWindowManager implements WindowManagerPolicy {
}
public boolean performHapticFeedbackLw(WindowState win, int effectId, boolean always) {
+ if (!mVibrator.hasVibrator()) {
+ return false;
+ }
final boolean hapticsDisabled = Settings.System.getIntForUser(mContext.getContentResolver(),
Settings.System.HAPTIC_FEEDBACK_ENABLED, 0, UserHandle.USER_CURRENT) == 0;
if (!always && (hapticsDisabled || mKeyguardMediator.isShowingAndNotHidden())) {
diff --git a/policy/src/com/android/internal/policy/impl/keyguard/KeyguardHostView.java b/policy/src/com/android/internal/policy/impl/keyguard/KeyguardHostView.java
index 840edaf..bf7be89 100644
--- a/policy/src/com/android/internal/policy/impl/keyguard/KeyguardHostView.java
+++ b/policy/src/com/android/internal/policy/impl/keyguard/KeyguardHostView.java
@@ -258,6 +258,9 @@ public class KeyguardHostView extends KeyguardViewBase {
}
public void dismiss(boolean authenticated) {
+ // If the biometric unlock was suppressed due to a user switch, it can now be safely
+ // unsuppressed because the user has left the unlock screen.
+ KeyguardUpdateMonitor.getInstance(mContext).clearBiometricUnlockUserSwitched();
showNextSecurityScreenOrFinish(authenticated);
}
diff --git a/policy/src/com/android/internal/policy/impl/keyguard/KeyguardSecurityModel.java b/policy/src/com/android/internal/policy/impl/keyguard/KeyguardSecurityModel.java
index 30cd67b..e573072 100644
--- a/policy/src/com/android/internal/policy/impl/keyguard/KeyguardSecurityModel.java
+++ b/policy/src/com/android/internal/policy/impl/keyguard/KeyguardSecurityModel.java
@@ -65,7 +65,8 @@ public class KeyguardSecurityModel {
KeyguardUpdateMonitor monitor = KeyguardUpdateMonitor.getInstance(mContext);
final boolean backupIsTimedOut = monitor.getFailedUnlockAttempts() >=
LockPatternUtils.FAILED_ATTEMPTS_BEFORE_TIMEOUT;
- return monitor.getMaxBiometricUnlockAttemptsReached() || backupIsTimedOut;
+ return monitor.getMaxBiometricUnlockAttemptsReached() || backupIsTimedOut
+ || monitor.didBiometricUnlockUserSwitch();
}
SecurityMode getSecurityMode() {
diff --git a/policy/src/com/android/internal/policy/impl/keyguard/KeyguardSelectorView.java b/policy/src/com/android/internal/policy/impl/keyguard/KeyguardSelectorView.java
index e3b7b01..b6b731e 100644
--- a/policy/src/com/android/internal/policy/impl/keyguard/KeyguardSelectorView.java
+++ b/policy/src/com/android/internal/policy/impl/keyguard/KeyguardSelectorView.java
@@ -28,6 +28,7 @@ import android.content.pm.ResolveInfo;
import android.os.RemoteException;
import android.os.UserHandle;
import android.provider.MediaStore;
+import android.provider.Settings;
import android.util.AttributeSet;
import android.util.Log;
import android.util.Slog;
@@ -203,11 +204,18 @@ public class KeyguardSelectorView extends LinearLayout implements KeyguardSecuri
} else if (disabledBySimState) {
Log.v(TAG, "Camera disabled by Sim State");
}
+ boolean currentUserSetup = 0 != Settings.Secure.getIntForUser(
+ mContext.getContentResolver(),
+ Settings.Secure.USER_SETUP_COMPLETE,
+ 0 /*default */,
+ currentUserHandle);
boolean searchActionAvailable =
((SearchManager) mContext.getSystemService(Context.SEARCH_SERVICE))
.getAssistIntent(mContext, UserHandle.USER_CURRENT) != null;
- mCameraDisabled = cameraDisabledByAdmin || disabledBySimState || !cameraTargetPresent;
- mSearchDisabled = disabledBySimState || !searchActionAvailable || !searchTargetPresent;
+ mCameraDisabled = cameraDisabledByAdmin || disabledBySimState || !cameraTargetPresent
+ || !currentUserSetup;
+ mSearchDisabled = disabledBySimState || !searchActionAvailable || !searchTargetPresent
+ || !currentUserSetup;
updateResources();
}
diff --git a/policy/src/com/android/internal/policy/impl/keyguard/KeyguardUpdateMonitor.java b/policy/src/com/android/internal/policy/impl/keyguard/KeyguardUpdateMonitor.java
index 15a6f9f..63a074a 100644
--- a/policy/src/com/android/internal/policy/impl/keyguard/KeyguardUpdateMonitor.java
+++ b/policy/src/com/android/internal/policy/impl/keyguard/KeyguardUpdateMonitor.java
@@ -102,6 +102,8 @@ public class KeyguardUpdateMonitor {
private int mFailedAttempts = 0;
private int mFailedBiometricUnlockAttempts = 0;
+ private boolean mBiometricUnlockUserSwitched;
+
private boolean mClockVisible;
private final ArrayList<WeakReference<KeyguardUpdateMonitorCallback>>
@@ -404,6 +406,7 @@ public class KeyguardUpdateMonitor {
cb.onUserSwitched(userId);
}
}
+ mBiometricUnlockUserSwitched = true;
try {
reply.sendResult(null);
} catch (RemoteException e) {
@@ -721,6 +724,14 @@ public class KeyguardUpdateMonitor {
return mFailedBiometricUnlockAttempts >= FAILED_BIOMETRIC_UNLOCK_ATTEMPTS_BEFORE_BACKUP;
}
+ public boolean didBiometricUnlockUserSwitch() {
+ return mBiometricUnlockUserSwitched;
+ }
+
+ public void clearBiometricUnlockUserSwitched() {
+ mBiometricUnlockUserSwitched = false;
+ }
+
public boolean isSimLocked() {
return isSimLocked(mSimState);
}
diff --git a/services/java/com/android/server/BluetoothManagerService.java b/services/java/com/android/server/BluetoothManagerService.java
index e7cd279..18182cd 100755
--- a/services/java/com/android/server/BluetoothManagerService.java
+++ b/services/java/com/android/server/BluetoothManagerService.java
@@ -4,6 +4,7 @@
package com.android.server;
+import android.app.ActivityManager;
import android.bluetooth.BluetoothAdapter;
import android.bluetooth.IBluetooth;
import android.bluetooth.IBluetoothCallback;
@@ -17,17 +18,21 @@ import android.content.Context;
import android.content.Intent;
import android.content.IntentFilter;
import android.content.ServiceConnection;
+import android.os.Binder;
import android.os.Handler;
+import android.os.HandlerThread;
import android.os.IBinder;
+import android.os.Looper;
import android.os.Message;
+import android.os.Process;
import android.os.RemoteCallbackList;
import android.os.RemoteException;
-import android.os.Binder;
+import android.os.SystemClock;
import android.os.UserHandle;
import android.provider.Settings;
import android.util.Log;
-import java.util.List;
import java.util.ArrayList;
+import java.util.List;
class BluetoothManagerService extends IBluetoothManager.Stub {
private static final String TAG = "BluetoothManagerService";
private static final boolean DBG = true;
@@ -42,6 +47,8 @@ class BluetoothManagerService extends IBluetoothManager.Stub {
private static final int TIMEOUT_SAVE_MS = 500; //Maximum msec to wait for a save
//Maximum msec to wait for service restart
private static final int SERVICE_RESTART_TIME_MS = 200;
+ //Maximum msec to delay MESSAGE_USER_SWITCHED
+ private static final int USER_SWITCHED_TIME_MS = 200;
private static final int MESSAGE_ENABLE = 1;
private static final int MESSAGE_DISABLE = 2;
@@ -57,6 +64,7 @@ class BluetoothManagerService extends IBluetoothManager.Stub {
private static final int MESSAGE_TIMEOUT_UNBIND =101;
private static final int MESSAGE_GET_NAME_AND_ADDRESS=200;
private static final int MESSAGE_SAVE_NAME_AND_ADDRESS=201;
+ private static final int MESSAGE_USER_SWITCHED = 300;
private static final int MAX_SAVE_RETRIES=3;
private final Context mContext;
@@ -72,6 +80,10 @@ class BluetoothManagerService extends IBluetoothManager.Stub {
private boolean mBinding;
private boolean mUnbinding;
private boolean mQuietEnable = false;
+ private boolean mEnable;
+ private int mState;
+ private HandlerThread mThread;
+ private final BluetoothHandler mHandler;
private void registerForAirplaneMode(IntentFilter filter) {
final ContentResolver resolver = mContext.getContentResolver();
@@ -106,23 +118,32 @@ class BluetoothManagerService extends IBluetoothManager.Stub {
}
} else if (Intent.ACTION_AIRPLANE_MODE_CHANGED.equals(action)) {
if (isAirplaneModeOn()) {
- // disable without persisting the setting
- handleDisable(false);
- } else {
- if (isBluetoothPersistedStateOn()) {
- // enable without persisting the setting
- handleEnable(false, false);
- }
+ // disable without persisting the setting
+ mHandler.sendMessage(mHandler.obtainMessage(MESSAGE_DISABLE,
+ 0, 0));
+ } else if (isBluetoothPersistedStateOn()) {
+ // enable without persisting the setting
+ mHandler.sendMessage(mHandler.obtainMessage(MESSAGE_ENABLE,
+ 0, 0));
}
+ } else if (Intent.ACTION_USER_SWITCHED.equals(action)) {
+ mHandler.sendMessage(mHandler.obtainMessage(MESSAGE_USER_SWITCHED,
+ intent.getIntExtra(Intent.EXTRA_USER_HANDLE, 0), 0));
}
}
};
BluetoothManagerService(Context context) {
+ mThread = new HandlerThread("BluetoothManager");
+ mThread.start();
+ mHandler = new BluetoothHandler(mThread.getLooper());
+
mContext = context;
mBluetooth = null;
mBinding = false;
mUnbinding = false;
+ mEnable = false;
+ mState = BluetoothAdapter.STATE_OFF;
mAddress = null;
mName = null;
mContentResolver = context.getContentResolver();
@@ -130,6 +151,7 @@ class BluetoothManagerService extends IBluetoothManager.Stub {
mStateChangeCallbacks = new RemoteCallbackList<IBluetoothStateChangeCallback>();
IntentFilter filter = new IntentFilter(BluetoothAdapter.ACTION_STATE_CHANGED);
filter.addAction(BluetoothAdapter.ACTION_LOCAL_NAME_CHANGED);
+ filter.addAction(Intent.ACTION_USER_SWITCHED);
registerForAirplaneMode(filter);
mContext.registerReceiver(mReceiver, filter);
boolean airplaneModeOn = isAirplaneModeOn();
@@ -139,7 +161,7 @@ class BluetoothManagerService extends IBluetoothManager.Stub {
if (bluetoothOn) {
//Enable
if (DBG) Log.d(TAG, "Auto-enabling Bluetooth.");
- enable();
+ enableHelper();
} else if (!isNameAndAddressSet()) {
//Sync the Bluetooth name and address from the Bluetooth Adapter
if (DBG) Log.d(TAG,"Retrieving Bluetooth Adapter name and address...");
@@ -251,6 +273,11 @@ class BluetoothManagerService extends IBluetoothManager.Stub {
}
public boolean isEnabled() {
+ if (!checkIfCallerIsForegroundUser()) {
+ Log.w(TAG,"isEnabled(): not allowed for non-active user");
+ return false;
+ }
+
synchronized(mConnection) {
try {
return (mBluetooth != null && mBluetooth.isEnabled());
@@ -266,10 +293,6 @@ class BluetoothManagerService extends IBluetoothManager.Stub {
Log.d(TAG,"getNameAndAddress(): mBluetooth = " + mBluetooth +
" mBinding = " + mBinding);
}
- synchronized(mConnection) {
- if (mBinding) return;
- if (mConnection == null) mBinding = true;
- }
Message msg = mHandler.obtainMessage(MESSAGE_GET_NAME_AND_ADDRESS);
mHandler.sendMessage(msg);
}
@@ -277,21 +300,19 @@ class BluetoothManagerService extends IBluetoothManager.Stub {
{
mContext.enforceCallingOrSelfPermission(BLUETOOTH_ADMIN_PERM,
"Need BLUETOOTH ADMIN permission");
+
+ if (!checkIfCallerIsForegroundUser()) {
+ Log.w(TAG,"enableNoAutoConnect(): not allowed for non-active user");
+ return false;
+ }
+
if (DBG) {
Log.d(TAG,"enableNoAutoConnect(): mBluetooth =" + mBluetooth +
" mBinding = " + mBinding);
}
- if (Binder.getCallingUid() != android.os.Process.NFC_UID) {
+ if (Binder.getCallingUid() != Process.NFC_UID) {
throw new SecurityException("no permission to enable Bluetooth quietly");
}
- synchronized(mConnection) {
- if (mBinding) {
- Log.w(TAG,"enableNoAutoConnect(): binding in progress. Returning..");
- return true;
- }
- if (mConnection == null) mBinding = true;
- }
-
Message msg = mHandler.obtainMessage(MESSAGE_ENABLE);
msg.arg1=0; //No persist
msg.arg2=1; //Quiet mode
@@ -300,39 +321,28 @@ class BluetoothManagerService extends IBluetoothManager.Stub {
}
public boolean enable() {
- mContext.enforceCallingOrSelfPermission(BLUETOOTH_ADMIN_PERM,
- "Need BLUETOOTH ADMIN permission");
- if (DBG) {
- Log.d(TAG,"enable(): mBluetooth =" + mBluetooth +
- " mBinding = " + mBinding);
+ if (!checkIfCallerIsForegroundUser()) {
+ Log.w(TAG,"enable(): not allowed for non-active user");
+ return false;
}
- synchronized(mConnection) {
- if (mBinding) {
- Log.w(TAG,"enable(): binding in progress. Returning..");
- return true;
- }
- if (mConnection == null) mBinding = true;
- }
-
- Message msg = mHandler.obtainMessage(MESSAGE_ENABLE);
- msg.arg1=1; //persist
- msg.arg2=0; //No Quiet Mode
- mHandler.sendMessage(msg);
- return true;
+ return enableHelper();
}
public boolean disable(boolean persist) {
mContext.enforceCallingOrSelfPermission(BLUETOOTH_ADMIN_PERM,
"Need BLUETOOTH ADMIN permissicacheNameAndAddresson");
+
+ if (!checkIfCallerIsForegroundUser()) {
+ Log.w(TAG,"disable(): not allowed for non-active user");
+ return false;
+ }
+
if (DBG) {
Log.d(TAG,"disable(): mBluetooth = " + mBluetooth +
" mBinding = " + mBinding);
}
- synchronized(mConnection) {
- if (mBluetooth == null) return false;
- }
Message msg = mHandler.obtainMessage(MESSAGE_DISABLE);
msg.arg1=(persist?1:0);
mHandler.sendMessage(msg);
@@ -348,13 +358,13 @@ class BluetoothManagerService extends IBluetoothManager.Stub {
synchronized (mConnection) {
if (mUnbinding) return;
mUnbinding = true;
- if (mConnection != null) {
+ if (mBluetooth != null) {
if (!mConnection.isGetNameAddressOnly()) {
//Unregister callback object
try {
mBluetooth.unregisterCallback(mBluetoothCallback);
} catch (RemoteException re) {
- Log.e(TAG, "Unable to register BluetoothCallback",re);
+ Log.e(TAG, "Unable to unregister BluetoothCallback",re);
}
}
if (DBG) Log.d(TAG, "Sending unbind request.");
@@ -362,6 +372,7 @@ class BluetoothManagerService extends IBluetoothManager.Stub {
//Unbind
mContext.unbindService(mConnection);
mUnbinding = false;
+ mBinding = false;
} else {
mUnbinding=false;
}
@@ -382,6 +393,24 @@ class BluetoothManagerService extends IBluetoothManager.Stub {
}
/**
+ * Inform BluetoothAdapter instances that Adapter service is up
+ */
+ private void sendBluetoothServiceUpCallback() {
+ if (!mConnection.isGetNameAddressOnly()) {
+ if (DBG) Log.d(TAG,"Calling onBluetoothServiceUp callbacks");
+ int n = mCallbacks.beginBroadcast();
+ Log.d(TAG,"Broadcasting onBluetoothServiceUp() to " + n + " receivers.");
+ for (int i=0; i <n;i++) {
+ try {
+ mCallbacks.getBroadcastItem(i).onBluetoothServiceUp(mBluetooth);
+ } catch (RemoteException e) {
+ Log.e(TAG, "Unable to call onBluetoothServiceUp() on callback #" + i, e);
+ }
+ }
+ mCallbacks.finishBroadcast();
+ }
+ }
+ /**
* Inform BluetoothAdapter instances that Adapter service is down
*/
private void sendBluetoothServiceDownCallback() {
@@ -402,6 +431,12 @@ class BluetoothManagerService extends IBluetoothManager.Stub {
public String getAddress() {
mContext.enforceCallingOrSelfPermission(BLUETOOTH_ADMIN_PERM,
"Need BLUETOOTH ADMIN permission");
+
+ if (!checkIfCallerIsForegroundUser()) {
+ Log.w(TAG,"getAddress(): not allowed for non-active user");
+ return mAddress;
+ }
+
synchronized(mConnection) {
if (mBluetooth != null) {
try {
@@ -420,6 +455,12 @@ class BluetoothManagerService extends IBluetoothManager.Stub {
public String getName() {
mContext.enforceCallingOrSelfPermission(BLUETOOTH_ADMIN_PERM,
"Need BLUETOOTH ADMIN permission");
+
+ if (!checkIfCallerIsForegroundUser()) {
+ Log.w(TAG,"getName(): not allowed for non-active user");
+ return mName;
+ }
+
synchronized(mConnection) {
if (mBluetooth != null) {
try {
@@ -464,7 +505,11 @@ class BluetoothManagerService extends IBluetoothManager.Stub {
private BluetoothServiceConnection mConnection = new BluetoothServiceConnection();
- private final Handler mHandler = new Handler() {
+ private class BluetoothHandler extends Handler {
+ public BluetoothHandler(Looper looper) {
+ super(looper);
+ }
+
@Override
public void handleMessage(Message msg) {
if (DBG) Log.d (TAG, "Message: " + msg.what);
@@ -473,7 +518,7 @@ class BluetoothManagerService extends IBluetoothManager.Stub {
if (DBG) Log.d(TAG,"MESSAGE_GET_NAME_AND_ADDRESS");
synchronized(mConnection) {
//Start bind request
- if (mBluetooth == null) {
+ if ((mBluetooth == null) && (!mBinding)) {
if (DBG) Log.d(TAG, "Binding to service to get name and address");
mConnection.setGetNameAddressOnly(true);
//Start bind timeout and bind
@@ -484,11 +529,20 @@ class BluetoothManagerService extends IBluetoothManager.Stub {
Context.BIND_AUTO_CREATE, UserHandle.USER_CURRENT)) {
mHandler.removeMessages(MESSAGE_TIMEOUT_BIND);
Log.e(TAG, "fail to bind to: " + IBluetooth.class.getName());
+ } else {
+ mBinding = true;
}
}
else {
Message saveMsg= mHandler.obtainMessage(MESSAGE_SAVE_NAME_AND_ADDRESS);
- mHandler.sendMessage(saveMsg);
+ saveMsg.arg1 = 0;
+ if (mBluetooth != null) {
+ mHandler.sendMessage(saveMsg);
+ } else {
+ // if enable is also called to bind the service
+ // wait for MESSAGE_BLUETOOTH_SERVICE_CONNECTED
+ mHandler.sendMessageDelayed(saveMsg, TIMEOUT_SAVE_MS);
+ }
}
}
break;
@@ -508,8 +562,9 @@ class BluetoothManagerService extends IBluetoothManager.Stub {
if (name != null && address != null) {
storeNameAndAddress(name,address);
- sendBluetoothServiceDownCallback();
- unbindAndFinish();
+ if (mConnection.isGetNameAddressOnly()) {
+ unbindAndFinish();
+ }
} else {
if (msg.arg1 < MAX_SAVE_RETRIES) {
Message retryMsg = mHandler.obtainMessage(MESSAGE_SAVE_NAME_AND_ADDRESS);
@@ -518,10 +573,17 @@ class BluetoothManagerService extends IBluetoothManager.Stub {
mHandler.sendMessageDelayed(retryMsg, TIMEOUT_SAVE_MS);
} else {
Log.w(TAG,"Maximum name/address remote retrieval retry exceeded");
- sendBluetoothServiceDownCallback();
- unbindAndFinish();
+ if (mConnection.isGetNameAddressOnly()) {
+ unbindAndFinish();
+ }
}
}
+ } else {
+ // rebind service by Request GET NAME AND ADDRESS
+ // if service is unbinded by disable or
+ // MESSAGE_BLUETOOTH_SERVICE_CONNECTED is not received
+ Message getMsg = mHandler.obtainMessage(MESSAGE_GET_NAME_AND_ADDRESS);
+ mHandler.sendMessage(getMsg);
}
}
break;
@@ -530,12 +592,22 @@ class BluetoothManagerService extends IBluetoothManager.Stub {
if (DBG) {
Log.d(TAG, "MESSAGE_ENABLE: mBluetooth = " + mBluetooth);
}
-
+ mHandler.removeMessages(MESSAGE_RESTART_BLUETOOTH_SERVICE);
+ mEnable = true;
handleEnable(msg.arg1 == 1, msg.arg2 ==1);
break;
case MESSAGE_DISABLE:
- handleDisable(msg.arg1 == 1);
+ mHandler.removeMessages(MESSAGE_RESTART_BLUETOOTH_SERVICE);
+ if (mEnable && mBluetooth != null) {
+ waitForOnOff(true, false);
+ mEnable = false;
+ handleDisable(msg.arg1 == 1);
+ waitForOnOff(false, false);
+ } else {
+ mEnable = false;
+ handleDisable(msg.arg1 == 1);
+ }
break;
case MESSAGE_REGISTER_ADAPTER:
@@ -580,27 +652,26 @@ class BluetoothManagerService extends IBluetoothManager.Stub {
//Request GET NAME AND ADDRESS
Message getMsg = mHandler.obtainMessage(MESSAGE_GET_NAME_AND_ADDRESS);
mHandler.sendMessage(getMsg);
- return;
+ if (!mEnable) return;
}
+ mConnection.setGetNameAddressOnly(false);
//Register callback object
try {
mBluetooth.registerCallback(mBluetoothCallback);
} catch (RemoteException re) {
Log.e(TAG, "Unable to register BluetoothCallback",re);
}
-
//Inform BluetoothAdapter instances that service is up
- int n = mCallbacks.beginBroadcast();
- Log.d(TAG,"Broadcasting onBluetoothServiceUp() to " + n + " receivers.");
- for (int i=0; i <n;i++) {
+ sendBluetoothServiceUpCallback();
+
+ //Check if name and address is loaded if not get it first.
+ if (!isNameAndAddressSet()) {
try {
- mCallbacks.getBroadcastItem(i).onBluetoothServiceUp(mBluetooth);
- } catch (RemoteException e) {
- Log.e(TAG, "Unable to call onBluetoothServiceUp() on callback #" + i, e);
- }
+ storeNameAndAddress(mBluetooth.getName(),
+ mBluetooth.getAddress());
+ } catch (RemoteException e) {Log.e(TAG, "", e);};
}
- mCallbacks.finishBroadcast();
//Do enable request
try {
@@ -619,12 +690,19 @@ class BluetoothManagerService extends IBluetoothManager.Stub {
Log.e(TAG,"Unable to call enable()",e);
}
}
+
+ if (!mEnable) {
+ waitForOnOff(true, false);
+ handleDisable(false);
+ waitForOnOff(false, false);
+ }
break;
}
case MESSAGE_TIMEOUT_BIND: {
Log.e(TAG, "MESSAGE_TIMEOUT_BIND");
synchronized(mConnection) {
mBinding = false;
+ mEnable = false;
}
break;
}
@@ -633,51 +711,37 @@ class BluetoothManagerService extends IBluetoothManager.Stub {
int prevState = msg.arg1;
int newState = msg.arg2;
if (DBG) Log.d(TAG, "MESSAGE_BLUETOOTH_STATE_CHANGE: prevState = " + prevState + ", newState=" + newState);
- if (prevState != newState) {
- //Notify all proxy objects first of adapter state change
- if (newState == BluetoothAdapter.STATE_ON || newState == BluetoothAdapter.STATE_OFF) {
- boolean isUp = (newState==BluetoothAdapter.STATE_ON);
- sendBluetoothStateCallback(isUp);
-
- //If Bluetooth is off, send service down event to proxy objects, and unbind
- if (!isUp) {
- sendBluetoothServiceDownCallback();
- unbindAndFinish();
- }
- }
-
- //Send broadcast message to everyone else
- Intent intent = new Intent(BluetoothAdapter.ACTION_STATE_CHANGED);
- intent.putExtra(BluetoothAdapter.EXTRA_PREVIOUS_STATE, prevState);
- intent.putExtra(BluetoothAdapter.EXTRA_STATE, newState);
- intent.addFlags(Intent.FLAG_RECEIVER_REGISTERED_ONLY_BEFORE_BOOT);
- if (DBG) Log.d(TAG,"Bluetooth State Change Intent: " + prevState + " -> " + newState);
- mContext.sendBroadcastAsUser(intent, UserHandle.ALL,
- BLUETOOTH_PERM);
- }
+ mState = newState;
+ bluetoothStateChangeHandler(prevState, newState);
break;
}
case MESSAGE_BLUETOOTH_SERVICE_DISCONNECTED:
{
- if (DBG) Log.d(TAG, "MESSAGE_BLUETOOTH_SERVICE_DISCONNECTED");
- sendBluetoothServiceDownCallback();
-
- // Send BT state broadcast to update
- // the BT icon correctly
- Message stateChangeMsg = mHandler.obtainMessage(
- MESSAGE_BLUETOOTH_STATE_CHANGE);
- stateChangeMsg.arg1 = BluetoothAdapter.STATE_ON;
- stateChangeMsg.arg2 =
- BluetoothAdapter.STATE_TURNING_OFF;
- mHandler.sendMessage(stateChangeMsg);
+ Log.e(TAG, "MESSAGE_BLUETOOTH_SERVICE_DISCONNECTED");
synchronized(mConnection) {
+ // if service is unbinded already, do nothing and return
+ if (mBluetooth == null) return;
mBluetooth = null;
}
- // Send a Bluetooth Restart message
- Message restartMsg = mHandler.obtainMessage(
- MESSAGE_RESTART_BLUETOOTH_SERVICE);
- mHandler.sendMessageDelayed(restartMsg,
- SERVICE_RESTART_TIME_MS);
+
+ if (mEnable) {
+ mEnable = false;
+ // Send a Bluetooth Restart message
+ Message restartMsg = mHandler.obtainMessage(
+ MESSAGE_RESTART_BLUETOOTH_SERVICE);
+ mHandler.sendMessageDelayed(restartMsg,
+ SERVICE_RESTART_TIME_MS);
+ }
+
+ if (!mConnection.isGetNameAddressOnly()) {
+ sendBluetoothServiceDownCallback();
+
+ // Send BT state broadcast to update
+ // the BT icon correctly
+ bluetoothStateChangeHandler(BluetoothAdapter.STATE_ON,
+ BluetoothAdapter.STATE_TURNING_OFF);
+ mState = BluetoothAdapter.STATE_OFF;
+ }
break;
}
case MESSAGE_RESTART_BLUETOOTH_SERVICE:
@@ -687,6 +751,7 @@ class BluetoothManagerService extends IBluetoothManager.Stub {
/* Enable without persisting the setting as
it doesnt change when IBluetooth
service restarts */
+ mEnable = true;
handleEnable(false, mQuietEnable);
break;
}
@@ -699,9 +764,66 @@ class BluetoothManagerService extends IBluetoothManager.Stub {
}
break;
}
+
+ case MESSAGE_USER_SWITCHED:
+ {
+ if (DBG) {
+ Log.d(TAG, "MESSAGE_USER_SWITCHED");
+ }
+ mHandler.removeMessages(MESSAGE_USER_SWITCHED);
+ /* disable and enable BT when detect a user switch */
+ if (mEnable && mBluetooth != null) {
+ synchronized (mConnection) {
+ if (mBluetooth != null) {
+ //Unregister callback object
+ try {
+ mBluetooth.unregisterCallback(mBluetoothCallback);
+ } catch (RemoteException re) {
+ Log.e(TAG, "Unable to unregister",re);
+ }
+ }
+ }
+ mHandler.removeMessages(MESSAGE_BLUETOOTH_STATE_CHANGE);
+
+ waitForOnOff(true, false);
+
+ bluetoothStateChangeHandler(mState, BluetoothAdapter.STATE_ON);
+
+ // disable
+ handleDisable(false);
+
+ waitForOnOff(false, true);
+
+ bluetoothStateChangeHandler(BluetoothAdapter.STATE_ON,
+ BluetoothAdapter.STATE_OFF);
+ mState = BluetoothAdapter.STATE_OFF;
+ sendBluetoothServiceDownCallback();
+ synchronized (mConnection) {
+ if (mBluetooth != null) {
+ mBluetooth = null;
+ //Unbind
+ mContext.unbindService(mConnection);
+ }
+ }
+ SystemClock.sleep(100);
+
+ // enable
+ handleEnable(false, mQuietEnable);
+ } else if (mBinding || mBluetooth != null) {
+ Message userMsg = mHandler.obtainMessage(MESSAGE_USER_SWITCHED);
+ userMsg.arg2 = 1 + msg.arg2;
+ // if user is switched when service is being binding
+ // delay sending MESSAGE_USER_SWITCHED
+ mHandler.sendMessageDelayed(userMsg, USER_SWITCHED_TIME_MS);
+ if (DBG) {
+ Log.d(TAG, "delay MESSAGE_USER_SWITCHED " + userMsg.arg2);
+ }
+ }
+ break;
+ }
}
}
- };
+ }
private void handleEnable(boolean persist, boolean quietMode) {
if (persist) {
@@ -711,18 +833,35 @@ class BluetoothManagerService extends IBluetoothManager.Stub {
mQuietEnable = quietMode;
synchronized(mConnection) {
- if (mBluetooth == null) {
+ if ((mBluetooth == null) && (!mBinding)) {
//Start bind timeout and bind
Message timeoutMsg=mHandler.obtainMessage(MESSAGE_TIMEOUT_BIND);
mHandler.sendMessageDelayed(timeoutMsg,TIMEOUT_BIND_MS);
mConnection.setGetNameAddressOnly(false);
Intent i = new Intent(IBluetooth.class.getName());
- if (!mContext.bindService(i, mConnection, Context.BIND_AUTO_CREATE,
+ if (!mContext.bindService(i, mConnection,Context.BIND_AUTO_CREATE,
UserHandle.USER_CURRENT)) {
mHandler.removeMessages(MESSAGE_TIMEOUT_BIND);
Log.e(TAG, "Fail to bind to: " + IBluetooth.class.getName());
+ } else {
+ mBinding = true;
}
- } else {
+ } else if (mBluetooth != null) {
+ if (mConnection.isGetNameAddressOnly()) {
+ // if GetNameAddressOnly is set, we can clear this flag,
+ // so the service won't be unbind
+ // after name and address are saved
+ mConnection.setGetNameAddressOnly(false);
+ //Register callback object
+ try {
+ mBluetooth.registerCallback(mBluetoothCallback);
+ } catch (RemoteException re) {
+ Log.e(TAG, "Unable to register BluetoothCallback",re);
+ }
+ //Inform BluetoothAdapter instances that service is up
+ sendBluetoothServiceUpCallback();
+ }
+
//Check if name and address is loaded if not get it first.
if (!isNameAndAddressSet()) {
try {
@@ -751,12 +890,14 @@ class BluetoothManagerService extends IBluetoothManager.Stub {
}
private void handleDisable(boolean persist) {
+ if (persist) {
+ persistBluetoothSetting(false);
+ }
+
synchronized(mConnection) {
- if (mBluetooth != null ) {
- if (persist) {
- persistBluetoothSetting(false);
- }
- mConnection.setGetNameAddressOnly(false);
+ // don't need to disable if GetNameAddressOnly is set,
+ // service will be unbinded after Name and Address are saved
+ if ((mBluetooth != null) && (!mConnection.isGetNameAddressOnly())) {
if (DBG) Log.d(TAG,"Sending off request.");
try {
@@ -769,4 +910,102 @@ class BluetoothManagerService extends IBluetoothManager.Stub {
}
}
}
+
+ private boolean checkIfCallerIsForegroundUser() {
+ int foregroundUser;
+ int callingUser = UserHandle.getCallingUserId();
+ long callingIdentity = Binder.clearCallingIdentity();
+ boolean valid = false;
+ try {
+ foregroundUser = ActivityManager.getCurrentUser();
+ valid = (callingUser == foregroundUser);
+ if (DBG) {
+ Log.d(TAG, "checkIfCallerIsForegroundUser: valid=" + valid
+ + " callingUser=" + callingUser
+ + " foregroundUser=" + foregroundUser);
+ }
+ } finally {
+ Binder.restoreCallingIdentity(callingIdentity);
+ }
+ return valid;
+ }
+
+ private boolean enableHelper() {
+ mContext.enforceCallingOrSelfPermission(BLUETOOTH_ADMIN_PERM,
+ "Need BLUETOOTH ADMIN permission");
+ if (DBG) {
+ Log.d(TAG,"enable(): mBluetooth =" + mBluetooth +
+ " mBinding = " + mBinding);
+ }
+
+ Message msg = mHandler.obtainMessage(MESSAGE_ENABLE);
+ msg.arg1=1; //persist
+ msg.arg2=0; //No Quiet Mode
+ mHandler.sendMessage(msg);
+ return true;
+ }
+
+ private void bluetoothStateChangeHandler(int prevState, int newState) {
+ if (prevState != newState) {
+ //Notify all proxy objects first of adapter state change
+ if (newState == BluetoothAdapter.STATE_ON || newState == BluetoothAdapter.STATE_OFF) {
+ boolean isUp = (newState==BluetoothAdapter.STATE_ON);
+ sendBluetoothStateCallback(isUp);
+
+ //If Bluetooth is off, send service down event to proxy objects, and unbind
+ if (!isUp) {
+ //Only unbind with mEnable flag not set
+ //For race condition: disable and enable back-to-back
+ //Avoid unbind right after enable due to callback from disable
+ if ((!mEnable) && (mBluetooth != null)) {
+ sendBluetoothServiceDownCallback();
+ unbindAndFinish();
+ }
+ }
+ }
+
+ //Send broadcast message to everyone else
+ Intent intent = new Intent(BluetoothAdapter.ACTION_STATE_CHANGED);
+ intent.putExtra(BluetoothAdapter.EXTRA_PREVIOUS_STATE, prevState);
+ intent.putExtra(BluetoothAdapter.EXTRA_STATE, newState);
+ intent.addFlags(Intent.FLAG_RECEIVER_REGISTERED_ONLY_BEFORE_BOOT);
+ if (DBG) Log.d(TAG,"Bluetooth State Change Intent: " + prevState + " -> " + newState);
+ mContext.sendBroadcastAsUser(intent, UserHandle.ALL,
+ BLUETOOTH_PERM);
+ }
+ }
+
+ /**
+ * if on is true, wait for state become ON
+ * if off is true, wait for state become OFF
+ * if both on and off are false, wait for state not ON
+ */
+ private boolean waitForOnOff(boolean on, boolean off) {
+ int i = 0;
+ while (i < 10) {
+ synchronized(mConnection) {
+ try {
+ if (mBluetooth == null) break;
+ if (on) {
+ if (mBluetooth.getState() == BluetoothAdapter.STATE_ON) return true;
+ } else if (off) {
+ if (mBluetooth.getState() == BluetoothAdapter.STATE_OFF) return true;
+ } else {
+ if (mBluetooth.getState() != BluetoothAdapter.STATE_ON) return true;
+ }
+ } catch (RemoteException e) {
+ Log.e(TAG, "getState()", e);
+ break;
+ }
+ }
+ if (on || off) {
+ SystemClock.sleep(300);
+ } else {
+ SystemClock.sleep(50);
+ }
+ i++;
+ }
+ Log.e(TAG,"waitForOnOff time out");
+ return false;
+ }
}
diff --git a/services/java/com/android/server/NotificationManagerService.java b/services/java/com/android/server/NotificationManagerService.java
index 2e7a54b..09a606e 100755
--- a/services/java/com/android/server/NotificationManagerService.java
+++ b/services/java/com/android/server/NotificationManagerService.java
@@ -991,6 +991,14 @@ public class NotificationManagerService extends INotificationManager.Stub
| Notification.FLAG_NO_CLEAR;
}
+ final int currentUser;
+ final long token = Binder.clearCallingIdentity();
+ try {
+ currentUser = ActivityManager.getCurrentUser();
+ } finally {
+ Binder.restoreCallingIdentity(token);
+ }
+
if (notification.icon != 0) {
final StatusBarNotification n = new StatusBarNotification(
pkg, id, tag, r.uid, r.initialPid, score, notification, user);
@@ -1015,7 +1023,10 @@ public class NotificationManagerService extends INotificationManager.Stub
Binder.restoreCallingIdentity(identity);
}
}
- sendAccessibilityEvent(notification, pkg);
+ // Send accessibility events only for the current user.
+ if (currentUser == userId) {
+ sendAccessibilityEvent(notification, pkg);
+ }
} else {
Slog.e(TAG, "Ignoring notification with icon==0: " + notification);
if (old != null && old.statusBarKey != null) {
@@ -1029,14 +1040,6 @@ public class NotificationManagerService extends INotificationManager.Stub
}
}
- final int currentUser;
- final long token = Binder.clearCallingIdentity();
- try {
- currentUser = ActivityManager.getCurrentUser();
- } finally {
- Binder.restoreCallingIdentity(token);
- }
-
// If we're not supposed to beep, vibrate, etc. then don't.
if (((mDisabledNotifications & StatusBarManager.DISABLE_NOTIFICATION_ALERTS) == 0)
&& (!(old != null
diff --git a/services/java/com/android/server/WiredAccessoryManager.java b/services/java/com/android/server/WiredAccessoryManager.java
index 63e8895..d5c9c8f 100644
--- a/services/java/com/android/server/WiredAccessoryManager.java
+++ b/services/java/com/android/server/WiredAccessoryManager.java
@@ -152,7 +152,7 @@ final class WiredAccessoryManager implements WiredAccessoryCallbacks {
break;
}
- updateLocked(NAME_H2W, headset);
+ updateLocked(NAME_H2W, (mHeadsetState & ~(BIT_HEADSET | BIT_HEADSET_NO_MIC)) | headset);
}
}
diff --git a/services/java/com/android/server/accessibility/AccessibilityManagerService.java b/services/java/com/android/server/accessibility/AccessibilityManagerService.java
index 3d77b3a..7c482f5 100644
--- a/services/java/com/android/server/accessibility/AccessibilityManagerService.java
+++ b/services/java/com/android/server/accessibility/AccessibilityManagerService.java
@@ -643,6 +643,10 @@ public class AccessibilityManagerService extends IAccessibilityManager.Stub {
return mSecurityPolicy.mActiveWindowId;
}
+ void onTouchInteractionStart() {
+ mSecurityPolicy.onTouchInteractionStart();
+ }
+
void onTouchInteractionEnd() {
mSecurityPolicy.onTouchInteractionEnd();
}
@@ -2138,6 +2142,7 @@ public class AccessibilityManagerService extends IAccessibilityManager.Stub {
| AccessibilityEvent.TYPE_VIEW_ACCESSIBILITY_FOCUS_CLEARED;
private int mActiveWindowId;
+ private boolean mTouchInteractionInProgress;
private boolean canDispatchAccessibilityEvent(AccessibilityEvent event) {
final int eventType = event.getEventType();
@@ -2185,12 +2190,21 @@ public class AccessibilityManagerService extends IAccessibilityManager.Stub {
}
} break;
case AccessibilityEvent.TYPE_VIEW_HOVER_ENTER: {
- mActiveWindowId = windowId;
+ // Do not allow delayed hover events to confuse us
+ // which the active window is.
+ if (mTouchInteractionInProgress) {
+ mActiveWindowId = windowId;
+ }
} break;
}
}
+ public void onTouchInteractionStart() {
+ mTouchInteractionInProgress = true;
+ }
+
public void onTouchInteractionEnd() {
+ mTouchInteractionInProgress = false;
// We want to set the active window to be current immediately
// after the user has stopped touching the screen since if the
// user types with the IME he should get a feedback for the
diff --git a/services/java/com/android/server/accessibility/TouchExplorer.java b/services/java/com/android/server/accessibility/TouchExplorer.java
index 2688776..dcf87350 100644
--- a/services/java/com/android/server/accessibility/TouchExplorer.java
+++ b/services/java/com/android/server/accessibility/TouchExplorer.java
@@ -398,6 +398,7 @@ class TouchExplorer implements EventStreamTransformation {
switch (event.getActionMasked()) {
case MotionEvent.ACTION_DOWN:
+ mAms.onTouchInteractionStart();
// Pre-feed the motion events to the gesture detector since we
// have a distance slop before getting into gesture detection
// mode and not using the points within this slop significantly
diff --git a/services/java/com/android/server/am/ActiveServices.java b/services/java/com/android/server/am/ActiveServices.java
index 7e3fdbd..35999ea 100644
--- a/services/java/com/android/server/am/ActiveServices.java
+++ b/services/java/com/android/server/am/ActiveServices.java
@@ -248,8 +248,9 @@ public class ActiveServices {
synchronized (r.stats.getBatteryStats()) {
r.stats.startRunningLocked();
}
- if (!bringUpServiceLocked(r, service.getFlags(), false)) {
- return new ComponentName("!", "Service process is bad");
+ String error = bringUpServiceLocked(r, service.getFlags(), false);
+ if (error != null) {
+ return new ComponentName("!!", error);
}
return r.name;
}
@@ -518,7 +519,7 @@ public class ActiveServices {
if ((flags&Context.BIND_AUTO_CREATE) != 0) {
s.lastActivity = SystemClock.uptimeMillis();
- if (!bringUpServiceLocked(s, service.getFlags(), false)) {
+ if (bringUpServiceLocked(s, service.getFlags(), false) != null) {
return 0;
}
}
@@ -964,19 +965,19 @@ public class ActiveServices {
return true;
}
- private final boolean bringUpServiceLocked(ServiceRecord r,
+ private final String bringUpServiceLocked(ServiceRecord r,
int intentFlags, boolean whileRestarting) {
//Slog.i(TAG, "Bring up service:");
//r.dump(" ");
if (r.app != null && r.app.thread != null) {
sendServiceArgsLocked(r, false);
- return true;
+ return null;
}
if (!whileRestarting && r.restartDelay > 0) {
// If waiting for a restart, then do nothing.
- return true;
+ return null;
}
if (DEBUG_SERVICE) Slog.v(TAG, "Bringing up " + r + " " + r.intent);
@@ -988,12 +989,13 @@ public class ActiveServices {
// Make sure that the user who owns this service is started. If not,
// we don't want to allow it to run.
if (mAm.mStartedUsers.get(r.userId) == null) {
- Slog.w(TAG, "Unable to launch app "
+ String msg = "Unable to launch app "
+ r.appInfo.packageName + "/"
+ r.appInfo.uid + " for service "
- + r.intent.getIntent() + ": user " + r.userId + " is stopped");
+ + r.intent.getIntent() + ": user " + r.userId + " is stopped";
+ Slog.w(TAG, msg);
bringDownServiceLocked(r, true);
- return false;
+ return msg;
}
// Service is now being launched, its package can't be stopped.
@@ -1018,7 +1020,7 @@ public class ActiveServices {
try {
app.addPackage(r.appInfo.packageName);
realStartServiceLocked(r, app);
- return true;
+ return null;
} catch (RemoteException e) {
Slog.w(TAG, "Exception when starting service " + r.shortName, e);
}
@@ -1041,12 +1043,13 @@ public class ActiveServices {
if (app == null) {
if ((app=mAm.startProcessLocked(procName, r.appInfo, true, intentFlags,
"service", r.name, false, isolated)) == null) {
- Slog.w(TAG, "Unable to launch app "
+ String msg = "Unable to launch app "
+ r.appInfo.packageName + "/"
+ r.appInfo.uid + " for service "
- + r.intent.getIntent() + ": process is bad");
+ + r.intent.getIntent() + ": process is bad";
+ Slog.w(TAG, msg);
bringDownServiceLocked(r, true);
- return false;
+ return msg;
}
if (isolated) {
r.isolatedProc = app;
@@ -1057,7 +1060,7 @@ public class ActiveServices {
mPendingServices.add(r);
}
- return true;
+ return null;
}
private final void requestServiceBindingsLocked(ServiceRecord r) {
diff --git a/services/java/com/android/server/am/ActivityManagerService.java b/services/java/com/android/server/am/ActivityManagerService.java
index c2aa3a5..daed0a2 100644
--- a/services/java/com/android/server/am/ActivityManagerService.java
+++ b/services/java/com/android/server/am/ActivityManagerService.java
@@ -3585,7 +3585,7 @@ public final class ActivityManagerService extends ActivityManagerNative
Slog.w(TAG, "Failed trying to unstop package "
+ packageName + ": " + e);
}
- if (isUserRunningLocked(user)) {
+ if (isUserRunningLocked(user, false)) {
forceStopPackageLocked(packageName, pkgUid);
}
}
@@ -3739,7 +3739,8 @@ public final class ActivityManagerService extends ActivityManagerNative
private void forceStopUserLocked(int userId) {
forceStopPackageLocked(null, -1, false, false, true, false, userId);
Intent intent = new Intent(Intent.ACTION_USER_STOPPED);
- intent.addFlags(Intent.FLAG_RECEIVER_REGISTERED_ONLY);
+ intent.addFlags(Intent.FLAG_RECEIVER_REGISTERED_ONLY
+ | Intent.FLAG_RECEIVER_FOREGROUND);
intent.putExtra(Intent.EXTRA_USER_HANDLE, userId);
broadcastIntentLocked(null, null, intent,
null, null, 0, null, null, null,
@@ -7904,6 +7905,19 @@ public final class ActivityManagerService extends ActivityManagerNative
broadcastIntentLocked(null, null, intent,
null, null, 0, null, null, null,
false, false, MY_PID, Process.SYSTEM_UID, mCurrentUserId);
+ intent = new Intent(Intent.ACTION_USER_STARTING);
+ intent.addFlags(Intent.FLAG_RECEIVER_REGISTERED_ONLY);
+ intent.putExtra(Intent.EXTRA_USER_HANDLE, mCurrentUserId);
+ broadcastIntentLocked(null, null, intent,
+ null, new IIntentReceiver.Stub() {
+ @Override
+ public void performReceive(Intent intent, int resultCode, String data,
+ Bundle extras, boolean ordered, boolean sticky, int sendingUser)
+ throws RemoteException {
+ }
+ }, 0, null, null,
+ android.Manifest.permission.INTERACT_ACROSS_USERS,
+ false, false, MY_PID, Process.SYSTEM_UID, UserHandle.USER_ALL);
} finally {
Binder.restoreCallingIdentity(ident);
}
@@ -8879,7 +8893,7 @@ public final class ActivityManagerService extends ActivityManagerNative
pw.println(" [-a] [-c] [-h] [cmd] ...");
pw.println(" cmd may be one of:");
pw.println(" a[ctivities]: activity stack state");
- pw.println(" b[roadcasts] [PACKAGE_NAME]: broadcast state");
+ pw.println(" b[roadcasts] [PACKAGE_NAME] [history [-s]]: broadcast state");
pw.println(" i[ntents] [PACKAGE_NAME]: pending intent state");
pw.println(" p[rocesses] [PACKAGE_NAME]: process state");
pw.println(" o[om]: out of memory management");
@@ -9338,6 +9352,12 @@ public final class ActivityManagerService extends ActivityManagerNative
pw.print(" User #"); pw.print(uss.mHandle.getIdentifier());
pw.print(": "); uss.dump("", pw);
}
+ pw.print(" mStartedUserArray: [");
+ for (int i=0; i<mStartedUserArray.length; i++) {
+ if (i > 0) pw.print(", ");
+ pw.print(mStartedUserArray[i]);
+ }
+ pw.println("]");
pw.print(" mUserLru: [");
for (int i=0; i<mUserLru.size(); i++) {
if (i > 0) pw.print(", ");
@@ -9707,6 +9727,9 @@ public final class ActivityManagerService extends ActivityManagerNative
boolean onlyHistory = false;
if ("history".equals(dumpPackage)) {
+ if (opti < args.length && "-s".equals(args[opti])) {
+ dumpAll = false;
+ }
onlyHistory = true;
dumpPackage = null;
}
@@ -14106,11 +14129,14 @@ public final class ActivityManagerService extends ActivityManagerNative
mWindowManager.startFreezingScreen(R.anim.screen_user_exit,
R.anim.screen_user_enter);
+ boolean needStart = false;
+
// If the user we are switching to is not currently started, then
// we need to start it now.
if (mStartedUsers.get(userId) == null) {
mStartedUsers.put(userId, new UserStartedState(new UserHandle(userId), false));
updateStartedUserArrayLocked();
+ needStart = true;
}
mCurrentUserId = userId;
@@ -14134,10 +14160,14 @@ public final class ActivityManagerService extends ActivityManagerNative
// so we can just fairly silently bring the user back from
// the almost-dead.
uss.mState = UserStartedState.STATE_RUNNING;
+ updateStartedUserArrayLocked();
+ needStart = true;
} else if (uss.mState == UserStartedState.STATE_SHUTDOWN) {
// This means ACTION_SHUTDOWN has been sent, so we will
// need to treat this as a new boot of the user.
uss.mState = UserStartedState.STATE_BOOTING;
+ updateStartedUserArrayLocked();
+ needStart = true;
}
mHandler.removeMessages(REPORT_USER_SWITCH_MSG);
@@ -14146,17 +14176,19 @@ public final class ActivityManagerService extends ActivityManagerNative
oldUserId, userId, uss));
mHandler.sendMessageDelayed(mHandler.obtainMessage(USER_SWITCH_TIMEOUT_MSG,
oldUserId, userId, uss), USER_SWITCH_TIMEOUT);
- Intent intent = new Intent(Intent.ACTION_USER_STARTED);
- intent.addFlags(Intent.FLAG_RECEIVER_REGISTERED_ONLY
- | Intent.FLAG_RECEIVER_FOREGROUND);
- intent.putExtra(Intent.EXTRA_USER_HANDLE, userId);
- broadcastIntentLocked(null, null, intent,
- null, null, 0, null, null, null,
- false, false, MY_PID, Process.SYSTEM_UID, userId);
+ if (needStart) {
+ Intent intent = new Intent(Intent.ACTION_USER_STARTED);
+ intent.addFlags(Intent.FLAG_RECEIVER_REGISTERED_ONLY
+ | Intent.FLAG_RECEIVER_FOREGROUND);
+ intent.putExtra(Intent.EXTRA_USER_HANDLE, userId);
+ broadcastIntentLocked(null, null, intent,
+ null, null, 0, null, null, null,
+ false, false, MY_PID, Process.SYSTEM_UID, userId);
+ }
if ((userInfo.flags&UserInfo.FLAG_INITIALIZED) == 0) {
if (userId != 0) {
- intent = new Intent(Intent.ACTION_USER_INITIALIZE);
+ Intent intent = new Intent(Intent.ACTION_USER_INITIALIZE);
intent.addFlags(Intent.FLAG_RECEIVER_FOREGROUND);
broadcastIntentLocked(null, null, intent, null,
new IIntentReceiver.Stub() {
@@ -14180,6 +14212,21 @@ public final class ActivityManagerService extends ActivityManagerNative
getUserManagerLocked().userForeground(userId);
sendUserSwitchBroadcastsLocked(oldUserId, userId);
+ if (needStart) {
+ Intent intent = new Intent(Intent.ACTION_USER_STARTING);
+ intent.addFlags(Intent.FLAG_RECEIVER_REGISTERED_ONLY);
+ intent.putExtra(Intent.EXTRA_USER_HANDLE, userId);
+ broadcastIntentLocked(null, null, intent,
+ null, new IIntentReceiver.Stub() {
+ @Override
+ public void performReceive(Intent intent, int resultCode, String data,
+ Bundle extras, boolean ordered, boolean sticky, int sendingUser)
+ throws RemoteException {
+ }
+ }, 0, null, null,
+ android.Manifest.permission.INTERACT_ACROSS_USERS,
+ false, false, MY_PID, Process.SYSTEM_UID, UserHandle.USER_ALL);
+ }
}
} finally {
Binder.restoreCallingIdentity(ident);
@@ -14217,19 +14264,6 @@ public final class ActivityManagerService extends ActivityManagerNative
null, null, 0, null, null,
android.Manifest.permission.MANAGE_USERS,
false, false, MY_PID, Process.SYSTEM_UID, UserHandle.USER_ALL);
- intent = new Intent(Intent.ACTION_USER_STARTING);
- intent.addFlags(Intent.FLAG_RECEIVER_REGISTERED_ONLY);
- intent.putExtra(Intent.EXTRA_USER_HANDLE, newUserId);
- broadcastIntentLocked(null, null, intent,
- null, new IIntentReceiver.Stub() {
- @Override
- public void performReceive(Intent intent, int resultCode, String data,
- Bundle extras, boolean ordered, boolean sticky, int sendingUser)
- throws RemoteException {
- }
- }, 0, null, null,
- android.Manifest.permission.INTERACT_ACROSS_USERS,
- false, false, MY_PID, Process.SYSTEM_UID, UserHandle.USER_ALL);
}
} finally {
Binder.restoreCallingIdentity(ident);
@@ -14318,8 +14352,7 @@ public final class ActivityManagerService extends ActivityManagerNative
void finishUserSwitch(UserStartedState uss) {
synchronized (this) {
- if ((uss.mState == UserStartedState.STATE_BOOTING
- || uss.mState == UserStartedState.STATE_SHUTDOWN)
+ if (uss.mState == UserStartedState.STATE_BOOTING
&& mStartedUsers.get(uss.mHandle.getIdentifier()) == uss) {
uss.mState = UserStartedState.STATE_RUNNING;
final int userId = uss.mHandle.getIdentifier();
@@ -14410,6 +14443,7 @@ public final class ActivityManagerService extends ActivityManagerNative
if (uss.mState != UserStartedState.STATE_STOPPING
&& uss.mState != UserStartedState.STATE_SHUTDOWN) {
uss.mState = UserStartedState.STATE_STOPPING;
+ updateStartedUserArrayLocked();
long ident = Binder.clearCallingIdentity();
try {
@@ -14514,7 +14548,7 @@ public final class ActivityManagerService extends ActivityManagerNative
}
@Override
- public boolean isUserRunning(int userId) {
+ public boolean isUserRunning(int userId, boolean orStopped) {
if (checkCallingPermission(android.Manifest.permission.INTERACT_ACROSS_USERS)
!= PackageManager.PERMISSION_GRANTED) {
String msg = "Permission Denial: isUserRunning() from pid="
@@ -14525,13 +14559,19 @@ public final class ActivityManagerService extends ActivityManagerNative
throw new SecurityException(msg);
}
synchronized (this) {
- return isUserRunningLocked(userId);
+ return isUserRunningLocked(userId, orStopped);
}
}
- boolean isUserRunningLocked(int userId) {
+ boolean isUserRunningLocked(int userId, boolean orStopped) {
UserStartedState state = mStartedUsers.get(userId);
- return state != null && state.mState != UserStartedState.STATE_STOPPING
+ if (state == null) {
+ return false;
+ }
+ if (orStopped) {
+ return true;
+ }
+ return state.mState != UserStartedState.STATE_STOPPING
&& state.mState != UserStartedState.STATE_SHUTDOWN;
}
@@ -14552,9 +14592,24 @@ public final class ActivityManagerService extends ActivityManagerNative
}
private void updateStartedUserArrayLocked() {
- mStartedUserArray = new int[mStartedUsers.size()];
+ int num = 0;
for (int i=0; i<mStartedUsers.size(); i++) {
- mStartedUserArray[i] = mStartedUsers.keyAt(i);
+ UserStartedState uss = mStartedUsers.valueAt(i);
+ // This list does not include stopping users.
+ if (uss.mState != UserStartedState.STATE_STOPPING
+ && uss.mState != UserStartedState.STATE_SHUTDOWN) {
+ num++;
+ }
+ }
+ mStartedUserArray = new int[num];
+ num = 0;
+ for (int i=0; i<mStartedUsers.size(); i++) {
+ UserStartedState uss = mStartedUsers.valueAt(i);
+ if (uss.mState != UserStartedState.STATE_STOPPING
+ && uss.mState != UserStartedState.STATE_SHUTDOWN) {
+ mStartedUserArray[num] = mStartedUsers.keyAt(i);
+ num++;
+ }
}
}
diff --git a/services/java/com/android/server/am/BroadcastQueue.java b/services/java/com/android/server/am/BroadcastQueue.java
index 95c22ec..f9630ae 100644
--- a/services/java/com/android/server/am/BroadcastQueue.java
+++ b/services/java/com/android/server/am/BroadcastQueue.java
@@ -54,6 +54,7 @@ public class BroadcastQueue {
static final boolean DEBUG_MU = ActivityManagerService.DEBUG_MU;
static final int MAX_BROADCAST_HISTORY = 25;
+ static final int MAX_BROADCAST_SUMMARY_HISTORY = 100;
final ActivityManagerService mService;
@@ -93,6 +94,12 @@ public class BroadcastQueue {
= new BroadcastRecord[MAX_BROADCAST_HISTORY];
/**
+ * Summary of historical data of past broadcasts, for debugging.
+ */
+ final Intent[] mBroadcastSummaryHistory
+ = new Intent[MAX_BROADCAST_SUMMARY_HISTORY];
+
+ /**
* Set when we current have a BROADCAST_INTENT_MSG in flight.
*/
boolean mBroadcastsScheduled = false;
@@ -922,6 +929,9 @@ public class BroadcastQueue {
MAX_BROADCAST_HISTORY-1);
r.finishTime = SystemClock.uptimeMillis();
mBroadcastHistory[0] = r;
+ System.arraycopy(mBroadcastSummaryHistory, 0, mBroadcastSummaryHistory, 1,
+ MAX_BROADCAST_SUMMARY_HISTORY-1);
+ mBroadcastSummaryHistory[0] = r.intent;
}
final void logBroadcastReceiverDiscardLocked(BroadcastRecord r) {
@@ -1006,8 +1016,9 @@ public class BroadcastQueue {
}
}
+ int i;
boolean printed = false;
- for (int i=0; i<MAX_BROADCAST_HISTORY; i++) {
+ for (i=0; i<MAX_BROADCAST_HISTORY; i++) {
BroadcastRecord r = mBroadcastHistory[i];
if (r == null) {
break;
@@ -1028,11 +1039,44 @@ public class BroadcastQueue {
pw.print(i); pw.println(":");
r.dump(pw, " ");
} else {
- if (i >= 50) {
+ pw.print(" #"); pw.print(i); pw.print(": "); pw.println(r);
+ pw.print(" ");
+ pw.println(r.intent.toShortString(false, true, true, false));
+ Bundle bundle = r.intent.getExtras();
+ if (bundle != null) {
+ pw.print(" extras: "); pw.println(bundle.toString());
+ }
+ }
+ }
+
+ if (dumpPackage == null) {
+ if (dumpAll) {
+ i = 0;
+ printed = false;
+ }
+ for (; i<MAX_BROADCAST_SUMMARY_HISTORY; i++) {
+ Intent intent = mBroadcastSummaryHistory[i];
+ if (intent == null) {
+ break;
+ }
+ if (!printed) {
+ if (needSep) {
+ pw.println();
+ }
+ needSep = true;
+ pw.println(" Historical broadcasts summary [" + mQueueName + "]:");
+ printed = true;
+ }
+ if (!dumpAll && i >= 50) {
pw.println(" ...");
break;
}
- pw.print(" #"); pw.print(i); pw.print(": "); pw.println(r);
+ pw.print(" #"); pw.print(i); pw.print(": ");
+ pw.println(intent.toShortString(false, true, true, false));
+ Bundle bundle = intent.getExtras();
+ if (bundle != null) {
+ pw.print(" extras: "); pw.println(bundle.toString());
+ }
}
}
diff --git a/services/java/com/android/server/am/BroadcastRecord.java b/services/java/com/android/server/am/BroadcastRecord.java
index 85ec328..1cf5b9c 100644
--- a/services/java/com/android/server/am/BroadcastRecord.java
+++ b/services/java/com/android/server/am/BroadcastRecord.java
@@ -81,12 +81,10 @@ class BroadcastRecord extends Binder {
final long now = SystemClock.uptimeMillis();
pw.print(prefix); pw.print(this); pw.print(" to user "); pw.println(userId);
- pw.print(prefix); pw.println(intent);
- if (sticky) {
- Bundle bundle = intent.getExtras();
- if (bundle != null) {
- pw.print(prefix); pw.print("extras: "); pw.println(bundle.toString());
- }
+ pw.print(prefix); pw.println(intent.toInsecureString());
+ Bundle bundle = intent.getExtras();
+ if (bundle != null) {
+ pw.print(prefix); pw.print("extras: "); pw.println(bundle.toString());
}
pw.print(prefix); pw.print("caller="); pw.print(callerPackage); pw.print(" ");
pw.print(callerApp != null ? callerApp.toShortString() : "null");
diff --git a/services/java/com/android/server/power/PowerManagerService.java b/services/java/com/android/server/power/PowerManagerService.java
index 85bf17a..b76ad45 100644
--- a/services/java/com/android/server/power/PowerManagerService.java
+++ b/services/java/com/android/server/power/PowerManagerService.java
@@ -48,6 +48,7 @@ import android.os.PowerManager;
import android.os.Process;
import android.os.RemoteException;
import android.os.SystemClock;
+import android.os.SystemService;
import android.os.UserHandle;
import android.os.WorkSource;
import android.provider.Settings;
@@ -81,6 +82,8 @@ public final class PowerManagerService extends IPowerManager.Stub
private static final int MSG_SANDMAN = 2;
// Message: Sent when the screen on blocker is released.
private static final int MSG_SCREEN_ON_BLOCKER_RELEASED = 3;
+ // Message: Sent to poll whether the boot animation has terminated.
+ private static final int MSG_CHECK_IF_BOOT_ANIMATION_FINISHED = 4;
// Dirty bit: mWakeLocks changed
private static final int DIRTY_WAKE_LOCKS = 1 << 0;
@@ -127,6 +130,7 @@ public final class PowerManagerService extends IPowerManager.Stub
private static final int WAKE_LOCK_SCREEN_DIM = 1 << 2;
private static final int WAKE_LOCK_BUTTON_BRIGHT = 1 << 3;
private static final int WAKE_LOCK_PROXIMITY_SCREEN_OFF = 1 << 4;
+ private static final int WAKE_LOCK_STAY_AWAKE = 1 << 5; // only set if already awake
// Summarizes the user activity state.
private static final int USER_ACTIVITY_SCREEN_BRIGHT = 1 << 0;
@@ -152,6 +156,12 @@ public final class PowerManagerService extends IPowerManager.Stub
// See point of use for more details.
private static final int WIRELESS_CHARGER_TURN_ON_BATTERY_LEVEL_LIMIT = 95;
+ // The name of the boot animation service in init.rc.
+ private static final String BOOT_ANIMATION_SERVICE = "bootanim";
+
+ // Poll interval in milliseconds for watching boot animation finished.
+ private static final int BOOT_ANIMATION_POLL_INTERVAL = 200;
+
private Context mContext;
private LightsService mLightsService;
private BatteryService mBatteryService;
@@ -1172,16 +1182,25 @@ public final class PowerManagerService extends IPowerManager.Stub
if (mWakefulness != WAKEFULNESS_ASLEEP) {
mWakeLockSummary |= WAKE_LOCK_CPU
| WAKE_LOCK_SCREEN_BRIGHT | WAKE_LOCK_BUTTON_BRIGHT;
+ if (mWakefulness == WAKEFULNESS_AWAKE) {
+ mWakeLockSummary |= WAKE_LOCK_STAY_AWAKE;
+ }
}
break;
case PowerManager.SCREEN_BRIGHT_WAKE_LOCK:
if (mWakefulness != WAKEFULNESS_ASLEEP) {
mWakeLockSummary |= WAKE_LOCK_CPU | WAKE_LOCK_SCREEN_BRIGHT;
+ if (mWakefulness == WAKEFULNESS_AWAKE) {
+ mWakeLockSummary |= WAKE_LOCK_STAY_AWAKE;
+ }
}
break;
case PowerManager.SCREEN_DIM_WAKE_LOCK:
if (mWakefulness != WAKEFULNESS_ASLEEP) {
mWakeLockSummary |= WAKE_LOCK_CPU | WAKE_LOCK_SCREEN_DIM;
+ if (mWakefulness == WAKEFULNESS_AWAKE) {
+ mWakeLockSummary |= WAKE_LOCK_STAY_AWAKE;
+ }
}
break;
case PowerManager.PROXIMITY_SCREEN_OFF_WAKE_LOCK:
@@ -1339,7 +1358,7 @@ public final class PowerManagerService extends IPowerManager.Stub
private boolean isBeingKeptAwakeLocked() {
return mStayOn
|| mProximityPositive
- || (mWakeLockSummary & (WAKE_LOCK_SCREEN_BRIGHT | WAKE_LOCK_SCREEN_DIM)) != 0
+ || (mWakeLockSummary & WAKE_LOCK_STAY_AWAKE) != 0
|| (mUserActivitySummary & (USER_ACTIVITY_SCREEN_BRIGHT
| USER_ACTIVITY_SCREEN_DIM)) != 0;
}
@@ -1652,6 +1671,29 @@ public final class PowerManagerService extends IPowerManager.Stub
updatePowerStateLocked();
}
+ private void startWatchingForBootAnimationFinished() {
+ mHandler.sendEmptyMessage(MSG_CHECK_IF_BOOT_ANIMATION_FINISHED);
+ }
+
+ private void checkIfBootAnimationFinished() {
+ if (DEBUG) {
+ Slog.d(TAG, "Check if boot animation finished...");
+ }
+
+ if (SystemService.isRunning(BOOT_ANIMATION_SERVICE)) {
+ mHandler.sendEmptyMessageDelayed(MSG_CHECK_IF_BOOT_ANIMATION_FINISHED,
+ BOOT_ANIMATION_POLL_INTERVAL);
+ return;
+ }
+
+ synchronized (mLock) {
+ if (!mBootCompleted) {
+ Slog.i(TAG, "Boot animation finished.");
+ handleBootCompletedLocked();
+ }
+ }
+ }
+
private void handleBootCompletedLocked() {
final long now = SystemClock.uptimeMillis();
mBootCompleted = true;
@@ -2160,9 +2202,13 @@ public final class PowerManagerService extends IPowerManager.Stub
private final class BootCompletedReceiver extends BroadcastReceiver {
@Override
public void onReceive(Context context, Intent intent) {
- synchronized (mLock) {
- handleBootCompletedLocked();
- }
+ // This is our early signal that the system thinks it has finished booting.
+ // However, the boot animation may still be running for a few more seconds
+ // since it is ultimately in charge of when it terminates.
+ // Defer transitioning into the boot completed state until the animation exits.
+ // We do this so that the screen does not start to dim prematurely before
+ // the user has actually had a chance to interact with the device.
+ startWatchingForBootAnimationFinished();
}
}
@@ -2217,6 +2263,9 @@ public final class PowerManagerService extends IPowerManager.Stub
case MSG_SCREEN_ON_BLOCKER_RELEASED:
handleScreenOnBlockerReleased();
break;
+ case MSG_CHECK_IF_BOOT_ANIMATION_FINISHED:
+ checkIfBootAnimationFinished();
+ break;
}
}
}
diff --git a/services/java/com/android/server/wm/WindowManagerService.java b/services/java/com/android/server/wm/WindowManagerService.java
index 0db9f19..54f6deb 100755
--- a/services/java/com/android/server/wm/WindowManagerService.java
+++ b/services/java/com/android/server/wm/WindowManagerService.java
@@ -264,6 +264,9 @@ public class WindowManagerService extends IWindowManager.Stub
*/
static final int DEFAULT_FADE_IN_OUT_DURATION = 400;
+ /** Amount of time (in milliseconds) to delay before declaring a window freeze timeout. */
+ static final int WINDOW_FREEZE_TIMEOUT_DURATION = 3000;
+
/**
* If true, the window manager will do its own custom freezing and general
* management of the screen during rotation.
@@ -5651,12 +5654,12 @@ public class WindowManagerService extends IWindowManager.Stub
@Override
public void showStrictModeViolation(boolean on) {
if (mHeadless) return;
- mH.sendMessage(mH.obtainMessage(H.SHOW_STRICT_MODE_VIOLATION, on ? 1 : 0, 0));
+ int pid = Binder.getCallingPid();
+ mH.sendMessage(mH.obtainMessage(H.SHOW_STRICT_MODE_VIOLATION, on ? 1 : 0, pid));
}
- private void showStrictModeViolation(int arg) {
+ private void showStrictModeViolation(int arg, int pid) {
final boolean on = arg != 0;
- int pid = Binder.getCallingPid();
synchronized(mWindowMap) {
// Ignoring requests to enable the red border from clients
// which aren't on screen. (e.g. Broadcast Receivers in
@@ -6021,7 +6024,8 @@ public class WindowManagerService extends IWindowManager.Stub
mWindowsFreezingScreen = true;
mH.removeMessages(H.WINDOW_FREEZE_TIMEOUT);
- mH.sendMessageDelayed(mH.obtainMessage(H.WINDOW_FREEZE_TIMEOUT), 2000);
+ mH.sendMessageDelayed(mH.obtainMessage(H.WINDOW_FREEZE_TIMEOUT),
+ WINDOW_FREEZE_TIMEOUT_DURATION);
mWaitingForConfig = true;
getDefaultDisplayContentLocked().layoutNeeded = true;
startFreezingDisplayLocked(inTransaction, 0, 0);
@@ -7646,7 +7650,7 @@ public class WindowManagerService extends IWindowManager.Stub
}
case SHOW_STRICT_MODE_VIOLATION: {
- showStrictModeViolation(msg.arg1);
+ showStrictModeViolation(msg.arg1, msg.arg2);
break;
}
@@ -8383,7 +8387,7 @@ public class WindowManagerService extends IWindowManager.Stub
// when we first froze the display.
mH.removeMessages(H.WINDOW_FREEZE_TIMEOUT);
mH.sendMessageDelayed(mH.obtainMessage(
- H.WINDOW_FREEZE_TIMEOUT), 2000);
+ H.WINDOW_FREEZE_TIMEOUT), WINDOW_FREEZE_TIMEOUT_DURATION);
}
}
}
diff --git a/services/jni/com_android_server_power_PowerManagerService.cpp b/services/jni/com_android_server_power_PowerManagerService.cpp
index 75f77b9..23c33af 100644
--- a/services/jni/com_android_server_power_PowerManagerService.cpp
+++ b/services/jni/com_android_server_power_PowerManagerService.cpp
@@ -168,12 +168,14 @@ static void nativeReleaseSuspendBlocker(JNIEnv *env, jclass clazz, jstring nameS
}
static void nativeSetInteractive(JNIEnv *env, jclass clazz, jboolean enable) {
- if (enable) {
- ALOGD_IF_SLOW(20, "Excessive delay in setInteractive(true) while turning screen on");
- gPowerModule->setInteractive(gPowerModule, true);
- } else {
- ALOGD_IF_SLOW(20, "Excessive delay in setInteractive(false) while turning screen off");
- gPowerModule->setInteractive(gPowerModule, false);
+ if (gPowerModule) {
+ if (enable) {
+ ALOGD_IF_SLOW(20, "Excessive delay in setInteractive(true) while turning screen on");
+ gPowerModule->setInteractive(gPowerModule, true);
+ } else {
+ ALOGD_IF_SLOW(20, "Excessive delay in setInteractive(false) while turning screen off");
+ gPowerModule->setInteractive(gPowerModule, false);
+ }
}
}
diff --git a/wifi/java/android/net/wifi/WifiWatchdogStateMachine.java b/wifi/java/android/net/wifi/WifiWatchdogStateMachine.java
index 4440145..9c727f9 100644
--- a/wifi/java/android/net/wifi/WifiWatchdogStateMachine.java
+++ b/wifi/java/android/net/wifi/WifiWatchdogStateMachine.java
@@ -98,6 +98,8 @@ public class WifiWatchdogStateMachine extends StateMachine {
static final int POOR_LINK_DETECTED = BASE + 21;
static final int GOOD_LINK_DETECTED = BASE + 22;
+ public static final boolean DEFAULT_POOR_NETWORK_AVOIDANCE_ENABLED = false;
+
/*
* RSSI levels as used by notification icon
* Level 4 -55 <= RSSI
@@ -345,13 +347,6 @@ public class WifiWatchdogStateMachine extends StateMachine {
// watchdog in an enabled state
putSettingsGlobalBoolean(contentResolver, Settings.Global.WIFI_WATCHDOG_ON, true);
- // disable poor network avoidance
- if (sWifiOnly) {
- logd("Disabling poor network avoidance for wi-fi only device");
- putSettingsGlobalBoolean(contentResolver,
- Settings.Global.WIFI_WATCHDOG_POOR_NETWORK_TEST_ENABLED, false);
- }
-
WifiWatchdogStateMachine wwsm = new WifiWatchdogStateMachine(context);
wwsm.start();
return wwsm;
@@ -441,8 +436,15 @@ public class WifiWatchdogStateMachine extends StateMachine {
private void updateSettings() {
if (DBG) logd("Updating secure settings");
- mPoorNetworkDetectionEnabled = getSettingsGlobalBoolean(mContentResolver,
- Settings.Global.WIFI_WATCHDOG_POOR_NETWORK_TEST_ENABLED, true);
+ // disable poor network avoidance
+ if (sWifiOnly) {
+ logd("Disabling poor network avoidance for wi-fi only device");
+ mPoorNetworkDetectionEnabled = false;
+ } else {
+ mPoorNetworkDetectionEnabled = getSettingsGlobalBoolean(mContentResolver,
+ Settings.Global.WIFI_WATCHDOG_POOR_NETWORK_TEST_ENABLED,
+ DEFAULT_POOR_NETWORK_AVOIDANCE_ENABLED);
+ }
}
/**