diff options
162 files changed, 3094 insertions, 1231 deletions
@@ -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"; @@ -25675,7 +25677,7 @@ package android.view { ctor public ViewGroup.LayoutParams(android.content.Context, android.util.AttributeSet); ctor public ViewGroup.LayoutParams(int, int); ctor public ViewGroup.LayoutParams(android.view.ViewGroup.LayoutParams); - method public void onResolveLayoutDirection(int); + method public void resolveLayoutDirection(int); method protected void setBaseAttributes(android.content.res.TypedArray, int, int); field public static final deprecated int FILL_PARENT = -1; // 0xffffffff field public static final int MATCH_PARENT = -1; // 0xffffffff diff --git a/api/current.txt b/api/current.txt index f8ad4d9..7cadf9b 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"; @@ -25675,7 +25677,7 @@ package android.view { ctor public ViewGroup.LayoutParams(android.content.Context, android.util.AttributeSet); ctor public ViewGroup.LayoutParams(int, int); ctor public ViewGroup.LayoutParams(android.view.ViewGroup.LayoutParams); - method public void onResolveLayoutDirection(int); + method public void resolveLayoutDirection(int); method protected void setBaseAttributes(android.content.res.TypedArray, int, int); field public static final deprecated int FILL_PARENT = -1; // 0xffffffff field public static final int MATCH_PARENT = -1; // 0xffffffff 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/cmds/settings/Android.mk b/cmds/settings/Android.mk new file mode 100644 index 0000000..05deb99 --- /dev/null +++ b/cmds/settings/Android.mk @@ -0,0 +1,18 @@ +# Copyright 2011 The Android Open Source Project +# +LOCAL_PATH:= $(call my-dir) +include $(CLEAR_VARS) + +LOCAL_SRC_FILES := $(call all-subdir-java-files) +LOCAL_MODULE := settings +LOCAL_MODULE_TAGS := optional +include $(BUILD_JAVA_LIBRARY) + +include $(CLEAR_VARS) +LOCAL_MODULE := settings +LOCAL_SRC_FILES := settings +LOCAL_MODULE_CLASS := EXECUTABLES +LOCAL_MODULE_TAGS := optional +include $(BUILD_PREBUILT) + + diff --git a/cmds/settings/settings b/cmds/settings/settings new file mode 100755 index 0000000..ef459ca --- /dev/null +++ b/cmds/settings/settings @@ -0,0 +1,5 @@ +# Script to start "settings" on the device +# +base=/system +export CLASSPATH=$base/framework/settings.jar +exec app_process $base/bin com.android.commands.settings.SettingsCmd "$@" diff --git a/cmds/settings/src/com/android/commands/settings/SettingsCmd.java b/cmds/settings/src/com/android/commands/settings/SettingsCmd.java new file mode 100644 index 0000000..0c69f01 --- /dev/null +++ b/cmds/settings/src/com/android/commands/settings/SettingsCmd.java @@ -0,0 +1,220 @@ +/* + * Copyright (C) 2012 The Android Open Source Project + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +package com.android.commands.settings; + +import android.app.ActivityManagerNative; +import android.app.IActivityManager; +import android.app.IActivityManager.ContentProviderHolder; +import android.content.IContentProvider; +import android.os.Binder; +import android.os.Bundle; +import android.os.IBinder; +import android.os.RemoteException; +import android.os.UserHandle; +import android.provider.Settings; + +public final class SettingsCmd { + static final String TAG = "settings"; + + enum CommandVerb { + UNSPECIFIED, + GET, + PUT + } + + static String[] mArgs; + int mNextArg; + int mUser = -1; // unspecified + CommandVerb mVerb = CommandVerb.UNSPECIFIED; + String mTable = null; + String mKey = null; + String mValue = null; + + public static void main(String[] args) { + if (args == null || args.length < 3) { + printUsage(); + return; + } + + mArgs = args; + try { + new SettingsCmd().run(); + } catch (Exception e) { + System.err.println("Unable to run settings command"); + } + } + + public void run() { + boolean valid = false; + String arg; + try { + while ((arg = nextArg()) != null) { + if ("--user".equals(arg)) { + if (mUser != -1) { + // --user specified more than once; invalid + break; + } + mUser = Integer.parseInt(nextArg()); + } else if (mVerb == CommandVerb.UNSPECIFIED) { + if ("get".equalsIgnoreCase(arg)) { + mVerb = CommandVerb.GET; + } else if ("put".equalsIgnoreCase(arg)) { + mVerb = CommandVerb.PUT; + } else { + // invalid + System.err.println("Invalid command: " + arg); + break; + } + } else if (mTable == null) { + if (!"system".equalsIgnoreCase(arg) + && !"secure".equalsIgnoreCase(arg) + && !"global".equalsIgnoreCase(arg)) { + System.err.println("Invalid namespace '" + arg + "'"); + break; // invalid + } + mTable = arg.toLowerCase(); + } else if (mVerb == CommandVerb.GET) { + mKey = arg; + if (mNextArg >= mArgs.length) { + valid = true; + } else { + System.err.println("Too many arguments"); + } + break; + } else if (mKey == null) { + mKey = arg; + // keep going; there's another PUT arg + } else { // PUT, final arg + mValue = arg; + if (mNextArg >= mArgs.length) { + valid = true; + } else { + System.err.println("Too many arguments"); + } + break; + } + } + } catch (Exception e) { + valid = false; + } + + if (valid) { + if (mUser < 0) { + mUser = UserHandle.USER_OWNER; + } + + try { + IActivityManager activityManager = ActivityManagerNative.getDefault(); + IContentProvider provider = null; + IBinder token = new Binder(); + try { + ContentProviderHolder holder = activityManager.getContentProviderExternal( + "settings", UserHandle.USER_OWNER, token); + if (holder == null) { + throw new IllegalStateException("Could not find settings provider"); + } + provider = holder.provider; + + switch (mVerb) { + case GET: + System.out.println(getForUser(provider, mUser, mTable, mKey)); + break; + case PUT: + putForUser(provider, mUser, mTable, mKey, mValue); + break; + default: + System.err.println("Unspecified command"); + break; + } + + } finally { + if (provider != null) { + activityManager.removeContentProviderExternal("settings", token); + } + } + } catch (Exception e) { + System.err.println("Error while accessing settings provider"); + e.printStackTrace(); + } + + } else { + printUsage(); + } + } + + private String nextArg() { + if (mNextArg >= mArgs.length) { + return null; + } + String arg = mArgs[mNextArg]; + mNextArg++; + return arg; + } + + String getForUser(IContentProvider provider, int userHandle, + final String table, final String key) { + final String callGetCommand; + if ("system".equals(table)) callGetCommand = Settings.CALL_METHOD_GET_SYSTEM; + else if ("secure".equals(table)) callGetCommand = Settings.CALL_METHOD_GET_SECURE; + else if ("global".equals(table)) callGetCommand = Settings.CALL_METHOD_GET_GLOBAL; + else { + System.err.println("Invalid table; no put performed"); + throw new IllegalArgumentException("Invalid table " + table); + } + + String result = null; + try { + Bundle arg = new Bundle(); + arg.putInt(Settings.CALL_METHOD_USER_KEY, userHandle); + Bundle b = provider.call(callGetCommand, key, arg); + if (b != null) { + result = b.getPairValue(); + } + } catch (RemoteException e) { + System.err.println("Can't read key " + key + " in " + table + " for user " + userHandle); + } + return result; + } + + void putForUser(IContentProvider provider, int userHandle, + final String table, final String key, final String value) { + final String callPutCommand; + if ("system".equals(table)) callPutCommand = Settings.CALL_METHOD_PUT_SYSTEM; + else if ("secure".equals(table)) callPutCommand = Settings.CALL_METHOD_PUT_SECURE; + else if ("global".equals(table)) callPutCommand = Settings.CALL_METHOD_PUT_GLOBAL; + else { + System.err.println("Invalid table; no put performed"); + return; + } + + try { + Bundle arg = new Bundle(); + arg.putString(Settings.NameValueTable.VALUE, value); + arg.putInt(Settings.CALL_METHOD_USER_KEY, userHandle); + provider.call(callPutCommand, key, arg); + } catch (RemoteException e) { + System.err.println("Can't set key " + key + " in " + table + " for user " + userHandle); + } + } + + private static void printUsage() { + System.err.println("usage: settings [--user NUM] get namespace key"); + System.err.println(" settings [--user NUM] put namespace key value"); + System.err.println("\n'namespace' is one of {system, secure, global}, case-insensitive"); + System.err.println("If '--user NUM' is not given, the operations are performed on the owner user."); + } +} 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..e0e2995 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); } }; @@ -214,20 +219,13 @@ public class SyncManager implements OnAccountsUpdateListener { // Use this as a random offset to seed all periodic syncs private int mSyncRandomOffsetMillis; - private UserManager mUserManager; + private final UserManager mUserManager; private static final long SYNC_ALARM_TIMEOUT_MIN = 30 * 1000; // 30 seconds private static final long SYNC_ALARM_TIMEOUT_MAX = 2 * 60 * 60 * 1000; // two hours - private UserManager getUserManager() { - if (mUserManager == null) { - mUserManager = (UserManager) mContext.getSystemService(Context.USER_SERVICE); - } - return mUserManager; - } - private List<UserInfo> getAllUsers() { - return getUserManager().getUsers(); + return mUserManager.getUsers(); } private boolean containsAccountAndUser(AccountAndUser[] accounts, Account account, int userId) { @@ -242,33 +240,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 +256,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 +295,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); } } }; @@ -372,6 +330,7 @@ public class SyncManager implements OnAccountsUpdateListener { // Initialize the SyncStorageEngine first, before registering observers // and creating threads and so on; it may fail if the disk is full. mContext = context; + SyncStorageEngine.init(context); mSyncStorageEngine = SyncStorageEngine.getSingleton(); mSyncStorageEngine.setOnSyncRequestListener(new OnSyncRequestListener() { @@ -390,7 +349,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 +382,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); @@ -435,6 +396,7 @@ public class SyncManager implements OnAccountsUpdateListener { mNotificationMgr = null; } mPowerManager = (PowerManager) context.getSystemService(Context.POWER_SERVICE); + mUserManager = (UserManager) mContext.getSystemService(Context.USER_SERVICE); // This WakeLock is used to ensure that we stay awake between the time that we receive // a sync alarm notification and when we finish processing it. We need to do this @@ -467,8 +429,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 +511,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 +542,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 +645,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 +883,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 removeUser(int userId) { + private void onUserStopping(int userId) { + updateRunningAccounts(); + + cancelActiveSync( + null /* any account */, + userId, + null /* any authority */); + } + + 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 +1051,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 +1070,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 +1138,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 +1515,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 +1615,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 +1808,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 +1863,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 +1922,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 +1993,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 +2025,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 +2162,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/hardware/Camera.java b/core/java/android/hardware/Camera.java index 1e8671b..6624eb8 100644 --- a/core/java/android/hardware/Camera.java +++ b/core/java/android/hardware/Camera.java @@ -18,13 +18,18 @@ package android.hardware; import android.annotation.SdkConstant; import android.annotation.SdkConstant.SdkConstantType; +import android.content.Context; import android.graphics.ImageFormat; import android.graphics.Point; import android.graphics.Rect; import android.graphics.SurfaceTexture; +import android.media.IAudioService; import android.os.Handler; +import android.os.IBinder; import android.os.Looper; import android.os.Message; +import android.os.RemoteException; +import android.os.ServiceManager; import android.util.Log; import android.text.TextUtils; import android.view.Surface; @@ -192,7 +197,21 @@ public class Camera { * Returns the information about a particular camera. * If {@link #getNumberOfCameras()} returns N, the valid id is 0 to N-1. */ - public native static void getCameraInfo(int cameraId, CameraInfo cameraInfo); + public static void getCameraInfo(int cameraId, CameraInfo cameraInfo) { + _getCameraInfo(cameraId, cameraInfo); + IBinder b = ServiceManager.getService(Context.AUDIO_SERVICE); + IAudioService audioService = IAudioService.Stub.asInterface(b); + try { + if (audioService.isCameraSoundForced()) { + // Only set this when sound is forced; otherwise let native code + // decide. + cameraInfo.canDisableShutterSound = false; + } + } catch (RemoteException e) { + Log.e(TAG, "Audio service is unavailable for queries"); + } + } + private native static void _getCameraInfo(int cameraId, CameraInfo cameraInfo); /** * Information about a camera @@ -1185,7 +1204,20 @@ public class Camera { * @see CameraInfo#canDisableShutterSound * @see ShutterCallback */ - public native final boolean enableShutterSound(boolean enabled); + public final boolean enableShutterSound(boolean enabled) { + if (!enabled) { + IBinder b = ServiceManager.getService(Context.AUDIO_SERVICE); + IAudioService audioService = IAudioService.Stub.asInterface(b); + try { + if (audioService.isCameraSoundForced()) return false; + } catch (RemoteException e) { + Log.e(TAG, "Audio service is unavailable for queries"); + } + } + return _enableShutterSound(enabled); + } + + private native final boolean _enableShutterSound(boolean enabled); /** * Callback interface for zoom changes during a smooth zoom operation. 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 3bbdf36..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, @@ -4052,7 +4058,20 @@ public final class Settings { * @return true if the provider is enabled */ public static final boolean isLocationProviderEnabled(ContentResolver cr, String provider) { - String allowedProviders = Settings.Secure.getString(cr, LOCATION_PROVIDERS_ALLOWED); + return isLocationProviderEnabledForUser(cr, provider, UserHandle.myUserId()); + } + + /** + * Helper method for determining if a location provider is enabled. + * @param cr the content resolver to use + * @param provider the location provider to query + * @param userId the userId to query + * @return true if the provider is enabled + * @hide + */ + public static final boolean isLocationProviderEnabledForUser(ContentResolver cr, String provider, int userId) { + String allowedProviders = Settings.Secure.getStringForUser(cr, + LOCATION_PROVIDERS_ALLOWED, userId); return TextUtils.delimitedStringContains(allowedProviders, ',', provider); } @@ -4064,6 +4083,19 @@ public final class Settings { */ public static final void setLocationProviderEnabled(ContentResolver cr, String provider, boolean enabled) { + setLocationProviderEnabledForUser(cr, provider, enabled, UserHandle.myUserId()); + } + + /** + * Thread-safe method for enabling or disabling a single location provider. + * @param cr the content resolver to use + * @param provider the location provider to enable or disable + * @param enabled true if the provider should be enabled + * @param userId the userId for which to enable/disable providers + * @hide + */ + public static final void setLocationProviderEnabledForUser(ContentResolver cr, + String provider, boolean enabled, int userId) { // to ensure thread safety, we write the provider name with a '+' or '-' // and let the SettingsProvider handle it rather than reading and modifying // the list of enabled providers. @@ -4072,7 +4104,8 @@ public final class Settings { } else { provider = "-" + provider; } - putString(cr, Settings.Secure.LOCATION_PROVIDERS_ALLOWED, provider); + putStringForUser(cr, Settings.Secure.LOCATION_PROVIDERS_ALLOWED, provider, + userId); } } @@ -5294,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 bdb86d9..c72b714 100644 --- a/core/java/android/service/dreams/DreamService.java +++ b/core/java/android/service/dreams/DreamService.java @@ -121,7 +121,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/ScaleGestureDetector.java b/core/java/android/view/ScaleGestureDetector.java index a74e438..ee3f5d8 100644 --- a/core/java/android/view/ScaleGestureDetector.java +++ b/core/java/android/view/ScaleGestureDetector.java @@ -17,6 +17,7 @@ package android.view; import android.content.Context; +import android.content.res.Resources; import android.os.SystemClock; import android.util.FloatMath; @@ -162,9 +163,11 @@ public class ScaleGestureDetector { mContext = context; mListener = listener; mSpanSlop = ViewConfiguration.get(context).getScaledTouchSlop() * 2; - mTouchMinMajor = - (int) (context.getResources().getDisplayMetrics().density * TOUCH_MIN_MAJOR + 0.5f); - mMinSpan = context.getResources().getDimensionPixelSize( + + final Resources res = context.getResources(); + mTouchMinMajor = res.getDimensionPixelSize( + com.android.internal.R.dimen.config_minScalingTouchMajor); + mMinSpan = res.getDimensionPixelSize( com.android.internal.R.dimen.config_minScalingSpan); } diff --git a/core/java/android/view/Surface.java b/core/java/android/view/Surface.java index 8f4626f..07bb8f9 100644 --- a/core/java/android/view/Surface.java +++ b/core/java/android/view/Surface.java @@ -266,6 +266,8 @@ public class Surface implements Parcelable { IBinder displayToken, int orientation, Rect layerStackRect, Rect displayRect); private static native boolean nativeGetDisplayInfo( IBinder displayToken, PhysicalDisplayInfo outInfo); + private static native void nativeBlankDisplay(IBinder displayToken); + private static native void nativeUnblankDisplay(IBinder displayToken); private native void nativeCopyFrom(Surface other); private native void nativeTransferFrom(Surface other); @@ -638,6 +640,22 @@ public class Surface implements Parcelable { return nativeGetDisplayInfo(displayToken, outInfo); } + /** @hide */ + public static void blankDisplay(IBinder displayToken) { + if (displayToken == null) { + throw new IllegalArgumentException("displayToken must not be null"); + } + nativeBlankDisplay(displayToken); + } + + /** @hide */ + public static void unblankDisplay(IBinder displayToken) { + if (displayToken == null) { + throw new IllegalArgumentException("displayToken must not be null"); + } + nativeUnblankDisplay(displayToken); + } + /** * Copy another surface to this one. This surface now holds a reference * to the same data as the original surface, and is -not- the owner. diff --git a/core/java/android/view/View.java b/core/java/android/view/View.java index ae51c1d..22243b1 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: { @@ -9998,7 +10000,7 @@ public class View implements Drawable.Callback, KeyEvent.Callback, */ private void resolveLayoutParams() { if (mLayoutParams != null) { - mLayoutParams.onResolveLayoutDirection(getLayoutDirection()); + mLayoutParams.resolveLayoutDirection(getLayoutDirection()); } } diff --git a/core/java/android/view/ViewGroup.java b/core/java/android/view/ViewGroup.java index db1c00a..6436059 100644 --- a/core/java/android/view/ViewGroup.java +++ b/core/java/android/view/ViewGroup.java @@ -5542,7 +5542,7 @@ public abstract class ViewGroup extends View implements ViewParent, ViewManager * {@link View#LAYOUT_DIRECTION_LTR} * {@link View#LAYOUT_DIRECTION_RTL} */ - public void onResolveLayoutDirection(int layoutDirection) { + public void resolveLayoutDirection(int layoutDirection) { } /** @@ -5886,7 +5886,7 @@ public abstract class ViewGroup extends View implements ViewParent, ViewManager * may be overridden depending on layout direction. */ @Override - public void onResolveLayoutDirection(int layoutDirection) { + public void resolveLayoutDirection(int layoutDirection) { setLayoutDirection(layoutDirection); if (!isMarginRelative()) return; diff --git a/core/java/android/webkit/AccessibilityInjector.java b/core/java/android/webkit/AccessibilityInjector.java index a51a8f6..d6576e1 100644 --- a/core/java/android/webkit/AccessibilityInjector.java +++ b/core/java/android/webkit/AccessibilityInjector.java @@ -21,6 +21,10 @@ import android.os.Bundle; import android.os.SystemClock; import android.provider.Settings; import android.speech.tts.TextToSpeech; +import android.speech.tts.TextToSpeech.Engine; +import android.speech.tts.TextToSpeech.OnInitListener; +import android.speech.tts.UtteranceProgressListener; +import android.util.Log; import android.view.KeyEvent; import android.view.View; import android.view.accessibility.AccessibilityManager; @@ -44,6 +48,10 @@ import java.util.concurrent.atomic.AtomicInteger; * APIs. */ class AccessibilityInjector { + private static final String TAG = AccessibilityInjector.class.getSimpleName(); + + private static boolean DEBUG = false; + // The WebViewClassic this injector is responsible for managing. private final WebViewClassic mWebViewClassic; @@ -90,6 +98,10 @@ class AccessibilityInjector { private static final String ACCESSIBILITY_ANDROIDVOX_TEMPLATE = "cvox.AndroidVox.performAction('%1s')"; + // JS code used to shut down an active AndroidVox instance. + private static final String TOGGLE_CVOX_TEMPLATE = + "javascript:(function() { cvox.ChromeVox.host.activateOrDeactivateChromeVox(%b); })();"; + /** * Creates an instance of the AccessibilityInjector based on * {@code webViewClassic}. @@ -117,6 +129,7 @@ class AccessibilityInjector { addTtsApis(); addCallbackApis(); + toggleAndroidVox(true); } /** @@ -126,10 +139,20 @@ class AccessibilityInjector { * </p> */ public void removeAccessibilityApisIfNecessary() { + toggleAndroidVox(false); removeTtsApis(); removeCallbackApis(); } + private void toggleAndroidVox(boolean state) { + if (!mAccessibilityScriptInjected) { + return; + } + + final String code = String.format(TOGGLE_CVOX_TEMPLATE, state); + mWebView.loadUrl(code); + } + /** * Initializes an {@link AccessibilityNodeInfo} with the actions and * movement granularity levels supported by this @@ -196,7 +219,7 @@ class AccessibilityInjector { if (mAccessibilityScriptInjected) { return sendActionToAndroidVox(action, arguments); } - + if (mAccessibilityInjectorFallback != null) { return mAccessibilityInjectorFallback.performAccessibilityAction(action, arguments); } @@ -262,6 +285,9 @@ class AccessibilityInjector { */ public void onPageStarted(String url) { mAccessibilityScriptInjected = false; + if (DEBUG) + Log.w(TAG, "[" + mWebView.hashCode() + "] Started loading new page"); + addAccessibilityApisIfNecessary(); } /** @@ -282,15 +308,23 @@ class AccessibilityInjector { if (!shouldInjectJavaScript(url)) { mAccessibilityScriptInjected = false; toggleFallbackAccessibilityInjector(true); + if (DEBUG) + Log.d(TAG, "[" + mWebView.hashCode() + "] Using fallback accessibility support"); return; } toggleFallbackAccessibilityInjector(false); - final String injectionUrl = getScreenReaderInjectionUrl(); - mWebView.loadUrl(injectionUrl); - - mAccessibilityScriptInjected = true; + if (!mAccessibilityScriptInjected) { + mAccessibilityScriptInjected = true; + final String injectionUrl = getScreenReaderInjectionUrl(); + mWebView.loadUrl(injectionUrl); + if (DEBUG) + Log.d(TAG, "[" + mWebView.hashCode() + "] Loading screen reader into WebView"); + } else { + if (DEBUG) + Log.w(TAG, "[" + mWebView.hashCode() + "] Attempted to inject screen reader twice"); + } } /** @@ -368,6 +402,8 @@ class AccessibilityInjector { if (mTextToSpeech != null) { return; } + if (DEBUG) + Log.d(TAG, "[" + mWebView.hashCode() + "] Adding TTS APIs into WebView"); mTextToSpeech = new TextToSpeechWrapper(mContext); mWebView.addJavascriptInterface(mTextToSpeech, ALIAS_TTS_JS_INTERFACE); } @@ -381,6 +417,8 @@ class AccessibilityInjector { return; } + if (DEBUG) + Log.d(TAG, "[" + mWebView.hashCode() + "] Removing TTS APIs from WebView"); mWebView.removeJavascriptInterface(ALIAS_TTS_JS_INTERFACE); mTextToSpeech.stop(); mTextToSpeech.shutdown(); @@ -527,35 +565,141 @@ class AccessibilityInjector { * Used to protect the TextToSpeech class, only exposing the methods we want to expose. */ private static class TextToSpeechWrapper { - private TextToSpeech mTextToSpeech; + private static final String WRAP_TAG = TextToSpeechWrapper.class.getSimpleName(); + + private final HashMap<String, String> mTtsParams; + private final TextToSpeech mTextToSpeech; + + /** + * Whether this wrapper is ready to speak. If this is {@code true} then + * {@link #mShutdown} is guaranteed to be {@code false}. + */ + private volatile boolean mReady; + + /** + * Whether this wrapper was shut down. If this is {@code true} then + * {@link #mReady} is guaranteed to be {@code false}. + */ + private volatile boolean mShutdown; public TextToSpeechWrapper(Context context) { + if (DEBUG) + Log.d(WRAP_TAG, "[" + hashCode() + "] Initializing text-to-speech on thread " + + Thread.currentThread().getId() + "..."); + final String pkgName = context.getPackageName(); - mTextToSpeech = new TextToSpeech(context, null, null, pkgName + ".**webview**", true); + + mReady = false; + mShutdown = false; + + mTtsParams = new HashMap<String, String>(); + mTtsParams.put(Engine.KEY_PARAM_UTTERANCE_ID, WRAP_TAG); + + mTextToSpeech = new TextToSpeech( + context, mInitListener, null, pkgName + ".**webview**", true); + mTextToSpeech.setOnUtteranceProgressListener(mErrorListener); } @JavascriptInterface @SuppressWarnings("unused") public boolean isSpeaking() { - return mTextToSpeech.isSpeaking(); + synchronized (mTextToSpeech) { + if (!mReady) { + return false; + } + + return mTextToSpeech.isSpeaking(); + } } @JavascriptInterface @SuppressWarnings("unused") public int speak(String text, int queueMode, HashMap<String, String> params) { - return mTextToSpeech.speak(text, queueMode, params); + synchronized (mTextToSpeech) { + if (!mReady) { + if (DEBUG) + Log.w(WRAP_TAG, "[" + hashCode() + "] Attempted to speak before TTS init"); + return TextToSpeech.ERROR; + } else { + if (DEBUG) + Log.i(WRAP_TAG, "[" + hashCode() + "] Speak called from JS binder"); + } + + return mTextToSpeech.speak(text, queueMode, params); + } } @JavascriptInterface @SuppressWarnings("unused") public int stop() { - return mTextToSpeech.stop(); + synchronized (mTextToSpeech) { + if (!mReady) { + if (DEBUG) + Log.w(WRAP_TAG, "[" + hashCode() + "] Attempted to stop before initialize"); + return TextToSpeech.ERROR; + } else { + if (DEBUG) + Log.i(WRAP_TAG, "[" + hashCode() + "] Stop called from JS binder"); + } + + return mTextToSpeech.stop(); + } } @SuppressWarnings("unused") protected void shutdown() { - mTextToSpeech.shutdown(); + synchronized (mTextToSpeech) { + if (!mReady) { + if (DEBUG) + Log.w(WRAP_TAG, "[" + hashCode() + "] Called shutdown before initialize"); + } else { + if (DEBUG) + Log.i(WRAP_TAG, "[" + hashCode() + "] Shutting down text-to-speech from " + + "thread " + Thread.currentThread().getId() + "..."); + } + mShutdown = true; + mReady = false; + mTextToSpeech.shutdown(); + } } + + private final OnInitListener mInitListener = new OnInitListener() { + @Override + public void onInit(int status) { + synchronized (mTextToSpeech) { + if (!mShutdown && (status == TextToSpeech.SUCCESS)) { + if (DEBUG) + Log.d(WRAP_TAG, "[" + TextToSpeechWrapper.this.hashCode() + + "] Initialized successfully"); + mReady = true; + } else { + if (DEBUG) + Log.w(WRAP_TAG, "[" + TextToSpeechWrapper.this.hashCode() + + "] Failed to initialize"); + mReady = false; + } + } + } + }; + + private final UtteranceProgressListener mErrorListener = new UtteranceProgressListener() { + @Override + public void onStart(String utteranceId) { + // Do nothing. + } + + @Override + public void onError(String utteranceId) { + if (DEBUG) + Log.w(WRAP_TAG, "[" + TextToSpeechWrapper.this.hashCode() + + "] Failed to speak utterance"); + } + + @Override + public void onDone(String utteranceId) { + // Do nothing. + } + }; } /** @@ -625,6 +769,8 @@ class AccessibilityInjector { * @return Whether the result was received. */ private boolean waitForResultTimedLocked(int resultId) { + if (DEBUG) + Log.d(TAG, "Waiting for CVOX result..."); long waitTimeMillis = RESULT_TIMEOUT; final long startTimeMillis = SystemClock.uptimeMillis(); while (true) { @@ -642,6 +788,8 @@ class AccessibilityInjector { } mResultLock.wait(waitTimeMillis); } catch (InterruptedException ie) { + if (DEBUG) + Log.w(TAG, "Timed out while waiting for CVOX result"); /* ignore */ } } diff --git a/core/java/android/widget/LinearLayout.java b/core/java/android/widget/LinearLayout.java index b6f0862..83c15bb 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.resolveLayoutDirection(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.resolveLayoutDirection(layoutDirection); totalWeight += lp.weight; diff --git a/core/java/android/widget/RelativeLayout.java b/core/java/android/widget/RelativeLayout.java index 455355f..4ca405b 100644 --- a/core/java/android/widget/RelativeLayout.java +++ b/core/java/android/widget/RelativeLayout.java @@ -729,7 +729,7 @@ public class RelativeLayout extends ViewGroup { final int layoutDirection = getLayoutDirection(); int[] rules = params.getRules(layoutDirection); - params.onResolveLayoutDirection(layoutDirection); + params.resolveLayoutDirection(layoutDirection); if (params.mLeft < 0 && params.mRight >= 0) { // Right is fixed, but left varies @@ -983,7 +983,7 @@ public class RelativeLayout extends ViewGroup { if (child.getVisibility() != GONE) { RelativeLayout.LayoutParams st = (RelativeLayout.LayoutParams) child.getLayoutParams(); - st.onResolveLayoutDirection(getLayoutDirection()); + st.resolveLayoutDirection(getLayoutDirection()); child.layout(st.mLeft, st.mTop, st.mRight, st.mBottom); } } @@ -1393,7 +1393,7 @@ public class RelativeLayout extends ViewGroup { } @Override - public void onResolveLayoutDirection(int layoutDirection) { + public void resolveLayoutDirection(int layoutDirection) { final boolean isLayoutRtl = isLayoutRtl(); if (isLayoutRtl) { if (mStart != DEFAULT_RELATIVE) mRight = mStart; @@ -1407,7 +1407,7 @@ public class RelativeLayout extends ViewGroup { resolveRules(layoutDirection); } // This will set the layout direction - super.onResolveLayoutDirection(layoutDirection); + super.resolveLayoutDirection(layoutDirection); } } 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/ActionBarView.java b/core/java/com/android/internal/widget/ActionBarView.java index 991c699..5a7f10c 100644 --- a/core/java/com/android/internal/widget/ActionBarView.java +++ b/core/java/com/android/internal/widget/ActionBarView.java @@ -1098,7 +1098,7 @@ public class ActionBarView extends AbsActionBarView { if (customView != null) { final int resolvedLayoutDirection = getLayoutDirection(); ViewGroup.LayoutParams lp = customView.getLayoutParams(); - lp.onResolveLayoutDirection(resolvedLayoutDirection); + lp.resolveLayoutDirection(resolvedLayoutDirection); final ActionBar.LayoutParams ablp = lp instanceof ActionBar.LayoutParams ? (ActionBar.LayoutParams) lp : null; final int gravity = ablp != null ? ablp.gravity : DEFAULT_CUSTOM_GRAVITY; @@ -1385,7 +1385,7 @@ public class ActionBarView extends AbsActionBarView { int upOffset = 0; if (mUpView.getVisibility() != GONE) { final LayoutParams upLp = (LayoutParams) mUpView.getLayoutParams(); - upLp.onResolveLayoutDirection(layoutDirection); + upLp.resolveLayoutDirection(layoutDirection); final int upHeight = mUpView.getMeasuredHeight(); final int upWidth = mUpView.getMeasuredWidth(); upOffset = upLp.leftMargin + upWidth + upLp.rightMargin; @@ -1406,7 +1406,7 @@ public class ActionBarView extends AbsActionBarView { } final LayoutParams iconLp = (LayoutParams) mIconView.getLayoutParams(); - iconLp.onResolveLayoutDirection(layoutDirection); + iconLp.resolveLayoutDirection(layoutDirection); final int iconHeight = mIconView.getMeasuredHeight(); final int iconWidth = mIconView.getMeasuredWidth(); final int hCenter = (r - l) / 2; 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/jni/android_hardware_Camera.cpp b/core/jni/android_hardware_Camera.cpp index 99d49ec..67d831c 100644 --- a/core/jni/android_hardware_Camera.cpp +++ b/core/jni/android_hardware_Camera.cpp @@ -854,7 +854,7 @@ static JNINativeMethod camMethods[] = { { "getNumberOfCameras", "()I", (void *)android_hardware_Camera_getNumberOfCameras }, - { "getCameraInfo", + { "_getCameraInfo", "(ILandroid/hardware/Camera$CameraInfo;)V", (void*)android_hardware_Camera_getCameraInfo }, { "native_setup", @@ -917,7 +917,7 @@ static JNINativeMethod camMethods[] = { { "setDisplayOrientation", "(I)V", (void *)android_hardware_Camera_setDisplayOrientation }, - { "enableShutterSound", + { "_enableShutterSound", "(Z)Z", (void *)android_hardware_Camera_enableShutterSound }, { "_startFaceDetection", diff --git a/core/jni/android_media_AudioSystem.cpp b/core/jni/android_media_AudioSystem.cpp index 56db116..631cdae 100644 --- a/core/jni/android_media_AudioSystem.cpp +++ b/core/jni/android_media_AudioSystem.cpp @@ -75,6 +75,14 @@ android_media_AudioSystem_isStreamActive(JNIEnv *env, jobject thiz, jint stream, return state; } +static jboolean +android_media_AudioSystem_isSourceActive(JNIEnv *env, jobject thiz, jint source) +{ + bool state = false; + AudioSystem::isSourceActive((audio_source_t) source, &state); + return state; +} + static int android_media_AudioSystem_setParameters(JNIEnv *env, jobject thiz, jstring keyValuePairs) { @@ -261,7 +269,8 @@ static JNINativeMethod gMethods[] = { {"getParameters", "(Ljava/lang/String;)Ljava/lang/String;", (void *)android_media_AudioSystem_getParameters}, {"muteMicrophone", "(Z)I", (void *)android_media_AudioSystem_muteMicrophone}, {"isMicrophoneMuted", "()Z", (void *)android_media_AudioSystem_isMicrophoneMuted}, - {"isStreamActive", "(II)Z", (void *)android_media_AudioSystem_isStreamActive}, + {"isStreamActive", "(II)Z", (void *)android_media_AudioSystem_isStreamActive}, + {"isSourceActive", "(I)Z", (void *)android_media_AudioSystem_isSourceActive}, {"setDeviceConnectionState", "(IILjava/lang/String;)I", (void *)android_media_AudioSystem_setDeviceConnectionState}, {"getDeviceConnectionState", "(ILjava/lang/String;)I", (void *)android_media_AudioSystem_getDeviceConnectionState}, {"setPhoneState", "(I)I", (void *)android_media_AudioSystem_setPhoneState}, diff --git a/core/jni/android_view_Surface.cpp b/core/jni/android_view_Surface.cpp index fc04cd1..4982f31 100644 --- a/core/jni/android_view_Surface.cpp +++ b/core/jni/android_view_Surface.cpp @@ -48,6 +48,7 @@ #include <android_runtime/android_view_SurfaceSession.h> #include <android_runtime/android_graphics_SurfaceTexture.h> #include <utils/misc.h> +#include <utils/Log.h> #include <ScopedUtfChars.h> @@ -710,6 +711,22 @@ static jboolean nativeGetDisplayInfo(JNIEnv* env, jclass clazz, return JNI_TRUE; } +static void nativeBlankDisplay(JNIEnv* env, jclass clazz, jobject tokenObj) { + sp<IBinder> token(ibinderForJavaObject(env, tokenObj)); + if (token == NULL) return; + + ALOGD_IF_SLOW(100, "Excessive delay in blankDisplay() while turning screen off"); + SurfaceComposerClient::blankDisplay(token); +} + +static void nativeUnblankDisplay(JNIEnv* env, jclass clazz, jobject tokenObj) { + sp<IBinder> token(ibinderForJavaObject(env, tokenObj)); + if (token == NULL) return; + + ALOGD_IF_SLOW(100, "Excessive delay in unblankDisplay() while turning screen on"); + SurfaceComposerClient::unblankDisplay(token); +} + // ---------------------------------------------------------------------------- static void nativeCopyFrom(JNIEnv* env, jobject surfaceObj, jobject otherObj) { @@ -832,6 +849,10 @@ static JNINativeMethod gSurfaceMethods[] = { (void*)nativeSetDisplayProjection }, {"nativeGetDisplayInfo", "(Landroid/os/IBinder;Landroid/view/Surface$PhysicalDisplayInfo;)Z", (void*)nativeGetDisplayInfo }, + {"nativeBlankDisplay", "(Landroid/os/IBinder;)V", + (void*)nativeBlankDisplay }, + {"nativeUnblankDisplay", "(Landroid/os/IBinder;)V", + (void*)nativeUnblankDisplay }, {"nativeCopyFrom", "(Landroid/view/Surface;)V", (void*)nativeCopyFrom }, {"nativeTransferFrom", "(Landroid/view/Surface;)V", diff --git a/core/res/res/values-af/strings.xml b/core/res/res/values-af/strings.xml index 987d794..61b1148 100644 --- a/core/res/res/values-af/strings.xml +++ b/core/res/res/values-af/strings.xml @@ -1407,7 +1407,7 @@ <string name="kg_login_submit_button" msgid="5355904582674054702">"Meld aan"</string> <string name="kg_login_invalid_input" msgid="5754664119319872197">"Ongeldige gebruikernaam of wagwoord."</string> <string name="kg_login_account_recovery_hint" msgid="5690709132841752974">"Het jy jou gebruikernaam of wagwoord vergeet?"\n"Besoek "<b>"google.com/accounts/recovery"</b>"."</string> - <string name="kg_login_checking_password" msgid="5316091912653672681">"Ontsluit tans SIM..."</string> + <string name="kg_login_checking_password" msgid="1052685197710252395">"Kontroleer tans rekening..."</string> <string name="kg_too_many_failed_pin_attempts_dialog_message" msgid="8276745642049502550">"Jy het jou PIN <xliff:g id="NUMBER_0">%d</xliff:g> keer verkeerd ingetik. "\n\n"Probeer weer oor <xliff:g id="NUMBER_1">%d</xliff:g> sekondes."</string> <string name="kg_too_many_failed_password_attempts_dialog_message" msgid="7813713389422226531">"Jy het <xliff:g id="NUMBER_0">%d</xliff:g> keer jou wagwoord verkeerdelik getik. "\n\n"Probeer weer oor <xliff:g id="NUMBER_1">%d</xliff:g> sekondes."</string> <string name="kg_too_many_failed_pattern_attempts_dialog_message" msgid="74089475965050805">"Jy het jou ontsluitpatroon <xliff:g id="NUMBER_0">%d</xliff:g> keer verkeerdelik geteken. "\n\n"Probeer weer oor <xliff:g id="NUMBER_1">%d</xliff:g> sekondes."</string> diff --git a/core/res/res/values-am/strings.xml b/core/res/res/values-am/strings.xml index f9b7c57..214d819 100644 --- a/core/res/res/values-am/strings.xml +++ b/core/res/res/values-am/strings.xml @@ -1407,7 +1407,7 @@ <string name="kg_login_submit_button" msgid="5355904582674054702">"ግባ"</string> <string name="kg_login_invalid_input" msgid="5754664119319872197">"ልክ ያልሆነ የተጠቃሚ ስም ወይም የይለፍ ቃል።"</string> <string name="kg_login_account_recovery_hint" msgid="5690709132841752974">"የተጠቃሚ ስምዎን ወይም የይለፍ ቃልዎን ረሱት?"\n<b>"google.com/accounts/recovery"</b>"ይጎብኙ።"</string> - <string name="kg_login_checking_password" msgid="5316091912653672681">"ሲም በመክፈት ላይ…"</string> + <string name="kg_login_checking_password" msgid="1052685197710252395">"መለያውን በማረጋገጥ ላይ…"</string> <string name="kg_too_many_failed_pin_attempts_dialog_message" msgid="8276745642049502550">"ፒንዎን <xliff:g id="NUMBER_0">%d</xliff:g> ጊዜ በትክክል አልተየቡም። "\n\n"በ<xliff:g id="NUMBER_1">%d</xliff:g> ሰኮንዶች ውስጥ እንደገና ይሞክሩ።"</string> <string name="kg_too_many_failed_password_attempts_dialog_message" msgid="7813713389422226531">"የይለፍ ቃልዎን <xliff:g id="NUMBER_0">%d</xliff:g> ጊዜ ትክክል ባልሆነ መንገድ ተይበዋል።"\n\n"በ<xliff:g id="NUMBER_1">%d</xliff:g> ሰኮንዶች ውስጥ እንደገና ይሞክሩ።"</string> <string name="kg_too_many_failed_pattern_attempts_dialog_message" msgid="74089475965050805">"የመክፈቻ ስርዓተ ጥለትዎን <xliff:g id="NUMBER_0">%d</xliff:g> ጊዜ በትክክል አልሳሉትም። "\n\n" ከ<xliff:g id="NUMBER_1">%d</xliff:g> ሰከንዶች በኋላ እንደገና ይሞክሩ።"</string> diff --git a/core/res/res/values-ar/strings.xml b/core/res/res/values-ar/strings.xml index 6a17465..790acac 100644 --- a/core/res/res/values-ar/strings.xml +++ b/core/res/res/values-ar/strings.xml @@ -1407,7 +1407,7 @@ <string name="kg_login_submit_button" msgid="5355904582674054702">"تسجيل الدخول"</string> <string name="kg_login_invalid_input" msgid="5754664119319872197">"اسم مستخدم غير صحيح أو كلمة مرور غير صالحة."</string> <string name="kg_login_account_recovery_hint" msgid="5690709132841752974">"هل نسيت اسم المستخدم أو كلمة المرور؟"\n"انتقل إلى "<b>"google.com/accounts/recovery"</b>"."</string> - <string name="kg_login_checking_password" msgid="5316091912653672681">"جارٍ إلغاء قفل بطاقة SIM…"</string> + <string name="kg_login_checking_password" msgid="1052685197710252395">"جارٍ فحص الحساب…"</string> <string name="kg_too_many_failed_pin_attempts_dialog_message" msgid="8276745642049502550">"لقد كتبت رقم التعريف الشخصي بشكل غير صحيح <xliff:g id="NUMBER_0">%d</xliff:g> مرة. "\n\n"أعد المحاولة خلال <xliff:g id="NUMBER_1">%d</xliff:g> ثانية."</string> <string name="kg_too_many_failed_password_attempts_dialog_message" msgid="7813713389422226531">"لقد كتبت كلمة المرور بشكل غير صحيح <xliff:g id="NUMBER_0">%d</xliff:g> مرة. "\n\n"أعد المحاولة خلال <xliff:g id="NUMBER_1">%d</xliff:g> ثانية."</string> <string name="kg_too_many_failed_pattern_attempts_dialog_message" msgid="74089475965050805">"لقد رسمت نقش إلغاء التأمين بطريقة غير صحيحة <xliff:g id="NUMBER_0">%d</xliff:g> مرة. "\n\n"أعد المحاولة خلال <xliff:g id="NUMBER_1">%d</xliff:g> ثانية."</string> diff --git a/core/res/res/values-be/strings.xml b/core/res/res/values-be/strings.xml index 4705a33..4342bb8 100644 --- a/core/res/res/values-be/strings.xml +++ b/core/res/res/values-be/strings.xml @@ -1408,7 +1408,7 @@ <string name="kg_login_submit_button" msgid="5355904582674054702">"Увайсцi"</string> <string name="kg_login_invalid_input" msgid="5754664119319872197">"Няправільнае імя карыстальніка ці пароль."</string> <string name="kg_login_account_recovery_hint" msgid="5690709132841752974">"Забыліся на імя карыстальніка або пароль?"\n"Наведайце "<b>"google.com/accounts/recovery"</b></string> - <string name="kg_login_checking_password" msgid="5316091912653672681">"Разблакiроўка SIM-карты..."</string> + <string name="kg_login_checking_password" msgid="1052685197710252395">"Праверка ўлiковага запiсу..."</string> <string name="kg_too_many_failed_pin_attempts_dialog_message" msgid="8276745642049502550">"Вы няправільна ўвялі PIN-код пэўную колькасць разоў: <xliff:g id="NUMBER_0">%d</xliff:g>. "\n\n"Паўтарыце спробу праз <xliff:g id="NUMBER_1">%d</xliff:g> с."</string> <string name="kg_too_many_failed_password_attempts_dialog_message" msgid="7813713389422226531">"Вы няправільна ўвялі пароль пэўную колькасць разоў: <xliff:g id="NUMBER_0">%d</xliff:g>. "\n\n"Паўтарыце спробу праз <xliff:g id="NUMBER_1">%d</xliff:g> с."</string> <string name="kg_too_many_failed_pattern_attempts_dialog_message" msgid="74089475965050805">"Вы няправільна ўвялі графічны ключ разблакiроўкi пэўную колькасць разоў: <xliff:g id="NUMBER_0">%d</xliff:g>. "\n\n"Паўтарыце спробу праз <xliff:g id="NUMBER_1">%d</xliff:g> с."</string> diff --git a/core/res/res/values-bg/strings.xml b/core/res/res/values-bg/strings.xml index 235416e..e71363a 100644 --- a/core/res/res/values-bg/strings.xml +++ b/core/res/res/values-bg/strings.xml @@ -1407,7 +1407,7 @@ <string name="kg_login_submit_button" msgid="5355904582674054702">"Вход"</string> <string name="kg_login_invalid_input" msgid="5754664119319872197">"Невалидно потребителско име или парола."</string> <string name="kg_login_account_recovery_hint" msgid="5690709132841752974">"Забравили сте потребителското име или паролата си?"\n"Посетете "<b>"google.com/accounts/recovery"</b>"."</string> - <string name="kg_login_checking_password" msgid="5316091912653672681">"SIM картата се отключва…"</string> + <string name="kg_login_checking_password" msgid="1052685197710252395">"Профилът се проверява…"</string> <string name="kg_too_many_failed_pin_attempts_dialog_message" msgid="8276745642049502550">"Въведохте неправилно ПИН кода си <xliff:g id="NUMBER_0">%d</xliff:g> пъти. "\n\n"Опитайте отново след <xliff:g id="NUMBER_1">%d</xliff:g> секунди."</string> <string name="kg_too_many_failed_password_attempts_dialog_message" msgid="7813713389422226531">"Въведохте неправилно паролата си <xliff:g id="NUMBER_0">%d</xliff:g> пъти. "\n\n"Опитайте отново след <xliff:g id="NUMBER_1">%d</xliff:g> секунди."</string> <string name="kg_too_many_failed_pattern_attempts_dialog_message" msgid="74089475965050805">"Начертахте неправилно фигурата си за отключване <xliff:g id="NUMBER_0">%d</xliff:g> пъти. "\n\n"Опитайте отново след <xliff:g id="NUMBER_1">%d</xliff:g> секунди."</string> diff --git a/core/res/res/values-ca/strings.xml b/core/res/res/values-ca/strings.xml index 93778a9..aa2d431 100644 --- a/core/res/res/values-ca/strings.xml +++ b/core/res/res/values-ca/strings.xml @@ -1407,7 +1407,7 @@ <string name="kg_login_submit_button" msgid="5355904582674054702">"Inicia la sessió"</string> <string name="kg_login_invalid_input" msgid="5754664119319872197">"Nom d\'usuari o contrasenya no vàlids."</string> <string name="kg_login_account_recovery_hint" msgid="5690709132841752974">"Has oblidat el teu nom d\'usuari o la contrasenya?"\n"Visita "<b>"google.com/accounts/recovery"</b>"."</string> - <string name="kg_login_checking_password" msgid="5316091912653672681">"S\'està desbloquejant la targeta SIM…"</string> + <string name="kg_login_checking_password" msgid="1052685197710252395">"S\'està comprovant el compte…"</string> <string name="kg_too_many_failed_pin_attempts_dialog_message" msgid="8276745642049502550">"Has escrit malament el PIN <xliff:g id="NUMBER_0">%d</xliff:g> vegades. "\n\n"Torna-ho a provar d\'aquí a <xliff:g id="NUMBER_1">%d</xliff:g> segons."</string> <string name="kg_too_many_failed_password_attempts_dialog_message" msgid="7813713389422226531">"Has escrit malament la contrasenya <xliff:g id="NUMBER_0">%d</xliff:g> vegades. "\n\n"Torna-ho a provar d\'aquí a <xliff:g id="NUMBER_1">%d</xliff:g> segons."</string> <string name="kg_too_many_failed_pattern_attempts_dialog_message" msgid="74089475965050805">"Has dibuixat el patró de desbloqueig de manera incorrecta <xliff:g id="NUMBER_0">%d</xliff:g> vegades. "\n\n"Torna-ho a provar d\'aquí a <xliff:g id="NUMBER_1">%d</xliff:g> segons."</string> diff --git a/core/res/res/values-cs/strings.xml b/core/res/res/values-cs/strings.xml index f5e4299..b6517dc 100644 --- a/core/res/res/values-cs/strings.xml +++ b/core/res/res/values-cs/strings.xml @@ -1407,7 +1407,7 @@ <string name="kg_login_submit_button" msgid="5355904582674054702">"Přihlásit se"</string> <string name="kg_login_invalid_input" msgid="5754664119319872197">"Neplatné uživatelské jméno nebo heslo."</string> <string name="kg_login_account_recovery_hint" msgid="5690709132841752974">"Zapomněli jste uživatelské jméno nebo heslo?"\n"Přejděte na stránku "<b>"google.com/accounts/recovery"</b>"."</string> - <string name="kg_login_checking_password" msgid="5316091912653672681">"Odblokování SIM karty…"</string> + <string name="kg_login_checking_password" msgid="1052685197710252395">"Kontrola účtu…"</string> <string name="kg_too_many_failed_pin_attempts_dialog_message" msgid="8276745642049502550">"Již <xliff:g id="NUMBER_0">%d</xliff:g>krát jste zadali nesprávný kód PIN. "\n\n"Zkuste to znovu za <xliff:g id="NUMBER_1">%d</xliff:g> s."</string> <string name="kg_too_many_failed_password_attempts_dialog_message" msgid="7813713389422226531">"Již <xliff:g id="NUMBER_0">%d</xliff:g>krát jste nesprávně zadali heslo. "\n\n"Zkuste to znovu za <xliff:g id="NUMBER_1">%d</xliff:g> s."</string> <string name="kg_too_many_failed_pattern_attempts_dialog_message" msgid="74089475965050805">"Již <xliff:g id="NUMBER_0">%d</xliff:g>krát jste zadali nesprávné bezpečnostní gesto. "\n\n"Zkuste to znovu za <xliff:g id="NUMBER_1">%d</xliff:g> s."</string> diff --git a/core/res/res/values-da/strings.xml b/core/res/res/values-da/strings.xml index fcf0d5c..650fb72 100644 --- a/core/res/res/values-da/strings.xml +++ b/core/res/res/values-da/strings.xml @@ -1407,7 +1407,7 @@ <string name="kg_login_submit_button" msgid="5355904582674054702">"Log ind"</string> <string name="kg_login_invalid_input" msgid="5754664119319872197">"Ugyldigt brugernavn eller ugyldig adgangskode."</string> <string name="kg_login_account_recovery_hint" msgid="5690709132841752974">"Har du glemt dit brugernavn eller din adgangskode?"\n"Gå til "<b>"google.com/accounts/recovery"</b>"."</string> - <string name="kg_login_checking_password" msgid="5316091912653672681">"SIM-kortet låses op..."</string> + <string name="kg_login_checking_password" msgid="1052685197710252395">"Kontoen kontrolleres…"</string> <string name="kg_too_many_failed_pin_attempts_dialog_message" msgid="8276745642049502550">"Du har indtastet en forkert pinkode <xliff:g id="NUMBER_0">%d</xliff:g> gange. "\n\n"Prøv igen om <xliff:g id="NUMBER_1">%d</xliff:g> sekunder."</string> <string name="kg_too_many_failed_password_attempts_dialog_message" msgid="7813713389422226531">"Du har indtastet din adgangskode forkert <xliff:g id="NUMBER_0">%d</xliff:g> gange. "\n\n"Prøv igen om <xliff:g id="NUMBER_1">%d</xliff:g> sekunder."</string> <string name="kg_too_many_failed_pattern_attempts_dialog_message" msgid="74089475965050805">"Du har tegnet dit oplåsningsmønster forkert <xliff:g id="NUMBER_0">%d</xliff:g> gange. "\n\n"Prøv igen om <xliff:g id="NUMBER_1">%d</xliff:g> sekunder."</string> diff --git a/core/res/res/values-de/strings.xml b/core/res/res/values-de/strings.xml index a605506..9fbea68 100644 --- a/core/res/res/values-de/strings.xml +++ b/core/res/res/values-de/strings.xml @@ -1407,7 +1407,7 @@ <string name="kg_login_submit_button" msgid="5355904582674054702">"Anmelden"</string> <string name="kg_login_invalid_input" msgid="5754664119319872197">"Ungültiger Nutzername oder ungültiges Passwort"</string> <string name="kg_login_account_recovery_hint" msgid="5690709132841752974">"Nutzernamen oder Passwort vergessen?"\n"Besuchen Sie "<b>"google.com/accounts/recovery"</b>"."</string> - <string name="kg_login_checking_password" msgid="5316091912653672681">"SIM-Karte wird entsperrt…"</string> + <string name="kg_login_checking_password" msgid="1052685197710252395">"Konto wird geprüft…"</string> <string name="kg_too_many_failed_pin_attempts_dialog_message" msgid="8276745642049502550">"Sie haben Ihre PIN <xliff:g id="NUMBER_0">%d</xliff:g>-mal falsch eingegeben."\n\n"Versuchen Sie es in <xliff:g id="NUMBER_1">%d</xliff:g> Sekunden erneut."</string> <string name="kg_too_many_failed_password_attempts_dialog_message" msgid="7813713389422226531">"Sie haben Ihr Passwort <xliff:g id="NUMBER_0">%d</xliff:g>-mal falsch eingegeben."\n\n"Versuchen Sie es in <xliff:g id="NUMBER_1">%d</xliff:g> Sekunden erneut."</string> <string name="kg_too_many_failed_pattern_attempts_dialog_message" msgid="74089475965050805">"Sie haben Ihr Entsperrungsmuster <xliff:g id="NUMBER_0">%d</xliff:g>-mal falsch gezeichnet. "\n\n"Versuchen Sie es in <xliff:g id="NUMBER_1">%d</xliff:g> Sekunden erneut."</string> diff --git a/core/res/res/values-el/strings.xml b/core/res/res/values-el/strings.xml index 97acffc..b177c0f 100644 --- a/core/res/res/values-el/strings.xml +++ b/core/res/res/values-el/strings.xml @@ -1407,7 +1407,7 @@ <string name="kg_login_submit_button" msgid="5355904582674054702">"Σύνδεση"</string> <string name="kg_login_invalid_input" msgid="5754664119319872197">"Μη έγκυρο όνομα χρήστη ή κωδικός πρόσβασης."</string> <string name="kg_login_account_recovery_hint" msgid="5690709132841752974">"Ξεχάσατε το όνομα χρήστη ή τον κωδικό πρόσβασής σας;"\n"Επισκεφτείτε τη διεύθυνση "<b>"google.com/accounts/recovery"</b>"."</string> - <string name="kg_login_checking_password" msgid="5316091912653672681">"Ξεκλείδωμα SIM…"</string> + <string name="kg_login_checking_password" msgid="1052685197710252395">"Έλεγχος λογαριασμού…"</string> <string name="kg_too_many_failed_pin_attempts_dialog_message" msgid="8276745642049502550">"Έχετε πληκτρολογήσει εσφαλμένα τον κωδικό σας PIN <xliff:g id="NUMBER_0">%d</xliff:g> φορές. "\n\n"Δοκιμάστε ξανά σε <xliff:g id="NUMBER_1">%d</xliff:g> δευτερόλεπτα."</string> <string name="kg_too_many_failed_password_attempts_dialog_message" msgid="7813713389422226531">"Έχετε πληκτρολογήσει τον κωδικό πρόσβασης εσφαλμένα <xliff:g id="NUMBER_0">%d</xliff:g> φορές. "\n\n"Δοκιμάστε ξανά σε <xliff:g id="NUMBER_1">%d</xliff:g> δευτερόλεπτα."</string> <string name="kg_too_many_failed_pattern_attempts_dialog_message" msgid="74089475965050805">"Σχεδιάσατε εσφαλμένα το μοτίβο ξεκλειδώματος <xliff:g id="NUMBER_0">%d</xliff:g> φορές. "\n\n"Δοκιμάστε ξανά σε <xliff:g id="NUMBER_1">%d</xliff:g> δευτερόλετπα."</string> diff --git a/core/res/res/values-en-rGB/strings.xml b/core/res/res/values-en-rGB/strings.xml index c470542..9eaa77a 100644 --- a/core/res/res/values-en-rGB/strings.xml +++ b/core/res/res/values-en-rGB/strings.xml @@ -1407,7 +1407,7 @@ <string name="kg_login_submit_button" msgid="5355904582674054702">"Sign in"</string> <string name="kg_login_invalid_input" msgid="5754664119319872197">"Invalid username or password."</string> <string name="kg_login_account_recovery_hint" msgid="5690709132841752974">"Forgot your username or password?"\n"Visit "<b>"google.com/accounts/recovery"</b>"."</string> - <string name="kg_login_checking_password" msgid="5316091912653672681">"Unlocking SIM…"</string> + <string name="kg_login_checking_password" msgid="1052685197710252395">"Checking account…"</string> <string name="kg_too_many_failed_pin_attempts_dialog_message" msgid="8276745642049502550">"You have incorrectly typed your PIN <xliff:g id="NUMBER_0">%d</xliff:g> times. "\n\n"Try again in <xliff:g id="NUMBER_1">%d</xliff:g> seconds."</string> <string name="kg_too_many_failed_password_attempts_dialog_message" msgid="7813713389422226531">"You have incorrectly typed your password <xliff:g id="NUMBER_0">%d</xliff:g> times. "\n\n"Try again in <xliff:g id="NUMBER_1">%d</xliff:g> seconds."</string> <string name="kg_too_many_failed_pattern_attempts_dialog_message" msgid="74089475965050805">"You have incorrectly drawn your unlock pattern <xliff:g id="NUMBER_0">%d</xliff:g> times. "\n\n"Try again in <xliff:g id="NUMBER_1">%d</xliff:g> seconds."</string> diff --git a/core/res/res/values-es-rUS/strings.xml b/core/res/res/values-es-rUS/strings.xml index 4915352..2b4e06a 100644 --- a/core/res/res/values-es-rUS/strings.xml +++ b/core/res/res/values-es-rUS/strings.xml @@ -737,7 +737,7 @@ <string name="relationTypeSister" msgid="1735983554479076481">"Hermana"</string> <string name="relationTypeSpouse" msgid="394136939428698117">"Cónyuge"</string> <string name="sipAddressTypeCustom" msgid="2473580593111590945">"Personalizado"</string> - <string name="sipAddressTypeHome" msgid="6093598181069359295">"Página principal"</string> + <string name="sipAddressTypeHome" msgid="6093598181069359295">"Casa"</string> <string name="sipAddressTypeWork" msgid="6920725730797099047">"Trabajo"</string> <string name="sipAddressTypeOther" msgid="4408436162950119849">"Otro"</string> <string name="keyguard_password_enter_pin_code" msgid="3037685796058495017">"Ingresa el código PIN"</string> @@ -813,7 +813,7 @@ <string name="keyguard_accessibility_widget_changed" msgid="5678624624681400191">"%1$s. Widget %2$d de %3$d"</string> <string name="keyguard_accessibility_user_selector" msgid="1226798370913698896">"Selector de usuarios"</string> <string name="keyguard_accessibility_status" msgid="8008264603935930611">"Estado"</string> - <string name="keygaurd_accessibility_media_controls" msgid="262209654292161806">"Controles de medios de comunicación"</string> + <string name="keygaurd_accessibility_media_controls" msgid="262209654292161806">"Controles de medios"</string> <string name="password_keyboard_label_symbol_key" msgid="992280756256536042">"?123"</string> <string name="password_keyboard_label_alpha_key" msgid="8001096175167485649">"ABC"</string> <string name="password_keyboard_label_alt_key" msgid="1284820942620288678">"ALT"</string> @@ -1103,8 +1103,8 @@ <string name="sms_control_yes" msgid="3663725993855816807">"Permitir"</string> <string name="sms_control_no" msgid="625438561395534982">"Rechazar"</string> <string name="sms_short_code_confirm_message" msgid="1645436466285310855">"<b><xliff:g id="APP_NAME">%1$s</xliff:g></b> desea enviar un mensaje a <b><xliff:g id="DEST_ADDRESS">%2$s</xliff:g></b>."</string> - <string name="sms_short_code_details" msgid="3492025719868078457">"Esto "<font fgcolor="#ffffb060">" puede causar que se apliquen cargos "</font>" a tu cuenta móvil."</string> - <string name="sms_premium_short_code_details" msgid="5523826349105123687"><font fgcolor="#ffffb060">"Se aplicarán cargos a tu cuenta móvil."</font></string> + <string name="sms_short_code_details" msgid="3492025719868078457">"Esto "<font fgcolor="#ffffb060">" puede causar que se apliquen cargos "</font>" en tu cuenta móvil."</string> + <string name="sms_premium_short_code_details" msgid="5523826349105123687"><font fgcolor="#ffffb060">"Se aplicarán cargos en tu cuenta móvil."</font></string> <string name="sms_short_code_confirm_allow" msgid="4458878637111023413">"Enviar"</string> <string name="sms_short_code_confirm_deny" msgid="2927389840209170706">"Cancelar"</string> <string name="sms_short_code_remember_choice" msgid="5289538592272218136">"Recordar mi elección"</string> @@ -1373,7 +1373,7 @@ <string name="bluetooth_a2dp_audio_route_name" msgid="8575624030406771015">"Audio Bluetooth"</string> <string name="media_route_chooser_grouping_done" msgid="7966438307723317169">"Listo"</string> <string name="media_route_button_content_description" msgid="5758553567065145276">"Salida multimedia"</string> - <string name="media_route_status_scanning" msgid="7279908761758293783">"Analizando..."</string> + <string name="media_route_status_scanning" msgid="7279908761758293783">"Examinando..."</string> <string name="media_route_status_connecting" msgid="6422571716007825440">"Conectando..."</string> <string name="media_route_status_available" msgid="6983258067194649391">"Disponible"</string> <string name="media_route_status_not_available" msgid="6739899962681886401">"No disponible"</string> @@ -1391,14 +1391,14 @@ <string name="kg_sim_pin_instructions" msgid="2319508550934557331">"Ingresa el PIN de la tarjeta SIM."</string> <string name="kg_pin_instructions" msgid="2377242233495111557">"Ingresa el PIN."</string> <string name="kg_password_instructions" msgid="5753646556186936819">"Ingresa tu contraseña."</string> - <string name="kg_puk_enter_puk_hint" msgid="453227143861735537">"La tarjeta SIM está inhabilitada. Para continuar, introduce el código PUK. Si quieres obtener más información, ponte en contacto con el operador."</string> - <string name="kg_puk_enter_pin_hint" msgid="7871604527429602024">"Introduce el código PIN deseado"</string> + <string name="kg_puk_enter_puk_hint" msgid="453227143861735537">"La tarjeta SIM está inhabilitada. Para continuar, ingresa el código PUK. Si quieres obtener más información, ponte en contacto con el proveedor."</string> + <string name="kg_puk_enter_pin_hint" msgid="7871604527429602024">"Ingresa el código PIN deseado"</string> <string name="kg_enter_confirm_pin_hint" msgid="325676184762529976">"Confirmar código PIN deseado"</string> <string name="kg_sim_unlock_progress_dialog_message" msgid="8950398016976865762">"Desbloqueando tarjeta SIM…"</string> <string name="kg_password_wrong_pin_code" msgid="1139324887413846912">"Código PIN incorrecto"</string> <string name="kg_invalid_sim_pin_hint" msgid="8795159358110620001">"Escribe un PIN que tenga de cuatro a ocho números."</string> <string name="kg_invalid_sim_puk_hint" msgid="7553388325654369575">"El código PUK debe tener ocho números como mínimo."</string> - <string name="kg_invalid_puk" msgid="3638289409676051243">"Vuelve a introducir el código PUK correcto. Si introduces un código incorrecto varias veces, se inhabilitará la tarjeta SIM."</string> + <string name="kg_invalid_puk" msgid="3638289409676051243">"Vuelve a ingresar el código PUK correcto. Si ingresas un código incorrecto varias veces, se inhabilitará la tarjeta SIM."</string> <string name="kg_invalid_confirm_pin_hint" product="default" msgid="7003469261464593516">"Los códigos PIN no coinciden."</string> <string name="kg_login_too_many_attempts" msgid="6486842094005698475">"Demasiados intentos incorrectos de ingresar el patrón"</string> <string name="kg_login_instructions" msgid="1100551261265506448">"Para desbloquear, accede con tu cuenta de Google."</string> @@ -1407,7 +1407,7 @@ <string name="kg_login_submit_button" msgid="5355904582674054702">"Acceder"</string> <string name="kg_login_invalid_input" msgid="5754664119319872197">"Nombre de usuario o contraseña incorrectos"</string> <string name="kg_login_account_recovery_hint" msgid="5690709132841752974">"¿Olvidaste tu nombre de usuario o contraseña?"\n"Accede a "<b>"google.com/accounts/recovery"</b>"."</string> - <string name="kg_login_checking_password" msgid="5316091912653672681">"Desbloqueando tarjeta SIM…"</string> + <string name="kg_login_checking_password" msgid="1052685197710252395">"Comprobando la cuenta…"</string> <string name="kg_too_many_failed_pin_attempts_dialog_message" msgid="8276745642049502550">"Escribiste incorrectamente tu PIN <xliff:g id="NUMBER_0">%d</xliff:g> veces. "\n\n"Vuelve a intentarlo en <xliff:g id="NUMBER_1">%d</xliff:g> segundos."</string> <string name="kg_too_many_failed_password_attempts_dialog_message" msgid="7813713389422226531">"Escribiste incorrectamente tu contraseña <xliff:g id="NUMBER_0">%d</xliff:g> veces. "\n\n"Vuelve a intentarlo en <xliff:g id="NUMBER_1">%d</xliff:g> segundos."</string> <string name="kg_too_many_failed_pattern_attempts_dialog_message" msgid="74089475965050805">"Dibujaste incorrectamente tu patrón de desbloqueo <xliff:g id="NUMBER_0">%d</xliff:g> veces. "\n\n"Vuelve a intentarlo en <xliff:g id="NUMBER_1">%d</xliff:g> segundos."</string> @@ -1421,5 +1421,5 @@ <string name="continue_to_enable_accessibility" msgid="2184747411804432885">"Mantén presionada el área con dos dedos para activar la accesibilidad."</string> <string name="accessibility_enabled" msgid="1381972048564547685">"Se activó la accesibilidad."</string> <string name="enable_accessibility_canceled" msgid="3833923257966635673">"Se canceló la accesibilidad."</string> - <string name="user_switched" msgid="3768006783166984410">"Usuario actual <xliff:g id="NAME">%1$s</xliff:g>"</string> + <string name="user_switched" msgid="3768006783166984410">"Usuario actual: <xliff:g id="NAME">%1$s</xliff:g>"</string> </resources> diff --git a/core/res/res/values-es/strings.xml b/core/res/res/values-es/strings.xml index fde88dd..0cb26d6 100644 --- a/core/res/res/values-es/strings.xml +++ b/core/res/res/values-es/strings.xml @@ -1407,7 +1407,7 @@ <string name="kg_login_submit_button" msgid="5355904582674054702">"Iniciar sesión"</string> <string name="kg_login_invalid_input" msgid="5754664119319872197">"El nombre de usuario o la contraseña no son válidos."</string> <string name="kg_login_account_recovery_hint" msgid="5690709132841752974">"Si has olvidado tu nombre de usuario o tu contraseña,"\n"accede a la página "<b>"google.com/accounts/recovery"</b>"."</string> - <string name="kg_login_checking_password" msgid="5316091912653672681">"Desbloqueando tarjeta SIM…"</string> + <string name="kg_login_checking_password" msgid="1052685197710252395">"Comprobando cuenta…"</string> <string name="kg_too_many_failed_pin_attempts_dialog_message" msgid="8276745642049502550">"Has introducido un código PIN incorrecto <xliff:g id="NUMBER_0">%d</xliff:g> veces. "\n\n"Inténtalo de nuevo en <xliff:g id="NUMBER_1">%d</xliff:g> segundos."</string> <string name="kg_too_many_failed_password_attempts_dialog_message" msgid="7813713389422226531">"Has introducido una contraseña incorrecta <xliff:g id="NUMBER_0">%d</xliff:g> veces. "\n\n"Inténtalo de nuevo en <xliff:g id="NUMBER_1">%d</xliff:g> segundos."</string> <string name="kg_too_many_failed_pattern_attempts_dialog_message" msgid="74089475965050805">"Has fallado <xliff:g id="NUMBER_0">%d</xliff:g> veces al dibujar tu patrón de desbloqueo. "\n\n"Inténtalo de nuevo en <xliff:g id="NUMBER_1">%d</xliff:g> segundos."</string> diff --git a/core/res/res/values-et/strings.xml b/core/res/res/values-et/strings.xml index e096e31..682650b 100644 --- a/core/res/res/values-et/strings.xml +++ b/core/res/res/values-et/strings.xml @@ -1407,7 +1407,7 @@ <string name="kg_login_submit_button" msgid="5355904582674054702">"Logi sisse"</string> <string name="kg_login_invalid_input" msgid="5754664119319872197">"Vale kasutajanimi või parool."</string> <string name="kg_login_account_recovery_hint" msgid="5690709132841752974">"Kas unustasite kasutajanime või parooli?"\n"Külastage aadressi "<b>"google.com/accounts/recovery"</b>"."</string> - <string name="kg_login_checking_password" msgid="5316091912653672681">"SIM-i avamine ..."</string> + <string name="kg_login_checking_password" msgid="1052685197710252395">"Konto kontrollimine ..."</string> <string name="kg_too_many_failed_pin_attempts_dialog_message" msgid="8276745642049502550">"Olete PIN-koodi <xliff:g id="NUMBER_0">%d</xliff:g> korda valesti sisestanud."\n\n"Proovige <xliff:g id="NUMBER_1">%d</xliff:g> sekundi pärast uuesti."</string> <string name="kg_too_many_failed_password_attempts_dialog_message" msgid="7813713389422226531">"Olete parooli <xliff:g id="NUMBER_0">%d</xliff:g> korda valesti sisestanud. "\n\n"Proovige uuesti <xliff:g id="NUMBER_1">%d</xliff:g> sekundi pärast."</string> <string name="kg_too_many_failed_pattern_attempts_dialog_message" msgid="74089475965050805">"Joonistasite oma avamismustri <xliff:g id="NUMBER_0">%d</xliff:g> korda valesti."\n\n"Proovige <xliff:g id="NUMBER_1">%d</xliff:g> sekundi pärast uuesti."</string> diff --git a/core/res/res/values-fa/strings.xml b/core/res/res/values-fa/strings.xml index 2ca2bcb..8369261 100644 --- a/core/res/res/values-fa/strings.xml +++ b/core/res/res/values-fa/strings.xml @@ -1407,7 +1407,7 @@ <string name="kg_login_submit_button" msgid="5355904582674054702">"ورود به سیستم"</string> <string name="kg_login_invalid_input" msgid="5754664119319872197">"نام کاربری یا گذرواژه نامعتبر."</string> <string name="kg_login_account_recovery_hint" msgid="5690709132841752974">"نام کاربری یا گذرواژه خود را فراموش کردید؟"\n"از "<b>"google.com/accounts/recovery"</b>" بازدید کنید."</string> - <string name="kg_login_checking_password" msgid="5316091912653672681">"در حال باز کردن قفل سیم کارت…"</string> + <string name="kg_login_checking_password" msgid="1052685197710252395">"درحال بررسی حساب..."</string> <string name="kg_too_many_failed_pin_attempts_dialog_message" msgid="8276745642049502550">"پین خود را <xliff:g id="NUMBER_0">%d</xliff:g> بار اشتباه تایپ کردید. "\n\n"پس از <xliff:g id="NUMBER_1">%d</xliff:g> ثانیه دوباره امتحان کنید."</string> <string name="kg_too_many_failed_password_attempts_dialog_message" msgid="7813713389422226531">"گذرواژه خود را <xliff:g id="NUMBER_0">%d</xliff:g> بار اشتباه تایپ کردید. "\n\n"پس از <xliff:g id="NUMBER_1">%d</xliff:g> ثانیه دوباره امتحان کنید."</string> <string name="kg_too_many_failed_pattern_attempts_dialog_message" msgid="74089475965050805">"الگوی بازگشایی قفل خود را <xliff:g id="NUMBER_0">%d</xliff:g> بار اشتباه کشیدید. "\n\n"لطفاً پس از <xliff:g id="NUMBER_1">%d</xliff:g> ثانیه دوباره امتحان کنید."</string> diff --git a/core/res/res/values-fi/strings.xml b/core/res/res/values-fi/strings.xml index 4b14301..ff4a8a2 100644 --- a/core/res/res/values-fi/strings.xml +++ b/core/res/res/values-fi/strings.xml @@ -1407,7 +1407,7 @@ <string name="kg_login_submit_button" msgid="5355904582674054702">"Kirjaudu sisään"</string> <string name="kg_login_invalid_input" msgid="5754664119319872197">"Virheellinen käyttäjänimi tai salasana."</string> <string name="kg_login_account_recovery_hint" msgid="5690709132841752974">"Unohditko käyttäjänimesi tai salasanasi?"\n"Käy osoitteessa "<b>"google.com/accounts/recovery"</b>"."</string> - <string name="kg_login_checking_password" msgid="5316091912653672681">"SIM-kortin lukitusta poistetaan…"</string> + <string name="kg_login_checking_password" msgid="1052685197710252395">"Tarkistetaan tiliä..."</string> <string name="kg_too_many_failed_pin_attempts_dialog_message" msgid="8276745642049502550">"Olet kirjoittanut PIN-koodin väärin <xliff:g id="NUMBER_0">%d</xliff:g> kertaa. "\n\n"Yritä uudelleen <xliff:g id="NUMBER_1">%d</xliff:g> sekunnin kuluttua."</string> <string name="kg_too_many_failed_password_attempts_dialog_message" msgid="7813713389422226531">"Olet kirjoittanut salasanan väärin <xliff:g id="NUMBER_0">%d</xliff:g> kertaa. "\n\n"Yritä uudelleen <xliff:g id="NUMBER_1">%d</xliff:g> sekunnin kuluttua."</string> <string name="kg_too_many_failed_pattern_attempts_dialog_message" msgid="74089475965050805">"Olet piirtänyt lukituksenpoistokuvion väärin <xliff:g id="NUMBER_0">%d</xliff:g> kertaa. "\n\n"Yritä uudelleen <xliff:g id="NUMBER_1">%d</xliff:g> sekunnin kuluttua."</string> diff --git a/core/res/res/values-fr/strings.xml b/core/res/res/values-fr/strings.xml index d2d2b5e..c3a5be2 100644 --- a/core/res/res/values-fr/strings.xml +++ b/core/res/res/values-fr/strings.xml @@ -1407,7 +1407,7 @@ <string name="kg_login_submit_button" msgid="5355904582674054702">"Connexion"</string> <string name="kg_login_invalid_input" msgid="5754664119319872197">"Nom d\'utilisateur ou mot de passe non valide."</string> <string name="kg_login_account_recovery_hint" msgid="5690709132841752974">"Vous avez oublié votre nom d\'utilisateur ou votre mot de passe ?"\n"Rendez-vous sur la page "<b>"google.com/accounts/recovery"</b>"."</string> - <string name="kg_login_checking_password" msgid="5316091912653672681">"Déblocage de la carte SIM en cours…"</string> + <string name="kg_login_checking_password" msgid="1052685197710252395">"Vérification du compte en cours…"</string> <string name="kg_too_many_failed_pin_attempts_dialog_message" msgid="8276745642049502550">"Vous avez saisi un code PIN incorrect à <xliff:g id="NUMBER_0">%d</xliff:g> reprises. "\n\n"Veuillez réessayer dans <xliff:g id="NUMBER_1">%d</xliff:g> secondes."</string> <string name="kg_too_many_failed_password_attempts_dialog_message" msgid="7813713389422226531">"Vous avez saisi un mot de passe incorrect à <xliff:g id="NUMBER_0">%d</xliff:g> reprises. "\n\n"Veuillez réessayer dans <xliff:g id="NUMBER_1">%d</xliff:g> secondes."</string> <string name="kg_too_many_failed_pattern_attempts_dialog_message" msgid="74089475965050805">"Vous avez dessiné un schéma de déverrouillage incorrect à <xliff:g id="NUMBER_0">%d</xliff:g> reprises."\n\n"Veuillez réessayer dans <xliff:g id="NUMBER_1">%d</xliff:g> secondes."</string> diff --git a/core/res/res/values-hi/strings.xml b/core/res/res/values-hi/strings.xml index 8e89f30..c53e0fd 100644 --- a/core/res/res/values-hi/strings.xml +++ b/core/res/res/values-hi/strings.xml @@ -1407,7 +1407,7 @@ <string name="kg_login_submit_button" msgid="5355904582674054702">"साइन इन करें"</string> <string name="kg_login_invalid_input" msgid="5754664119319872197">"अमान्य उपयोगकर्ता नाम या पासवर्ड."</string> <string name="kg_login_account_recovery_hint" msgid="5690709132841752974">"अपना उपयोगकर्ता नाम या पासवर्ड भूल गए?"\n" "<b>"google.com/accounts/recovery"</b>" पर जाएं."</string> - <string name="kg_login_checking_password" msgid="5316091912653672681">"सिम अनलॉक कर रहा है…"</string> + <string name="kg_login_checking_password" msgid="1052685197710252395">"खाते की जांच की जा रही है…"</string> <string name="kg_too_many_failed_pin_attempts_dialog_message" msgid="8276745642049502550">"आप अपना PIN <xliff:g id="NUMBER_0">%d</xliff:g> बार गलत तरीके से लिख चुके हैं. "\n\n" <xliff:g id="NUMBER_1">%d</xliff:g> सेकंड में पुन: प्रयास करें."</string> <string name="kg_too_many_failed_password_attempts_dialog_message" msgid="7813713389422226531">"आप अपना पासवर्ड <xliff:g id="NUMBER_0">%d</xliff:g> बार गलत तरीके से लिख चुके हैं. "\n\n" <xliff:g id="NUMBER_1">%d</xliff:g> सेकंड में पुन: प्रयास करें."</string> <string name="kg_too_many_failed_pattern_attempts_dialog_message" msgid="74089475965050805">"आपने अपना अनलॉक प्रतिमान <xliff:g id="NUMBER_0">%d</xliff:g> बार गलत तरीके से आरेखित किया है. "\n\n" <xliff:g id="NUMBER_1">%d</xliff:g> सेकंड में पुन: प्रयास करें."</string> diff --git a/core/res/res/values-hr/strings.xml b/core/res/res/values-hr/strings.xml index 8ee9895..a57d287 100644 --- a/core/res/res/values-hr/strings.xml +++ b/core/res/res/values-hr/strings.xml @@ -1108,7 +1108,7 @@ <string name="sms_short_code_confirm_allow" msgid="4458878637111023413">"Pošalji"</string> <string name="sms_short_code_confirm_deny" msgid="2927389840209170706">"Odustani"</string> <string name="sms_short_code_remember_choice" msgid="5289538592272218136">"Zapamti odabir"</string> - <string name="sms_short_code_remember_undo_instruction" msgid="4960944133052287484">"Kasnije to možete promijenili u odjeljku Postavke > Aplikacije"</string> + <string name="sms_short_code_remember_undo_instruction" msgid="4960944133052287484">"Kasnije to možete promijeniti u odjeljku Postavke > Aplikacije"</string> <string name="sms_short_code_confirm_always_allow" msgid="3241181154869493368">"Dopusti uvijek"</string> <string name="sms_short_code_confirm_never_allow" msgid="446992765774269673">"Ne dopuštaj nikada"</string> <string name="sim_removed_title" msgid="6227712319223226185">"SIM kartica uklonjena"</string> @@ -1407,7 +1407,7 @@ <string name="kg_login_submit_button" msgid="5355904582674054702">"Prijava"</string> <string name="kg_login_invalid_input" msgid="5754664119319872197">"Nevažeće korisničko ime ili zaporka."</string> <string name="kg_login_account_recovery_hint" msgid="5690709132841752974">"Zaboravili ste korisničko ime ili zaporku?"\n"Posjetite "<b>"google.com/accounts/recovery"</b>"."</string> - <string name="kg_login_checking_password" msgid="5316091912653672681">"Otključavanje SIM-a…"</string> + <string name="kg_login_checking_password" msgid="1052685197710252395">"Provjera računa..."</string> <string name="kg_too_many_failed_pin_attempts_dialog_message" msgid="8276745642049502550">"Netočno ste napisali PIN <xliff:g id="NUMBER_0">%d</xliff:g> puta. "\n\n"Pokušajte ponovo za <xliff:g id="NUMBER_1">%d</xliff:g> s."</string> <string name="kg_too_many_failed_password_attempts_dialog_message" msgid="7813713389422226531">"Netočno ste napisali zaporku <xliff:g id="NUMBER_0">%d</xliff:g> puta. "\n\n"Pokušajte ponovo za <xliff:g id="NUMBER_1">%d</xliff:g> s."</string> <string name="kg_too_many_failed_pattern_attempts_dialog_message" msgid="74089475965050805">"Netočno ste iscrtali obrazac za otključavanje <xliff:g id="NUMBER_0">%d</xliff:g> puta. "\n\n"Pokušajte ponovo za <xliff:g id="NUMBER_1">%d</xliff:g> s."</string> diff --git a/core/res/res/values-hu/strings.xml b/core/res/res/values-hu/strings.xml index f5f1fb4..d997deb 100644 --- a/core/res/res/values-hu/strings.xml +++ b/core/res/res/values-hu/strings.xml @@ -1407,7 +1407,7 @@ <string name="kg_login_submit_button" msgid="5355904582674054702">"Bejelentkezés"</string> <string name="kg_login_invalid_input" msgid="5754664119319872197">"Érvénytelen felhasználónév vagy jelszó."</string> <string name="kg_login_account_recovery_hint" msgid="5690709132841752974">"Elfelejtette a felhasználónevét vagy jelszavát?"\n"Keresse fel a "<b>"google.com/accounts/recovery"</b>" webhelyet."</string> - <string name="kg_login_checking_password" msgid="5316091912653672681">"SIM kártya feloldása..."</string> + <string name="kg_login_checking_password" msgid="1052685197710252395">"Fiók ellenőrzése..."</string> <string name="kg_too_many_failed_pin_attempts_dialog_message" msgid="8276745642049502550">"<xliff:g id="NUMBER_0">%d</xliff:g> alkalommal helytelenül adta meg PIN kódját. "\n\n"Próbálja újra <xliff:g id="NUMBER_1">%d</xliff:g> másodperc múlva."</string> <string name="kg_too_many_failed_password_attempts_dialog_message" msgid="7813713389422226531">"<xliff:g id="NUMBER_0">%d</xliff:g> alkalommal helytelenül adta meg a jelszót. "\n\n" Próbálja újra <xliff:g id="NUMBER_1">%d</xliff:g> másodperc múlva."</string> <string name="kg_too_many_failed_pattern_attempts_dialog_message" msgid="74089475965050805">"<xliff:g id="NUMBER_0">%d</xliff:g> alkalommal rosszul rajzolta le feloldási mintát. "\n\n"Próbálja újra <xliff:g id="NUMBER_1">%d</xliff:g> másodperc múlva."</string> diff --git a/core/res/res/values-in/strings.xml b/core/res/res/values-in/strings.xml index 78d6bc2..90b81b1 100644 --- a/core/res/res/values-in/strings.xml +++ b/core/res/res/values-in/strings.xml @@ -1407,7 +1407,7 @@ <string name="kg_login_submit_button" msgid="5355904582674054702">"Masuk"</string> <string name="kg_login_invalid_input" msgid="5754664119319872197">"Nama pengguna atau sandi tidak valid."</string> <string name="kg_login_account_recovery_hint" msgid="5690709132841752974">"Lupa nama pengguna atau sandi Anda?"\n"Kunjungi "<b>"google.com/accounts/recovery"</b>"."</string> - <string name="kg_login_checking_password" msgid="5316091912653672681">"Membuka kunci kartu SIM…"</string> + <string name="kg_login_checking_password" msgid="1052685197710252395">"Memeriksa akun…"</string> <string name="kg_too_many_failed_pin_attempts_dialog_message" msgid="8276745642049502550">"Anda telah <xliff:g id="NUMBER_0">%d</xliff:g> kali salah mengetik PIN. "\n\n"Coba lagi dalam <xliff:g id="NUMBER_1">%d</xliff:g> detik."</string> <string name="kg_too_many_failed_password_attempts_dialog_message" msgid="7813713389422226531">"Anda telah <xliff:g id="NUMBER_0">%d</xliff:g> kali salah mengetik sandi. "\n\n"Coba lagi dalam <xliff:g id="NUMBER_1">%d</xliff:g> detik."</string> <string name="kg_too_many_failed_pattern_attempts_dialog_message" msgid="74089475965050805">"Anda telah <xliff:g id="NUMBER_0">%d</xliff:g> kali salah menggambar pola pembuka kunci. "\n\n"Coba lagi dalam <xliff:g id="NUMBER_1">%d</xliff:g> detik."</string> diff --git a/core/res/res/values-it/strings.xml b/core/res/res/values-it/strings.xml index 5dd161c..c54f8bc 100644 --- a/core/res/res/values-it/strings.xml +++ b/core/res/res/values-it/strings.xml @@ -1407,7 +1407,7 @@ <string name="kg_login_submit_button" msgid="5355904582674054702">"Accedi"</string> <string name="kg_login_invalid_input" msgid="5754664119319872197">"Nome utente o password non validi."</string> <string name="kg_login_account_recovery_hint" msgid="5690709132841752974">"Hai dimenticato il nome utente o la password?"\n"Visita "<b>"google.com/accounts/recovery"</b>"."</string> - <string name="kg_login_checking_password" msgid="5316091912653672681">"Sblocco SIM…"</string> + <string name="kg_login_checking_password" msgid="1052685197710252395">"Controllo account…"</string> <string name="kg_too_many_failed_pin_attempts_dialog_message" msgid="8276745642049502550">"Hai digitato il tuo PIN <xliff:g id="NUMBER_0">%d</xliff:g> volte in modo errato. "\n\n"Riprova tra <xliff:g id="NUMBER_1">%d</xliff:g> secondi."</string> <string name="kg_too_many_failed_password_attempts_dialog_message" msgid="7813713389422226531">"Hai digitato la tua password <xliff:g id="NUMBER_0">%d</xliff:g> volte in modo errato. "\n\n"Riprova tra <xliff:g id="NUMBER_1">%d</xliff:g> secondi."</string> <string name="kg_too_many_failed_pattern_attempts_dialog_message" msgid="74089475965050805">"<xliff:g id="NUMBER_0">%d</xliff:g> tentativi errati di inserimento della sequenza di sblocco. "\n\n"Riprova tra <xliff:g id="NUMBER_1">%d</xliff:g> secondi."</string> diff --git a/core/res/res/values-iw/strings.xml b/core/res/res/values-iw/strings.xml index 041d8f4..7c91c84 100644 --- a/core/res/res/values-iw/strings.xml +++ b/core/res/res/values-iw/strings.xml @@ -1407,7 +1407,7 @@ <string name="kg_login_submit_button" msgid="5355904582674054702">"היכנס"</string> <string name="kg_login_invalid_input" msgid="5754664119319872197">"שם משתמש או סיסמה לא חוקיים."</string> <string name="kg_login_account_recovery_hint" msgid="5690709132841752974">"שכחת את שם המשתמש או הסיסמה?"\n"בקר בכתובת "<b>"google.com/accounts/recovery"</b></string> - <string name="kg_login_checking_password" msgid="5316091912653672681">"משחרר כרטיס SIM ..."</string> + <string name="kg_login_checking_password" msgid="1052685197710252395">"בודק חשבון…"</string> <string name="kg_too_many_failed_pin_attempts_dialog_message" msgid="8276745642049502550">"הקלדת מספר PIN שגוי <xliff:g id="NUMBER_0">%d</xliff:g> פעמים. "\n\n"נסה שוב בעוד <xliff:g id="NUMBER_1">%d</xliff:g> שניות."</string> <string name="kg_too_many_failed_password_attempts_dialog_message" msgid="7813713389422226531">"הקלדת סיסמה שגויה <xliff:g id="NUMBER_0">%d</xliff:g> פעמים."\n\n"נסה שוב בעוד <xliff:g id="NUMBER_1">%d</xliff:g> שניות."</string> <string name="kg_too_many_failed_pattern_attempts_dialog_message" msgid="74089475965050805">"שרטטת את קו ביטול הנעילה באופן שגוי <xliff:g id="NUMBER_0">%d</xliff:g> פעמים. "\n\n"נסה שוב בעוד <xliff:g id="NUMBER_1">%d</xliff:g> שניות."</string> diff --git a/core/res/res/values-ja/strings.xml b/core/res/res/values-ja/strings.xml index 20c23f0..d5555b8 100644 --- a/core/res/res/values-ja/strings.xml +++ b/core/res/res/values-ja/strings.xml @@ -1407,7 +1407,7 @@ <string name="kg_login_submit_button" msgid="5355904582674054702">"ログイン"</string> <string name="kg_login_invalid_input" msgid="5754664119319872197">"ユーザー名またはパスワードが無効です。"</string> <string name="kg_login_account_recovery_hint" msgid="5690709132841752974">"ユーザー名またはパスワードを忘れた場合は"\n" "<b>"google.com/accounts/recovery"</b>" にアクセスしてください。"</string> - <string name="kg_login_checking_password" msgid="5316091912653672681">"SIMのロック解除中…"</string> + <string name="kg_login_checking_password" msgid="1052685197710252395">"アカウントをチェックしています…"</string> <string name="kg_too_many_failed_pin_attempts_dialog_message" msgid="8276745642049502550">"PINの入力を<xliff:g id="NUMBER_0">%d</xliff:g>回間違えました。"\n\n"<xliff:g id="NUMBER_1">%d</xliff:g>秒以内にもう一度お試しください。"</string> <string name="kg_too_many_failed_password_attempts_dialog_message" msgid="7813713389422226531">"パスワードの入力を<xliff:g id="NUMBER_0">%d</xliff:g>回間違えました。"\n\n"<xliff:g id="NUMBER_1">%d</xliff:g>秒以内にもう一度お試しください。"</string> <string name="kg_too_many_failed_pattern_attempts_dialog_message" msgid="74089475965050805">"ロック解除パターンの入力を<xliff:g id="NUMBER_0">%d</xliff:g>回間違えました。"\n\n"<xliff:g id="NUMBER_1">%d</xliff:g>秒以内にもう一度お試しください。"</string> diff --git a/core/res/res/values-ko/strings.xml b/core/res/res/values-ko/strings.xml index 505e512..fc0492d 100644 --- a/core/res/res/values-ko/strings.xml +++ b/core/res/res/values-ko/strings.xml @@ -737,7 +737,7 @@ <string name="relationTypeSister" msgid="1735983554479076481">"여자 형제"</string> <string name="relationTypeSpouse" msgid="394136939428698117">"배우자"</string> <string name="sipAddressTypeCustom" msgid="2473580593111590945">"맞춤설정"</string> - <string name="sipAddressTypeHome" msgid="6093598181069359295">"홈"</string> + <string name="sipAddressTypeHome" msgid="6093598181069359295">"집"</string> <string name="sipAddressTypeWork" msgid="6920725730797099047">"직장"</string> <string name="sipAddressTypeOther" msgid="4408436162950119849">"기타"</string> <string name="keyguard_password_enter_pin_code" msgid="3037685796058495017">"PIN 코드 입력"</string> @@ -1407,7 +1407,7 @@ <string name="kg_login_submit_button" msgid="5355904582674054702">"로그인"</string> <string name="kg_login_invalid_input" msgid="5754664119319872197">"사용자 이름 또는 비밀번호가 잘못되었습니다."</string> <string name="kg_login_account_recovery_hint" msgid="5690709132841752974">"사용자 이름이나 비밀번호를 잊어버렸습니까?"\n<b>"google.com/accounts/recovery"</b>" 페이지를 방문하세요."</string> - <string name="kg_login_checking_password" msgid="5316091912653672681">"SIM 잠금해제 중…"</string> + <string name="kg_login_checking_password" msgid="1052685197710252395">"계정 확인 중…"</string> <string name="kg_too_many_failed_pin_attempts_dialog_message" msgid="8276745642049502550">"PIN을 <xliff:g id="NUMBER_0">%d</xliff:g>회 잘못 입력했습니다. "\n\n"<xliff:g id="NUMBER_1">%d</xliff:g>초 후에 다시 시도해 주세요."</string> <string name="kg_too_many_failed_password_attempts_dialog_message" msgid="7813713389422226531">"비밀번호를 <xliff:g id="NUMBER_0">%d</xliff:g>회 잘못 입력했습니다. "\n\n"<xliff:g id="NUMBER_1">%d</xliff:g>초 후에 다시 시도해 주세요."</string> <string name="kg_too_many_failed_pattern_attempts_dialog_message" msgid="74089475965050805">"잠금해제 패턴을 <xliff:g id="NUMBER_0">%d</xliff:g>회 잘못 그렸습니다. "\n\n"<xliff:g id="NUMBER_1">%d</xliff:g>초 후에 다시 시도해 주세요."</string> diff --git a/core/res/res/values-lt/strings.xml b/core/res/res/values-lt/strings.xml index 83499b8..4170a16 100644 --- a/core/res/res/values-lt/strings.xml +++ b/core/res/res/values-lt/strings.xml @@ -1407,7 +1407,7 @@ <string name="kg_login_submit_button" msgid="5355904582674054702">"Prisijungti"</string> <string name="kg_login_invalid_input" msgid="5754664119319872197">"Netinkamas naudotojo vardas ar slaptažodis."</string> <string name="kg_login_account_recovery_hint" msgid="5690709132841752974">"Pamiršote naudotojo vardą ar slaptažodį?"\n"Apsilankykite šiuo adresu: "<b>"google.com/accounts/recovery"</b></string> - <string name="kg_login_checking_password" msgid="5316091912653672681">"Atrakinama SIM…"</string> + <string name="kg_login_checking_password" msgid="1052685197710252395">"Tikrinama paskyra…"</string> <string name="kg_too_many_failed_pin_attempts_dialog_message" msgid="8276745642049502550">"PIN kodą netinkamai įvedėte <xliff:g id="NUMBER_0">%d</xliff:g> k. "\n\n"Bandykite dar kartą po <xliff:g id="NUMBER_1">%d</xliff:g> sek."</string> <string name="kg_too_many_failed_password_attempts_dialog_message" msgid="7813713389422226531">"Neteisingai įvedėte slaptažodį <xliff:g id="NUMBER_0">%d</xliff:g> k. "\n\n"Bandykite dar kartą po <xliff:g id="NUMBER_1">%d</xliff:g> sek."</string> <string name="kg_too_many_failed_pattern_attempts_dialog_message" msgid="74089475965050805">"Netinkamai nupiešėte atrakinimo piešinį <xliff:g id="NUMBER_0">%d</xliff:g> k. "\n\n"Bandykite dar kartą po <xliff:g id="NUMBER_1">%d</xliff:g> sek."</string> diff --git a/core/res/res/values-lv/strings.xml b/core/res/res/values-lv/strings.xml index 1411ce6..9ce4daa 100644 --- a/core/res/res/values-lv/strings.xml +++ b/core/res/res/values-lv/strings.xml @@ -1407,7 +1407,7 @@ <string name="kg_login_submit_button" msgid="5355904582674054702">"Pierakstīties"</string> <string name="kg_login_invalid_input" msgid="5754664119319872197">"Nederīgs lietotājvārds vai parole."</string> <string name="kg_login_account_recovery_hint" msgid="5690709132841752974">"Vai aizmirsāt lietotājvārdu vai paroli?"\n"Apmeklējiet vietni "<b>"google.com/accounts/recovery"</b>"."</string> - <string name="kg_login_checking_password" msgid="5316091912653672681">"Notiek SIM kartes atbloķēšana..."</string> + <string name="kg_login_checking_password" msgid="1052685197710252395">"Notiek konta pārbaude…"</string> <string name="kg_too_many_failed_pin_attempts_dialog_message" msgid="8276745642049502550">"Jūs nepareizi ievadījāt PIN <xliff:g id="NUMBER_0">%d</xliff:g> reizes."\n\n"Mēģiniet vēlreiz pēc <xliff:g id="NUMBER_1">%d</xliff:g> sekundēm."</string> <string name="kg_too_many_failed_password_attempts_dialog_message" msgid="7813713389422226531">"Jūs nepareizi ievadījāt paroli <xliff:g id="NUMBER_0">%d</xliff:g> reizes."\n\n"Mēģiniet vēlreiz pēc <xliff:g id="NUMBER_1">%d</xliff:g> sekundēm."</string> <string name="kg_too_many_failed_pattern_attempts_dialog_message" msgid="74089475965050805">"Jūs nepareizi norādījāt atbloķēšanas kombināciju <xliff:g id="NUMBER_0">%d</xliff:g> reizes."\n\n"Mēģiniet vēlreiz pēc <xliff:g id="NUMBER_1">%d</xliff:g> sekundēm."</string> diff --git a/core/res/res/values-mcc440/config.xml b/core/res/res/values-mcc440/config.xml new file mode 100644 index 0000000..4ca1677 --- /dev/null +++ b/core/res/res/values-mcc440/config.xml @@ -0,0 +1,25 @@ +<?xml version="1.0" encoding="utf-8"?> +<!-- +/* +** Copyright 2012, The Android Open Source Project +** +** Licensed under the Apache License, Version 2.0 (the "License"); +** you may not use this file except in compliance with the License. +** You may obtain a copy of the License at +** +** http://www.apache.org/licenses/LICENSE-2.0 +** +** Unless required by applicable law or agreed to in writing, software +** distributed under the License is distributed on an "AS IS" BASIS, +** WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +** See the License for the specific language governing permissions and +** limitations under the License. +*/ +--> + +<resources xmlns:xliff="urn:oasis:names:tc:xliff:document:1.2"> + + <!-- Whether camera shutter sound is forced or not (country specific). --> + <bool name="config_camera_sound_forced">true</bool> + +</resources> diff --git a/core/res/res/values-ms/strings.xml b/core/res/res/values-ms/strings.xml index 86477c0..6124e00 100644 --- a/core/res/res/values-ms/strings.xml +++ b/core/res/res/values-ms/strings.xml @@ -1407,7 +1407,7 @@ <string name="kg_login_submit_button" msgid="5355904582674054702">"Log masuk"</string> <string name="kg_login_invalid_input" msgid="5754664119319872197">"Nama pengguna atau kata laluan tidak sah."</string> <string name="kg_login_account_recovery_hint" msgid="5690709132841752974">"Lupa nama pengguna atau kata laluan anda?"\n"Lawati"<b>"google.com/accounts/recovery"</b>"."</string> - <string name="kg_login_checking_password" msgid="5316091912653672681">"Membuka kunci SIM..."</string> + <string name="kg_login_checking_password" msgid="1052685197710252395">"Menyemak akaun…"</string> <string name="kg_too_many_failed_pin_attempts_dialog_message" msgid="8276745642049502550">"Anda telah menaip PIN anda secara salah sebanyak <xliff:g id="NUMBER_0">%d</xliff:g> kali. "\n\n"Cuba lagi dalam <xliff:g id="NUMBER_1">%d</xliff:g> saat."</string> <string name="kg_too_many_failed_password_attempts_dialog_message" msgid="7813713389422226531">"Anda telah menaip kata laluan anda secara salah sebanyak <xliff:g id="NUMBER_0">%d</xliff:g> kali. "\n\n"Cuba lagi dalam <xliff:g id="NUMBER_1">%d</xliff:g> saat."</string> <string name="kg_too_many_failed_pattern_attempts_dialog_message" msgid="74089475965050805">"Anda telah tersilap melukis corak buka kunci anda sebanyak <xliff:g id="NUMBER_0">%d</xliff:g> kali. "\n\n"Cuba lagi dalam <xliff:g id="NUMBER_1">%d</xliff:g> saat."</string> diff --git a/core/res/res/values-nb/strings.xml b/core/res/res/values-nb/strings.xml index e579f19..d805783 100644 --- a/core/res/res/values-nb/strings.xml +++ b/core/res/res/values-nb/strings.xml @@ -1407,7 +1407,7 @@ <string name="kg_login_submit_button" msgid="5355904582674054702">"Logg på"</string> <string name="kg_login_invalid_input" msgid="5754664119319872197">"Ugyldig brukernavn eller passord."</string> <string name="kg_login_account_recovery_hint" msgid="5690709132841752974">"Har du glemt brukernavnet eller passordet?"\n"Gå til "<b>"google.com/accounts/recovery"</b>"."</string> - <string name="kg_login_checking_password" msgid="5316091912653672681">"Låser opp SIM-kortet ..."</string> + <string name="kg_login_checking_password" msgid="1052685197710252395">"Sjekker kontoen ..."</string> <string name="kg_too_many_failed_pin_attempts_dialog_message" msgid="8276745642049502550">"Du har oppgitt feil PIN-kode <xliff:g id="NUMBER_0">%d</xliff:g> ganger. "\n\n"Prøv på nytt om <xliff:g id="NUMBER_1">%d</xliff:g> sekunder."</string> <string name="kg_too_many_failed_password_attempts_dialog_message" msgid="7813713389422226531">"Du har tastet inn passordet ditt feil <xliff:g id="NUMBER_0">%d</xliff:g> ganger. "\n\n"Prøv på nytt om <xliff:g id="NUMBER_1">%d</xliff:g> sekunder."</string> <string name="kg_too_many_failed_pattern_attempts_dialog_message" msgid="74089475965050805">"Du har tegnet opplåsningsmønsteret ditt feil <xliff:g id="NUMBER_0">%d</xliff:g> ganger. "\n\n"Prøv på nytt om <xliff:g id="NUMBER_1">%d</xliff:g> sekunder."</string> diff --git a/core/res/res/values-nl/strings.xml b/core/res/res/values-nl/strings.xml index 5817d7b..74cbf07 100644 --- a/core/res/res/values-nl/strings.xml +++ b/core/res/res/values-nl/strings.xml @@ -1407,7 +1407,7 @@ <string name="kg_login_submit_button" msgid="5355904582674054702">"Aanmelden"</string> <string name="kg_login_invalid_input" msgid="5754664119319872197">"Ongeldige gebruikersnaam of wachtwoord."</string> <string name="kg_login_account_recovery_hint" msgid="5690709132841752974">"Bent u uw gebruikersnaam of wachtwoord vergeten?"\n"Ga naar "<b>"google.com/accounts/recovery"</b>"."</string> - <string name="kg_login_checking_password" msgid="5316091912653672681">"Simkaart ontgrendelen…"</string> + <string name="kg_login_checking_password" msgid="1052685197710252395">"Account controleren…"</string> <string name="kg_too_many_failed_pin_attempts_dialog_message" msgid="8276745642049502550">"U heeft uw pincode <xliff:g id="NUMBER_0">%d</xliff:g> keer onjuist getypt. "\n\n"Probeer het over <xliff:g id="NUMBER_1">%d</xliff:g> seconden opnieuw."</string> <string name="kg_too_many_failed_password_attempts_dialog_message" msgid="7813713389422226531">"U heeft uw wachtwoord <xliff:g id="NUMBER_0">%d</xliff:g> keer onjuist getypt. "\n\n"Probeer het over <xliff:g id="NUMBER_1">%d</xliff:g> seconden opnieuw."</string> <string name="kg_too_many_failed_pattern_attempts_dialog_message" msgid="74089475965050805">"U heeft uw ontgrendelingspatroon <xliff:g id="NUMBER_0">%d</xliff:g> keer onjuist getekend. "\n\n"Probeer het over <xliff:g id="NUMBER_1">%d</xliff:g> seconden opnieuw."</string> diff --git a/core/res/res/values-pl/strings.xml b/core/res/res/values-pl/strings.xml index 3a8ce22..6150e0b 100644 --- a/core/res/res/values-pl/strings.xml +++ b/core/res/res/values-pl/strings.xml @@ -1375,7 +1375,7 @@ <string name="media_route_chooser_grouping_done" msgid="7966438307723317169">"Gotowe"</string> <string name="media_route_button_content_description" msgid="5758553567065145276">"Wyjście multimediów"</string> <string name="media_route_status_scanning" msgid="7279908761758293783">"Skanuję..."</string> - <string name="media_route_status_connecting" msgid="6422571716007825440">"Nawiązuję połączenie..."</string> + <string name="media_route_status_connecting" msgid="6422571716007825440">"Łączę..."</string> <string name="media_route_status_available" msgid="6983258067194649391">"Dostępne"</string> <string name="media_route_status_not_available" msgid="6739899962681886401">"Niedostępne"</string> <string name="display_manager_built_in_display_name" msgid="2583134294292563941">"Wbudowany ekran"</string> @@ -1408,7 +1408,7 @@ <string name="kg_login_submit_button" msgid="5355904582674054702">"Zaloguj się"</string> <string name="kg_login_invalid_input" msgid="5754664119319872197">"Nieprawidłowa nazwa użytkownika lub hasło."</string> <string name="kg_login_account_recovery_hint" msgid="5690709132841752974">"Nie pamiętasz nazwy użytkownika lub hasła?"\n"Wejdź na "<b>"google.com/accounts/recovery"</b>"."</string> - <string name="kg_login_checking_password" msgid="5316091912653672681">"Odblokowuję kartę SIM…"</string> + <string name="kg_login_checking_password" msgid="1052685197710252395">"Sprawdzam konto"</string> <string name="kg_too_many_failed_pin_attempts_dialog_message" msgid="8276745642049502550">"Po raz <xliff:g id="NUMBER_0">%d</xliff:g> wpisałeś nieprawidłowy PIN. "\n\n"Spróbuj ponownie za <xliff:g id="NUMBER_1">%d</xliff:g> s."</string> <string name="kg_too_many_failed_password_attempts_dialog_message" msgid="7813713389422226531">"Po raz <xliff:g id="NUMBER_0">%d</xliff:g> wpisałeś nieprawidłowe hasło. "\n\n"Spróbuj ponownie za <xliff:g id="NUMBER_1">%d</xliff:g> s."</string> <string name="kg_too_many_failed_pattern_attempts_dialog_message" msgid="74089475965050805">"Po raz <xliff:g id="NUMBER_0">%d</xliff:g> narysowałeś nieprawidłowy wzór odblokowania. "\n\n"Spróbuj ponownie za <xliff:g id="NUMBER_1">%d</xliff:g> s."</string> diff --git a/core/res/res/values-pt-rPT/strings.xml b/core/res/res/values-pt-rPT/strings.xml index 21ebe17..de48970 100644 --- a/core/res/res/values-pt-rPT/strings.xml +++ b/core/res/res/values-pt-rPT/strings.xml @@ -155,7 +155,7 @@ <string name="global_action_lock" msgid="2844945191792119712">"Bloqueio de ecrã"</string> <string name="global_action_power_off" msgid="4471879440839879722">"Desligar"</string> <string name="global_action_bug_report" msgid="7934010578922304799">"Relatório de erros"</string> - <string name="bugreport_title" msgid="2667494803742548533">"Criar um relatório de erros"</string> + <string name="bugreport_title" msgid="2667494803742548533">"Criar Rel. Erro"</string> <string name="bugreport_message" msgid="398447048750350456">"Será recolhida informação sobre o estado atual do seu dispositivo a enviar através de uma mensagem de email. Demorará algum tempo até que o relatório de erro esteja pronto para ser enviado. Aguarde um pouco."</string> <string name="global_action_toggle_silent_mode" msgid="8219525344246810925">"Modo silencioso"</string> <string name="global_action_silent_mode_on_status" msgid="3289841937003758806">"Som desactivado"</string> @@ -1407,7 +1407,7 @@ <string name="kg_login_submit_button" msgid="5355904582674054702">"Iniciar sessão"</string> <string name="kg_login_invalid_input" msgid="5754664119319872197">"Nome de utilizador ou palavra-passe inválidos."</string> <string name="kg_login_account_recovery_hint" msgid="5690709132841752974">"Esqueceu-se do nome de utilizador ou da palavra-passe?"\n"Aceda a "<b>"google.com/accounts/recovery"</b>"."</string> - <string name="kg_login_checking_password" msgid="5316091912653672681">"A desbloquear SIM..."</string> + <string name="kg_login_checking_password" msgid="1052685197710252395">"A verificar a conta…"</string> <string name="kg_too_many_failed_pin_attempts_dialog_message" msgid="8276745642049502550">"Escreveu o PIN incorretamente <xliff:g id="NUMBER_0">%d</xliff:g> vezes. "\n\n"Tente novamente dentro de <xliff:g id="NUMBER_1">%d</xliff:g> segundos."</string> <string name="kg_too_many_failed_password_attempts_dialog_message" msgid="7813713389422226531">"Escreveu a palavra-passe incorretamente <xliff:g id="NUMBER_0">%d</xliff:g> vezes. "\n\n"Tente novamente dentro de <xliff:g id="NUMBER_1">%d</xliff:g> segundos."</string> <string name="kg_too_many_failed_pattern_attempts_dialog_message" msgid="74089475965050805">"Desenhou a sua sequência de desbloqueio incorretamente <xliff:g id="NUMBER_0">%d</xliff:g> vezes. "\n\n"Tente novamente dentro de <xliff:g id="NUMBER_1">%d</xliff:g> segundos."</string> diff --git a/core/res/res/values-pt/strings.xml b/core/res/res/values-pt/strings.xml index e95ed6d..9f37c07 100644 --- a/core/res/res/values-pt/strings.xml +++ b/core/res/res/values-pt/strings.xml @@ -1407,7 +1407,7 @@ <string name="kg_login_submit_button" msgid="5355904582674054702">"Fazer login"</string> <string name="kg_login_invalid_input" msgid="5754664119319872197">"Nome de usuário ou senha inválidos."</string> <string name="kg_login_account_recovery_hint" msgid="5690709132841752974">"Esqueceu seu nome de usuário ou senha?"\n"Acesse "<b>"google.com.br/accounts/recovery"</b>"."</string> - <string name="kg_login_checking_password" msgid="5316091912653672681">"Desbloqueando SIM..."</string> + <string name="kg_login_checking_password" msgid="1052685197710252395">"Verificando a conta..."</string> <string name="kg_too_many_failed_pin_attempts_dialog_message" msgid="8276745642049502550">"Você digitou seu PIN incorretamente <xliff:g id="NUMBER_0">%d</xliff:g> vezes. "\n\n"Tente novamente em <xliff:g id="NUMBER_1">%d</xliff:g> segundos."</string> <string name="kg_too_many_failed_password_attempts_dialog_message" msgid="7813713389422226531">"Você digitou sua senha incorretamente <xliff:g id="NUMBER_0">%d</xliff:g> vezes. "\n\n"Tente novamente em <xliff:g id="NUMBER_1">%d</xliff:g> segundos."</string> <string name="kg_too_many_failed_pattern_attempts_dialog_message" msgid="74089475965050805">"Você desenhou sua sequência de desbloqueio incorretamente <xliff:g id="NUMBER_0">%d</xliff:g> vezes. "\n\n"Tente novamente em <xliff:g id="NUMBER_1">%d</xliff:g> segundos."</string> diff --git a/core/res/res/values-rm/strings.xml b/core/res/res/values-rm/strings.xml index c783c61e..5e6fc27 100644 --- a/core/res/res/values-rm/strings.xml +++ b/core/res/res/values-rm/strings.xml @@ -2268,7 +2268,7 @@ <skip /> <!-- no translation found for kg_login_account_recovery_hint (5690709132841752974) --> <skip /> - <!-- no translation found for kg_login_checking_password (5316091912653672681) --> + <!-- no translation found for kg_login_checking_password (1052685197710252395) --> <skip /> <!-- no translation found for kg_too_many_failed_pin_attempts_dialog_message (8276745642049502550) --> <skip /> diff --git a/core/res/res/values-ro/strings.xml b/core/res/res/values-ro/strings.xml index 6e6671b..051cd22 100644 --- a/core/res/res/values-ro/strings.xml +++ b/core/res/res/values-ro/strings.xml @@ -1407,7 +1407,7 @@ <string name="kg_login_submit_button" msgid="5355904582674054702">"Conectaţi-vă"</string> <string name="kg_login_invalid_input" msgid="5754664119319872197">"Nume de utilizator sau parolă nevalide."</string> <string name="kg_login_account_recovery_hint" msgid="5690709132841752974">"Aţi uitat numele de utilizator sau parola?"\n"Accesaţi "<b>"google.com/accounts/recovery"</b>"."</string> - <string name="kg_login_checking_password" msgid="5316091912653672681">"Se deblochează cardul SIM…"</string> + <string name="kg_login_checking_password" msgid="1052685197710252395">"Se verifică contul…"</string> <string name="kg_too_many_failed_pin_attempts_dialog_message" msgid="8276745642049502550">"Aţi introdus incorect codul PIN de <xliff:g id="NUMBER_0">%d</xliff:g> ori."\n\n"Încercaţi din nou peste <xliff:g id="NUMBER_1">%d</xliff:g> (de) secunde."</string> <string name="kg_too_many_failed_password_attempts_dialog_message" msgid="7813713389422226531">"Aţi introdus incorect parola de <xliff:g id="NUMBER_0">%d</xliff:g> ori. "\n\n"Încercaţi din nou peste <xliff:g id="NUMBER_1">%d</xliff:g> (de) secunde."</string> <string name="kg_too_many_failed_pattern_attempts_dialog_message" msgid="74089475965050805">"Aţi desenat incorect modelul pentru deblocare de <xliff:g id="NUMBER_0">%d</xliff:g> ori. "\n\n"Încercaţi din nou peste <xliff:g id="NUMBER_1">%d</xliff:g> (de) secunde."</string> diff --git a/core/res/res/values-ru/strings.xml b/core/res/res/values-ru/strings.xml index c547ba5..13d3e0e 100644 --- a/core/res/res/values-ru/strings.xml +++ b/core/res/res/values-ru/strings.xml @@ -1407,7 +1407,7 @@ <string name="kg_login_submit_button" msgid="5355904582674054702">"Войти"</string> <string name="kg_login_invalid_input" msgid="5754664119319872197">"Неверное имя пользователя или пароль."</string> <string name="kg_login_account_recovery_hint" msgid="5690709132841752974">"Забыли имя пользователя или пароль?"\n"Перейдите на страницу "<b>"google.com/accounts/recovery"</b>"."</string> - <string name="kg_login_checking_password" msgid="5316091912653672681">"Разблокировка SIM-карты…"</string> + <string name="kg_login_checking_password" msgid="1052685197710252395">"Проверка данных…"</string> <string name="kg_too_many_failed_pin_attempts_dialog_message" msgid="8276745642049502550">"Вы <xliff:g id="NUMBER_0">%d</xliff:g> раз неверно указали PIN-код. "\n\n"Повтор через <xliff:g id="NUMBER_1">%d</xliff:g> сек."</string> <string name="kg_too_many_failed_password_attempts_dialog_message" msgid="7813713389422226531">"Вы <xliff:g id="NUMBER_0">%d</xliff:g> раз неверно указали пароль."\n\n"Повтор через <xliff:g id="NUMBER_1">%d</xliff:g> сек."</string> <string name="kg_too_many_failed_pattern_attempts_dialog_message" msgid="74089475965050805">"Вы <xliff:g id="NUMBER_0">%d</xliff:g> раз неверно указали графический ключ."\n\n"Повтор через <xliff:g id="NUMBER_1">%d</xliff:g> сек."</string> diff --git a/core/res/res/values-sk/strings.xml b/core/res/res/values-sk/strings.xml index cfa9ebe..7c60378 100644 --- a/core/res/res/values-sk/strings.xml +++ b/core/res/res/values-sk/strings.xml @@ -1407,7 +1407,7 @@ <string name="kg_login_submit_button" msgid="5355904582674054702">"Prihlásiť sa"</string> <string name="kg_login_invalid_input" msgid="5754664119319872197">"Neplatné používateľské meno alebo heslo."</string> <string name="kg_login_account_recovery_hint" msgid="5690709132841752974">"Zabudli ste svoje používateľské meno alebo heslo?"\n" Navštívte stránky "<b>"google.com/accounts/recovery"</b>"."</string> - <string name="kg_login_checking_password" msgid="5316091912653672681">"Prebieha odomykanie karty SIM..."</string> + <string name="kg_login_checking_password" msgid="1052685197710252395">"Prebieha kontrola účtu..."</string> <string name="kg_too_many_failed_pin_attempts_dialog_message" msgid="8276745642049502550">"<xliff:g id="NUMBER_0">%d</xliff:g>-krát ste zadali nesprávny kód PIN. "\n\n"Skúste to znova o <xliff:g id="NUMBER_1">%d</xliff:g> s."</string> <string name="kg_too_many_failed_password_attempts_dialog_message" msgid="7813713389422226531">"<xliff:g id="NUMBER_0">%d</xliff:g>-krát ste zadali nesprávne heslo. "\n\n"Skúste to znova o <xliff:g id="NUMBER_1">%d</xliff:g>."</string> <string name="kg_too_many_failed_pattern_attempts_dialog_message" msgid="74089475965050805">"<xliff:g id="NUMBER_0">%d</xliff:g>-krát ste použili nesprávny bezpečnostný vzor. "\n\n"Skúste to znova o <xliff:g id="NUMBER_1">%d</xliff:g> s."</string> diff --git a/core/res/res/values-sl/strings.xml b/core/res/res/values-sl/strings.xml index 35ce1f4..bff5550 100644 --- a/core/res/res/values-sl/strings.xml +++ b/core/res/res/values-sl/strings.xml @@ -1407,7 +1407,7 @@ <string name="kg_login_submit_button" msgid="5355904582674054702">"Prijava"</string> <string name="kg_login_invalid_input" msgid="5754664119319872197">"Neveljavno uporabniško ime ali geslo."</string> <string name="kg_login_account_recovery_hint" msgid="5690709132841752974">"Ali ste pozabili uporabniško ime ali geslo?"\n"Obiščite "<b>"google.com/accounts/recovery"</b>"."</string> - <string name="kg_login_checking_password" msgid="5316091912653672681">"Odklepanje kartice SIM …"</string> + <string name="kg_login_checking_password" msgid="1052685197710252395">"Preverjanje računa ..."</string> <string name="kg_too_many_failed_pin_attempts_dialog_message" msgid="8276745642049502550">"PIN ste <xliff:g id="NUMBER_0">%d</xliff:g>-krat vnesli napačno. "\n\n"Znova poskusite čez <xliff:g id="NUMBER_1">%d</xliff:g> s."</string> <string name="kg_too_many_failed_password_attempts_dialog_message" msgid="7813713389422226531">"Geslo ste <xliff:g id="NUMBER_0">%d</xliff:g>-krat vnesli napačno. "\n\n"Znova poskusite čez <xliff:g id="NUMBER_1">%d</xliff:g> s."</string> <string name="kg_too_many_failed_pattern_attempts_dialog_message" msgid="74089475965050805">"Vzorec za odklepanje ste nepravilno narisali <xliff:g id="NUMBER_0">%d</xliff:g>-krat. "\n\n"Poskusite znova čez <xliff:g id="NUMBER_1">%d</xliff:g> s."</string> diff --git a/core/res/res/values-sr/strings.xml b/core/res/res/values-sr/strings.xml index 9273c06..2ed84ce 100644 --- a/core/res/res/values-sr/strings.xml +++ b/core/res/res/values-sr/strings.xml @@ -1407,7 +1407,7 @@ <string name="kg_login_submit_button" msgid="5355904582674054702">"Пријави ме"</string> <string name="kg_login_invalid_input" msgid="5754664119319872197">"Неважеће корисничко име или лозинка."</string> <string name="kg_login_account_recovery_hint" msgid="5690709132841752974">"Заборавили сте корисничко име или лозинку?"\n"Посетите адресу "<b>"google.com/accounts/recovery"</b>"."</string> - <string name="kg_login_checking_password" msgid="5316091912653672681">"Откључавање SIM картице…"</string> + <string name="kg_login_checking_password" msgid="1052685197710252395">"Провера налога…"</string> <string name="kg_too_many_failed_pin_attempts_dialog_message" msgid="8276745642049502550">"Унели сте PIN неисправно <xliff:g id="NUMBER_0">%d</xliff:g> пута. "\n\n"Покушајте поново за <xliff:g id="NUMBER_1">%d</xliff:g> секунде(и)."</string> <string name="kg_too_many_failed_password_attempts_dialog_message" msgid="7813713389422226531">"Унели сте лозинку неисправно <xliff:g id="NUMBER_0">%d</xliff:g> пута. "\n\n"Покушајте поново за <xliff:g id="NUMBER_1">%d</xliff:g> секунде(и)."</string> <string name="kg_too_many_failed_pattern_attempts_dialog_message" msgid="74089475965050805">"Нацртали сте шаблон за откључавање неисправно <xliff:g id="NUMBER_0">%d</xliff:g> пута. "\n\n"Покушајте поново за <xliff:g id="NUMBER_1">%d</xliff:g> секунде(и)."</string> diff --git a/core/res/res/values-sv/strings.xml b/core/res/res/values-sv/strings.xml index 37187e0..c6fee48 100644 --- a/core/res/res/values-sv/strings.xml +++ b/core/res/res/values-sv/strings.xml @@ -1407,7 +1407,7 @@ <string name="kg_login_submit_button" msgid="5355904582674054702">"Logga in"</string> <string name="kg_login_invalid_input" msgid="5754664119319872197">"Ogiltigt användarnamn eller lösenord."</string> <string name="kg_login_account_recovery_hint" msgid="5690709132841752974">"Har du glömt ditt användarnamn eller lösenord?"\n"Besök "<b>"google.com/accounts/recovery"</b>"."</string> - <string name="kg_login_checking_password" msgid="5316091912653672681">"SIM-kortet låses upp …"</string> + <string name="kg_login_checking_password" msgid="1052685197710252395">"Kontot kontrolleras …"</string> <string name="kg_too_many_failed_pin_attempts_dialog_message" msgid="8276745642049502550">"Du har angett fel lösenord <xliff:g id="NUMBER_0">%d</xliff:g> gånger. "\n\n"Försök igen om <xliff:g id="NUMBER_1">%d</xliff:g> sekunder."</string> <string name="kg_too_many_failed_password_attempts_dialog_message" msgid="7813713389422226531">"Du har angett fel lösenord <xliff:g id="NUMBER_0">%d</xliff:g> gånger. "\n\n"Försök igen om <xliff:g id="NUMBER_1">%d</xliff:g> sekunder."</string> <string name="kg_too_many_failed_pattern_attempts_dialog_message" msgid="74089475965050805">"Du har ritat ditt grafiska lösenord fel <xliff:g id="NUMBER_0">%d</xliff:g> gånger. "\n\n"Försök igen om <xliff:g id="NUMBER_1">%d</xliff:g> sekunder."</string> diff --git a/core/res/res/values-sw/strings.xml b/core/res/res/values-sw/strings.xml index 39d54a6..65f429f 100644 --- a/core/res/res/values-sw/strings.xml +++ b/core/res/res/values-sw/strings.xml @@ -1407,7 +1407,7 @@ <string name="kg_login_submit_button" msgid="5355904582674054702">"Ingia"</string> <string name="kg_login_invalid_input" msgid="5754664119319872197">"Jina la mtumiaji au nenosiri batili."</string> <string name="kg_login_account_recovery_hint" msgid="5690709132841752974">"Je, umesahau jina lako la mtumiaji au nenosiri?"\n"Tembela "<b>"Bgoogle.com/accounts/recovery"</b>"."</string> - <string name="kg_login_checking_password" msgid="5316091912653672681">"Inafungua SIM…"</string> + <string name="kg_login_checking_password" msgid="1052685197710252395">"Inakagua akaunti…"</string> <string name="kg_too_many_failed_pin_attempts_dialog_message" msgid="8276745642049502550">"Umeingiza nenosiri lako kwa makosa mara <xliff:g id="NUMBER_0">%d</xliff:g>. "\n\n" Jaribu tena baada ya sekunde <xliff:g id="NUMBER_1">%d</xliff:g>."</string> <string name="kg_too_many_failed_password_attempts_dialog_message" msgid="7813713389422226531">"Umeingiza nenosiri lako kwa makosa mara <xliff:g id="NUMBER_0">%d</xliff:g>. "\n\n" Jaribu tena baada ya sekunde <xliff:g id="NUMBER_1">%d</xliff:g>."</string> <string name="kg_too_many_failed_pattern_attempts_dialog_message" msgid="74089475965050805">"Umechora ruwaza yako ya kufunga kwa makosa mara <xliff:g id="NUMBER_0">%d</xliff:g>. "\n\n" Jaribu tena baada ya sekunde <xliff:g id="NUMBER_1">%d</xliff:g>."</string> diff --git a/core/res/res/values-th/strings.xml b/core/res/res/values-th/strings.xml index eba41a5..2daf807 100644 --- a/core/res/res/values-th/strings.xml +++ b/core/res/res/values-th/strings.xml @@ -1407,7 +1407,7 @@ <string name="kg_login_submit_button" msgid="5355904582674054702">"ลงชื่อเข้าใช้"</string> <string name="kg_login_invalid_input" msgid="5754664119319872197">"ชื่อผู้ใช้หรือรหัสผ่านไม่ถูกต้อง"</string> <string name="kg_login_account_recovery_hint" msgid="5690709132841752974">"หากลืมชื่อผู้ใช้หรือรหัสผ่าน"\n"โปรดไปที่ "<b>"google.com/accounts/recovery"</b></string> - <string name="kg_login_checking_password" msgid="5316091912653672681">"กำลังปลดล็อกซิม…"</string> + <string name="kg_login_checking_password" msgid="1052685197710252395">"กำลังตรวจสอบบัญชี…"</string> <string name="kg_too_many_failed_pin_attempts_dialog_message" msgid="8276745642049502550">"คุณพิมพ์ PIN ไม่ถูกต้องไป <xliff:g id="NUMBER_0">%d</xliff:g> ครั้งแล้ว "\n\n"โปรดลองอีกครั้งใน <xliff:g id="NUMBER_1">%d</xliff:g> วินาที"</string> <string name="kg_too_many_failed_password_attempts_dialog_message" msgid="7813713389422226531">"คุณพิมพ์รหัสผ่านไม่ถูกต้องไป <xliff:g id="NUMBER_0">%d</xliff:g> ครั้งแล้ว "\n\n"โปรดลองอีกครั้งใน <xliff:g id="NUMBER_1">%d</xliff:g> วินาที"</string> <string name="kg_too_many_failed_pattern_attempts_dialog_message" msgid="74089475965050805">"คุณวาดรูปแบบการปลดล็อกไม่ถูกต้องไป <xliff:g id="NUMBER_0">%d</xliff:g> ครั้งแล้ว "\n\n"โปรดลองอีกครั้งใน <xliff:g id="NUMBER_1">%d</xliff:g> วินาที"</string> diff --git a/core/res/res/values-tl/strings.xml b/core/res/res/values-tl/strings.xml index 617e812..a25caed 100644 --- a/core/res/res/values-tl/strings.xml +++ b/core/res/res/values-tl/strings.xml @@ -1407,7 +1407,7 @@ <string name="kg_login_submit_button" msgid="5355904582674054702">"Mag-sign in"</string> <string name="kg_login_invalid_input" msgid="5754664119319872197">"Di-wastong username o password."</string> <string name="kg_login_account_recovery_hint" msgid="5690709132841752974">"Nakalimutan ang iyong username o password?"\n"Bisitahin ang "<b>"google.com/accounts/recovery"</b>"."</string> - <string name="kg_login_checking_password" msgid="5316091912653672681">"Ina-unlock ang SIM…"</string> + <string name="kg_login_checking_password" msgid="1052685197710252395">"Tinitingnan ang account…"</string> <string name="kg_too_many_failed_pin_attempts_dialog_message" msgid="8276745642049502550">"Na-type mo nang hindi tama ang iyong PIN nang <xliff:g id="NUMBER_0">%d</xliff:g> (na) beses. "\n\n"Subukang muli sa loob ng <xliff:g id="NUMBER_1">%d</xliff:g> (na) segundo."</string> <string name="kg_too_many_failed_password_attempts_dialog_message" msgid="7813713389422226531">"Na-type mo nang hindi tama ang iyong password nang <xliff:g id="NUMBER_0">%d</xliff:g> (na) beses. "\n\n"Subukang muli sa loob ng <xliff:g id="NUMBER_1">%d</xliff:g> (na) segundo."</string> <string name="kg_too_many_failed_pattern_attempts_dialog_message" msgid="74089475965050805">"Naguhit mo nang hindi tama ang iyong pattern sa pag-unlock nang <xliff:g id="NUMBER_0">%d</xliff:g> (na) beses. "\n\n"Subukang muli sa loob ng <xliff:g id="NUMBER_1">%d</xliff:g> (na) segundo."</string> diff --git a/core/res/res/values-tr/strings.xml b/core/res/res/values-tr/strings.xml index 62672ae..289919a 100644 --- a/core/res/res/values-tr/strings.xml +++ b/core/res/res/values-tr/strings.xml @@ -1407,7 +1407,7 @@ <string name="kg_login_submit_button" msgid="5355904582674054702">"Oturum aç"</string> <string name="kg_login_invalid_input" msgid="5754664119319872197">"Geçersiz kullanıcı adı veya şifre."</string> <string name="kg_login_account_recovery_hint" msgid="5690709132841752974">"Kullanıcı adınızı veya şifrenizi mi unuttunuz?"\n<b>"google.com/accounts/recovery"</b>" adresini ziyaret edin."</string> - <string name="kg_login_checking_password" msgid="5316091912653672681">"SIM kartın kilidi açılıyor…"</string> + <string name="kg_login_checking_password" msgid="1052685197710252395">"Hesap denetleniyor…"</string> <string name="kg_too_many_failed_pin_attempts_dialog_message" msgid="8276745642049502550">"PIN kodunuzu <xliff:g id="NUMBER_0">%d</xliff:g> kez yanlış girdiniz. "\n\n"<xliff:g id="NUMBER_1">%d</xliff:g> saniye içinde tekrar deneyin."</string> <string name="kg_too_many_failed_password_attempts_dialog_message" msgid="7813713389422226531">"Şifrenizi <xliff:g id="NUMBER_0">%d</xliff:g> kez yanlış yazdınız. "\n\n"<xliff:g id="NUMBER_1">%d</xliff:g> saniye içinde tekrar deneyin."</string> <string name="kg_too_many_failed_pattern_attempts_dialog_message" msgid="74089475965050805">"Kilit açma deseninizi <xliff:g id="NUMBER_0">%d</xliff:g> kez yanlış çizdiniz. "\n\n"<xliff:g id="NUMBER_1">%d</xliff:g> saniye içinde tekrar deneyin."</string> diff --git a/core/res/res/values-uk/strings.xml b/core/res/res/values-uk/strings.xml index 0813134..730d7bd 100644 --- a/core/res/res/values-uk/strings.xml +++ b/core/res/res/values-uk/strings.xml @@ -1407,7 +1407,7 @@ <string name="kg_login_submit_button" msgid="5355904582674054702">"Увійти"</string> <string name="kg_login_invalid_input" msgid="5754664119319872197">"Недійсне ім’я користувача чи пароль."</string> <string name="kg_login_account_recovery_hint" msgid="5690709132841752974">"Не пам’ятаєте ім’я користувача чи пароль?"\n"Відвідайте сторінку "<b>"google.com/accounts/recovery"</b>"."</string> - <string name="kg_login_checking_password" msgid="5316091912653672681">"Розблокування SIM-карти…"</string> + <string name="kg_login_checking_password" msgid="1052685197710252395">"Перевірка облікового запису…"</string> <string name="kg_too_many_failed_pin_attempts_dialog_message" msgid="8276745642049502550">"PIN-код неправильно введено стільки разів: <xliff:g id="NUMBER_0">%d</xliff:g>. "\n\n"Повторіть спробу через <xliff:g id="NUMBER_1">%d</xliff:g> с."</string> <string name="kg_too_many_failed_password_attempts_dialog_message" msgid="7813713389422226531">"Пароль неправильно введено стільки разів: <xliff:g id="NUMBER_0">%d</xliff:g>. "\n\n"Повторіть спробу через <xliff:g id="NUMBER_1">%d</xliff:g> с."</string> <string name="kg_too_many_failed_pattern_attempts_dialog_message" msgid="74089475965050805">"Ключ розблокування неправильно намальовано стільки разів: <xliff:g id="NUMBER_0">%d</xliff:g>. "\n\n"Повторіть спробу через <xliff:g id="NUMBER_1">%d</xliff:g> с."</string> diff --git a/core/res/res/values-vi/strings.xml b/core/res/res/values-vi/strings.xml index 0f3df28..495d056 100644 --- a/core/res/res/values-vi/strings.xml +++ b/core/res/res/values-vi/strings.xml @@ -813,7 +813,7 @@ <string name="keyguard_accessibility_widget_changed" msgid="5678624624681400191">"%1$s. Tiện ích %2$d trong số %3$d."</string> <string name="keyguard_accessibility_user_selector" msgid="1226798370913698896">"Bộ chọn người dùng"</string> <string name="keyguard_accessibility_status" msgid="8008264603935930611">"Trạng thái"</string> - <string name="keygaurd_accessibility_media_controls" msgid="262209654292161806">"Kiểm soát phương tiện"</string> + <string name="keygaurd_accessibility_media_controls" msgid="262209654292161806">"Điều khiển phương tiện"</string> <string name="password_keyboard_label_symbol_key" msgid="992280756256536042">"?123"</string> <string name="password_keyboard_label_alpha_key" msgid="8001096175167485649">"ABC"</string> <string name="password_keyboard_label_alt_key" msgid="1284820942620288678">"ALT"</string> @@ -1407,7 +1407,7 @@ <string name="kg_login_submit_button" msgid="5355904582674054702">"Đăng nhập"</string> <string name="kg_login_invalid_input" msgid="5754664119319872197">"Tên người dùng hoặc mật khẩu không hợp lệ."</string> <string name="kg_login_account_recovery_hint" msgid="5690709132841752974">"Bạn quên tên người dùng hoặc mật khẩu?"\n"Hãy truy cập "<b>"google.com/accounts/recovery"</b>"."</string> - <string name="kg_login_checking_password" msgid="5316091912653672681">"Đang mở khóa SIM…"</string> + <string name="kg_login_checking_password" msgid="1052685197710252395">"Đang kiểm tra tài khoản…"</string> <string name="kg_too_many_failed_pin_attempts_dialog_message" msgid="8276745642049502550">"Bạn đã <xliff:g id="NUMBER_0">%d</xliff:g> lần nhập sai mã PIN của mình. Hãy "\n\n"thử lại sau <xliff:g id="NUMBER_1">%d</xliff:g> giây."</string> <string name="kg_too_many_failed_password_attempts_dialog_message" msgid="7813713389422226531">"Bạn đã <xliff:g id="NUMBER_0">%d</xliff:g> lần nhập sai mật khẩu của mình. Hãy "\n\n"thử lại sau <xliff:g id="NUMBER_1">%d</xliff:g> giây."</string> <string name="kg_too_many_failed_pattern_attempts_dialog_message" msgid="74089475965050805">"Bạn đã <xliff:g id="NUMBER_0">%d</xliff:g> lần vẽ không chính xác hình mở khóa của mình. Hãy "\n\n"thử lại sau <xliff:g id="NUMBER_1">%d</xliff:g> giây."</string> diff --git a/core/res/res/values-zh-rCN/strings.xml b/core/res/res/values-zh-rCN/strings.xml index 9768629..d98ea83 100644 --- a/core/res/res/values-zh-rCN/strings.xml +++ b/core/res/res/values-zh-rCN/strings.xml @@ -1407,7 +1407,7 @@ <string name="kg_login_submit_button" msgid="5355904582674054702">"登录"</string> <string name="kg_login_invalid_input" msgid="5754664119319872197">"用户名或密码无效。"</string> <string name="kg_login_account_recovery_hint" msgid="5690709132841752974">"忘记了用户名或密码?"\n"请访问 "<b>"google.com/accounts/recovery"</b>"。"</string> - <string name="kg_login_checking_password" msgid="5316091912653672681">"正在解锁 SIM 卡…"</string> + <string name="kg_login_checking_password" msgid="1052685197710252395">"正在检查帐户…"</string> <string name="kg_too_many_failed_pin_attempts_dialog_message" msgid="8276745642049502550">"您已经 <xliff:g id="NUMBER_0">%d</xliff:g> 次错误地输入了 PIN。"\n\n"请在 <xliff:g id="NUMBER_1">%d</xliff:g> 秒后重试。"</string> <string name="kg_too_many_failed_password_attempts_dialog_message" msgid="7813713389422226531">"您已经 <xliff:g id="NUMBER_0">%d</xliff:g> 次错误地输入了密码。"\n\n"请在 <xliff:g id="NUMBER_1">%d</xliff:g> 秒后重试。"</string> <string name="kg_too_many_failed_pattern_attempts_dialog_message" msgid="74089475965050805">"您已经 <xliff:g id="NUMBER_0">%d</xliff:g> 次错误地绘制了解锁图案。"\n\n"请在 <xliff:g id="NUMBER_1">%d</xliff:g> 秒后重试。"</string> diff --git a/core/res/res/values-zh-rTW/strings.xml b/core/res/res/values-zh-rTW/strings.xml index 2185d68..8d00574 100644 --- a/core/res/res/values-zh-rTW/strings.xml +++ b/core/res/res/values-zh-rTW/strings.xml @@ -1391,14 +1391,14 @@ <string name="kg_sim_pin_instructions" msgid="2319508550934557331">"輸入 SIM PIN"</string> <string name="kg_pin_instructions" msgid="2377242233495111557">"輸入 PIN"</string> <string name="kg_password_instructions" msgid="5753646556186936819">"輸入密碼"</string> - <string name="kg_puk_enter_puk_hint" msgid="453227143861735537">"SIM 卡已遭停用,需輸入 PUK 碼才能繼續使用。詳情請洽您的行動通訊業者。"</string> + <string name="kg_puk_enter_puk_hint" msgid="453227143861735537">"SIM 卡已遭停用,必須輸入 PUK 碼才能繼續使用。詳情請洽您的行動通訊業者。"</string> <string name="kg_puk_enter_pin_hint" msgid="7871604527429602024">"輸入所需的 PIN 碼"</string> <string name="kg_enter_confirm_pin_hint" msgid="325676184762529976">"確認所需的 PIN 碼"</string> <string name="kg_sim_unlock_progress_dialog_message" msgid="8950398016976865762">"正在解除 SIM 卡鎖定..."</string> <string name="kg_password_wrong_pin_code" msgid="1139324887413846912">"PIN 碼不正確。"</string> <string name="kg_invalid_sim_pin_hint" msgid="8795159358110620001">"請輸入 4 到 8 碼的 PIN。"</string> - <string name="kg_invalid_sim_puk_hint" msgid="7553388325654369575">"PUK 碼應為 8 個以上數字。"</string> - <string name="kg_invalid_puk" msgid="3638289409676051243">"重新輸入正確的 PUK 碼。如果嘗試錯誤次數過多,SIM 卡將會永久停用。"</string> + <string name="kg_invalid_sim_puk_hint" msgid="7553388325654369575">"PUK 碼至少必須為 8 碼。"</string> + <string name="kg_invalid_puk" msgid="3638289409676051243">"重新輸入正確的 PUK 碼。如果錯誤次數過多,SIM 卡將會永久停用。"</string> <string name="kg_invalid_confirm_pin_hint" product="default" msgid="7003469261464593516">"PIN 碼不符"</string> <string name="kg_login_too_many_attempts" msgid="6486842094005698475">"圖形嘗試次數過多"</string> <string name="kg_login_instructions" msgid="1100551261265506448">"如要解除鎖定,請使用 Google 帳戶登入。"</string> @@ -1407,7 +1407,7 @@ <string name="kg_login_submit_button" msgid="5355904582674054702">"登入"</string> <string name="kg_login_invalid_input" msgid="5754664119319872197">"使用者名稱或密碼無效。"</string> <string name="kg_login_account_recovery_hint" msgid="5690709132841752974">"忘了使用者名稱或密碼?"\n"請前往 "<b>"google.com/accounts/recovery"</b>"。"</string> - <string name="kg_login_checking_password" msgid="5316091912653672681">"正在解除 SIM 卡鎖定…"</string> + <string name="kg_login_checking_password" msgid="1052685197710252395">"正在檢查帳戶…"</string> <string name="kg_too_many_failed_pin_attempts_dialog_message" msgid="8276745642049502550">"您的 PIN 已輸錯 <xliff:g id="NUMBER_0">%d</xliff:g> 次。"\n\n"請在 <xliff:g id="NUMBER_1">%d</xliff:g> 秒後再試一次。"</string> <string name="kg_too_many_failed_password_attempts_dialog_message" msgid="7813713389422226531">"您的密碼已輸錯 <xliff:g id="NUMBER_0">%d</xliff:g> 次。"\n\n"請在 <xliff:g id="NUMBER_1">%d</xliff:g> 秒後再試一次。"</string> <string name="kg_too_many_failed_pattern_attempts_dialog_message" msgid="74089475965050805">"您的解鎖圖形已畫錯 <xliff:g id="NUMBER_0">%d</xliff:g> 次。"\n\n"請在 <xliff:g id="NUMBER_1">%d</xliff:g> 秒後再試一次。"</string> @@ -1418,7 +1418,7 @@ <string name="kg_failed_attempts_almost_at_login" product="tablet" msgid="3253575572118914370">"您的解鎖圖形已畫錯 <xliff:g id="NUMBER_0">%d</xliff:g> 次,如果再嘗試 <xliff:g id="NUMBER_1">%d</xliff:g> 次仍未成功,系統就會要求您透過電子郵件帳戶解除平板電腦的鎖定狀態。"\n\n"請在 <xliff:g id="NUMBER_2">%d</xliff:g> 秒後再試一次。"</string> <string name="kg_failed_attempts_almost_at_login" product="default" msgid="1437638152015574839">"您的解鎖圖形已畫錯 <xliff:g id="NUMBER_0">%d</xliff:g> 次,如果再嘗試 <xliff:g id="NUMBER_1">%d</xliff:g> 次仍未成功,系統就會要求您透過電子郵件帳戶解除手機的鎖定狀態。"\n\n"請在 <xliff:g id="NUMBER_2">%d</xliff:g> 秒後再試一次。"</string> <string name="safe_media_volume_warning" product="default" msgid="7382971871993371648">"要將音量調高到安全等級以上嗎?"\n"長時間聆聽偏高音量可能會損害您的聽力。"</string> - <string name="continue_to_enable_accessibility" msgid="2184747411804432885">"持續使用兩指按住即可啟用協助工具。"</string> + <string name="continue_to_enable_accessibility" msgid="2184747411804432885">"持續用兩指按住即可啟用協助工具。"</string> <string name="accessibility_enabled" msgid="1381972048564547685">"協助工具已啟用。"</string> <string name="enable_accessibility_canceled" msgid="3833923257966635673">"協助工具已取消。"</string> <string name="user_switched" msgid="3768006783166984410">"目前的使用者是 <xliff:g id="NAME">%1$s</xliff:g>。"</string> diff --git a/core/res/res/values-zu/strings.xml b/core/res/res/values-zu/strings.xml index 2c9290c..db7987d 100644 --- a/core/res/res/values-zu/strings.xml +++ b/core/res/res/values-zu/strings.xml @@ -1407,7 +1407,7 @@ <string name="kg_login_submit_button" msgid="5355904582674054702">"Ngena ngemvume"</string> <string name="kg_login_invalid_input" msgid="5754664119319872197">"Igama lomsebezisi elingalungile noma iphasiwedi."</string> <string name="kg_login_account_recovery_hint" msgid="5690709132841752974">"Ukhohlwe igama lomsebenzisi noma iphasiwedi?"\n"Vakashela"<b>"google.com/accounts/recovery"</b></string> - <string name="kg_login_checking_password" msgid="5316091912653672681">"Ivula i-SIM…"</string> + <string name="kg_login_checking_password" msgid="1052685197710252395">"Ukuhlola i-akhawunti…"</string> <string name="kg_too_many_failed_pin_attempts_dialog_message" msgid="8276745642049502550">"Ubhale iphinikhodi ykho ngendlela engafanele izikhathi ezingu-<xliff:g id="NUMBER_0">%d</xliff:g>. "\n\n"Zama futhi emasekhondini angu-<xliff:g id="NUMBER_1">%d</xliff:g>."</string> <string name="kg_too_many_failed_password_attempts_dialog_message" msgid="7813713389422226531">"Ubhale iphasiwedi yakho ngendlela engafanele <xliff:g id="NUMBER_0">%d</xliff:g> izikhathi. "\n\n"Zama futhi emasekhondini angu-<xliff:g id="NUMBER_1">%d</xliff:g>."</string> <string name="kg_too_many_failed_pattern_attempts_dialog_message" msgid="74089475965050805">"Udwebe iphathini yakho yokuvula ngendlela engafanele-<xliff:g id="NUMBER_0">%d</xliff:g>. "\n\n" Zama futhi emasekhondini angu-<xliff:g id="NUMBER_1">%d</xliff:g>"</string> diff --git a/core/res/res/values/config.xml b/core/res/res/values/config.xml index 16960c8..4698002 100755 --- a/core/res/res/values/config.xml +++ b/core/res/res/values/config.xml @@ -941,6 +941,10 @@ reported by the hardware. --> <dimen name="config_minScalingSpan">27mm</dimen> + <!-- Minimum accepted value for touchMajor while scaling. This may be tuned + per-device in overlays. --> + <dimen name="config_minScalingTouchMajor">48dp</dimen> + <!-- Safe headphone volume index. When music stream volume is below this index the SPL on headphone output is compliant to EN 60950 requirements for portable music players. --> @@ -981,4 +985,12 @@ --> <bool name="config_wifiDisplaySupportsProtectedBuffers">false</bool> + <!-- Whether camera shutter sound is forced or not (country specific). --> + <bool name="config_camera_sound_forced">false</bool> + + <!-- Set to true if we need to not prefer an APN. + This is being added to enable a simple scenario of pre-paid + provisioning on some carriers, working around a bug (7305641) + where if the preferred is used we don't try the others. --> + <bool name="config_dontPreferApn">false</bool> </resources> 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/res/res/values/symbols.xml b/core/res/res/values/symbols.xml index 281d92a..c48de1f 100644 --- a/core/res/res/values/symbols.xml +++ b/core/res/res/values/symbols.xml @@ -275,6 +275,8 @@ <java-symbol type="bool" name="config_enableWifiDisplay" /> <java-symbol type="bool" name="config_useDevInputEventForAudioJack" /> <java-symbol type="bool" name="config_safe_media_volume_enabled" /> + <java-symbol type="bool" name="config_camera_sound_forced" /> + <java-symbol type="bool" name="config_dontPreferApn" /> <java-symbol type="integer" name="config_cursorWindowSize" /> <java-symbol type="integer" name="config_longPressOnPowerBehavior" /> @@ -1149,6 +1151,7 @@ <java-symbol type="string" name="bluetooth_a2dp_audio_route_name" /> <java-symbol type="dimen" name="config_minScalingSpan" /> + <java-symbol type="dimen" name="config_minScalingTouchMajor" /> <!-- From android.policy --> <java-symbol type="anim" name="app_starting_exit" /> 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/about/monetizing.jd b/docs/html/distribute/googleplay/about/monetizing.jd index d5c6dfa..47d5266 100644 --- a/docs/html/distribute/googleplay/about/monetizing.jd +++ b/docs/html/distribute/googleplay/about/monetizing.jd @@ -76,7 +76,7 @@ games, or other content. <p>The payment methods available to users worldwide may vary, based on location, carrier network, and other factors.</p> -<div style="float:left;margin-right:2em;margin-top:1em;width:220px;"> +<div style="float:left;margin-right:2em;margin-top:3em;margin-bottom:1em;width:220px;"> <img src="{@docRoot}images/gp-subs.png" style="width:220px"> </div> @@ -97,7 +97,7 @@ targeted advertising.</p> <ul> <li>Free (no charge to download)</li> <li>Priced (user charged before download)</li> -<li>In-App products and subscriptions</li> +<li>In-app products and subscriptions</li> </ul> </div> </div> @@ -111,6 +111,9 @@ could set a nominal price for your app at download and sell value add-ons, gameplay levels, and upgrades as in-app products. The only restriction is that free apps must remain free (to download) for the life of the app.</p> +<p>For details about in-app products or subscriptions, +see <a href="/guide/google/play/billing/index.html">Google Play In-app Billing</a>.</p> + <h2 id="buyer-currency" style="margin-top:1.5em;">Flexible pricing in the currencies of your customers</h2> <div style="float:right;margin-left:18px;border:1px solid #DDD;"> 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 — 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/docs/html/guide/google/play/billing/billing_subscriptions.jd b/docs/html/guide/google/play/billing/billing_subscriptions.jd index ae12951..68eda19 100755 --- a/docs/html/guide/google/play/billing/billing_subscriptions.jd +++ b/docs/html/guide/google/play/billing/billing_subscriptions.jd @@ -12,6 +12,7 @@ parent.link=index.html <li><a href="#publishing">Subscription publishing and unpublishing</a></li> <li><a href="#pricing">Subscription pricing</a></li> <li><a href="#user-billing">User billing</a></li> + <li><a href="#trials">Free trial period</a></li> <li><a href="#cancellation">Subscription cancellation</a></li> <li><a href="#uninstallation">App uninstallation</a></li> <li><a href="#refunds">Refunds</a></li> @@ -94,8 +95,10 @@ digital content, from any type of app or game.</p> <p>As with other in-app products, you configure and publish subscriptions using the Developer Console and then sell them from inside apps installed on an Android-powered devices. In the Developer console, you create subscription -products and add them to a product list, setting a price for each, choosing a -billing interval of monthly or annually, and then publishing. In your apps, it’s +products and add them to a product list, then set a price and optional trial +period for each, choose a billing interval (monthly or annual), and then publish.</p> + +<p>In your apps, it’s straightforward to add support for subscription purchases. The implementation extends the standard In-app Billing API to support a new product type but uses the same communication model, data structures, and user interactions as for @@ -145,6 +148,7 @@ subscription and if so, allow access to your content. </p> <li>You can set up subscriptions with either monthly or annual billing</li> <li>You can sell multiple subscription items in an app with various billing intervals or prices, such as for promotions</li> + <li>You can offer a configurable trial period for any subscription. <span class="new" style="font-size:.78em;">New!</span></li> <li>Users purchase your subscriptions from inside your apps, rather than directly from Google Play</li> <li>Users manage their purchased subscriptions from the My Apps screen in @@ -251,6 +255,41 @@ notify your backend servers of subscription purchases, tokens, and any billing errors that may occur. Your backend servers can use the server-side API to query and update your records and follow up with customers directly, if needed.</p> +<h3 id="trials">Free Trial Period</h3> + +<p>For any subscription, you can set up a free trial period that lets users +try your subscription content before buying it. The trial period +runs for the period of time that you set and then automatically converts to a full subscription +managed according to the subscription's billing interval and price.</p> + +<p>To take advantage of a free trial, a user must "purchase" the full +subscription through the standard In-app Billing flow, providing a valid form of +payment to use for billing and completing the normal purchase transaction. +However, the user is not charged any money, since the initial period corresponds +to the free trial. Instead, Google Play records a transaction of $0.00 and the +subscription is marked as purchased for the duration of the trial period or +until cancellation. When the transaction is complete, Google Play notifies users +by email that they have purchased a subscription that includes a free trial +period and that the initial charge was $0.00. </p> + +<p>When the trial period ends, Google Play automatically initiates billing +against the credit card that the user provided during the initial purchase, at the amount set +for the full subscription, and continuing at the subscription interval. If +necessary, the user can cancel the subscription at any time during the trial +period. In this case, Google Play <em>marks the subscription as expired immediately</em>, +rather than waiting until the end of the trial period. The user has not +paid for the trial period and so is not entitled to continued access after +cancellation.</p> + +<p>You can set up a trial period for a subscription in the Developer Console, +without needing to modify or update your APK. Just locate and edit the +subscription in your product list, set a valid number of days for the trial +(must be 7 days or longer), and publish. You can change the period any time, +although note that Google Play does not apply the change to users who have +already "purchased" a trial period for the subscription. Only new subscription +purchases will use the updated trial period. You can create one free trial +period per subscription product.</p> + <h3 id="cancellation">Subscription cancellation</h3> <p>Users can view the status of all of their subscriptions and cancel them if diff --git a/docs/html/guide/google/play/billing/index.jd b/docs/html/guide/google/play/billing/index.jd index a33b199..134140d 100755 --- a/docs/html/guide/google/play/billing/index.jd +++ b/docs/html/guide/google/play/billing/index.jd @@ -42,10 +42,8 @@ and features, and more. You can use In-app Billing to sell products as</p> <div class="sidebox-wrapper"> <div class="sidebox"> - <h2>Support for subscriptions <span class="new">New!</span></h2> - <p>In-app Billing now lets you sell subscriptions in your apps, as well as standard in-app products. - For details on how to sell subscriptions to content, services, and features, see the - <a href="{@docRoot}guide/google/play/billing/billing_subscriptions.html">Subscriptions</a> documentation.</p> + <p><strong>Free trials for subscriptions</strong> <span class="new" style="font-size:.78em;">New!</span></p> + <p>You can now offer users a configurable <a href="{@docRoot}guide/google/play/billing/billing_subscriptions.html#trials">free trial period</a> for your in-app subscriptions. You can set up trials with a simple change in the Developer Console—no change to your app code is needed. </div> </div> diff --git a/docs/html/guide/topics/connectivity/nfc/nfc.jd b/docs/html/guide/topics/connectivity/nfc/nfc.jd index 51c7bee..5011872 100644 --- a/docs/html/guide/topics/connectivity/nfc/nfc.jd +++ b/docs/html/guide/topics/connectivity/nfc/nfc.jd @@ -318,8 +318,8 @@ other two intents, giving the user a better experience.</p> </pre> </li> - <li>The <code>uses-feature</code> element so that your application shows up in Google -Play only for devices that have NFC hardware: + <li>The <code>uses-feature</code> element so that your application shows up in Google Play + only for devices that have NFC hardware: <pre> <uses-feature android:name="android.hardware.nfc" android:required="true" /> </pre> @@ -511,13 +511,24 @@ contain the payload and allow you to enumerate the tag's technologies:</p> <h2 id="creating-records">Creating Common Types of NDEF Records</h2> <p>This section describes how to create common types of NDEF records to help you when writing to -NFC tags or sending data with Android Beam. It also describes how to create the corresponding +NFC tags or sending data with Android Beam. Starting with Android 4.0 (API level 14), the +{@link android.nfc.NdefRecord#createUri createUri()} method is available to help you create +URI records automatically. Starting in Android 4.1 (API level 16), {@link android.nfc.NdefRecord#createExternal createExternal()} +and {@link android.nfc.NdefRecord#createMime createMime()} are available to help you create +MIME and external type NDEF records. Use these helper methods whenever possible to avoid mistakes +when manually creating NDEF records.</p> + +<p> +This section also describes how to create the corresponding intent filter for the record. All of these NDEF record examples should be in the first NDEF record of the NDEF message that you are writing to a tag or beaming.</p> <h3 id="abs-uri">TNF_ABSOLUTE_URI</h3> -<p>Given the following {@link android.nfc.NdefRecord#TNF_ABSOLUTE_URI} NDEF record, which is -stored as the first record inside of an {@link android.nfc.NdefMessage}:</p> +<p class="note"><strong>Note:</strong> We recommend that you use the + <a href="#well-known-uri"><code>RTD_URI</code></a> type instead + of {@link android.nfc.NdefRecord#TNF_ABSOLUTE_URI}, because it is more efficient.</p> + +<p>You can create a {@link android.nfc.NdefRecord#TNF_ABSOLUTE_URI} NDEF record in the following way:</p> <pre> NdefRecord uriRecord = new NdefRecord( @@ -526,7 +537,7 @@ NdefRecord uriRecord = new NdefRecord( new byte[0], new byte[0]); </pre> -<p>the intent filter would look like this:</p> +<p>The intent filter for the previous NDEF record would look like this:</p> <pre> <intent-filter> <action android:name="android.nfc.action.NDEF_DISCOVERED" /> @@ -537,32 +548,35 @@ NdefRecord uriRecord = new NdefRecord( </intent-filter> </pre> - <h3 id="mime">TNF_MIME_MEDIA</h3> -<p>Given the following {@link android.nfc.NdefRecord#TNF_MIME_MEDIA} NDEF record, which is stored as -the first record inside -of an {@link android.nfc.NdefMessage}:</p> +<p>You can create a {@link android.nfc.NdefRecord#TNF_MIME_MEDIA} NDEF record in the following ways.</p> + +<p>Using the {@link android.nfc.NdefRecord#createMime createMime()} method:</p> +<pre> +NdefRecord mimeRecord = NdefRecord.createMime("application/vnd.com.example.android.beam", + "Beam me up, Android".getBytes(Charset.forName("US-ASCII"))); +</pre> + +<p>Creating the {@link android.nfc.NdefRecord} manually:</p> <pre> NdefRecord mimeRecord = new NdefRecord( NdefRecord.TNF_MIME_MEDIA , - "application/com.example.android.beam".getBytes(Charset.forName("US-ASCII")), + "application/vnd.com.example.android.beam".getBytes(Charset.forName("US-ASCII")), new byte[0], "Beam me up, Android!".getBytes(Charset.forName("US-ASCII"))); </pre> -<p>the intent filter would look like this:</p> +<p>The intent filter for the previous NDEF records would look like this:</p> <pre> <intent-filter> <action android:name="android.nfc.action.NDEF_DISCOVERED" /> <category android:name="android.intent.category.DEFAULT" /> - <data android:mimeType="application/com.example.android.beam" /> + <data android:mimeType="application/vnd.com.example.android.beam" /> </intent-filter> </pre> - <h3 id="well-known-text">TNF_WELL_KNOWN with RTD_TEXT</h3> -<p>Given the following {@link android.nfc.NdefRecord#TNF_WELL_KNOWN} NDEF record, which is stored as -the first record inside of an {@link android.nfc.NdefMessage}:</p> +<p>You can create a {@link android.nfc.NdefRecord#TNF_WELL_KNOWN} NDEF record in the following way:</p> <pre> public NdefRecord createTextRecord(String payload, Locale locale, boolean encodeInUtf8) { byte[] langBytes = locale.getLanguage().getBytes(Charset.forName("US-ASCII")); @@ -592,9 +606,20 @@ public NdefRecord createTextRecord(String payload, Locale locale, boolean encode <h3 id="well-known-uri">TNF_WELL_KNOWN with RTD_URI</h3> -<p>Given the following {@link android.nfc.NdefRecord#TNF_WELL_KNOWN} NDEF record, which is stored as -the first record inside of an {@link android.nfc.NdefMessage}:</p> +<p>You can create a {@link android.nfc.NdefRecord#TNF_WELL_KNOWN} NDEF record in the following ways.</p> + +<p>Using the {@link android.nfc.NdefRecord#createUri(String)} method:</p> +<pre> +NdefRecord rtdUriRecord1 = NdefRecord.createUri("http://example.com"); +</pre> +<p>Using the {@link android.nfc.NdefRecord#createUri(Uri)} method:</p> +<pre> +Uri uri = new Uri("http://example.com"); +NdefRecord rtdUriRecord2 = NdefRecord.createUri(uri); +</pre> + +<p>Creating the {@link android.nfc.NdefRecord} manually:</p> <pre> byte[] uriField = "example.com".getBytes(Charset.forName("US-ASCII")); byte[] payload = new byte[uriField.length + 1]; //add 1 for the URI Prefix @@ -604,7 +629,7 @@ NdefRecord rtdUriRecord = new NdefRecord( NdefRecord.TNF_WELL_KNOWN, NdefRecord.RTD_URI, new byte[0], payload); </pre> -<p>the intent filter would look like this:</p> +<p>The intent filter for the previous NDEF records would look like this:</p> <pre> <intent-filter> @@ -617,24 +642,32 @@ NdefRecord rtdUriRecord = new NdefRecord( </pre> <h3 id="ext-type">TNF_EXTERNAL_TYPE</h3> -<p>Given the following {@link android.nfc.NdefRecord#TNF_EXTERNAL_TYPE} NDEF record, which is stored -as the first record inside of an {@link android.nfc.NdefMessage}:</p> +<p>You can create a {@link android.nfc.NdefRecord#TNF_EXTERNAL_TYPE} NDEF record in the following ways:</p> + +<p>Using the {@link android.nfc.NdefRecord#createExternal createExternal()} method: +<pre> +byte[] payload; //assign to your data +String domain = "com.example"; //usually your app's package name +String type = "externalType"; +NdefRecord extRecord = NdefRecord.createExternal(domain, type, payload); +</pre> +<p>Creating the {@link android.nfc.NdefRecord} manually:</p> <pre> byte[] payload; ... -NdefRecord mimeRecord = new NdefRecord( - NdefRecord.TNF_EXTERNAL_TYPE, "example.com:externalType", new byte[0], payload); +NdefRecord extRecord = new NdefRecord( + NdefRecord.TNF_EXTERNAL_TYPE, "com.example:externalType", new byte[0], payload); </pre> -<p>the intent filter would look like this:</p> +<p>The intent filter for the previous NDEF records would look like this:</p> <pre> <intent-filter> <action android:name="android.nfc.action.NDEF_DISCOVERED" /> <category android:name="android.intent.category.DEFAULT" /> <data android:scheme="vnd.android.nfc" android:host="ext" - android:pathPrefix="/example.com:externalType"/> + android:pathPrefix="/com.example:externalType"/> </intent-filter> </pre> @@ -840,8 +873,8 @@ public class Beam extends Activity implements CreateNdefMessageCallback { String text = ("Beam me up, Android!\n\n" + "Beam Time: " + System.currentTimeMillis()); NdefMessage msg = new NdefMessage( - new NdefRecord[] { createMimeRecord( - "application/com.example.android.beam", text.getBytes()) + new NdefRecord[] { createMime( + "application/vnd.com.example.android.beam", text.getBytes()) /** * The Android Application Record (AAR) is commented out. When a device * receives a push with an AAR in it, the application specified in the AAR @@ -882,22 +915,12 @@ public class Beam extends Activity implements CreateNdefMessageCallback { // record 0 contains the MIME type, record 1 is the AAR, if present textView.setText(new String(msg.getRecords()[0].getPayload())); } - - /** - * Creates a custom MIME type encapsulated in an NDEF record - */ - public NdefRecord createMimeRecord(String mimeType, byte[] payload) { - byte[] mimeBytes = mimeType.getBytes(Charset.forName("US-ASCII")); - NdefRecord mimeRecord = new NdefRecord( - NdefRecord.TNF_MIME_MEDIA, mimeBytes, new byte[0], payload); - return mimeRecord; - } } </pre> <p>Note that this code comments out an AAR, which you can remove. If you enable the AAR, the application specified in the AAR always receives the Android Beam message. If the application is not -present, Google Play launches to download the application. Therefore, the following intent +present, Google Play is started to download the application. Therefore, the following intent filter is not technically necessary for Android 4.0 devices or later if the AAR is used: </p> @@ -905,13 +928,13 @@ filter is not technically necessary for Android 4.0 devices or later if the AAR <intent-filter> <action android:name="android.nfc.action.NDEF_DISCOVERED"/> <category android:name="android.intent.category.DEFAULT"/> - <data android:mimeType="application/com.example.android.beam"/> + <data android:mimeType="application/vnd.com.example.android.beam"/> </intent-filter> </pre> <p>With this intent filter, the <code>com.example.android.beam</code> application now can be started when it scans an NFC tag or receives an Android Beam with an AAR of type <code>com.example.android.beam</code>, or when an NDEF formatted message contains a MIME record -of type <code>application/com.example.android.beam</code>.</p> +of type <code>application/vnd.com.example.android.beam</code>.</p> <p>Even though AARs guarantee an application is started or downloaded, intent filters are recommended, because they let you start an Activity of your choice in your 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/graphics/java/android/renderscript/ScriptGroup.java b/graphics/java/android/renderscript/ScriptGroup.java index 4efb45b..8943f75 100644 --- a/graphics/java/android/renderscript/ScriptGroup.java +++ b/graphics/java/android/renderscript/ScriptGroup.java @@ -32,6 +32,12 @@ import java.util.ArrayList; * user supplied allocation. Inputs are similar but supply the * input of a kernal. Inputs bounds to a script are set directly * upon the script. + * <p> + * A ScriptGroup must contain at least one kernel. A ScriptGroup + * must contain only a single directed acyclic graph (DAG) of + * script kernels and connections. Attempting to create a + * ScriptGroup with multiple DAGs or attempting to create + * a cycle within a ScriptGroup will throw an exception. * **/ public final class ScriptGroup extends BaseObj { @@ -72,6 +78,7 @@ public final class ScriptGroup extends BaseObj { ArrayList<ConnectLine> mInputs = new ArrayList<ConnectLine>(); ArrayList<ConnectLine> mOutputs = new ArrayList<ConnectLine>(); boolean mSeen; + int dagNumber; Node mNext; @@ -169,10 +176,10 @@ public final class ScriptGroup extends BaseObj { mRS = rs; } - private void validateRecurse(Node n, int depth) { + private void validateCycleRecurse(Node n, int depth) { n.mSeen = true; - //android.util.Log.v("RSR", " validateRecurse outputCount " + n.mOutputs.size()); + //android.util.Log.v("RSR", " validateCycleRecurse outputCount " + n.mOutputs.size()); for (int ct=0; ct < n.mOutputs.size(); ct++) { final ConnectLine cl = n.mOutputs.get(ct); if (cl.mToK != null) { @@ -180,20 +187,20 @@ public final class ScriptGroup extends BaseObj { if (tn.mSeen) { throw new RSInvalidStateException("Loops in group not allowed."); } - validateRecurse(tn, depth + 1); + validateCycleRecurse(tn, depth + 1); } if (cl.mToF != null) { Node tn = findNode(cl.mToF.mScript); if (tn.mSeen) { throw new RSInvalidStateException("Loops in group not allowed."); } - validateRecurse(tn, depth + 1); + validateCycleRecurse(tn, depth + 1); } } } - private void validate() { - //android.util.Log.v("RSR", "validate"); + private void validateCycle() { + //android.util.Log.v("RSR", "validateCycle"); for (int ct=0; ct < mNodes.size(); ct++) { for (int ct2=0; ct2 < mNodes.size(); ct2++) { @@ -201,7 +208,53 @@ public final class ScriptGroup extends BaseObj { } Node n = mNodes.get(ct); if (n.mInputs.size() == 0) { - validateRecurse(n, 0); + validateCycleRecurse(n, 0); + } + } + } + + private void mergeDAGs(int valueUsed, int valueKilled) { + for (int ct=0; ct < mNodes.size(); ct++) { + if (mNodes.get(ct).dagNumber == valueKilled) + mNodes.get(ct).dagNumber = valueUsed; + } + } + + private void validateDAGRecurse(Node n, int dagNumber) { + // combine DAGs if this node has been seen already + if (n.dagNumber != 0 && n.dagNumber != dagNumber) { + mergeDAGs(n.dagNumber, dagNumber); + return; + } + + n.dagNumber = dagNumber; + for (int ct=0; ct < n.mOutputs.size(); ct++) { + final ConnectLine cl = n.mOutputs.get(ct); + if (cl.mToK != null) { + Node tn = findNode(cl.mToK.mScript); + validateDAGRecurse(tn, dagNumber); + } + if (cl.mToF != null) { + Node tn = findNode(cl.mToF.mScript); + validateDAGRecurse(tn, dagNumber); + } + } + } + + private void validateDAG() { + for (int ct=0; ct < mNodes.size(); ct++) { + Node n = mNodes.get(ct); + if (n.mInputs.size() == 0) { + if (n.mOutputs.size() == 0 && mNodes.size() > 1) { + throw new RSInvalidStateException("Groups cannot contain unconnected scripts"); + } + validateDAGRecurse(n, ct+1); + } + } + int dagNumber = mNodes.get(0).dagNumber; + for (int ct=0; ct < mNodes.size(); ct++) { + if (mNodes.get(ct).dagNumber != dagNumber) { + throw new RSInvalidStateException("Multiple DAGs in group not allowed."); } } } @@ -288,7 +341,7 @@ public final class ScriptGroup extends BaseObj { nf.mOutputs.add(cl); nt.mInputs.add(cl); - validate(); + validateCycle(); return this; } @@ -323,7 +376,7 @@ public final class ScriptGroup extends BaseObj { nf.mOutputs.add(cl); nt.mInputs.add(cl); - validate(); + validateCycle(); return this; } @@ -336,6 +389,17 @@ public final class ScriptGroup extends BaseObj { * @return ScriptGroup The new ScriptGroup */ public ScriptGroup create() { + + if (mNodes.size() == 0) { + throw new RSInvalidStateException("Empty script groups are not allowed"); + } + + // reset DAG numbers in case we're building a second group + for (int ct=0; ct < mNodes.size(); ct++) { + mNodes.get(ct).dagNumber = 0; + } + validateDAG(); + ArrayList<IO> inputs = new ArrayList<IO>(); ArrayList<IO> outputs = new ArrayList<IO>(); diff --git a/libs/hwui/Patch.cpp b/libs/hwui/Patch.cpp index abc88fa..902c82f 100644 --- a/libs/hwui/Patch.cpp +++ b/libs/hwui/Patch.cpp @@ -118,7 +118,10 @@ void Patch::updateVertices(const float bitmapWidth, const float bitmapHeight, const uint32_t yStretchCount = (mYCount + 1) >> 1; float stretchX = 0.0f; - float stretchY = 0.0; + float stretchY = 0.0f; + + float rescaleX = 1.0f; + float rescaleY = 1.0f; const float meshWidth = right - left; @@ -129,8 +132,9 @@ void Patch::updateVertices(const float bitmapWidth, const float bitmapHeight, } const float xStretchTex = stretchSize; const float fixed = bitmapWidth - stretchSize; - const float xStretch = right - left - fixed; + const float xStretch = fmaxf(right - left - fixed, 0.0f); stretchX = xStretch / xStretchTex; + rescaleX = fixed == 0.0f ? 0.0f : fminf(fmaxf(right - left, 0.0f) / fixed, 1.0f); } if (yStretchCount > 0) { @@ -140,8 +144,9 @@ void Patch::updateVertices(const float bitmapWidth, const float bitmapHeight, } const float yStretchTex = stretchSize; const float fixed = bitmapHeight - stretchSize; - const float yStretch = bottom - top - fixed; + const float yStretch = fmaxf(bottom - top - fixed, 0.0f); stretchY = yStretch / yStretchTex; + rescaleY = fixed == 0.0f ? 0.0f : fminf(fmaxf(bottom - top, 0.0f) / fixed, 1.0f); } TextureVertex* vertex = mVertices; @@ -160,7 +165,7 @@ void Patch::updateVertices(const float bitmapWidth, const float bitmapHeight, if (i & 1) { y2 = y1 + floorf(segment * stretchY + 0.5f); } else { - y2 = y1 + segment; + y2 = y1 + segment * rescaleY; } float vOffset = y1 == y2 ? 0.0f : 0.5 - (0.5 * segment / (y2 - y1)); @@ -172,7 +177,7 @@ void Patch::updateVertices(const float bitmapWidth, const float bitmapHeight, y1 += i * EXPLODE_GAP; y2 += i * EXPLODE_GAP; #endif - generateRow(vertex, y1, y2, v1, v2, stretchX, right - left, + generateRow(vertex, y1, y2, v1, v2, stretchX, rescaleX, right - left, bitmapWidth, quadCount); #if DEBUG_EXPLODE_PATCHES y2 -= i * EXPLODE_GAP; @@ -191,7 +196,8 @@ void Patch::updateVertices(const float bitmapWidth, const float bitmapHeight, y1 += mYCount * EXPLODE_GAP; y2 += mYCount * EXPLODE_GAP; #endif - generateRow(vertex, y1, y2, v1, 1.0f, stretchX, right - left, bitmapWidth, quadCount); + generateRow(vertex, y1, y2, v1, 1.0f, stretchX, rescaleX, right - left, + bitmapWidth, quadCount); } if (verticesCount > 0) { @@ -212,7 +218,7 @@ void Patch::updateVertices(const float bitmapWidth, const float bitmapHeight, } void Patch::generateRow(TextureVertex*& vertex, float y1, float y2, float v1, float v2, - float stretchX, float width, float bitmapWidth, uint32_t& quadCount) { + float stretchX, float rescaleX, float width, float bitmapWidth, uint32_t& quadCount) { float previousStepX = 0.0f; float x1 = 0.0f; @@ -227,7 +233,7 @@ void Patch::generateRow(TextureVertex*& vertex, float y1, float y2, float v1, fl if (i & 1) { x2 = x1 + floorf(segment * stretchX + 0.5f); } else { - x2 = x1 + segment; + x2 = x1 + segment * rescaleX; } float uOffset = x1 == x2 ? 0.0f : 0.5 - (0.5 * segment / (x2 - x1)); @@ -272,7 +278,7 @@ void Patch::generateQuad(TextureVertex*& vertex, float x1, float y1, float x2, f if (y2 < 0.0f) y2 = 0.0f; // Skip degenerate and transparent (empty) quads - if ((mColorKey >> oldQuadCount) & 0x1) { + if (((mColorKey >> oldQuadCount) & 0x1) || x1 >= x2 || y1 >= y2) { #if DEBUG_PATCHES_EMPTY_VERTICES PATCH_LOGD(" quad %d (empty)", oldQuadCount); PATCH_LOGD(" left, top = %.2f, %.2f\t\tu1, v1 = %.4f, %.4f", x1, y1, u1, v1); diff --git a/libs/hwui/Patch.h b/libs/hwui/Patch.h index 28c9048..0518d91 100644 --- a/libs/hwui/Patch.h +++ b/libs/hwui/Patch.h @@ -75,8 +75,8 @@ private: void copy(const int32_t* yDivs); void generateRow(TextureVertex*& vertex, float y1, float y2, - float v1, float v2, float stretchX, float width, float bitmapWidth, - uint32_t& quadCount); + float v1, float v2, float stretchX, float rescaleX, + float width, float bitmapWidth, uint32_t& quadCount); void generateQuad(TextureVertex*& vertex, float x1, float y1, float x2, float y2, float u1, float v1, float u2, float v2, diff --git a/media/java/android/media/AudioManager.java b/media/java/android/media/AudioManager.java index ee17bd3..035b282 100644 --- a/media/java/android/media/AudioManager.java +++ b/media/java/android/media/AudioManager.java @@ -1526,6 +1526,16 @@ public class AudioManager { /** * @hide + * Checks whether speech recognition is active + * @return true if a recording with source {@link MediaRecorder.AudioSource#VOICE_RECOGNITION} + * is underway. + */ + public boolean isSpeechRecognitionActive() { + return AudioSystem.isSourceActive(MediaRecorder.AudioSource.VOICE_RECOGNITION); + } + + /** + * @hide * If the stream is active locally or remotely, adjust its volume according to the enforced * priority rules. * Note: only AudioManager.STREAM_MUSIC is supported at the moment diff --git a/media/java/android/media/AudioService.java b/media/java/android/media/AudioService.java index 7d17391..f26d322 100644 --- a/media/java/android/media/AudioService.java +++ b/media/java/android/media/AudioService.java @@ -462,7 +462,21 @@ public class AudioService extends IAudioService.Stub implements OnFinished { mVolumePanel = new VolumePanel(context, this); mMode = AudioSystem.MODE_NORMAL; mForcedUseForComm = AudioSystem.FORCE_NONE; + createAudioSystemThread(); + + boolean cameraSoundForced = mContext.getResources().getBoolean( + com.android.internal.R.bool.config_camera_sound_forced); + mCameraSoundForced = new Boolean(cameraSoundForced); + sendMsg(mAudioHandler, + MSG_SET_FORCE_USE, + SENDMSG_QUEUE, + AudioSystem.FOR_SYSTEM, + cameraSoundForced ? + AudioSystem.FORCE_SYSTEM_ENFORCED : AudioSystem.FORCE_NONE, + null, + 0); + readPersistedSettings(); mSettingsObserver = new SettingsObserver(); updateStreamVolumeAlias(false /*updateVolumes*/); @@ -585,6 +599,8 @@ public class AudioService extends IAudioService.Stub implements OnFinished { mStreamStates[i].dump(pw); pw.println(""); } + pw.print("\n- mute affected streams = 0x"); + pw.println(Integer.toHexString(mMuteAffectedStreams)); } @@ -634,35 +650,44 @@ public class AudioService extends IAudioService.Stub implements OnFinished { } synchronized(mSettingsLock) { mRingerMode = ringerMode; - } - // System.VIBRATE_ON is not used any more but defaults for mVibrateSetting - // are still needed while setVibrateSetting() and getVibrateSetting() are being deprecated. - mVibrateSetting = getValueForVibrateSetting(0, - AudioManager.VIBRATE_TYPE_NOTIFICATION, - mHasVibrator ? AudioManager.VIBRATE_SETTING_ONLY_SILENT - : AudioManager.VIBRATE_SETTING_OFF); - mVibrateSetting = getValueForVibrateSetting(mVibrateSetting, - AudioManager.VIBRATE_TYPE_RINGER, - mHasVibrator ? AudioManager.VIBRATE_SETTING_ONLY_SILENT - : AudioManager.VIBRATE_SETTING_OFF); - - // make sure settings for ringer mode are consistent with device type: non voice capable - // devices (tablets) include media stream in silent mode whereas phones don't. - mRingerModeAffectedStreams = Settings.System.getIntForUser(cr, - Settings.System.MODE_RINGER_STREAMS_AFFECTED, - ((1 << AudioSystem.STREAM_RING)|(1 << AudioSystem.STREAM_NOTIFICATION)| - (1 << AudioSystem.STREAM_SYSTEM)|(1 << AudioSystem.STREAM_SYSTEM_ENFORCED)), - UserHandle.USER_CURRENT); - if (mVoiceCapable) { - mRingerModeAffectedStreams &= ~(1 << AudioSystem.STREAM_MUSIC); - } else { - mRingerModeAffectedStreams |= (1 << AudioSystem.STREAM_MUSIC); + // System.VIBRATE_ON is not used any more but defaults for mVibrateSetting + // are still needed while setVibrateSetting() and getVibrateSetting() are being + // deprecated. + mVibrateSetting = getValueForVibrateSetting(0, + AudioManager.VIBRATE_TYPE_NOTIFICATION, + mHasVibrator ? AudioManager.VIBRATE_SETTING_ONLY_SILENT + : AudioManager.VIBRATE_SETTING_OFF); + mVibrateSetting = getValueForVibrateSetting(mVibrateSetting, + AudioManager.VIBRATE_TYPE_RINGER, + mHasVibrator ? AudioManager.VIBRATE_SETTING_ONLY_SILENT + : AudioManager.VIBRATE_SETTING_OFF); + + // make sure settings for ringer mode are consistent with device type: non voice capable + // devices (tablets) include media stream in silent mode whereas phones don't. + mRingerModeAffectedStreams = Settings.System.getIntForUser(cr, + Settings.System.MODE_RINGER_STREAMS_AFFECTED, + ((1 << AudioSystem.STREAM_RING)|(1 << AudioSystem.STREAM_NOTIFICATION)| + (1 << AudioSystem.STREAM_SYSTEM)|(1 << AudioSystem.STREAM_SYSTEM_ENFORCED)), + UserHandle.USER_CURRENT); + if (mVoiceCapable) { + mRingerModeAffectedStreams &= ~(1 << AudioSystem.STREAM_MUSIC); + } else { + mRingerModeAffectedStreams |= (1 << AudioSystem.STREAM_MUSIC); + } + synchronized (mCameraSoundForced) { + if (mCameraSoundForced) { + mRingerModeAffectedStreams &= ~(1 << AudioSystem.STREAM_SYSTEM_ENFORCED); + } else { + mRingerModeAffectedStreams |= (1 << AudioSystem.STREAM_SYSTEM_ENFORCED); + } + } + + Settings.System.putIntForUser(cr, + Settings.System.MODE_RINGER_STREAMS_AFFECTED, + mRingerModeAffectedStreams, + UserHandle.USER_CURRENT); } - Settings.System.putIntForUser(cr, - Settings.System.MODE_RINGER_STREAMS_AFFECTED, - mRingerModeAffectedStreams, - UserHandle.USER_CURRENT); mMuteAffectedStreams = System.getIntForUser(cr, System.MUTE_STREAMS_AFFECTED, @@ -2601,12 +2626,18 @@ public class AudioService extends IAudioService.Stub implements OnFinished { // only be stale values // on first call to readSettings() at init time, muteCount() is always 0 so we will // always create entries for default device - if ((muteCount() == 0) && (mStreamType == AudioSystem.STREAM_SYSTEM) || + if ((mStreamType == AudioSystem.STREAM_SYSTEM) || (mStreamType == AudioSystem.STREAM_SYSTEM_ENFORCED)) { - mLastAudibleIndex.put(AudioSystem.DEVICE_OUT_DEFAULT, - 10 * AudioManager.DEFAULT_STREAM_VOLUME[mStreamType]); - mIndex.put(AudioSystem.DEVICE_OUT_DEFAULT, - 10 * AudioManager.DEFAULT_STREAM_VOLUME[mStreamType]); + int index = 10 * AudioManager.DEFAULT_STREAM_VOLUME[mStreamType]; + synchronized (mCameraSoundForced) { + if (mCameraSoundForced) { + index = mIndexMax; + } + } + if (muteCount() == 0) { + mIndex.put(AudioSystem.DEVICE_OUT_DEFAULT, index); + } + mLastAudibleIndex.put(AudioSystem.DEVICE_OUT_DEFAULT, index); return; } @@ -2618,10 +2649,11 @@ public class AudioService extends IAudioService.Stub implements OnFinished { remainingDevices &= ~device; // ignore settings for fixed volume devices: volume should always be at max - if ((muteCount() == 0) && - (mStreamVolumeAlias[mStreamType] == AudioSystem.STREAM_MUSIC) && + if ((mStreamVolumeAlias[mStreamType] == AudioSystem.STREAM_MUSIC) && ((device & mFixedVolumeDevices) != 0)) { - mIndex.put(device, mIndexMax); + if (muteCount() == 0) { + mIndex.put(device, mIndexMax); + } mLastAudibleIndex.put(device, mIndexMax); continue; } @@ -2676,7 +2708,9 @@ public class AudioService extends IAudioService.Stub implements OnFinished { this, PERSIST_DELAY); } - mIndex.put(device, getValidIndex(10 * index)); + if (muteCount() == 0) { + mIndex.put(device, getValidIndex(10 * index)); + } } } @@ -2716,6 +2750,11 @@ public class AudioService extends IAudioService.Stub implements OnFinished { public synchronized boolean setIndex(int index, int device, boolean lastAudible) { int oldIndex = getIndex(device, false /* lastAudible */); index = getValidIndex(index); + synchronized (mCameraSoundForced) { + if ((mStreamType == AudioSystem.STREAM_SYSTEM_ENFORCED) && mCameraSoundForced) { + index = mIndexMax; + } + } mIndex.put(device, getValidIndex(index)); if (oldIndex != index) { @@ -2819,6 +2858,21 @@ public class AudioService extends IAudioService.Stub implements OnFinished { } } + public synchronized void setAllIndexesToMax() { + Set set = mIndex.entrySet(); + Iterator i = set.iterator(); + while (i.hasNext()) { + Map.Entry entry = (Map.Entry)i.next(); + entry.setValue(mIndexMax); + } + set = mLastAudibleIndex.entrySet(); + i = set.iterator(); + while (i.hasNext()) { + Map.Entry entry = (Map.Entry)i.next(); + entry.setValue(mIndexMax); + } + } + public synchronized void mute(IBinder cb, boolean state) { VolumeDeathHandler handler = getDeathHandler(cb, state); if (handler == null) { @@ -2967,6 +3021,8 @@ public class AudioService extends IAudioService.Stub implements OnFinished { } private void dump(PrintWriter pw) { + pw.print(" Mute count: "); + pw.println(muteCount()); pw.print(" Current: "); Set set = mIndex.entrySet(); Iterator i = set.iterator(); @@ -3215,6 +3271,8 @@ public class AudioService extends IAudioService.Stub implements OnFinished { // Restore forced usage for communcations and record AudioSystem.setForceUse(AudioSystem.FOR_COMMUNICATION, mForcedUseForComm); AudioSystem.setForceUse(AudioSystem.FOR_RECORD, mForcedUseForComm); + AudioSystem.setForceUse(AudioSystem.FOR_SYSTEM, mCameraSoundForced ? + AudioSystem.FORCE_SYSTEM_ENFORCED : AudioSystem.FORCE_NONE); // Restore stream volumes int numStreamTypes = AudioSystem.getNumStreamTypes(); @@ -3372,6 +3430,13 @@ public class AudioService extends IAudioService.Stub implements OnFinished { } else { ringerModeAffectedStreams |= (1 << AudioSystem.STREAM_MUSIC); } + synchronized (mCameraSoundForced) { + if (mCameraSoundForced) { + ringerModeAffectedStreams &= ~(1 << AudioSystem.STREAM_SYSTEM_ENFORCED); + } else { + ringerModeAffectedStreams |= (1 << AudioSystem.STREAM_SYSTEM_ENFORCED); + } + } if (ringerModeAffectedStreams != mRingerModeAffectedStreams) { /* * Ensure all stream types that should be affected by ringer mode @@ -5587,6 +5652,48 @@ public class AudioService extends IAudioService.Stub implements OnFinished { 0, null, 0); + + boolean cameraSoundForced = mContext.getResources().getBoolean( + com.android.internal.R.bool.config_camera_sound_forced); + synchronized (mSettingsLock) { + synchronized (mCameraSoundForced) { + if (cameraSoundForced != mCameraSoundForced) { + mCameraSoundForced = cameraSoundForced; + + VolumeStreamState s = mStreamStates[AudioSystem.STREAM_SYSTEM_ENFORCED]; + if (cameraSoundForced) { + s.setAllIndexesToMax(); + mRingerModeAffectedStreams &= + ~(1 << AudioSystem.STREAM_SYSTEM_ENFORCED); + } else { + s.setAllIndexes(mStreamStates[AudioSystem.STREAM_SYSTEM], + false /*lastAudible*/); + s.setAllIndexes(mStreamStates[AudioSystem.STREAM_SYSTEM], + true /*lastAudible*/); + mRingerModeAffectedStreams |= + (1 << AudioSystem.STREAM_SYSTEM_ENFORCED); + } + // take new state into account for streams muted by ringer mode + setRingerModeInt(getRingerMode(), false); + + sendMsg(mAudioHandler, + MSG_SET_FORCE_USE, + SENDMSG_QUEUE, + AudioSystem.FOR_SYSTEM, + cameraSoundForced ? + AudioSystem.FORCE_SYSTEM_ENFORCED : AudioSystem.FORCE_NONE, + null, + 0); + + sendMsg(mAudioHandler, + MSG_SET_ALL_VOLUMES, + SENDMSG_QUEUE, + 0, + 0, + mStreamStates[AudioSystem.STREAM_SYSTEM_ENFORCED], 0); + } + } + } } catch (Exception e) { Log.e(TAG, "Error retrieving device orientation: " + e); } @@ -5762,6 +5869,38 @@ public class AudioService extends IAudioService.Stub implements OnFinished { } + //========================================================================================== + // Camera shutter sound policy. + // config_camera_sound_forced configuration option in config.xml defines if the camera shutter + // sound is forced (sound even if the device is in silent mode) or not. This option is false by + // default and can be overridden by country specific overlay in values-mccXXX/config.xml. + //========================================================================================== + + // cached value of com.android.internal.R.bool.config_camera_sound_forced + private Boolean mCameraSoundForced; + + // called by android.hardware.Camera to populate CameraInfo.canDisableShutterSound + public boolean isCameraSoundForced() { + synchronized (mCameraSoundForced) { + return mCameraSoundForced; + } + } + + private static final String[] RINGER_MODE_NAMES = new String[] { + "SILENT", + "VIBRATE", + "NORMAL" + }; + + private void dumpRingerMode(PrintWriter pw) { + pw.println("\nRinger mode: "); + pw.println("- mode: "+RINGER_MODE_NAMES[mRingerMode]); + pw.print("- ringer mode affected streams = 0x"); + pw.println(Integer.toHexString(mRingerModeAffectedStreams)); + pw.print("- ringer mode muted streams = 0x"); + pw.println(Integer.toHexString(mRingerModeMutedStreams)); + } + @Override protected void dump(FileDescriptor fd, PrintWriter pw, String[] args) { mContext.enforceCallingOrSelfPermission(android.Manifest.permission.DUMP, TAG); @@ -5770,6 +5909,7 @@ public class AudioService extends IAudioService.Stub implements OnFinished { dumpRCStack(pw); dumpRCCStack(pw); dumpStreamStates(pw); + dumpRingerMode(pw); pw.println("\nAudio routes:"); pw.print(" mMainType=0x"); pw.println(Integer.toHexString(mCurAudioRoutes.mMainType)); pw.print(" mBluetoothName="); pw.println(mCurAudioRoutes.mBluetoothName); diff --git a/media/java/android/media/AudioSystem.java b/media/java/android/media/AudioSystem.java index 2cff4ff..260ddc7 100644 --- a/media/java/android/media/AudioSystem.java +++ b/media/java/android/media/AudioSystem.java @@ -111,6 +111,13 @@ public class AudioSystem public static native boolean isStreamActive(int stream, int inPastMs); /* + * Checks whether the specified audio source is active. + * + * return true if any recorder using this source is currently recording + */ + public static native boolean isSourceActive(int source); + + /* * Sets a group generic audio configuration parameters. The use of these parameters * are platform dependent, see libaudio * @@ -354,7 +361,8 @@ public class AudioSystem public static final int FORCE_DIGITAL_DOCK = 9; public static final int FORCE_NO_BT_A2DP = 10; public static final int FORCE_REMOTE_SUBMIX = 11; - private static final int NUM_FORCE_CONFIG = 12; + public static final int FORCE_SYSTEM_ENFORCED = 12; + private static final int NUM_FORCE_CONFIG = 13; public static final int FORCE_DEFAULT = FORCE_NONE; // usage for setForceUse, must match AudioSystem::force_use @@ -362,7 +370,8 @@ public class AudioSystem public static final int FOR_MEDIA = 1; public static final int FOR_RECORD = 2; public static final int FOR_DOCK = 3; - private static final int NUM_FORCE_USE = 4; + public static final int FOR_SYSTEM = 4; + private static final int NUM_FORCE_USE = 5; // usage for AudioRecord.startRecordingSync(), must match AudioSystem::sync_event_t public static final int SYNC_EVENT_NONE = 0; diff --git a/media/java/android/media/IAudioService.aidl b/media/java/android/media/IAudioService.aidl index 7ae61cd..ea99069 100644 --- a/media/java/android/media/IAudioService.aidl +++ b/media/java/android/media/IAudioService.aidl @@ -153,4 +153,6 @@ interface IAudioService { int setBluetoothA2dpDeviceConnectionState(in BluetoothDevice device, int state); AudioRoutesInfo startWatchingRoutes(in IAudioRoutesObserver observer); + + boolean isCameraSoundForced(); } 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/DatabaseHelper.java b/packages/SettingsProvider/src/com/android/providers/settings/DatabaseHelper.java index 0b61abe..0689268 100644 --- a/packages/SettingsProvider/src/com/android/providers/settings/DatabaseHelper.java +++ b/packages/SettingsProvider/src/com/android/providers/settings/DatabaseHelper.java @@ -68,7 +68,7 @@ public class DatabaseHelper extends SQLiteOpenHelper { // database gets upgraded properly. At a minimum, please confirm that 'upgradeVersion' // is properly propagated through your change. Not doing so will result in a loss of user // settings. - private static final int DATABASE_VERSION = 93; + private static final int DATABASE_VERSION = 94; private Context mContext; private int mUserHandle; @@ -1438,7 +1438,7 @@ public class DatabaseHelper extends SQLiteOpenHelper { db.beginTransaction(); try { // Move ringer mode from system to global settings - String[] settingsToMove = { Settings.System.MODE_RINGER }; + String[] settingsToMove = { Settings.Global.MODE_RINGER }; moveSettingsToNewTable(db, TABLE_SYSTEM, TABLE_GLOBAL, settingsToMove, true); db.setTransactionSuccessful(); @@ -1473,6 +1473,27 @@ public class DatabaseHelper extends SQLiteOpenHelper { upgradeVersion = 93; } + if (upgradeVersion == 93) { + // Redo this step, since somehow it didn't work the first time for some users + if (mUserHandle == UserHandle.USER_OWNER) { + db.beginTransaction(); + SQLiteStatement stmt = null; + try { + // Migrate now-global settings + String[] settingsToMove = hashsetToStringArray(SettingsProvider.sSystemGlobalKeys); + moveSettingsToNewTable(db, TABLE_SYSTEM, TABLE_GLOBAL, settingsToMove, true); + settingsToMove = hashsetToStringArray(SettingsProvider.sSecureGlobalKeys); + moveSettingsToNewTable(db, TABLE_SECURE, TABLE_GLOBAL, settingsToMove, true); + + db.setTransactionSuccessful(); + } finally { + db.endTransaction(); + if (stmt != null) stmt.close(); + } + } + upgradeVersion = 94; + } + // *** Remember to update DATABASE_VERSION above! if (upgradeVersion != currentVersion) { @@ -1921,10 +1942,6 @@ public class DatabaseHelper extends SQLiteOpenHelper { } private void loadUISoundEffectsSettings(SQLiteStatement stmt) { - loadIntegerSetting(stmt, Settings.System.POWER_SOUNDS_ENABLED, - R.integer.def_power_sounds_enabled); - loadStringSetting(stmt, Settings.System.LOW_BATTERY_SOUND, - R.string.def_low_battery_sound); loadBooleanSetting(stmt, Settings.System.DTMF_TONE_WHEN_DIALING, R.bool.def_dtmf_tones_enabled); loadBooleanSetting(stmt, Settings.System.SOUND_EFFECTS_ENABLED, @@ -1932,17 +1949,6 @@ public class DatabaseHelper extends SQLiteOpenHelper { loadBooleanSetting(stmt, Settings.System.HAPTIC_FEEDBACK_ENABLED, R.bool.def_haptic_feedback); - loadIntegerSetting(stmt, Settings.System.DOCK_SOUNDS_ENABLED, - R.integer.def_dock_sounds_enabled); - loadStringSetting(stmt, Settings.System.DESK_DOCK_SOUND, - R.string.def_desk_dock_sound); - loadStringSetting(stmt, Settings.System.DESK_UNDOCK_SOUND, - R.string.def_desk_undock_sound); - loadStringSetting(stmt, Settings.System.CAR_DOCK_SOUND, - R.string.def_car_dock_sound); - loadStringSetting(stmt, Settings.System.CAR_UNDOCK_SOUND, - R.string.def_car_undock_sound); - loadIntegerSetting(stmt, Settings.System.LOCKSCREEN_SOUNDS_ENABLED, R.integer.def_lockscreen_sounds_enabled); } @@ -2158,9 +2164,22 @@ public class DatabaseHelper extends SQLiteOpenHelper { loadStringSetting(stmt, Settings.Global.LOCK_SOUND, R.string.def_lock_sound); - loadStringSetting(stmt, Settings.Global.UNLOCK_SOUND, R.string.def_unlock_sound); + loadIntegerSetting(stmt, Settings.Global.POWER_SOUNDS_ENABLED, + R.integer.def_power_sounds_enabled); + loadStringSetting(stmt, Settings.Global.LOW_BATTERY_SOUND, + R.string.def_low_battery_sound); + loadIntegerSetting(stmt, Settings.Global.DOCK_SOUNDS_ENABLED, + R.integer.def_dock_sounds_enabled); + loadStringSetting(stmt, Settings.Global.DESK_DOCK_SOUND, + R.string.def_desk_dock_sound); + loadStringSetting(stmt, Settings.Global.DESK_UNDOCK_SOUND, + R.string.def_desk_undock_sound); + loadStringSetting(stmt, Settings.Global.CAR_DOCK_SOUND, + R.string.def_car_dock_sound); + loadStringSetting(stmt, Settings.Global.CAR_UNDOCK_SOUND, + R.string.def_car_undock_sound); loadSetting(stmt, Settings.Global.SET_INSTALL_LOCATION, 0); loadSetting(stmt, Settings.Global.DEFAULT_INSTALL_LOCATION, 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/recent/RecentsActivity.java b/packages/SystemUI/src/com/android/systemui/recent/RecentsActivity.java index 140cc80..79069b8 100644 --- a/packages/SystemUI/src/com/android/systemui/recent/RecentsActivity.java +++ b/packages/SystemUI/src/com/android/systemui/recent/RecentsActivity.java @@ -205,4 +205,8 @@ public class RecentsActivity extends Activity { boolean isForeground() { return mForeground; } + + boolean isActivityShowing() { + return mShowing; + } } diff --git a/packages/SystemUI/src/com/android/systemui/recent/RecentsPanelView.java b/packages/SystemUI/src/com/android/systemui/recent/RecentsPanelView.java index ff51996..8607508 100644 --- a/packages/SystemUI/src/com/android/systemui/recent/RecentsPanelView.java +++ b/packages/SystemUI/src/com/android/systemui/recent/RecentsPanelView.java @@ -579,7 +579,7 @@ public class RecentsPanelView extends FrameLayout implements OnItemClickListener } else { mRecentTaskDescriptions.addAll(tasks); } - if (((RecentsActivity)mContext).isForeground()) { + if (((RecentsActivity) mContext).isActivityShowing()) { refreshViews(); } } 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..32af5e3 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)); } @@ -761,6 +767,10 @@ class QuickSettings { mBrightnessDialog.getWindow().clearFlags(WindowManager.LayoutParams.FLAG_DIM_BEHIND); } if (!mBrightnessDialog.isShowing()) { + try { + WindowManagerGlobal.getWindowManagerService().dismissKeyguard(); + } catch (RemoteException e) { + } mBrightnessDialog.show(); dismissBrightnessDialog(mBrightnessDialogLongTimeout); } @@ -799,6 +809,10 @@ class QuickSettings { builder.setCancelable(true); final Dialog dialog = builder.create(); dialog.getWindow().setType(WindowManager.LayoutParams.TYPE_SYSTEM_ALERT); + try { + WindowManagerGlobal.getWindowManagerService().dismissKeyguard(); + } catch (RemoteException e) { + } dialog.show(); } diff --git a/policy/src/com/android/internal/policy/impl/PhoneWindowManager.java b/policy/src/com/android/internal/policy/impl/PhoneWindowManager.java index 3351e61..7e047fd 100755 --- a/policy/src/com/android/internal/policy/impl/PhoneWindowManager.java +++ b/policy/src/com/android/internal/policy/impl/PhoneWindowManager.java @@ -447,6 +447,8 @@ public class PhoneWindowManager implements WindowManagerPolicy { // Screenshot trigger states // Time to volume and power must be pressed within this interval of each other. private static final long SCREENSHOT_CHORD_DEBOUNCE_DELAY_MILLIS = 150; + // Increase the chord delay when taking a screenshot from the keyguard + private static final float KEYGUARD_SCREENSHOT_CHORD_DELAY_MULTIPLIER = 2.5f; private boolean mScreenshotChordEnabled; private boolean mVolumeDownKeyTriggered; private long mVolumeDownKeyTime; @@ -669,12 +671,21 @@ public class PhoneWindowManager implements WindowManagerPolicy { mVolumeDownKeyConsumedByScreenshotChord = true; cancelPendingPowerKeyAction(); - mHandler.postDelayed(mScreenshotChordLongPress, - ViewConfiguration.getGlobalActionKeyTimeout()); + mHandler.postDelayed(mScreenshotChordLongPress, getScreenshotChordLongPressDelay()); } } } + private long getScreenshotChordLongPressDelay() { + if (mKeyguardMediator.isShowing()) { + // Double the time it takes to take a screenshot from the keyguard + return (long) (KEYGUARD_SCREENSHOT_CHORD_DELAY_MULTIPLIER * + ViewConfiguration.getGlobalActionKeyTimeout()); + } else { + return ViewConfiguration.getGlobalActionKeyTimeout(); + } + } + private void cancelPendingScreenshotChordAction() { mHandler.removeCallbacks(mScreenshotChordLongPress); } @@ -4291,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 6ea3513..bf7be89 100644 --- a/policy/src/com/android/internal/policy/impl/keyguard/KeyguardHostView.java +++ b/policy/src/com/android/internal/policy/impl/keyguard/KeyguardHostView.java @@ -33,6 +33,8 @@ import android.content.res.Resources; import android.graphics.Canvas; import android.graphics.Rect; import android.os.Looper; +import android.os.Parcel; +import android.os.Parcelable; import android.os.UserManager; import android.util.AttributeSet; import android.util.Log; @@ -43,12 +45,14 @@ import android.view.MotionEvent; import android.view.View; import android.view.ViewGroup; import android.view.WindowManager; +import android.view.View.BaseSavedState; import android.view.animation.AnimationUtils; import android.widget.RemoteViews.OnClickHandler; import android.widget.ViewFlipper; import com.android.internal.R; import com.android.internal.policy.impl.keyguard.KeyguardSecurityModel.SecurityMode; +import com.android.internal.policy.impl.keyguard.KeyguardTransportControlView.SavedState; import com.android.internal.widget.LockPatternUtils; import java.io.File; @@ -64,6 +68,10 @@ public class KeyguardHostView extends KeyguardViewBase { static final int APPWIDGET_HOST_ID = 0x4B455947; private static final String KEYGUARD_WIDGET_PREFS = "keyguard_widget_prefs"; + private static final int TRANSPORT_GONE = 0; + private static final int TRANSPORT_INVISIBLE = 1; + private static final int TRANSPORT_VISIBLE = 2; + private AppWidgetHost mAppWidgetHost; private KeyguardWidgetRegion mAppWidgetRegion; private KeyguardWidgetPager mAppWidgetContainer; @@ -83,10 +91,12 @@ public class KeyguardHostView extends KeyguardViewBase { private KeyguardSecurityModel mSecurityModel; private Rect mTempRect = new Rect(); + private int mTransportState = TRANSPORT_GONE; /*package*/ interface TransportCallback { - void hide(); - void show(); + void onListenerDetached(); + void onListenerAttached(); + void onPlayStateChanged(); } /*package*/ interface UserSwitcherCallback { @@ -185,7 +195,7 @@ public class KeyguardHostView extends KeyguardViewBase { mAppWidgetHost.startListening(); maybePopulateWidgets(); disableStatusViewInteraction(); - showAppropriateWidgetPage(); + post(mSwitchPageRunnable); } private void disableStatusViewInteraction() { @@ -248,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); } @@ -712,7 +725,7 @@ public class KeyguardHostView extends KeyguardViewBase { private void addDefaultWidgets() { LayoutInflater inflater = LayoutInflater.from(mContext); inflater.inflate(R.layout.keyguard_status_view, mAppWidgetContainer, true); - inflater.inflate(R.layout.keyguard_transport_control_view, mAppWidgetContainer, true); + inflater.inflate(R.layout.keyguard_transport_control_view, this, true); inflateAndAddUserSelectorWidgetIfNecessary(); initializeTransportControl(); @@ -721,16 +734,16 @@ public class KeyguardHostView extends KeyguardViewBase { private void initializeTransportControl() { mTransportControl = (KeyguardTransportControlView) findViewById(R.id.keyguard_transport_control); + mTransportControl.setVisibility(View.GONE); // This code manages showing/hiding the transport control. We keep it around and only // add it to the hierarchy if it needs to be present. if (mTransportControl != null) { mTransportControl.setKeyguardCallback(new TransportCallback() { - boolean mSticky = false; @Override - public void hide() { + public void onListenerDetached() { int page = getWidgetPosition(R.id.keyguard_transport_control); - if (page != -1 && !mSticky) { + if (page != -1) { if (page == mAppWidgetContainer.getCurrentPage()) { // Switch back to clock view if music was showing. mAppWidgetContainer @@ -741,20 +754,23 @@ public class KeyguardHostView extends KeyguardViewBase { // from AudioManager KeyguardHostView.this.addView(mTransportControl); mTransportControl.setVisibility(View.GONE); + mTransportState = TRANSPORT_GONE; } } @Override - public void show() { + public void onListenerAttached() { if (getWidgetPosition(R.id.keyguard_transport_control) == -1) { KeyguardHostView.this.removeView(mTransportControl); - mAppWidgetContainer.addView(mTransportControl, - getWidgetPosition(R.id.keyguard_status_view) + 1); + mAppWidgetContainer.addView(mTransportControl, 0); mTransportControl.setVisibility(View.VISIBLE); - // Once shown, leave it showing - mSticky = true; } } + + @Override + public void onPlayStateChanged() { + mTransportControl.post(mSwitchPageRunnable); + } }); } } @@ -796,12 +812,87 @@ public class KeyguardHostView extends KeyguardViewBase { } } + Runnable mSwitchPageRunnable = new Runnable() { + @Override + public void run() { + showAppropriateWidgetPage(); + } + }; + + static class SavedState extends BaseSavedState { + int transportState; + + SavedState(Parcelable superState) { + super(superState); + } + + private SavedState(Parcel in) { + super(in); + this.transportState = in.readInt(); + } + + @Override + public void writeToParcel(Parcel out, int flags) { + super.writeToParcel(out, flags); + out.writeInt(this.transportState); + } + + public static final Parcelable.Creator<SavedState> CREATOR + = new Parcelable.Creator<SavedState>() { + public SavedState createFromParcel(Parcel in) { + return new SavedState(in); + } + + public SavedState[] newArray(int size) { + return new SavedState[size]; + } + }; + } + + @Override + public Parcelable onSaveInstanceState() { + Parcelable superState = super.onSaveInstanceState(); + SavedState ss = new SavedState(superState); + ss.transportState = mTransportState; + return ss; + } + + @Override + public void onRestoreInstanceState(Parcelable state) { + if (!(state instanceof SavedState)) { + super.onRestoreInstanceState(state); + return; + } + SavedState ss = (SavedState) state; + super.onRestoreInstanceState(ss.getSuperState()); + mTransportState = ss.transportState; + post(mSwitchPageRunnable); + } + private void showAppropriateWidgetPage() { - int page = mAppWidgetContainer.indexOfChild(findViewById(R.id.keyguard_status_view)); - if (mAppWidgetContainer.indexOfChild(mTransportControl) != -1) { - page = mAppWidgetContainer.indexOfChild(mTransportControl); + + // The following sets the priority for showing widgets. Transport should be shown if + // music is playing, followed by the multi-user widget if enabled, followed by the + // status widget. + final int pageToShow; + if (mTransportControl.isMusicPlaying() || mTransportState == TRANSPORT_VISIBLE) { + mTransportState = TRANSPORT_VISIBLE; + pageToShow = mAppWidgetContainer.indexOfChild(mTransportControl); + } else { + UserManager mUm = (UserManager) mContext.getSystemService(Context.USER_SERVICE); + final View multiUserView = findViewById(R.id.keyguard_multi_user_selector); + final int multiUserPosition = mAppWidgetContainer.indexOfChild(multiUserView); + if (multiUserPosition != -1 && mUm.getUsers(true).size() > 1) { + pageToShow = multiUserPosition; + } else { + final View statusView = findViewById(R.id.keyguard_status_view); + pageToShow = mAppWidgetContainer.indexOfChild(statusView); + } + if (mTransportState == TRANSPORT_VISIBLE) { + mTransportState = TRANSPORT_INVISIBLE; + } } - mAppWidgetContainer.setCurrentPage(page); + mAppWidgetContainer.setCurrentPage(pageToShow); } private void inflateAndAddUserSelectorWidgetIfNecessary() { 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/KeyguardTransportControlView.java b/policy/src/com/android/internal/policy/impl/keyguard/KeyguardTransportControlView.java index e2f3059..7e71f94 100644 --- a/policy/src/com/android/internal/policy/impl/keyguard/KeyguardTransportControlView.java +++ b/policy/src/com/android/internal/policy/impl/keyguard/KeyguardTransportControlView.java @@ -42,7 +42,6 @@ import android.util.Log; import android.view.KeyEvent; import android.view.View; import android.view.View.OnClickListener; -import android.widget.FrameLayout; import android.widget.ImageView; import android.widget.TextView; @@ -59,7 +58,7 @@ public class KeyguardTransportControlView extends KeyguardWidgetFrame implements private static final int MSG_SET_GENERATION_ID = 104; private static final int MAXDIM = 512; private static final int DISPLAY_TIMEOUT_MS = 5000; // 5s - protected static final boolean DEBUG = false; + protected static final boolean DEBUG = true; protected static final String TAG = "TransportControlView"; private ImageView mAlbumArt; @@ -75,6 +74,7 @@ public class KeyguardTransportControlView extends KeyguardWidgetFrame implements private int mCurrentPlayState; private AudioManager mAudioManager; private IRemoteControlDisplayWeak mIRCD; + private boolean mMusicClientPresent = true; /** * The metadata which should be populated into the view once we've been attached @@ -112,7 +112,9 @@ public class KeyguardTransportControlView extends KeyguardWidgetFrame implements case MSG_SET_GENERATION_ID: if (msg.arg2 != 0) { // This means nobody is currently registered. Hide the view. - hide(); + onListenerDetached(); + } else { + onListenerAttached(); } if (DEBUG) Log.v(TAG, "New genId = " + msg.arg1 + ", clearing = " + msg.arg2); mClientGeneration = msg.arg1; @@ -193,28 +195,26 @@ public class KeyguardTransportControlView extends KeyguardWidgetFrame implements mIRCD = new IRemoteControlDisplayWeak(mHandler); } - protected void hide() { - if (DEBUG) Log.v(TAG, "Transport was told to hide"); + protected void onListenerDetached() { + mMusicClientPresent = false; + if (DEBUG) Log.v(TAG, "onListenerDetached()"); if (mTransportCallback != null) { - mTransportCallback.hide(); + mTransportCallback.onListenerDetached(); } else { - Log.w(TAG, "Hide music, but callback wasn't set"); + Log.w(TAG, "onListenerDetached: no callback"); } } - private void show() { - if (DEBUG) Log.v(TAG, "Transport was told to show"); + private void onListenerAttached() { + mMusicClientPresent = true; + if (DEBUG) Log.v(TAG, "onListenerAttached()"); if (mTransportCallback != null) { - mTransportCallback.show(); + mTransportCallback.onListenerAttached(); } else { - Log.w(TAG, "Show music, but callback wasn't set"); + Log.w(TAG, "onListenerAttached(): no callback"); } } - private void userActivity() { - // TODO Auto-generated method stub - } - private void updateTransportControls(int transportControlFlags) { mTransportControlFlags = transportControlFlags; } @@ -341,6 +341,11 @@ public class KeyguardTransportControlView extends KeyguardWidgetFrame implements updatePlayPauseState(mCurrentPlayState); } + public boolean isMusicPlaying() { + return mCurrentPlayState == RemoteControlClient.PLAYSTATE_PLAYING + || mCurrentPlayState == RemoteControlClient.PLAYSTATE_BUFFERING; + } + private static void setVisibilityBasedOnFlag(View view, int flags, int flag) { if ((flags & flag) != 0) { view.setVisibility(View.VISIBLE); @@ -357,7 +362,6 @@ public class KeyguardTransportControlView extends KeyguardWidgetFrame implements } final int imageResId; final int imageDescId; - boolean showIfHidden = false; switch (state) { case RemoteControlClient.PLAYSTATE_ERROR: imageResId = com.android.internal.R.drawable.stat_sys_warning; @@ -369,32 +373,27 @@ public class KeyguardTransportControlView extends KeyguardWidgetFrame implements case RemoteControlClient.PLAYSTATE_PLAYING: imageResId = com.android.internal.R.drawable.ic_media_pause; imageDescId = com.android.internal.R.string.lockscreen_transport_pause_description; - showIfHidden = true; break; case RemoteControlClient.PLAYSTATE_BUFFERING: imageResId = com.android.internal.R.drawable.ic_media_stop; imageDescId = com.android.internal.R.string.lockscreen_transport_stop_description; - showIfHidden = true; break; case RemoteControlClient.PLAYSTATE_PAUSED: default: imageResId = com.android.internal.R.drawable.ic_media_play; imageDescId = com.android.internal.R.string.lockscreen_transport_play_description; - showIfHidden = false; break; } mBtnPlay.setImageResource(imageResId); mBtnPlay.setContentDescription(getResources().getString(imageDescId)); - if (showIfHidden) { - show(); - } mCurrentPlayState = state; + mTransportCallback.onPlayStateChanged(); } static class SavedState extends BaseSavedState { - boolean wasShowing; + boolean clientPresent; SavedState(Parcelable superState) { super(superState); @@ -402,13 +401,13 @@ public class KeyguardTransportControlView extends KeyguardWidgetFrame implements private SavedState(Parcel in) { super(in); - this.wasShowing = in.readInt() != 0; + this.clientPresent = in.readInt() != 0; } @Override public void writeToParcel(Parcel out, int flags) { super.writeToParcel(out, flags); - out.writeInt(this.wasShowing ? 1 : 0); + out.writeInt(this.clientPresent ? 1 : 0); } public static final Parcelable.Creator<SavedState> CREATOR @@ -425,24 +424,23 @@ public class KeyguardTransportControlView extends KeyguardWidgetFrame implements @Override public Parcelable onSaveInstanceState() { - if (DEBUG) Log.v(TAG, "onSaveInstanceState()"); Parcelable superState = super.onSaveInstanceState(); SavedState ss = new SavedState(superState); - ss.wasShowing = getVisibility() == View.VISIBLE; + ss.clientPresent = mMusicClientPresent; return ss; } @Override public void onRestoreInstanceState(Parcelable state) { - if (DEBUG) Log.v(TAG, "onRestoreInstanceState()"); if (!(state instanceof SavedState)) { super.onRestoreInstanceState(state); return; } SavedState ss = (SavedState) state; super.onRestoreInstanceState(ss.getSuperState()); - if (ss.wasShowing) { - show(); + if (ss.clientPresent) { + if (DEBUG) Log.v(TAG, "Reattaching client because it was attached"); + onListenerAttached(); } } @@ -458,7 +456,6 @@ public class KeyguardTransportControlView extends KeyguardWidgetFrame implements } if (keyCode != -1) { sendMediaButtonClick(keyCode); - userActivity(); } } 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/policy/src/com/android/internal/policy/impl/keyguard/KeyguardViewManager.java b/policy/src/com/android/internal/policy/impl/keyguard/KeyguardViewManager.java index 33ff71e..d25444f 100644 --- a/policy/src/com/android/internal/policy/impl/keyguard/KeyguardViewManager.java +++ b/policy/src/com/android/internal/policy/impl/keyguard/KeyguardViewManager.java @@ -48,7 +48,7 @@ import com.android.internal.widget.LockPatternUtils; * reported to this class by the current {@link KeyguardViewBase}. */ public class KeyguardViewManager { - private final static boolean DEBUG = false; + private final static boolean DEBUG = true; private static String TAG = "KeyguardViewManager"; public static boolean USE_UPPER_CASE = true; @@ -359,6 +359,12 @@ public class KeyguardViewManager { if (mKeyguardHost != null) { mKeyguardHost.setVisibility(View.GONE); + + // We really only want to preserve keyguard state for configuration changes. Hence + // we should clear state of widgets (e.g. Music) when we hide keyguard so it can + // start with a fresh state when we return. + mStateContainer.clear(); + // Don't do this right away, so we can let the view continue to animate // as it goes away. if (mKeyguardView != null) { 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/InputMethodManagerService.java b/services/java/com/android/server/InputMethodManagerService.java index e4998e11..ffbfef6 100644 --- a/services/java/com/android/server/InputMethodManagerService.java +++ b/services/java/com/android/server/InputMethodManagerService.java @@ -900,7 +900,7 @@ public class InputMethodManagerService extends IInputMethodManager.Stub Slog.d(TAG, "--- calledFromForegroundUserOrSystemProcess ? " + "calling uid = " + uid + " system uid = " + Process.SYSTEM_UID + " calling userId = " + userId + ", foreground user id = " - + mSettings.getCurrentUserId() + ", calling uid = " + Binder.getCallingPid()); + + mSettings.getCurrentUserId() + ", calling pid = " + Binder.getCallingPid()); } if (uid == Process.SYSTEM_UID || userId == mSettings.getCurrentUserId()) { return true; @@ -2673,6 +2673,8 @@ public class InputMethodManagerService extends IInputMethodManager.Stub mSwitchingDialog.setCanceledOnTouchOutside(true); mSwitchingDialog.getWindow().setType( WindowManager.LayoutParams.TYPE_INPUT_METHOD_DIALOG); + mSwitchingDialog.getWindow().getAttributes().privateFlags |= + WindowManager.LayoutParams.PRIVATE_FLAG_SHOW_FOR_ALL_USERS; mSwitchingDialog.getWindow().getAttributes().setTitle("Select input method"); mSwitchingDialog.show(); } diff --git a/services/java/com/android/server/LocationManagerService.java b/services/java/com/android/server/LocationManagerService.java index e73d599..c5016e6 100644 --- a/services/java/com/android/server/LocationManagerService.java +++ b/services/java/com/android/server/LocationManagerService.java @@ -226,7 +226,7 @@ public class LocationManagerService extends ILocationManager.Stub implements Run updateProvidersLocked(); } } - }); + }, UserHandle.USER_ALL); mPackageMonitor.register(mContext, Looper.myLooper(), true); // listen for user change @@ -289,7 +289,7 @@ public class LocationManagerService extends ILocationManager.Stub implements Run mContext, LocationManager.NETWORK_PROVIDER, NETWORK_LOCATION_SERVICE_ACTION, - providerPackageNames, mLocationHandler); + providerPackageNames, mLocationHandler, mCurrentUserId); if (networkProvider != null) { mRealProviders.put(LocationManager.NETWORK_PROVIDER, networkProvider); mProxyProviders.add(networkProvider); @@ -303,18 +303,20 @@ public class LocationManagerService extends ILocationManager.Stub implements Run mContext, LocationManager.FUSED_PROVIDER, FUSED_LOCATION_SERVICE_ACTION, - providerPackageNames, mLocationHandler); + providerPackageNames, mLocationHandler, mCurrentUserId); if (fusedLocationProvider != null) { addProviderLocked(fusedLocationProvider); mProxyProviders.add(fusedLocationProvider); mEnabledProviders.add(fusedLocationProvider.getName()); + mRealProviders.put(LocationManager.FUSED_PROVIDER, fusedLocationProvider); } else { Slog.e(TAG, "no fused location provider found", new IllegalStateException("Location service needs a fused location provider")); } // bind to geocoder provider - mGeocodeProvider = GeocoderProxy.createAndBind(mContext, providerPackageNames); + mGeocodeProvider = GeocoderProxy.createAndBind(mContext, providerPackageNames, + mCurrentUserId); if (mGeocodeProvider == null) { Slog.e(TAG, "no geocoder provider found"); } @@ -326,11 +328,14 @@ public class LocationManagerService extends ILocationManager.Stub implements Run */ private void switchUser(int userId) { mBlacklist.switchUser(userId); - //Log.d("LocationManagerService", "switchUser(" + mCurrentUserId + " -> " + userId + ")"); // TODO: remove this synchronized (mLock) { - // TODO: inform previous user's Receivers that they will no longer receive updates + mLastLocation.clear(); + for (LocationProviderInterface p : mProviders) { + updateProviderListenersLocked(p.getName(), false, mCurrentUserId); + p.switchUser(userId); + } mCurrentUserId = userId; - // TODO: inform new user's Receivers that they are back on the update train + updateProvidersLocked(); } } @@ -587,7 +592,10 @@ public class LocationManagerService extends ILocationManager.Stub implements Run } - private boolean isAllowedBySettingsLocked(String provider) { + private boolean isAllowedBySettingsLocked(String provider, int userId) { + if (userId != mCurrentUserId) { + return false; + } if (mEnabledProviders.contains(provider)) { return true; } @@ -597,7 +605,7 @@ public class LocationManagerService extends ILocationManager.Stub implements Run // Use system settings ContentResolver resolver = mContext.getContentResolver(); - return Settings.Secure.isLocationProviderEnabled(resolver, provider); + return Settings.Secure.isLocationProviderEnabledForUser(resolver, provider, mCurrentUserId); } /** @@ -695,24 +703,30 @@ public class LocationManagerService extends ILocationManager.Stub implements Run @Override public List<String> getProviders(Criteria criteria, boolean enabledOnly) { ArrayList<String> out; - synchronized (mLock) { - out = new ArrayList<String>(mProviders.size()); - for (LocationProviderInterface provider : mProviders) { - String name = provider.getName(); - if (LocationManager.FUSED_PROVIDER.equals(name)) { - continue; - } - if (isAllowedProviderSafe(name)) { - if (enabledOnly && !isAllowedBySettingsLocked(name)) { + int callingUserId = UserHandle.getCallingUserId(); + long identity = Binder.clearCallingIdentity(); + try { + synchronized (mLock) { + out = new ArrayList<String>(mProviders.size()); + for (LocationProviderInterface provider : mProviders) { + String name = provider.getName(); + if (LocationManager.FUSED_PROVIDER.equals(name)) { continue; } - if (criteria != null && !LocationProvider.propertiesMeetCriteria( - name, provider.getProperties(), criteria)) { - continue; + if (isAllowedProviderSafe(name)) { + if (enabledOnly && !isAllowedBySettingsLocked(name, callingUserId)) { + continue; + } + if (criteria != null && !LocationProvider.propertiesMeetCriteria( + name, provider.getProperties(), criteria)) { + continue; + } + out.add(name); } - out.add(name); } } + } finally { + Binder.restoreCallingIdentity(identity); } if (D) Log.d(TAG, "getProviders()=" + out); @@ -778,12 +792,12 @@ public class LocationManagerService extends ILocationManager.Stub implements Run LocationProviderInterface p = mProviders.get(i); boolean isEnabled = p.isEnabled(); String name = p.getName(); - boolean shouldBeEnabled = isAllowedBySettingsLocked(name); + boolean shouldBeEnabled = isAllowedBySettingsLocked(name, mCurrentUserId); if (isEnabled && !shouldBeEnabled) { - updateProviderListenersLocked(name, false); + updateProviderListenersLocked(name, false, mCurrentUserId); changesMade = true; } else if (!isEnabled && shouldBeEnabled) { - updateProviderListenersLocked(name, true); + updateProviderListenersLocked(name, true, mCurrentUserId); changesMade = true; } } @@ -793,7 +807,7 @@ public class LocationManagerService extends ILocationManager.Stub implements Run } } - private void updateProviderListenersLocked(String provider, boolean enabled) { + private void updateProviderListenersLocked(String provider, boolean enabled, int userId) { int listeners = 0; LocationProviderInterface p = mProvidersByName.get(provider); @@ -806,14 +820,16 @@ public class LocationManagerService extends ILocationManager.Stub implements Run final int N = records.size(); for (int i = 0; i < N; i++) { UpdateRecord record = records.get(i); - // Sends a notification message to the receiver - if (!record.mReceiver.callProviderEnabledLocked(provider, enabled)) { - if (deadReceivers == null) { - deadReceivers = new ArrayList<Receiver>(); + if (UserHandle.getUserId(record.mReceiver.mUid) == userId) { + // Sends a notification message to the receiver + if (!record.mReceiver.callProviderEnabledLocked(provider, enabled)) { + if (deadReceivers == null) { + deadReceivers = new ArrayList<Receiver>(); + } + deadReceivers.add(record.mReceiver); } - deadReceivers.add(record.mReceiver); + listeners++; } - listeners++; } } @@ -843,12 +859,13 @@ public class LocationManagerService extends ILocationManager.Stub implements Run if (records != null) { for (UpdateRecord record : records) { - LocationRequest locationRequest = record.mRequest; - - providerRequest.locationRequests.add(locationRequest); - if (locationRequest.getInterval() < providerRequest.interval) { - providerRequest.reportLocation = true; - providerRequest.interval = locationRequest.getInterval(); + if (UserHandle.getUserId(record.mReceiver.mUid) == mCurrentUserId) { + LocationRequest locationRequest = record.mRequest; + providerRequest.locationRequests.add(locationRequest); + if (locationRequest.getInterval() < providerRequest.interval) { + providerRequest.reportLocation = true; + providerRequest.interval = locationRequest.getInterval(); + } } } @@ -860,9 +877,11 @@ public class LocationManagerService extends ILocationManager.Stub implements Run // under that threshold. long thresholdInterval = (providerRequest.interval + 1000) * 3 / 2; for (UpdateRecord record : records) { - LocationRequest locationRequest = record.mRequest; - if (locationRequest.getInterval() <= thresholdInterval) { - worksource.add(record.mReceiver.mUid); + if (UserHandle.getUserId(record.mReceiver.mUid) == mCurrentUserId) { + LocationRequest locationRequest = record.mRequest; + if (locationRequest.getInterval() <= thresholdInterval) { + worksource.add(record.mReceiver.mUid); + } } } } @@ -1084,7 +1103,7 @@ public class LocationManagerService extends ILocationManager.Stub implements Run oldRecord.disposeLocked(false); } - boolean isProviderEnabled = isAllowedBySettingsLocked(name); + boolean isProviderEnabled = isAllowedBySettingsLocked(name, UserHandle.getUserId(uid)); if (isProviderEnabled) { applyRequirementsLocked(name); } else { @@ -1141,7 +1160,7 @@ public class LocationManagerService extends ILocationManager.Stub implements Run // update provider for (String provider : providers) { // If provider is already disabled, don't need to do anything - if (!isAllowedBySettingsLocked(provider)) { + if (!isAllowedBySettingsLocked(provider, mCurrentUserId)) { continue; } @@ -1156,36 +1175,41 @@ public class LocationManagerService extends ILocationManager.Stub implements Run String perm = checkPermissionAndRequest(request); checkPackageName(packageName); - if (mBlacklist.isBlacklisted(packageName)) { - if (D) Log.d(TAG, "not returning last loc for blacklisted app: " + - packageName); - return null; - } - - synchronized (mLock) { - // Figure out the provider. Either its explicitly request (deprecated API's), - // or use the fused provider - String name = request.getProvider(); - if (name == null) name = LocationManager.FUSED_PROVIDER; - LocationProviderInterface provider = mProvidersByName.get(name); - if (provider == null) return null; - - if (!isAllowedBySettingsLocked(name)) return null; - - Location location = mLastLocation.get(name); - if (location == null) { + long identity = Binder.clearCallingIdentity(); + try { + if (mBlacklist.isBlacklisted(packageName)) { + if (D) Log.d(TAG, "not returning last loc for blacklisted app: " + + packageName); return null; } - if (ACCESS_FINE_LOCATION.equals(perm)) { - return location; - } else { - Location noGPSLocation = location.getExtraLocation(Location.EXTRA_NO_GPS_LOCATION); - if (noGPSLocation != null) { - return mLocationFudger.getOrCreate(noGPSLocation); + + synchronized (mLock) { + // Figure out the provider. Either its explicitly request (deprecated API's), + // or use the fused provider + String name = request.getProvider(); + if (name == null) name = LocationManager.FUSED_PROVIDER; + LocationProviderInterface provider = mProvidersByName.get(name); + if (provider == null) return null; + + if (!isAllowedBySettingsLocked(name, mCurrentUserId)) return null; + + Location location = mLastLocation.get(name); + if (location == null) { + return null; + } + if (ACCESS_FINE_LOCATION.equals(perm)) { + return location; + } else { + Location noGPSLocation = location.getExtraLocation(Location.EXTRA_NO_GPS_LOCATION); + if (noGPSLocation != null) { + return mLocationFudger.getOrCreate(noGPSLocation); + } } } + return null; + } finally { + Binder.restoreCallingIdentity(identity); } - return null; } @Override @@ -1321,11 +1345,16 @@ public class LocationManagerService extends ILocationManager.Stub implements Run "\" provider requires ACCESS_FINE_LOCATION permission"); } - synchronized (mLock) { - LocationProviderInterface p = mProvidersByName.get(provider); - if (p == null) return false; + long identity = Binder.clearCallingIdentity(); + try { + synchronized (mLock) { + LocationProviderInterface p = mProvidersByName.get(provider); + if (p == null) return false; - return isAllowedBySettingsLocked(provider); + return isAllowedBySettingsLocked(provider, mCurrentUserId); + } + } finally { + Binder.restoreCallingIdentity(identity); } } @@ -1461,6 +1490,16 @@ public class LocationManagerService extends ILocationManager.Stub implements Run Receiver receiver = r.mReceiver; boolean receiverDead = false; + int receiverUserId = UserHandle.getUserId(receiver.mUid); + if (receiverUserId != mCurrentUserId) { + if (D) { + Log.d(TAG, "skipping loc update for background user " + receiverUserId + + " (current user: " + mCurrentUserId + ", app: " + + receiver.mPackageName + ")"); + } + continue; + } + if (mBlacklist.isBlacklisted(receiver.mPackageName)) { if (D) Log.d(TAG, "skipping loc update for blacklisted app: " + receiver.mPackageName); @@ -1551,7 +1590,7 @@ public class LocationManagerService extends ILocationManager.Stub implements Run } synchronized (mLock) { - if (isAllowedBySettingsLocked(provider)) { + if (isAllowedBySettingsLocked(provider, mCurrentUserId)) { handleLocationChangedLocked(location, passive); } } diff --git a/services/java/com/android/server/NotificationManagerService.java b/services/java/com/android/server/NotificationManagerService.java index 85b488c..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 @@ -1066,8 +1069,9 @@ public class NotificationManagerService extends INotificationManager.Stub } mSoundNotification = r; // do not play notifications if stream volume is 0 - // (typically because ringer mode is silent). - if (audioManager.getStreamVolume(audioStreamType) != 0) { + // (typically because ringer mode is silent) or if speech recognition is active. + if ((audioManager.getStreamVolume(audioStreamType) != 0) + && !audioManager.isSpeechRecognitionActive()) { final long identity = Binder.clearCallingIdentity(); try { final IRingtonePlayer player = mAudioService.getRingtonePlayer(); diff --git a/services/java/com/android/server/ServiceWatcher.java b/services/java/com/android/server/ServiceWatcher.java index e99949b..5598b0a 100644 --- a/services/java/com/android/server/ServiceWatcher.java +++ b/services/java/com/android/server/ServiceWatcher.java @@ -27,6 +27,7 @@ import android.content.pm.ResolveInfo; import android.content.pm.Signature; import android.os.Handler; import android.os.IBinder; +import android.os.UserHandle; import android.util.Log; import com.android.internal.content.PackageMonitor; @@ -58,15 +59,17 @@ public class ServiceWatcher implements ServiceConnection { private IBinder mBinder; // connected service private String mPackageName; // current best package private int mVersion; // current best version + private int mCurrentUserId; public ServiceWatcher(Context context, String logTag, String action, - List<String> initialPackageNames, Runnable newServiceWork, Handler handler) { + List<String> initialPackageNames, Runnable newServiceWork, Handler handler, int userId) { mContext = context; mTag = logTag; mAction = action; mPm = mContext.getPackageManager(); mNewServiceWork = newServiceWork; mHandler = handler; + mCurrentUserId = userId; mSignatureSets = new ArrayList<HashSet<Signature>>(); for (int i=0; i < initialPackageNames.size(); i++) { @@ -85,9 +88,11 @@ public class ServiceWatcher implements ServiceConnection { } public boolean start() { - if (!bindBestPackage(null)) return false; + synchronized (mLock) { + if (!bindBestPackageLocked(null)) return false; + } - mPackageMonitor.register(mContext, null, true); + mPackageMonitor.register(mContext, null, UserHandle.ALL, true); return true; } @@ -98,13 +103,13 @@ public class ServiceWatcher implements ServiceConnection { * is null. * Return true if a new package was found to bind to. */ - private boolean bindBestPackage(String justCheckThisPackage) { + private boolean bindBestPackageLocked(String justCheckThisPackage) { Intent intent = new Intent(mAction); if (justCheckThisPackage != null) { intent.setPackage(justCheckThisPackage); } - List<ResolveInfo> rInfos = mPm.queryIntentServices(new Intent(mAction), - PackageManager.GET_META_DATA); + List<ResolveInfo> rInfos = mPm.queryIntentServicesAsUser(new Intent(mAction), + PackageManager.GET_META_DATA, mCurrentUserId); int bestVersion = Integer.MIN_VALUE; String bestPackage = null; for (ResolveInfo rInfo : rInfos) { @@ -141,36 +146,32 @@ public class ServiceWatcher implements ServiceConnection { (bestPackage == null ? "no new best package" : "new best packge: " + bestPackage))); if (bestPackage != null) { - bindToPackage(bestPackage, bestVersion); + bindToPackageLocked(bestPackage, bestVersion); return true; } return false; } - private void unbind() { + private void unbindLocked() { String pkg; - synchronized (mLock) { - pkg = mPackageName; - mPackageName = null; - mVersion = Integer.MIN_VALUE; - } + pkg = mPackageName; + mPackageName = null; + mVersion = Integer.MIN_VALUE; if (pkg != null) { if (D) Log.d(mTag, "unbinding " + pkg); mContext.unbindService(this); } } - private void bindToPackage(String packageName, int version) { - unbind(); + private void bindToPackageLocked(String packageName, int version) { + unbindLocked(); Intent intent = new Intent(mAction); intent.setPackage(packageName); - synchronized (mLock) { - mPackageName = packageName; - mVersion = version; - } + mPackageName = packageName; + mVersion = version; if (D) Log.d(mTag, "binding " + packageName + " (version " + version + ")"); mContext.bindService(intent, this, Context.BIND_AUTO_CREATE | Context.BIND_NOT_FOREGROUND - | Context.BIND_ALLOW_OOM_MANAGEMENT | Context.BIND_NOT_VISIBLE); + | Context.BIND_ALLOW_OOM_MANAGEMENT | Context.BIND_NOT_VISIBLE, mCurrentUserId); } private boolean isSignatureMatch(Signature[] signatures) { @@ -197,31 +198,37 @@ public class ServiceWatcher implements ServiceConnection { */ @Override public void onPackageUpdateFinished(String packageName, int uid) { - if (packageName.equals(mPackageName)) { - // package updated, make sure to rebind - unbind(); + synchronized (mLock) { + if (packageName.equals(mPackageName)) { + // package updated, make sure to rebind + unbindLocked(); + } + // check the updated package in case it is better + bindBestPackageLocked(packageName); } - // check the updated package in case it is better - bindBestPackage(packageName); } @Override public void onPackageAdded(String packageName, int uid) { - if (packageName.equals(mPackageName)) { - // package updated, make sure to rebind - unbind(); + synchronized (mLock) { + if (packageName.equals(mPackageName)) { + // package updated, make sure to rebind + unbindLocked(); + } + // check the new package is case it is better + bindBestPackageLocked(packageName); } - // check the new package is case it is better - bindBestPackage(packageName); } @Override public void onPackageRemoved(String packageName, int uid) { - if (packageName.equals(mPackageName)) { - unbind(); - // the currently bound package was removed, - // need to search for a new package - bindBestPackage(null); + synchronized (mLock) { + if (packageName.equals(mPackageName)) { + unbindLocked(); + // the currently bound package was removed, + // need to search for a new package + bindBestPackageLocked(null); + } } } }; @@ -271,4 +278,12 @@ public class ServiceWatcher implements ServiceConnection { return mBinder; } } + + public void switchUser(int userId) { + synchronized (mLock) { + unbindLocked(); + mCurrentUserId = userId; + bindBestPackageLocked(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/display/DisplayDevice.java b/services/java/com/android/server/display/DisplayDevice.java index f5aa3d4..a3ab3c1 100644 --- a/services/java/com/android/server/display/DisplayDevice.java +++ b/services/java/com/android/server/display/DisplayDevice.java @@ -105,6 +105,18 @@ abstract class DisplayDevice { } /** + * Blanks the display, if supported. + */ + public void blankLocked() { + } + + /** + * Unblanks the display, if supported. + */ + public void unblankLocked() { + } + + /** * Sets the display layer stack while in a transaction. */ public final void setLayerStackInTransactionLocked(int layerStack) { diff --git a/services/java/com/android/server/display/DisplayManagerService.java b/services/java/com/android/server/display/DisplayManagerService.java index b8c6cd5..0a42528 100644 --- a/services/java/com/android/server/display/DisplayManagerService.java +++ b/services/java/com/android/server/display/DisplayManagerService.java @@ -103,6 +103,10 @@ public final class DisplayManagerService extends IDisplayManager.Stub { private static final int MSG_REQUEST_TRAVERSAL = 4; private static final int MSG_UPDATE_VIEWPORT = 5; + private static final int DISPLAY_BLANK_STATE_UNKNOWN = 0; + private static final int DISPLAY_BLANK_STATE_BLANKED = 1; + private static final int DISPLAY_BLANK_STATE_UNBLANKED = 2; + private final Context mContext; private final boolean mHeadless; private final DisplayManagerHandler mHandler; @@ -141,6 +145,9 @@ public final class DisplayManagerService extends IDisplayManager.Stub { new SparseArray<LogicalDisplay>(); private int mNextNonDefaultDisplayId = Display.DEFAULT_DISPLAY + 1; + // Set to true if all displays have been blanked by the power manager. + private int mAllDisplayBlankStateFromPowerManager; + // Set to true when there are pending display changes that have yet to be applied // to the surface flinger state. private boolean mPendingTraversal; @@ -286,6 +293,40 @@ public final class DisplayManagerService extends IDisplayManager.Stub { } /** + * Called by the power manager to blank all displays. + */ + public void blankAllDisplaysFromPowerManager() { + synchronized (mSyncRoot) { + if (mAllDisplayBlankStateFromPowerManager != DISPLAY_BLANK_STATE_BLANKED) { + mAllDisplayBlankStateFromPowerManager = DISPLAY_BLANK_STATE_BLANKED; + + final int count = mDisplayDevices.size(); + for (int i = 0; i < count; i++) { + DisplayDevice device = mDisplayDevices.get(i); + device.blankLocked(); + } + } + } + } + + /** + * Called by the power manager to unblank all displays. + */ + public void unblankAllDisplaysFromPowerManager() { + synchronized (mSyncRoot) { + if (mAllDisplayBlankStateFromPowerManager != DISPLAY_BLANK_STATE_UNBLANKED) { + mAllDisplayBlankStateFromPowerManager = DISPLAY_BLANK_STATE_UNBLANKED; + + final int count = mDisplayDevices.size(); + for (int i = 0; i < count; i++) { + DisplayDevice device = mDisplayDevices.get(i); + device.unblankLocked(); + } + } + } + } + + /** * Returns information about the specified logical display. * * @param displayId The logical display id. @@ -528,6 +569,17 @@ public final class DisplayManagerService extends IDisplayManager.Stub { mDisplayDevices.add(device); addLogicalDisplayLocked(device); scheduleTraversalLocked(false); + + // Blank or unblank the display immediately to match the state requested + // by the power manager (if known). + switch (mAllDisplayBlankStateFromPowerManager) { + case DISPLAY_BLANK_STATE_BLANKED: + device.blankLocked(); + break; + case DISPLAY_BLANK_STATE_UNBLANKED: + device.unblankLocked(); + break; + } } } @@ -788,9 +840,18 @@ public final class DisplayManagerService extends IDisplayManager.Stub { } pw.println("DISPLAY MANAGER (dumpsys display)"); - pw.println(" mHeadless=" + mHeadless); synchronized (mSyncRoot) { + pw.println(" mHeadless=" + mHeadless); + pw.println(" mOnlyCode=" + mOnlyCore); + pw.println(" mSafeMode=" + mSafeMode); + pw.println(" mPendingTraversal=" + mPendingTraversal); + pw.println(" mAllDisplayBlankStateFromPowerManager=" + + mAllDisplayBlankStateFromPowerManager); + pw.println(" mNextNonDefaultDisplayId=" + mNextNonDefaultDisplayId); + pw.println(" mDefaultViewport=" + mDefaultViewport); + pw.println(" mExternalTouchViewport=" + mExternalTouchViewport); + IndentingPrintWriter ipw = new IndentingPrintWriter(pw, " "); ipw.increaseIndent(); @@ -817,10 +878,6 @@ public final class DisplayManagerService extends IDisplayManager.Stub { pw.println(" Display " + displayId + ":"); display.dumpLocked(ipw); } - - pw.println(); - pw.println("Default viewport: " + mDefaultViewport); - pw.println("External touch viewport: " + mExternalTouchViewport); } } diff --git a/services/java/com/android/server/display/LocalDisplayAdapter.java b/services/java/com/android/server/display/LocalDisplayAdapter.java index 679a67e..d780006 100644 --- a/services/java/com/android/server/display/LocalDisplayAdapter.java +++ b/services/java/com/android/server/display/LocalDisplayAdapter.java @@ -92,6 +92,7 @@ final class LocalDisplayAdapter extends DisplayAdapter { private DisplayDeviceInfo mInfo; private boolean mHavePendingChanges; + private boolean mBlanked; public LocalDisplayDevice(IBinder displayToken, int builtInDisplayId, PhysicalDisplayInfo phys) { @@ -150,10 +151,23 @@ final class LocalDisplayAdapter extends DisplayAdapter { } @Override + public void blankLocked() { + mBlanked = true; + Surface.blankDisplay(getDisplayTokenLocked()); + } + + @Override + public void unblankLocked() { + mBlanked = false; + Surface.unblankDisplay(getDisplayTokenLocked()); + } + + @Override public void dumpLocked(PrintWriter pw) { super.dumpLocked(pw); pw.println("mBuiltInDisplayId=" + mBuiltInDisplayId); pw.println("mPhys=" + mPhys); + pw.println("mBlanked=" + mBlanked); } } diff --git a/services/java/com/android/server/location/GeocoderProxy.java b/services/java/com/android/server/location/GeocoderProxy.java index 7d030e9..f5cc59f 100644 --- a/services/java/com/android/server/location/GeocoderProxy.java +++ b/services/java/com/android/server/location/GeocoderProxy.java @@ -21,6 +21,7 @@ import android.location.Address; import android.location.GeocoderParams; import android.location.IGeocodeProvider; import android.os.RemoteException; +import android.os.UserHandle; import android.util.Log; import com.android.server.ServiceWatcher; @@ -38,8 +39,8 @@ public class GeocoderProxy { private final ServiceWatcher mServiceWatcher; public static GeocoderProxy createAndBind(Context context, - List<String> initialPackageNames) { - GeocoderProxy proxy = new GeocoderProxy(context, initialPackageNames); + List<String> initialPackageNames, int userId) { + GeocoderProxy proxy = new GeocoderProxy(context, initialPackageNames, userId); if (proxy.bind()) { return proxy; } else { @@ -47,11 +48,11 @@ public class GeocoderProxy { } } - public GeocoderProxy(Context context, List<String> initialPackageNames) { + public GeocoderProxy(Context context, List<String> initialPackageNames, int userId) { mContext = context; mServiceWatcher = new ServiceWatcher(mContext, TAG, SERVICE_ACTION, initialPackageNames, - null, null); + null, null, userId); } private boolean bind () { diff --git a/services/java/com/android/server/location/GpsLocationProvider.java b/services/java/com/android/server/location/GpsLocationProvider.java index a254d74..c272da4 100755 --- a/services/java/com/android/server/location/GpsLocationProvider.java +++ b/services/java/com/android/server/location/GpsLocationProvider.java @@ -783,6 +783,11 @@ public class GpsLocationProvider implements LocationProviderInterface { sendMessage(SET_REQUEST, 0, new GpsRequest(request, source)); } + @Override + public void switchUser(int userId) { + // nothing to do here + } + private void handleSetRequest(ProviderRequest request, WorkSource source) { if (DEBUG) Log.d(TAG, "setRequest " + request); diff --git a/services/java/com/android/server/location/LocationProviderInterface.java b/services/java/com/android/server/location/LocationProviderInterface.java index 6f09232..80e71f1 100644 --- a/services/java/com/android/server/location/LocationProviderInterface.java +++ b/services/java/com/android/server/location/LocationProviderInterface.java @@ -38,6 +38,8 @@ public interface LocationProviderInterface { public boolean isEnabled(); public void setRequest(ProviderRequest request, WorkSource source); + public void switchUser(int userId); + public void dump(FileDescriptor fd, PrintWriter pw, String[] args); // --- deprecated (but still supported) --- diff --git a/services/java/com/android/server/location/LocationProviderProxy.java b/services/java/com/android/server/location/LocationProviderProxy.java index 7faf72c..dd2e71c 100644 --- a/services/java/com/android/server/location/LocationProviderProxy.java +++ b/services/java/com/android/server/location/LocationProviderProxy.java @@ -25,6 +25,7 @@ import android.location.LocationProvider; import android.os.Bundle; import android.os.Handler; import android.os.RemoteException; +import android.os.UserHandle; import android.os.WorkSource; import android.util.Log; @@ -54,9 +55,9 @@ public class LocationProviderProxy implements LocationProviderInterface { private WorkSource mWorksource = new WorkSource(); public static LocationProviderProxy createAndBind(Context context, String name, String action, - List<String> initialPackageNames, Handler handler) { + List<String> initialPackageNames, Handler handler, int userId) { LocationProviderProxy proxy = new LocationProviderProxy(context, name, action, - initialPackageNames, handler); + initialPackageNames, handler, userId); if (proxy.bind()) { return proxy; } else { @@ -65,11 +66,11 @@ public class LocationProviderProxy implements LocationProviderInterface { } private LocationProviderProxy(Context context, String name, String action, - List<String> initialPackageNames, Handler handler) { + List<String> initialPackageNames, Handler handler, int userId) { mContext = context; mName = name; mServiceWatcher = new ServiceWatcher(mContext, TAG, action, initialPackageNames, - mNewServiceWork, handler); + mNewServiceWork, handler, userId); } private boolean bind () { @@ -211,6 +212,11 @@ public class LocationProviderProxy implements LocationProviderInterface { } @Override + public void switchUser(int userId) { + mServiceWatcher.switchUser(userId); + } + + @Override public void dump(FileDescriptor fd, PrintWriter pw, String[] args) { pw.append("REMOTE SERVICE"); pw.append(" name=").append(mName); diff --git a/services/java/com/android/server/location/MockProvider.java b/services/java/com/android/server/location/MockProvider.java index 36c43ff..1194cbc 100644 --- a/services/java/com/android/server/location/MockProvider.java +++ b/services/java/com/android/server/location/MockProvider.java @@ -156,6 +156,11 @@ public class MockProvider implements LocationProviderInterface { public void setRequest(ProviderRequest request, WorkSource source) { } @Override + public void switchUser(int userId) { + // nothing to do here + } + + @Override public boolean sendExtraCommand(String command, Bundle extras) { return false; } diff --git a/services/java/com/android/server/location/PassiveProvider.java b/services/java/com/android/server/location/PassiveProvider.java index 71bae07..734c572 100644 --- a/services/java/com/android/server/location/PassiveProvider.java +++ b/services/java/com/android/server/location/PassiveProvider.java @@ -96,6 +96,11 @@ public class PassiveProvider implements LocationProviderInterface { mReportLocation = request.reportLocation; } + @Override + public void switchUser(int userId) { + // nothing to do here + } + public void updateLocation(Location location) { if (mReportLocation) { try { diff --git a/services/java/com/android/server/pm/PackageManagerService.java b/services/java/com/android/server/pm/PackageManagerService.java index 0600f5c..b8d7286 100644 --- a/services/java/com/android/server/pm/PackageManagerService.java +++ b/services/java/com/android/server/pm/PackageManagerService.java @@ -6374,12 +6374,22 @@ public class PackageManagerService extends IPackageManager.Stub { mArgs = args; if (ret == PackageManager.INSTALL_SUCCEEDED) { + /* + * ADB installs appear as UserHandle.USER_ALL, and can only be performed by + * UserHandle.USER_OWNER, so use the package verifier for UserHandle.USER_OWNER. + */ + int userIdentifier = getUser().getIdentifier(); + if (userIdentifier == UserHandle.USER_ALL + && ((flags & PackageManager.INSTALL_FROM_ADB) != 0)) { + userIdentifier = UserHandle.USER_OWNER; + } + /* * Determine if we have any installed package verifiers. If we * do, then we'll defer to them to verify the packages. */ final int requiredUid = mRequiredVerifierPackage == null ? -1 - : getPackageUid(mRequiredVerifierPackage, getUser().getIdentifier()); + : getPackageUid(mRequiredVerifierPackage, userIdentifier); if (requiredUid != -1 && isVerificationEnabled(flags)) { final Intent verification = new Intent( Intent.ACTION_PACKAGE_NEEDS_VERIFICATION); diff --git a/services/java/com/android/server/power/DisplayBlanker.java b/services/java/com/android/server/power/DisplayBlanker.java new file mode 100644 index 0000000..6072053 --- /dev/null +++ b/services/java/com/android/server/power/DisplayBlanker.java @@ -0,0 +1,25 @@ +/* + * Copyright (C) 2012 The Android Open Source Project + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +package com.android.server.power; + +/** + * Blanks or unblanks all displays. + */ +interface DisplayBlanker { + public void blankAllDisplays(); + public void unblankAllDisplays(); +} diff --git a/services/java/com/android/server/power/DisplayPowerController.java b/services/java/com/android/server/power/DisplayPowerController.java index 82c3617..6a57372 100644 --- a/services/java/com/android/server/power/DisplayPowerController.java +++ b/services/java/com/android/server/power/DisplayPowerController.java @@ -159,6 +159,9 @@ final class DisplayPowerController { // A suspend blocker. private final SuspendBlocker mSuspendBlocker; + // The display blanker. + private final DisplayBlanker mDisplayBlanker; + // Our handler. private final DisplayControllerHandler mHandler; @@ -343,10 +346,12 @@ final class DisplayPowerController { */ public DisplayPowerController(Looper looper, Context context, Notifier notifier, LightsService lights, TwilightService twilight, SuspendBlocker suspendBlocker, + DisplayBlanker displayBlanker, Callbacks callbacks, Handler callbackHandler) { mHandler = new DisplayControllerHandler(looper); mNotifier = notifier; mSuspendBlocker = suspendBlocker; + mDisplayBlanker = displayBlanker; mCallbacks = callbacks; mCallbackHandler = callbackHandler; @@ -520,7 +525,8 @@ final class DisplayPowerController { new ElectronBeam(display), new PhotonicModulator(executor, mLights.getLight(LightsService.LIGHT_ID_BACKLIGHT), - mSuspendBlocker)); + mSuspendBlocker), + mDisplayBlanker); mElectronBeamOnAnimator = ObjectAnimator.ofFloat( mPowerState, DisplayPowerState.ELECTRON_BEAM_LEVEL, 0.0f, 1.0f); diff --git a/services/java/com/android/server/power/DisplayPowerState.java b/services/java/com/android/server/power/DisplayPowerState.java index f6ce7a7..fdfcacc 100644 --- a/services/java/com/android/server/power/DisplayPowerState.java +++ b/services/java/com/android/server/power/DisplayPowerState.java @@ -51,7 +51,8 @@ final class DisplayPowerState { private final Choreographer mChoreographer; private final ElectronBeam mElectronBeam; - private final PhotonicModulator mScreenBrightnessModulator; + private final PhotonicModulator mPhotonicModulator; + private final DisplayBlanker mDisplayBlanker; private int mDirty; private boolean mScreenOn; @@ -61,10 +62,11 @@ final class DisplayPowerState { private Runnable mCleanListener; public DisplayPowerState(ElectronBeam electronBean, - PhotonicModulator screenBrightnessModulator) { + PhotonicModulator photonicModulator, DisplayBlanker displayBlanker) { mChoreographer = Choreographer.getInstance(); mElectronBeam = electronBean; - mScreenBrightnessModulator = screenBrightnessModulator; + mPhotonicModulator = photonicModulator; + mDisplayBlanker = displayBlanker; // At boot time, we know that the screen is on and the electron beam // animation is not playing. We don't know the screen's brightness though, @@ -238,8 +240,8 @@ final class DisplayPowerState { private void apply() { if (mDirty != 0) { if ((mDirty & DIRTY_SCREEN_ON) != 0 && !mScreenOn) { - mScreenBrightnessModulator.setBrightness(0, true /*sync*/); - PowerManagerService.nativeSetScreenState(false); + mPhotonicModulator.setBrightness(0, true /*sync*/); + mDisplayBlanker.blankAllDisplays(); } if ((mDirty & DIRTY_ELECTRON_BEAM) != 0) { @@ -247,12 +249,12 @@ final class DisplayPowerState { } if ((mDirty & DIRTY_SCREEN_ON) != 0 && mScreenOn) { - PowerManagerService.nativeSetScreenState(true); + mDisplayBlanker.unblankAllDisplays(); } if ((mDirty & (DIRTY_BRIGHTNESS | DIRTY_SCREEN_ON | DIRTY_ELECTRON_BEAM)) != 0 && mScreenOn) { - mScreenBrightnessModulator.setBrightness( + mPhotonicModulator.setBrightness( (int)(mScreenBrightness * mElectronBeamLevel), false /*sync*/); } diff --git a/services/java/com/android/server/power/PowerManagerService.java b/services/java/com/android/server/power/PowerManagerService.java index abbae5b..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,9 +156,16 @@ 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; + private DisplayManagerService mDisplayManagerService; private IBatteryStats mBatteryStats; private HandlerThread mHandlerThread; private PowerManagerHandler mHandler; @@ -230,6 +241,9 @@ public final class PowerManagerService extends IPowerManager.Stub // screen is coming up. private final ScreenOnBlockerImpl mScreenOnBlocker; + // The display blanker used to turn the screen on or off. + private final DisplayBlankerImpl mDisplayBlanker; + // True if systemReady() has been called. private boolean mSystemReady; @@ -319,14 +333,15 @@ public final class PowerManagerService extends IPowerManager.Stub private static native void nativeSetPowerState(boolean screenOn, boolean screenBright); private static native void nativeAcquireSuspendBlocker(String name); private static native void nativeReleaseSuspendBlocker(String name); - - static native void nativeSetScreenState(boolean on); + private static native void nativeSetInteractive(boolean enable); + private static native void nativeSetAutoSuspend(boolean enable); public PowerManagerService() { synchronized (mLock) { mWakeLockSuspendBlocker = createSuspendBlockerLocked("PowerManagerService"); mWakeLockSuspendBlocker.acquire(); mScreenOnBlocker = new ScreenOnBlockerImpl(); + mDisplayBlanker = new DisplayBlankerImpl(); mHoldingWakeLockSuspendBlocker = true; mWakefulness = WAKEFULNESS_AWAKE; } @@ -342,23 +357,24 @@ public final class PowerManagerService extends IPowerManager.Stub public void init(Context context, LightsService ls, ActivityManagerService am, BatteryService bs, IBatteryStats bss, DisplayManagerService dm) { - // Forcibly turn the screen on at boot so that it is in a known power state. - // We do this in init() rather than in the constructor because setting the - // screen state requires a call into surface flinger which then needs to call back - // into the activity manager to check permissions. Unfortunately the - // activity manager is not running when the constructor is called, so we - // have to defer setting the screen state until this point. - nativeSetScreenState(true); - mContext = context; mLightsService = ls; mBatteryService = bs; mBatteryStats = bss; + mDisplayManagerService = dm; mHandlerThread = new HandlerThread(TAG); mHandlerThread.start(); mHandler = new PowerManagerHandler(mHandlerThread.getLooper()); Watchdog.getInstance().addMonitor(this); + + // Forcibly turn the screen on at boot so that it is in a known power state. + // We do this in init() rather than in the constructor because setting the + // screen state requires a call into surface flinger which then needs to call back + // into the activity manager to check permissions. Unfortunately the + // activity manager is not running when the constructor is called, so we + // have to defer setting the screen state until this point. + mDisplayBlanker.unblankAllDisplays(); } public void setPolicy(WindowManagerPolicy policy) { @@ -388,7 +404,7 @@ public final class PowerManagerService extends IPowerManager.Stub mDisplayPowerController = new DisplayPowerController(mHandler.getLooper(), mContext, mNotifier, mLightsService, twilight, createSuspendBlockerLocked("PowerManagerService.Display"), - mDisplayPowerControllerCallbacks, mHandler); + mDisplayBlanker, mDisplayPowerControllerCallbacks, mHandler); mSettingsObserver = new SettingsObserver(mHandler); mAttentionLight = mLightsService.getLight(LightsService.LIGHT_ID_ATTENTION); @@ -1166,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: @@ -1333,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; } @@ -1646,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; @@ -2106,6 +2154,9 @@ public final class PowerManagerService extends IPowerManager.Stub pw.println(); pw.println("Screen On Blocker: " + mScreenOnBlocker); + pw.println(); + pw.println("Display Blanker: " + mDisplayBlanker); + dpc = mDisplayPowerController; } @@ -2151,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(); } } @@ -2208,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; } } } @@ -2397,5 +2455,36 @@ public final class PowerManagerService extends IPowerManager.Stub return "held=" + (mNestCount != 0) + ", mNestCount=" + mNestCount; } } - }; + } + + private final class DisplayBlankerImpl implements DisplayBlanker { + private boolean mBlanked; + + @Override + public void blankAllDisplays() { + synchronized (this) { + mBlanked = true; + mDisplayManagerService.blankAllDisplaysFromPowerManager(); + nativeSetInteractive(false); + nativeSetAutoSuspend(true); + } + } + + @Override + public void unblankAllDisplays() { + synchronized (this) { + nativeSetAutoSuspend(false); + nativeSetInteractive(true); + mDisplayManagerService.unblankAllDisplaysFromPowerManager(); + mBlanked = false; + } + } + + @Override + public String toString() { + synchronized (this) { + return "blanked=" + mBlanked; + } + } + } } 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 dcc2b58..23c33af 100644 --- a/services/jni/com_android_server_power_PowerManagerService.cpp +++ b/services/jni/com_android_server_power_PowerManagerService.cpp @@ -26,7 +26,6 @@ #include <limits.h> #include <android_runtime/AndroidRuntime.h> -#include <gui/ISurfaceComposer.h> #include <utils/Timers.h> #include <utils/misc.h> #include <utils/String8.h> @@ -36,8 +35,6 @@ #include <cutils/android_reboot.h> #include <suspend/autosuspend.h> -#include <private/gui/ComposerService.h> - #include "com_android_server_power_PowerManagerService.h" namespace android { @@ -170,40 +167,25 @@ static void nativeReleaseSuspendBlocker(JNIEnv *env, jclass clazz, jstring nameS release_wake_lock(name.c_str()); } -static void nativeSetScreenState(JNIEnv *env, jclass clazz, jboolean on) { - sp<ISurfaceComposer> s(ComposerService::getComposerService()); - if (on) { - { - ALOGD_IF_SLOW(100, "Excessive delay in autosuspend_disable() while turning screen on"); - autosuspend_disable(); - } - - if (gPowerModule) { +static void nativeSetInteractive(JNIEnv *env, jclass clazz, jboolean enable) { + if (gPowerModule) { + if (enable) { ALOGD_IF_SLOW(20, "Excessive delay in setInteractive(true) while turning screen on"); gPowerModule->setInteractive(gPowerModule, true); - } - - const sp<IBinder>& display = s->getBuiltInDisplay(0); // TODO: support multiple displays - { - ALOGD_IF_SLOW(100, "Excessive delay in unblank() while turning screen on"); - s->unblank(display); - } - } else { - const sp<IBinder>& display = s->getBuiltInDisplay(0); // TODO: support multiple displays - { - ALOGD_IF_SLOW(100, "Excessive delay in blank() while turning screen off"); - s->blank(display); - } - - if (gPowerModule) { + } else { ALOGD_IF_SLOW(20, "Excessive delay in setInteractive(false) while turning screen off"); gPowerModule->setInteractive(gPowerModule, false); } + } +} - { - ALOGD_IF_SLOW(100, "Excessive delay in autosuspend_enable() while turning screen off"); - autosuspend_enable(); - } +static void nativeSetAutoSuspend(JNIEnv *env, jclass clazz, jboolean enable) { + if (enable) { + ALOGD_IF_SLOW(100, "Excessive delay in autosuspend_enable() while turning screen off"); + autosuspend_enable(); + } else { + ALOGD_IF_SLOW(100, "Excessive delay in autosuspend_disable() while turning screen on"); + autosuspend_disable(); } } @@ -235,8 +217,10 @@ static JNINativeMethod gPowerManagerServiceMethods[] = { (void*) nativeAcquireSuspendBlocker }, { "nativeReleaseSuspendBlocker", "(Ljava/lang/String;)V", (void*) nativeReleaseSuspendBlocker }, - { "nativeSetScreenState", "(Z)V", - (void*) nativeSetScreenState }, + { "nativeSetInteractive", "(Z)V", + (void*) nativeSetInteractive }, + { "nativeSetAutoSuspend", "(Z)V", + (void*) nativeSetAutoSuspend }, { "nativeShutdown", "()V", (void*) nativeShutdown }, { "nativeReboot", "(Ljava/lang/String;)V", diff --git a/wifi/java/android/net/wifi/WifiSsid.java b/wifi/java/android/net/wifi/WifiSsid.java index 6f36111..3e5f10f 100644 --- a/wifi/java/android/net/wifi/WifiSsid.java +++ b/wifi/java/android/net/wifi/WifiSsid.java @@ -156,7 +156,11 @@ public class WifiSsid implements Parcelable { @Override public String toString() { - if (octets.size() <= 0) return ""; + byte[] ssidBytes = octets.toByteArray(); + // Supplicant returns \x00\x00\x00\x00\x00\x00\x00\x00 hex string + // for a hidden access point. Make sure we maintain the previous + // behavior of returning empty string for this case. + if (octets.size() <= 0 || isArrayAllZeroes(ssidBytes)) return ""; // TODO: Handle conversion to other charsets upon failure Charset charset = Charset.forName("UTF-8"); CharsetDecoder decoder = charset.newDecoder() @@ -164,7 +168,7 @@ public class WifiSsid implements Parcelable { .onUnmappableCharacter(CodingErrorAction.REPLACE); CharBuffer out = CharBuffer.allocate(32); - CoderResult result = decoder.decode(ByteBuffer.wrap(octets.toByteArray()), out, true); + CoderResult result = decoder.decode(ByteBuffer.wrap(ssidBytes), out, true); out.flip(); if (result.isError()) { return NONE; @@ -172,6 +176,13 @@ public class WifiSsid implements Parcelable { return out.toString(); } + private boolean isArrayAllZeroes(byte[] ssidBytes) { + for (int i = 0; i< ssidBytes.length; i++) { + if (ssidBytes[i] != 0) return false; + } + return true; + } + /** @hide */ public byte[] getOctets() { return octets.toByteArray(); 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); + } } /** |
