Merge "Varying-based AA rect drawing" into jb-mr1-dev
diff --git a/api/current.txt b/api/current.txt
index 10ed68d..4a99427 100644
--- a/api/current.txt
+++ b/api/current.txt
@@ -773,6 +773,7 @@
field public static final int preferenceLayoutChild = 16842900; // 0x1010094
field public static final int preferenceScreenStyle = 16842891; // 0x101008b
field public static final int preferenceStyle = 16842894; // 0x101008e
+ field public static final int presentationTheme = 16843712; // 0x10103c0
field public static final int previewImage = 16843482; // 0x10102da
field public static final int priority = 16842780; // 0x101001c
field public static final int privateImeOptions = 16843299; // 0x1010223
@@ -946,6 +947,7 @@
field public static final int subtitle = 16843473; // 0x10102d1
field public static final int subtitleTextStyle = 16843513; // 0x10102f9
field public static final int subtypeExtraValue = 16843674; // 0x101039a
+ field public static final int subtypeId = 16843713; // 0x10103c1
field public static final int subtypeLocale = 16843673; // 0x1010399
field public static final int suggestActionMsg = 16843228; // 0x10101dc
field public static final int suggestActionMsgColumn = 16843229; // 0x10101dd
@@ -3901,6 +3903,15 @@
method public abstract void onSendFinished(android.app.PendingIntent, android.content.Intent, int, java.lang.String, android.os.Bundle);
}
+ public class Presentation extends android.app.Dialog {
+ ctor public Presentation(android.content.Context, android.view.Display);
+ ctor public Presentation(android.content.Context, android.view.Display, int);
+ method public android.view.Display getDisplay();
+ method public android.content.res.Resources getResources();
+ method public void onDisplayChanged();
+ method public void onDisplayRemoved();
+ }
+
public class ProgressDialog extends android.app.AlertDialog {
ctor public ProgressDialog(android.content.Context);
ctor public ProgressDialog(android.content.Context, int);
@@ -4169,6 +4180,7 @@
field public static final android.os.Parcelable.Creator CREATOR;
field public static final int USES_ENCRYPTED_STORAGE = 7; // 0x7
field public static final int USES_POLICY_DISABLE_CAMERA = 8; // 0x8
+ field public static final int USES_POLICY_DISABLE_KEYGUARD_WIDGETS = 9; // 0x9
field public static final int USES_POLICY_EXPIRE_PASSWORD = 6; // 0x6
field public static final int USES_POLICY_FORCE_LOCK = 3; // 0x3
field public static final int USES_POLICY_LIMIT_PASSWORD = 0; // 0x0
@@ -4204,6 +4216,7 @@
method public java.util.List<android.content.ComponentName> getActiveAdmins();
method public boolean getCameraDisabled(android.content.ComponentName);
method public int getCurrentFailedPasswordAttempts();
+ method public int getKeyguardWidgetsDisabled(android.content.ComponentName);
method public int getMaximumFailedPasswordsForWipe(android.content.ComponentName);
method public long getMaximumTimeToLock(android.content.ComponentName);
method public long getPasswordExpiration(android.content.ComponentName);
@@ -4227,6 +4240,7 @@
method public void removeActiveAdmin(android.content.ComponentName);
method public boolean resetPassword(java.lang.String, int);
method public void setCameraDisabled(android.content.ComponentName, boolean);
+ method public void setKeyguardWidgetsDisabled(android.content.ComponentName, int);
method public void setMaximumFailedPasswordsForWipe(android.content.ComponentName, int);
method public void setMaximumTimeToLock(android.content.ComponentName, long);
method public void setPasswordExpirationTimeout(android.content.ComponentName, long);
@@ -4250,6 +4264,8 @@
field public static final int ENCRYPTION_STATUS_UNSUPPORTED = 0; // 0x0
field public static final java.lang.String EXTRA_ADD_EXPLANATION = "android.app.extra.ADD_EXPLANATION";
field public static final java.lang.String EXTRA_DEVICE_ADMIN = "android.app.extra.DEVICE_ADMIN";
+ field public static final int KEYGUARD_DISABLE_WIDGETS_ALL = 2147483647; // 0x7fffffff
+ field public static final int KEYGUARD_DISABLE_WIDGETS_NONE = 0; // 0x0
field public static final int PASSWORD_QUALITY_ALPHABETIC = 262144; // 0x40000
field public static final int PASSWORD_QUALITY_ALPHANUMERIC = 327680; // 0x50000
field public static final int PASSWORD_QUALITY_BIOMETRIC_WEAK = 32768; // 0x8000
@@ -5268,6 +5284,7 @@
method public abstract int checkUriPermission(android.net.Uri, java.lang.String, java.lang.String, int, int, int);
method public abstract deprecated void clearWallpaper() throws java.io.IOException;
method public abstract android.content.Context createConfigurationContext(android.content.res.Configuration);
+ method public abstract android.content.Context createDisplayContext(android.view.Display);
method public abstract android.content.Context createPackageContext(java.lang.String, int) throws android.content.pm.PackageManager.NameNotFoundException;
method public abstract java.lang.String[] databaseList();
method public abstract boolean deleteDatabase(java.lang.String);
@@ -5417,6 +5434,7 @@
method public int checkUriPermission(android.net.Uri, java.lang.String, java.lang.String, int, int, int);
method public void clearWallpaper() throws java.io.IOException;
method public android.content.Context createConfigurationContext(android.content.res.Configuration);
+ method public android.content.Context createDisplayContext(android.view.Display);
method public android.content.Context createPackageContext(java.lang.String, int) throws android.content.pm.PackageManager.NameNotFoundException;
method public java.lang.String[] databaseList();
method public boolean deleteDatabase(java.lang.String);
@@ -6238,6 +6256,7 @@
field public static final int CONFIG_FONT_SCALE = 1073741824; // 0x40000000
field public static final int CONFIG_KEYBOARD = 16; // 0x10
field public static final int CONFIG_KEYBOARD_HIDDEN = 32; // 0x20
+ field public static final int CONFIG_LAYOUT_DIRECTION = 8192; // 0x2000
field public static final int CONFIG_LOCALE = 4; // 0x4
field public static final int CONFIG_MCC = 1; // 0x1
field public static final int CONFIG_MNC = 2; // 0x2
@@ -6840,9 +6859,12 @@
method public int describeContents();
method public int diff(android.content.res.Configuration);
method public boolean equals(android.content.res.Configuration);
+ method public int getLayoutDirection();
method public boolean isLayoutSizeAtLeast(int);
method public static boolean needNewResources(int, int);
method public void readFromParcel(android.os.Parcel);
+ method public void setLayoutDirection(java.util.Locale);
+ method public void setLocale(java.util.Locale);
method public void setTo(android.content.res.Configuration);
method public void setToDefaults();
method public int updateFrom(android.content.res.Configuration);
@@ -6871,6 +6893,11 @@
field public static final int ORIENTATION_PORTRAIT = 1; // 0x1
field public static final deprecated int ORIENTATION_SQUARE = 3; // 0x3
field public static final int ORIENTATION_UNDEFINED = 0; // 0x0
+ field public static final int SCREENLAYOUT_LAYOUTDIR_LTR = 64; // 0x40
+ field public static final int SCREENLAYOUT_LAYOUTDIR_MASK = 192; // 0xc0
+ field public static final int SCREENLAYOUT_LAYOUTDIR_RTL = 128; // 0x80
+ field public static final int SCREENLAYOUT_LAYOUTDIR_SHIFT = 6; // 0x6
+ field public static final int SCREENLAYOUT_LAYOUTDIR_UNDEFINED = 0; // 0x0
field public static final int SCREENLAYOUT_LONG_MASK = 48; // 0x30
field public static final int SCREENLAYOUT_LONG_NO = 16; // 0x10
field public static final int SCREENLAYOUT_LONG_UNDEFINED = 0; // 0x0
@@ -6881,6 +6908,7 @@
field public static final int SCREENLAYOUT_SIZE_SMALL = 1; // 0x1
field public static final int SCREENLAYOUT_SIZE_UNDEFINED = 0; // 0x0
field public static final int SCREENLAYOUT_SIZE_XLARGE = 4; // 0x4
+ field public static final int SCREENLAYOUT_UNDEFINED = 0; // 0x0
field public static final int SCREEN_HEIGHT_DP_UNDEFINED = 0; // 0x0
field public static final int SCREEN_WIDTH_DP_UNDEFINED = 0; // 0x0
field public static final int SMALLEST_SCREEN_WIDTH_DP_UNDEFINED = 0; // 0x0
@@ -21228,6 +21256,7 @@
method public int checkUriPermission(android.net.Uri, java.lang.String, java.lang.String, int, int, int);
method public void clearWallpaper();
method public android.content.Context createConfigurationContext(android.content.res.Configuration);
+ method public android.content.Context createDisplayContext(android.view.Display);
method public android.content.Context createPackageContext(java.lang.String, int) throws android.content.pm.PackageManager.NameNotFoundException;
method public java.lang.String[] databaseList();
method public boolean deleteDatabase(java.lang.String);
@@ -22911,6 +22940,7 @@
public class DisplayMetrics {
ctor public DisplayMetrics();
+ method public boolean equals(android.util.DisplayMetrics);
method public void setTo(android.util.DisplayMetrics);
method public void setToDefaults();
field public static final int DENSITY_DEFAULT = 160; // 0xa0
@@ -23459,6 +23489,7 @@
method public int getDisplayId();
method public deprecated int getHeight();
method public void getMetrics(android.util.DisplayMetrics);
+ method public java.lang.String getName();
method public deprecated int getOrientation();
method public deprecated int getPixelFormat();
method public void getRealMetrics(android.util.DisplayMetrics);
@@ -26627,6 +26658,7 @@
public final class InputMethodSubtype implements android.os.Parcelable {
ctor public InputMethodSubtype(int, int, java.lang.String, java.lang.String, java.lang.String, boolean, boolean);
+ ctor public InputMethodSubtype(int, int, java.lang.String, java.lang.String, java.lang.String, boolean, boolean, int);
method public boolean containsExtraValueKey(java.lang.String);
method public int describeContents();
method public java.lang.CharSequence getDisplayName(android.content.Context, java.lang.String, android.content.pm.ApplicationInfo);
diff --git a/cmds/am/src/com/android/commands/am/Am.java b/cmds/am/src/com/android/commands/am/Am.java
index 7f3dbe5..bcd4588 100644
--- a/cmds/am/src/com/android/commands/am/Am.java
+++ b/cmds/am/src/com/android/commands/am/Am.java
@@ -1181,12 +1181,12 @@
private class IntentReceiver extends IIntentReceiver.Stub {
private boolean mFinished = false;
- public synchronized void performReceive(
- Intent intent, int rc, String data, Bundle ext, boolean ord,
- boolean sticky) {
- String line = "Broadcast completed: result=" + rc;
+ @Override
+ public void performReceive(Intent intent, int resultCode, String data, Bundle extras,
+ boolean ordered, boolean sticky, int sendingUser) throws RemoteException {
+ String line = "Broadcast completed: result=" + resultCode;
if (data != null) line = line + ", data=\"" + data + "\"";
- if (ext != null) line = line + ", extras: " + ext;
+ if (extras != null) line = line + ", extras: " + extras;
System.out.println(line);
mFinished = true;
notifyAll();
diff --git a/cmds/installd/commands.c b/cmds/installd/commands.c
index ab64747..9e83a67 100644
--- a/cmds/installd/commands.c
+++ b/cmds/installd/commands.c
@@ -14,6 +14,7 @@
** limitations under the License.
*/
+#include <linux/capability.h>
#include "installd.h"
#include <diskusage/dirsize.h>
@@ -665,16 +666,16 @@
ALOGE("dexopt cannot open '%s' for output\n", dex_path);
goto fail;
}
- if (fchown(odex_fd, AID_SYSTEM, uid) < 0) {
- ALOGE("dexopt cannot chown '%s'\n", dex_path);
- goto fail;
- }
if (fchmod(odex_fd,
S_IRUSR|S_IWUSR|S_IRGRP |
(is_public ? S_IROTH : 0)) < 0) {
ALOGE("dexopt cannot chmod '%s'\n", dex_path);
goto fail;
}
+ if (fchown(odex_fd, AID_SYSTEM, uid) < 0) {
+ ALOGE("dexopt cannot chown '%s'\n", dex_path);
+ goto fail;
+ }
ALOGV("DexInv: --- BEGIN '%s' ---\n", apk_path);
@@ -690,13 +691,23 @@
ALOGE("setuid(%d) during dexopt\n", uid);
exit(65);
}
+ // drop capabilities
+ struct __user_cap_header_struct capheader;
+ struct __user_cap_data_struct capdata[2];
+ memset(&capheader, 0, sizeof(capheader));
+ memset(&capdata, 0, sizeof(capdata));
+ capheader.version = _LINUX_CAPABILITY_VERSION_3;
+ if (capset(&capheader, &capdata[0]) < 0) {
+ ALOGE("capset failed: %s\n", strerror(errno));
+ exit(66);
+ }
if (flock(odex_fd, LOCK_EX | LOCK_NB) != 0) {
ALOGE("flock(%s) failed: %s\n", dex_path, strerror(errno));
- exit(66);
+ exit(67);
}
run_dexopt(zip_fd, odex_fd, apk_path, dexopt_flags);
- exit(67); /* only get here on exec failure */
+ exit(68); /* only get here on exec failure */
} else {
res = wait_dexopt(pid, apk_path);
if (res != 0) {
diff --git a/cmds/installd/installd.c b/cmds/installd/installd.c
index d559639..652543fd 100644
--- a/cmds/installd/installd.c
+++ b/cmds/installd/installd.c
@@ -14,6 +14,9 @@
** limitations under the License.
*/
+#include <linux/capability.h>
+#include <linux/prctl.h>
+
#include "installd.h"
@@ -491,12 +494,53 @@
return res;
}
+static void drop_privileges() {
+ if (prctl(PR_SET_KEEPCAPS, 1) < 0) {
+ ALOGE("prctl(PR_SET_KEEPCAPS) failed: %s\n", strerror(errno));
+ exit(1);
+ }
+
+ if (setgid(AID_INSTALL) < 0) {
+ ALOGE("setgid() can't drop privileges; exiting.\n");
+ exit(1);
+ }
+
+ if (setuid(AID_INSTALL) < 0) {
+ ALOGE("setuid() can't drop privileges; exiting.\n");
+ exit(1);
+ }
+
+ struct __user_cap_header_struct capheader;
+ struct __user_cap_data_struct capdata[2];
+ memset(&capheader, 0, sizeof(capheader));
+ memset(&capdata, 0, sizeof(capdata));
+ capheader.version = _LINUX_CAPABILITY_VERSION_3;
+ capheader.pid = 0;
+
+ capdata[CAP_TO_INDEX(CAP_DAC_OVERRIDE)].permitted |= CAP_TO_MASK(CAP_DAC_OVERRIDE);
+ capdata[CAP_TO_INDEX(CAP_CHOWN)].permitted |= CAP_TO_MASK(CAP_CHOWN);
+ capdata[CAP_TO_INDEX(CAP_SETUID)].permitted |= CAP_TO_MASK(CAP_SETUID);
+ capdata[CAP_TO_INDEX(CAP_SETGID)].permitted |= CAP_TO_MASK(CAP_SETGID);
+
+ capdata[0].effective = capdata[0].permitted;
+ capdata[1].effective = capdata[1].permitted;
+ capdata[0].inheritable = 0;
+ capdata[1].inheritable = 0;
+
+ if (capset(&capheader, &capdata[0]) < 0) {
+ ALOGE("capset failed: %s\n", strerror(errno));
+ exit(1);
+ }
+}
+
int main(const int argc, const char *argv[]) {
char buf[BUFFER_MAX];
struct sockaddr addr;
socklen_t alen;
int lsocket, s, count;
+ ALOGI("installd firing up\n");
+
if (initialize_globals() < 0) {
ALOGE("Could not initialize globals; exiting.\n");
exit(1);
@@ -507,6 +551,8 @@
exit(1);
}
+ drop_privileges();
+
lsocket = android_get_control_socket(SOCKET_PATH);
if (lsocket < 0) {
ALOGE("Failed to get socket from environment: %s\n", strerror(errno));
diff --git a/cmds/pm/src/com/android/commands/pm/Pm.java b/cmds/pm/src/com/android/commands/pm/Pm.java
index e621ceb..36bf38a 100644
--- a/cmds/pm/src/com/android/commands/pm/Pm.java
+++ b/cmds/pm/src/com/android/commands/pm/Pm.java
@@ -1120,8 +1120,8 @@
ClearDataObserver obs = new ClearDataObserver();
try {
- if (!ActivityManagerNative.getDefault().clearApplicationUserData(pkg, obs,
- Binder.getOrigCallingUser())) {
+ // XXX TO DO: add user arg
+ if (!ActivityManagerNative.getDefault().clearApplicationUserData(pkg, obs, 0)) {
System.err.println("Failed");
}
diff --git a/core/java/android/accounts/AccountManagerService.java b/core/java/android/accounts/AccountManagerService.java
index 7a9f285..9caf84f 100644
--- a/core/java/android/accounts/AccountManagerService.java
+++ b/core/java/android/accounts/AccountManagerService.java
@@ -1052,7 +1052,7 @@
if (account == null) throw new IllegalArgumentException("account is null");
if (authTokenType == null) throw new IllegalArgumentException("authTokenType is null");
checkBinderPermission(Manifest.permission.USE_CREDENTIALS);
- UserAccounts accounts = getUserAccountsForCaller();
+ final UserAccounts accounts = getUserAccountsForCaller();
AccountAuthenticatorCache.ServiceInfo<AuthenticatorDescription> authenticatorInfo =
mAuthenticatorCache.getServiceInfo(
AuthenticatorDescription.newKey(account.type));
@@ -1141,7 +1141,7 @@
if (intent != null && notifyOnAuthFailure && !customTokens) {
doNotification(mAccounts,
account, result.getString(AccountManager.KEY_AUTH_FAILED_MESSAGE),
- intent);
+ intent, accounts.userId);
}
}
super.onResult(result);
@@ -1152,7 +1152,8 @@
}
}
- private void createNoCredentialsPermissionNotification(Account account, Intent intent) {
+ private void createNoCredentialsPermissionNotification(Account account, Intent intent,
+ int userId) {
int uid = intent.getIntExtra(
GrantCredentialsPermissionActivity.EXTRAS_REQUESTING_UID, -1);
String authTokenType = intent.getStringExtra(
@@ -1172,9 +1173,10 @@
title = titleAndSubtitle.substring(0, index);
subtitle = titleAndSubtitle.substring(index + 1);
}
- n.setLatestEventInfo(mContext,
- title, subtitle,
- PendingIntent.getActivity(mContext, 0, intent, PendingIntent.FLAG_CANCEL_CURRENT));
+ n.setLatestEventInfo(mContext, title, subtitle,
+ PendingIntent.getActivityAsUser(mContext, 0, intent,
+ PendingIntent.FLAG_CANCEL_CURRENT,
+ null, new UserHandle(userId)));
installNotification(getCredentialPermissionNotificationId(account, authTokenType, uid), n);
}
@@ -1879,7 +1881,7 @@
private static String getDatabaseName(int userId) {
File systemDir = Environment.getSystemSecureDirectory();
- File databaseFile = new File(systemDir, "users/" + userId + "/" + DATABASE_NAME);
+ File databaseFile = new File(Environment.getUserSystemDirectory(userId), DATABASE_NAME);
if (userId == 0) {
// Migrate old file, if it exists, to the new location.
// Make sure the new file doesn't already exist. A dummy file could have been
@@ -1888,7 +1890,7 @@
File oldFile = new File(systemDir, DATABASE_NAME);
if (oldFile.exists() && !databaseFile.exists()) {
// Check for use directory; create if it doesn't exist, else renameTo will fail
- File userDir = new File(systemDir, "users/" + userId);
+ File userDir = Environment.getUserSystemDirectory(userId);
if (!userDir.exists()) {
if (!userDir.mkdirs()) {
throw new IllegalStateException("User dir cannot be created: " + userDir);
@@ -2083,7 +2085,7 @@
}
private void doNotification(UserAccounts accounts, Account account, CharSequence message,
- Intent intent) {
+ Intent intent, int userId) {
long identityToken = clearCallingIdentity();
try {
if (Log.isLoggable(TAG, Log.VERBOSE)) {
@@ -2093,7 +2095,7 @@
if (intent.getComponent() != null &&
GrantCredentialsPermissionActivity.class.getName().equals(
intent.getComponent().getClassName())) {
- createNoCredentialsPermissionNotification(account, intent);
+ createNoCredentialsPermissionNotification(account, intent, userId);
} else {
final Integer notificationId = getSigninRequiredNotificationId(accounts, account);
intent.addCategory(String.valueOf(notificationId));
@@ -2103,8 +2105,9 @@
mContext.getText(R.string.notification_title).toString();
n.setLatestEventInfo(mContext,
String.format(notificationTitleFormat, account.name),
- message, PendingIntent.getActivity(
- mContext, 0, intent, PendingIntent.FLAG_CANCEL_CURRENT));
+ message, PendingIntent.getActivityAsUser(
+ mContext, 0, intent, PendingIntent.FLAG_CANCEL_CURRENT,
+ null, new UserHandle(userId)));
installNotification(notificationId, n);
}
} finally {
diff --git a/core/java/android/app/Activity.java b/core/java/android/app/Activity.java
index d5580b7..99dfccb 100644
--- a/core/java/android/app/Activity.java
+++ b/core/java/android/app/Activity.java
@@ -4278,7 +4278,8 @@
ActivityManagerNative.getDefault().getIntentSender(
ActivityManager.INTENT_SENDER_ACTIVITY_RESULT, packageName,
mParent == null ? mToken : mParent.mToken,
- mEmbeddedID, requestCode, new Intent[] { data }, null, flags, null);
+ mEmbeddedID, requestCode, new Intent[] { data }, null, flags, null,
+ UserHandle.myUserId());
return target != null ? new PendingIntent(target) : null;
} catch (RemoteException e) {
// Empty
diff --git a/core/java/android/app/ActivityManager.java b/core/java/android/app/ActivityManager.java
index 26d8c17..cd22aad 100644
--- a/core/java/android/app/ActivityManager.java
+++ b/core/java/android/app/ActivityManager.java
@@ -27,6 +27,7 @@
import android.content.pm.ConfigurationInfo;
import android.content.pm.IPackageDataObserver;
import android.content.pm.PackageManager;
+import android.content.pm.UserInfo;
import android.content.res.Resources;
import android.graphics.Bitmap;
import android.graphics.Point;
@@ -1225,7 +1226,7 @@
public boolean clearApplicationUserData(String packageName, IPackageDataObserver observer) {
try {
return ActivityManagerNative.getDefault().clearApplicationUserData(packageName,
- observer, Binder.getOrigCallingUser());
+ observer, UserHandle.myUserId());
} catch (RemoteException e) {
return false;
}
@@ -1902,6 +1903,31 @@
return PackageManager.PERMISSION_DENIED;
}
+ /** @hide */
+ public static int handleIncomingUser(int callingPid, int callingUid, int userId,
+ boolean allowAll, boolean requireFull, String name, String callerPackage) {
+ if (UserHandle.getUserId(callingUid) == userId) {
+ return userId;
+ }
+ try {
+ return ActivityManagerNative.getDefault().handleIncomingUser(callingPid,
+ callingUid, userId, allowAll, requireFull, name, callerPackage);
+ } catch (RemoteException e) {
+ throw new SecurityException("Failed calling activity manager", e);
+ }
+ }
+
+ /** @hide */
+ public static int getCurrentUser() {
+ UserInfo ui;
+ try {
+ ui = ActivityManagerNative.getDefault().getCurrentUser();
+ return ui != null ? ui.id : 0;
+ } catch (RemoteException e) {
+ return 0;
+ }
+ }
+
/**
* Returns the usage statistics of each installed package.
*
diff --git a/core/java/android/app/ActivityManagerNative.java b/core/java/android/app/ActivityManagerNative.java
index 16b7c2a..eed9254 100644
--- a/core/java/android/app/ActivityManagerNative.java
+++ b/core/java/android/app/ActivityManagerNative.java
@@ -199,8 +199,9 @@
Configuration config = Configuration.CREATOR.createFromParcel(data);
Bundle options = data.readInt() != 0
? Bundle.CREATOR.createFromParcel(data) : null;
+ int userId = data.readInt();
int result = startActivityWithConfig(app, intent, resolvedType,
- resultTo, resultWho, requestCode, startFlags, config, options);
+ resultTo, resultWho, requestCode, startFlags, config, options, userId);
reply.writeNoException();
reply.writeInt(result);
return true;
@@ -299,7 +300,8 @@
= b != null ? IIntentReceiver.Stub.asInterface(b) : null;
IntentFilter filter = IntentFilter.CREATOR.createFromParcel(data);
String perm = data.readString();
- Intent intent = registerReceiver(app, packageName, rec, filter, perm);
+ int userId = data.readInt();
+ Intent intent = registerReceiver(app, packageName, rec, filter, perm, userId);
reply.writeNoException();
if (intent != null) {
reply.writeInt(1);
@@ -897,9 +899,10 @@
int fl = data.readInt();
Bundle options = data.readInt() != 0
? Bundle.CREATOR.createFromParcel(data) : null;
+ int userId = data.readInt();
IIntentSender res = getIntentSender(type, packageName, token,
resultWho, requestCode, requestIntents,
- requestResolvedTypes, fl, options);
+ requestResolvedTypes, fl, options, userId);
reply.writeNoException();
reply.writeStrongBinder(res != null ? res.asBinder() : null);
return true;
@@ -934,6 +937,22 @@
return true;
}
+ case HANDLE_INCOMING_USER_TRANSACTION: {
+ data.enforceInterface(IActivityManager.descriptor);
+ int callingPid = data.readInt();
+ int callingUid = data.readInt();
+ int userId = data.readInt();
+ boolean allowAll = data.readInt() != 0 ;
+ boolean requireFull = data.readInt() != 0;
+ String name = data.readString();
+ String callerPackage = data.readString();
+ int res = handleIncomingUser(callingPid, callingUid, userId, allowAll,
+ requireFull, name, callerPackage);
+ reply.writeNoException();
+ reply.writeInt(res);
+ return true;
+ }
+
case SET_PROCESS_LIMIT_TRANSACTION: {
data.enforceInterface(IActivityManager.descriptor);
int max = data.readInt();
@@ -1304,25 +1323,6 @@
return true;
}
- case START_ACTIVITY_IN_PACKAGE_TRANSACTION:
- {
- data.enforceInterface(IActivityManager.descriptor);
- int uid = data.readInt();
- Intent intent = Intent.CREATOR.createFromParcel(data);
- String resolvedType = data.readString();
- IBinder resultTo = data.readStrongBinder();
- String resultWho = data.readString();
- int requestCode = data.readInt();
- int startFlags = data.readInt();
- Bundle options = data.readInt() != 0
- ? Bundle.CREATOR.createFromParcel(data) : null;
- int result = startActivityInPackage(uid, intent, resolvedType,
- resultTo, resultWho, requestCode, startFlags, options);
- reply.writeNoException();
- reply.writeInt(result);
- return true;
- }
-
case KILL_APPLICATION_WITH_UID_TRANSACTION: {
data.enforceInterface(IActivityManager.descriptor);
String pkg = data.readString();
@@ -1489,22 +1489,6 @@
return true;
}
- case START_ACTIVITIES_IN_PACKAGE_TRANSACTION:
- {
- data.enforceInterface(IActivityManager.descriptor);
- int uid = data.readInt();
- Intent[] intents = data.createTypedArray(Intent.CREATOR);
- String[] resolvedTypes = data.createStringArray();
- IBinder resultTo = data.readStrongBinder();
- Bundle options = data.readInt() != 0
- ? Bundle.CREATOR.createFromParcel(data) : null;
- int result = startActivitiesInPackage(uid, intents, resolvedTypes,
- resultTo, options);
- reply.writeNoException();
- reply.writeInt(result);
- return true;
- }
-
case START_ACTIVITIES_TRANSACTION:
{
data.enforceInterface(IActivityManager.descriptor);
@@ -1877,7 +1861,7 @@
public int startActivityWithConfig(IApplicationThread caller, Intent intent,
String resolvedType, IBinder resultTo, String resultWho,
int requestCode, int startFlags, Configuration config,
- Bundle options) throws RemoteException {
+ Bundle options, int userId) throws RemoteException {
Parcel data = Parcel.obtain();
Parcel reply = Parcel.obtain();
data.writeInterfaceToken(IActivityManager.descriptor);
@@ -1895,6 +1879,7 @@
} else {
data.writeInt(0);
}
+ data.writeInt(userId);
mRemote.transact(START_ACTIVITY_TRANSACTION, data, reply, 0);
reply.readException();
int result = reply.readInt();
@@ -2015,7 +2000,7 @@
}
public Intent registerReceiver(IApplicationThread caller, String packageName,
IIntentReceiver receiver,
- IntentFilter filter, String perm) throws RemoteException
+ IntentFilter filter, String perm, int userId) throws RemoteException
{
Parcel data = Parcel.obtain();
Parcel reply = Parcel.obtain();
@@ -2025,6 +2010,7 @@
data.writeStrongBinder(receiver != null ? receiver.asBinder() : null);
filter.writeToParcel(data, 0);
data.writeString(perm);
+ data.writeInt(userId);
mRemote.transact(REGISTER_RECEIVER_TRANSACTION, data, reply, 0);
reply.readException();
Intent intent = null;
@@ -2840,7 +2826,7 @@
public IIntentSender getIntentSender(int type,
String packageName, IBinder token, String resultWho,
int requestCode, Intent[] intents, String[] resolvedTypes, int flags,
- Bundle options) throws RemoteException {
+ Bundle options, int userId) throws RemoteException {
Parcel data = Parcel.obtain();
Parcel reply = Parcel.obtain();
data.writeInterfaceToken(IActivityManager.descriptor);
@@ -2863,6 +2849,7 @@
} else {
data.writeInt(0);
}
+ data.writeInt(userId);
mRemote.transact(GET_INTENT_SENDER_TRANSACTION, data, reply, 0);
reply.readException();
IIntentSender res = IIntentSender.Stub.asInterface(
@@ -2905,6 +2892,25 @@
reply.recycle();
return res;
}
+ public int handleIncomingUser(int callingPid, int callingUid, int userId, boolean allowAll,
+ boolean requireFull, String name, String callerPackage) throws RemoteException {
+ Parcel data = Parcel.obtain();
+ Parcel reply = Parcel.obtain();
+ data.writeInterfaceToken(IActivityManager.descriptor);
+ data.writeInt(callingPid);
+ data.writeInt(callingUid);
+ data.writeInt(userId);
+ data.writeInt(allowAll ? 1 : 0);
+ data.writeInt(requireFull ? 1 : 0);
+ data.writeString(name);
+ data.writeString(callerPackage);
+ mRemote.transact(HANDLE_INCOMING_USER_TRANSACTION, data, reply, 0);
+ reply.readException();
+ int res = reply.readInt();
+ data.recycle();
+ reply.recycle();
+ return res;
+ }
public void setProcessLimit(int max) throws RemoteException
{
Parcel data = Parcel.obtain();
@@ -3360,34 +3366,6 @@
data.recycle();
}
- public int startActivityInPackage(int uid,
- Intent intent, String resolvedType, IBinder resultTo,
- String resultWho, int requestCode, int startFlags, Bundle options)
- throws RemoteException {
- Parcel data = Parcel.obtain();
- Parcel reply = Parcel.obtain();
- data.writeInterfaceToken(IActivityManager.descriptor);
- data.writeInt(uid);
- intent.writeToParcel(data, 0);
- data.writeString(resolvedType);
- data.writeStrongBinder(resultTo);
- data.writeString(resultWho);
- data.writeInt(requestCode);
- data.writeInt(startFlags);
- if (options != null) {
- data.writeInt(1);
- options.writeToParcel(data, 0);
- } else {
- data.writeInt(0);
- }
- mRemote.transact(START_ACTIVITY_IN_PACKAGE_TRANSACTION, data, reply, 0);
- reply.readException();
- int result = reply.readInt();
- reply.recycle();
- data.recycle();
- return result;
- }
-
public void killApplicationWithUid(String pkg, int uid) throws RemoteException {
Parcel data = Parcel.obtain();
Parcel reply = Parcel.obtain();
@@ -3655,30 +3633,6 @@
return result;
}
- public int startActivitiesInPackage(int uid,
- Intent[] intents, String[] resolvedTypes, IBinder resultTo,
- Bundle options) throws RemoteException {
- Parcel data = Parcel.obtain();
- Parcel reply = Parcel.obtain();
- data.writeInterfaceToken(IActivityManager.descriptor);
- data.writeInt(uid);
- data.writeTypedArray(intents, 0);
- data.writeStringArray(resolvedTypes);
- data.writeStrongBinder(resultTo);
- if (options != null) {
- data.writeInt(1);
- options.writeToParcel(data, 0);
- } else {
- data.writeInt(0);
- }
- mRemote.transact(START_ACTIVITIES_IN_PACKAGE_TRANSACTION, data, reply, 0);
- reply.readException();
- int result = reply.readInt();
- reply.recycle();
- data.recycle();
- return result;
- }
-
public int getFrontActivityScreenCompatMode() throws RemoteException {
Parcel data = Parcel.obtain();
Parcel reply = Parcel.obtain();
diff --git a/core/java/android/app/ActivityThread.java b/core/java/android/app/ActivityThread.java
index 4a1bf75..97dcec0 100644
--- a/core/java/android/app/ActivityThread.java
+++ b/core/java/android/app/ActivityThread.java
@@ -203,7 +203,7 @@
= new HashMap<String, WeakReference<LoadedApk>>();
final HashMap<String, WeakReference<LoadedApk>> mResourcePackages
= new HashMap<String, WeakReference<LoadedApk>>();
- final HashMap<CompatibilityInfo, DisplayMetrics> mDisplayMetrics
+ final HashMap<CompatibilityInfo, DisplayMetrics> mDefaultDisplayMetrics
= new HashMap<CompatibilityInfo, DisplayMetrics>();
final HashMap<ResourcesKey, WeakReference<Resources> > mActiveResources
= new HashMap<ResourcesKey, WeakReference<Resources> >();
@@ -320,8 +320,9 @@
static final class ReceiverData extends BroadcastReceiver.PendingResult {
public ReceiverData(Intent intent, int resultCode, String resultData, Bundle resultExtras,
- boolean ordered, boolean sticky, IBinder token) {
- super(resultCode, resultData, resultExtras, TYPE_COMPONENT, ordered, sticky, token);
+ boolean ordered, boolean sticky, IBinder token, int sendingUser) {
+ super(resultCode, resultData, resultExtras, TYPE_COMPONENT, ordered, sticky,
+ token, sendingUser);
this.intent = intent;
}
@@ -613,9 +614,9 @@
public final void scheduleReceiver(Intent intent, ActivityInfo info,
CompatibilityInfo compatInfo, int resultCode, String data, Bundle extras,
- boolean sync) {
+ boolean sync, int sendingUser) {
ReceiverData r = new ReceiverData(intent, resultCode, data, extras,
- sync, false, mAppThread.asBinder());
+ sync, false, mAppThread.asBinder(), sendingUser);
r.info = info;
r.compatInfo = compatInfo;
queueOrSendMessage(H.RECEIVER, r);
@@ -774,8 +775,9 @@
// applies transaction ordering per object for such calls.
public void scheduleRegisteredReceiver(IIntentReceiver receiver, Intent intent,
int resultCode, String dataStr, Bundle extras, boolean ordered,
- boolean sticky) throws RemoteException {
- receiver.performReceive(intent, resultCode, dataStr, extras, ordered, sticky);
+ boolean sticky, int sendingUser) throws RemoteException {
+ receiver.performReceive(intent, resultCode, dataStr, extras, ordered,
+ sticky, sendingUser);
}
public void scheduleLowMemory() {
@@ -1475,12 +1477,14 @@
private static class ResourcesKey {
final private String mResDir;
+ final private int mDisplayId;
final private Configuration mOverrideConfiguration;
final private float mScale;
final private int mHash;
- ResourcesKey(String resDir, Configuration overrideConfiguration, float scale) {
+ ResourcesKey(String resDir, int displayId, Configuration overrideConfiguration, float scale) {
mResDir = resDir;
+ mDisplayId = displayId;
if (overrideConfiguration != null) {
if (Configuration.EMPTY.equals(overrideConfiguration)) {
overrideConfiguration = null;
@@ -1490,6 +1494,7 @@
mScale = scale;
int hash = 17;
hash = 31 * hash + mResDir.hashCode();
+ hash = 31 * hash + mDisplayId;
hash = 31 * hash + (mOverrideConfiguration != null
? mOverrideConfiguration.hashCode() : 0);
hash = 31 * hash + Float.floatToIntBits(mScale);
@@ -1510,6 +1515,9 @@
if (!mResDir.equals(peer.mResDir)) {
return false;
}
+ if (mDisplayId != peer.mDisplayId) {
+ return false;
+ }
if (mOverrideConfiguration != peer.mOverrideConfiguration) {
if (mOverrideConfiguration == null || peer.mOverrideConfiguration == null) {
return false;
@@ -1552,28 +1560,32 @@
return sPackageManager;
}
- DisplayMetrics getDisplayMetricsLocked(CompatibilityInfo ci, boolean forceUpdate) {
- DisplayMetrics dm = mDisplayMetrics.get(ci);
- if (dm != null && !forceUpdate) {
+ private void flushDisplayMetricsLocked() {
+ mDefaultDisplayMetrics.clear();
+ }
+
+ DisplayMetrics getDisplayMetricsLocked(int displayId, CompatibilityInfo ci) {
+ boolean isDefaultDisplay = (displayId == Display.DEFAULT_DISPLAY);
+ DisplayMetrics dm = isDefaultDisplay ? mDefaultDisplayMetrics.get(ci) : null;
+ if (dm != null) {
return dm;
}
+ dm = new DisplayMetrics();
DisplayManagerGlobal displayManager = DisplayManagerGlobal.getInstance();
if (displayManager == null) {
// may be null early in system startup
- dm = new DisplayMetrics();
dm.setToDefaults();
return dm;
}
- if (dm == null) {
- dm = new DisplayMetrics();
- mDisplayMetrics.put(ci, dm);
+ if (isDefaultDisplay) {
+ mDefaultDisplayMetrics.put(ci, dm);
}
CompatibilityInfoHolder cih = new CompatibilityInfoHolder();
cih.set(ci);
- Display d = displayManager.getCompatibleDisplay(Display.DEFAULT_DISPLAY, cih);
+ Display d = displayManager.getCompatibleDisplay(displayId, cih);
d.getMetrics(dm);
//Slog.i("foo", "New metrics: w=" + metrics.widthPixels + " h="
// + metrics.heightPixels + " den=" + metrics.density
@@ -1602,9 +1614,11 @@
* @param compInfo the compability info. It will use the default compatibility info when it's
* null.
*/
- Resources getTopLevelResources(String resDir, Configuration overrideConfiguration,
+ Resources getTopLevelResources(String resDir,
+ int displayId, Configuration overrideConfiguration,
CompatibilityInfo compInfo) {
- ResourcesKey key = new ResourcesKey(resDir, overrideConfiguration,
+ ResourcesKey key = new ResourcesKey(resDir,
+ displayId, overrideConfiguration,
compInfo.applicationScale);
Resources r;
synchronized (mPackages) {
@@ -1636,15 +1650,21 @@
}
//Slog.i(TAG, "Resource: key=" + key + ", display metrics=" + metrics);
- DisplayMetrics metrics = getDisplayMetricsLocked(null, false);
+ DisplayMetrics dm = getDisplayMetricsLocked(displayId, null);
Configuration config;
- if (key.mOverrideConfiguration != null) {
+ boolean isDefaultDisplay = (displayId == Display.DEFAULT_DISPLAY);
+ if (!isDefaultDisplay || key.mOverrideConfiguration != null) {
config = new Configuration(getConfiguration());
- config.updateFrom(key.mOverrideConfiguration);
+ if (!isDefaultDisplay) {
+ applyNonDefaultDisplayMetricsToConfigurationLocked(dm, config);
+ }
+ if (key.mOverrideConfiguration != null) {
+ config.updateFrom(key.mOverrideConfiguration);
+ }
} else {
config = getConfiguration();
}
- r = new Resources(assets, metrics, config, compInfo);
+ r = new Resources(assets, dm, config, compInfo);
if (false) {
Slog.i(TAG, "Created app resources " + resDir + " " + r + ": "
+ r.getConfiguration() + " appScale="
@@ -1670,9 +1690,10 @@
/**
* Creates the top level resources for the given package.
*/
- Resources getTopLevelResources(String resDir, Configuration overrideConfiguration,
+ Resources getTopLevelResources(String resDir,
+ int displayId, Configuration overrideConfiguration,
LoadedApk pkgInfo) {
- return getTopLevelResources(resDir, overrideConfiguration,
+ return getTopLevelResources(resDir, displayId, overrideConfiguration,
pkgInfo.mCompatibilityInfo.get());
}
@@ -1844,7 +1865,8 @@
context.init(info, null, this);
context.getResources().updateConfiguration(
getConfiguration(), getDisplayMetricsLocked(
- CompatibilityInfo.DEFAULT_COMPATIBILITY_INFO, false));
+ Display.DEFAULT_DISPLAY,
+ CompatibilityInfo.DEFAULT_COMPATIBILITY_INFO));
mSystemContext = context;
//Slog.i(TAG, "Created system resources " + context.getResources()
// + ": " + context.getResources().getConfiguration());
@@ -3707,7 +3729,9 @@
return false;
}
int changes = mResConfiguration.updateFrom(config);
- DisplayMetrics dm = getDisplayMetricsLocked(null, true);
+ flushDisplayMetricsLocked();
+ DisplayMetrics defaultDisplayMetrics = getDisplayMetricsLocked(
+ Display.DEFAULT_DISPLAY, null);
if (compat != null && (mResCompatibilityInfo == null ||
!mResCompatibilityInfo.equals(compat))) {
@@ -3722,7 +3746,7 @@
Locale.setDefault(config.locale);
}
- Resources.updateSystemConfiguration(config, dm, compat);
+ Resources.updateSystemConfiguration(config, defaultDisplayMetrics, compat);
ApplicationPackageManager.configurationChanged();
//Slog.i(TAG, "Configuration changed in " + currentPackageName());
@@ -3737,13 +3761,22 @@
if (r != null) {
if (DEBUG_CONFIGURATION) Slog.v(TAG, "Changing resources "
+ r + " config to: " + config);
- Configuration override = entry.getKey().mOverrideConfiguration;
- if (override != null) {
+ int displayId = entry.getKey().mDisplayId;
+ boolean isDefaultDisplay = (displayId == Display.DEFAULT_DISPLAY);
+ DisplayMetrics dm = defaultDisplayMetrics;
+ Configuration overrideConfig = entry.getKey().mOverrideConfiguration;
+ if (!isDefaultDisplay || overrideConfig != null) {
if (tmpConfig == null) {
tmpConfig = new Configuration();
}
tmpConfig.setTo(config);
- tmpConfig.updateFrom(override);
+ if (!isDefaultDisplay) {
+ dm = getDisplayMetricsLocked(displayId, null);
+ applyNonDefaultDisplayMetricsToConfigurationLocked(dm, tmpConfig);
+ }
+ if (overrideConfig != null) {
+ tmpConfig.updateFrom(overrideConfig);
+ }
r.updateConfiguration(tmpConfig, dm, compat);
} else {
r.updateConfiguration(config, dm, compat);
@@ -3759,6 +3792,22 @@
return changes != 0;
}
+ final void applyNonDefaultDisplayMetricsToConfigurationLocked(
+ DisplayMetrics dm, Configuration config) {
+ config.screenLayout = Configuration.SCREENLAYOUT_SIZE_XLARGE
+ | Configuration.SCREENLAYOUT_LONG_NO;
+ config.touchscreen = Configuration.TOUCHSCREEN_NOTOUCH;
+ config.orientation = (dm.widthPixels >= dm.heightPixels) ?
+ Configuration.ORIENTATION_LANDSCAPE : Configuration.ORIENTATION_PORTRAIT;
+ config.densityDpi = dm.densityDpi;
+ config.screenWidthDp = (int)(dm.widthPixels / dm.density);
+ config.screenHeightDp = (int)(dm.heightPixels / dm.density);
+ config.smallestScreenWidthDp = config.screenWidthDp; // assume screen does not rotate
+ config.compatScreenWidthDp = config.screenWidthDp;
+ config.compatScreenHeightDp = config.screenHeightDp;
+ config.compatSmallestScreenWidthDp = config.smallestScreenWidthDp;
+ }
+
final Configuration applyCompatConfiguration(int displayDensity) {
Configuration config = mConfiguration;
if (mCompatConfiguration == null) {
diff --git a/core/java/android/app/AlertDialog.java b/core/java/android/app/AlertDialog.java
index 7809e73..6ab2bd1 100644
--- a/core/java/android/app/AlertDialog.java
+++ b/core/java/android/app/AlertDialog.java
@@ -110,8 +110,9 @@
this(context, theme, true);
}
- AlertDialog(Context context, int theme, boolean createContextWrapper) {
- super(context, resolveDialogTheme(context, theme), createContextWrapper);
+ AlertDialog(Context context, int theme, boolean createThemeContextWrapper) {
+ super(context, resolveDialogTheme(context, theme), createThemeContextWrapper);
+
mWindow.alwaysReadCloseOnTouchAttr();
mAlert = new AlertController(getContext(), this, getWindow());
}
diff --git a/core/java/android/app/ApplicationPackageManager.java b/core/java/android/app/ApplicationPackageManager.java
index 91753aa..c4f1371 100644
--- a/core/java/android/app/ApplicationPackageManager.java
+++ b/core/java/android/app/ApplicationPackageManager.java
@@ -54,6 +54,7 @@
import android.os.RemoteException;
import android.os.UserHandle;
import android.util.Log;
+import android.view.Display;
import java.lang.ref.WeakReference;
import java.util.ArrayList;
@@ -722,8 +723,8 @@
return mContext.mMainThread.getSystemContext().getResources();
}
Resources r = mContext.mMainThread.getTopLevelResources(
- app.uid == Process.myUid() ? app.sourceDir
- : app.publicSourceDir, null, mContext.mPackageInfo);
+ app.uid == Process.myUid() ? app.sourceDir : app.publicSourceDir,
+ Display.DEFAULT_DISPLAY, null, mContext.mPackageInfo);
if (r != null) {
return r;
}
diff --git a/core/java/android/app/ApplicationThreadNative.java b/core/java/android/app/ApplicationThreadNative.java
index 8e6278d..63aa5f9 100644
--- a/core/java/android/app/ApplicationThreadNative.java
+++ b/core/java/android/app/ApplicationThreadNative.java
@@ -193,8 +193,9 @@
String resultData = data.readString();
Bundle resultExtras = data.readBundle();
boolean sync = data.readInt() != 0;
+ int sendingUser = data.readInt();
scheduleReceiver(intent, info, compatInfo, resultCode, resultData,
- resultExtras, sync);
+ resultExtras, sync, sendingUser);
return true;
}
@@ -378,8 +379,9 @@
Bundle extras = data.readBundle();
boolean ordered = data.readInt() != 0;
boolean sticky = data.readInt() != 0;
+ int sendingUser = data.readInt();
scheduleRegisteredReceiver(receiver, intent,
- resultCode, dataStr, extras, ordered, sticky);
+ resultCode, dataStr, extras, ordered, sticky, sendingUser);
return true;
}
@@ -755,7 +757,7 @@
public final void scheduleReceiver(Intent intent, ActivityInfo info,
CompatibilityInfo compatInfo, int resultCode, String resultData,
- Bundle map, boolean sync) throws RemoteException {
+ Bundle map, boolean sync, int sendingUser) throws RemoteException {
Parcel data = Parcel.obtain();
data.writeInterfaceToken(IApplicationThread.descriptor);
intent.writeToParcel(data, 0);
@@ -765,6 +767,7 @@
data.writeString(resultData);
data.writeBundle(map);
data.writeInt(sync ? 1 : 0);
+ data.writeInt(sendingUser);
mRemote.transact(SCHEDULE_RECEIVER_TRANSACTION, data, null,
IBinder.FLAG_ONEWAY);
data.recycle();
@@ -991,8 +994,8 @@
}
public void scheduleRegisteredReceiver(IIntentReceiver receiver, Intent intent,
- int resultCode, String dataStr, Bundle extras, boolean ordered, boolean sticky)
- throws RemoteException {
+ int resultCode, String dataStr, Bundle extras, boolean ordered,
+ boolean sticky, int sendingUser) throws RemoteException {
Parcel data = Parcel.obtain();
data.writeInterfaceToken(IApplicationThread.descriptor);
data.writeStrongBinder(receiver.asBinder());
@@ -1002,6 +1005,7 @@
data.writeBundle(extras);
data.writeInt(ordered ? 1 : 0);
data.writeInt(sticky ? 1 : 0);
+ data.writeInt(sendingUser);
mRemote.transact(SCHEDULE_REGISTERED_RECEIVER_TRANSACTION, data, null,
IBinder.FLAG_ONEWAY);
data.recycle();
diff --git a/core/java/android/app/ContextImpl.java b/core/java/android/app/ContextImpl.java
index 1b6f84b..65ea6a0 100644
--- a/core/java/android/app/ContextImpl.java
+++ b/core/java/android/app/ContextImpl.java
@@ -138,6 +138,17 @@
}
@Override
+ public Intent registerReceiverAsUser(BroadcastReceiver receiver, UserHandle user,
+ IntentFilter filter, String broadcastPermission, Handler scheduler) {
+ throw new ReceiverCallNotAllowedException(
+ "IntentReceiver components are not allowed to register to receive intents");
+ //ex.fillInStackTrace();
+ //Log.e("IntentReceiver", ex.getMessage(), ex);
+ //return mContext.registerReceiver(receiver, filter, broadcastPermission,
+ // scheduler);
+ }
+
+ @Override
public boolean bindService(Intent service, ServiceConnection conn, int flags) {
throw new ReceiverCallNotAllowedException(
"IntentReceiver components are not allowed to bind to services");
@@ -168,6 +179,7 @@
private int mThemeResource = 0;
private Resources.Theme mTheme = null;
private PackageManager mPackageManager;
+ private Display mDisplay; // may be null if default display
private Context mReceiverRestrictedContext = null;
private boolean mRestricted;
@@ -502,8 +514,13 @@
registerService(WINDOW_SERVICE, new ServiceFetcher() {
public Object getService(ContextImpl ctx) {
- return new WindowManagerImpl(ctx.getOuterContext(),
- Display.DEFAULT_DISPLAY);
+ Display display = ctx.mDisplay;
+ if (display == null) {
+ DisplayManager dm = (DisplayManager)ctx.getOuterContext().getSystemService(
+ Context.DISPLAY_SERVICE);
+ display = dm.getDisplay(Display.DEFAULT_DISPLAY);
+ }
+ return new WindowManagerImpl(display);
}});
registerService(USER_SERVICE, new ServiceFetcher() {
@@ -994,7 +1011,7 @@
ActivityManagerNative.getDefault().broadcastIntent(
mMainThread.getApplicationThread(), intent, resolvedType, null,
Activity.RESULT_OK, null, null, null, false, false,
- Binder.getOrigCallingUser());
+ UserHandle.myUserId());
} catch (RemoteException e) {
}
}
@@ -1007,7 +1024,7 @@
ActivityManagerNative.getDefault().broadcastIntent(
mMainThread.getApplicationThread(), intent, resolvedType, null,
Activity.RESULT_OK, null, null, receiverPermission, false, false,
- Binder.getOrigCallingUser());
+ UserHandle.myUserId());
} catch (RemoteException e) {
}
}
@@ -1021,7 +1038,7 @@
ActivityManagerNative.getDefault().broadcastIntent(
mMainThread.getApplicationThread(), intent, resolvedType, null,
Activity.RESULT_OK, null, null, receiverPermission, true, false,
- Binder.getOrigCallingUser());
+ UserHandle.myUserId());
} catch (RemoteException e) {
}
}
@@ -1054,7 +1071,7 @@
ActivityManagerNative.getDefault().broadcastIntent(
mMainThread.getApplicationThread(), intent, resolvedType, rd,
initialCode, initialData, initialExtras, receiverPermission,
- true, false, Binder.getOrigCallingUser());
+ true, false, UserHandle.myUserId());
} catch (RemoteException e) {
}
}
@@ -1125,7 +1142,7 @@
ActivityManagerNative.getDefault().broadcastIntent(
mMainThread.getApplicationThread(), intent, resolvedType, null,
Activity.RESULT_OK, null, null, null, false, true,
- Binder.getOrigCallingUser());
+ UserHandle.myUserId());
} catch (RemoteException e) {
}
}
@@ -1158,7 +1175,7 @@
ActivityManagerNative.getDefault().broadcastIntent(
mMainThread.getApplicationThread(), intent, resolvedType, rd,
initialCode, initialData, initialExtras, null,
- true, true, Binder.getOrigCallingUser());
+ true, true, UserHandle.myUserId());
} catch (RemoteException e) {
}
}
@@ -1173,7 +1190,7 @@
try {
intent.setAllowFds(false);
ActivityManagerNative.getDefault().unbroadcastIntent(
- mMainThread.getApplicationThread(), intent, Binder.getOrigCallingUser());
+ mMainThread.getApplicationThread(), intent, UserHandle.myUserId());
} catch (RemoteException e) {
}
}
@@ -1246,11 +1263,18 @@
@Override
public Intent registerReceiver(BroadcastReceiver receiver, IntentFilter filter,
String broadcastPermission, Handler scheduler) {
- return registerReceiverInternal(receiver, filter, broadcastPermission,
- scheduler, getOuterContext());
+ return registerReceiverInternal(receiver, UserHandle.myUserId(),
+ filter, broadcastPermission, scheduler, getOuterContext());
}
- private Intent registerReceiverInternal(BroadcastReceiver receiver,
+ @Override
+ public Intent registerReceiverAsUser(BroadcastReceiver receiver, UserHandle user,
+ IntentFilter filter, String broadcastPermission, Handler scheduler) {
+ return registerReceiverInternal(receiver, user.getIdentifier(),
+ filter, broadcastPermission, scheduler, getOuterContext());
+ }
+
+ private Intent registerReceiverInternal(BroadcastReceiver receiver, int userId,
IntentFilter filter, String broadcastPermission,
Handler scheduler, Context context) {
IIntentReceiver rd = null;
@@ -1273,7 +1297,7 @@
try {
return ActivityManagerNative.getDefault().registerReceiver(
mMainThread.getApplicationThread(), mBasePackageName,
- rd, filter, broadcastPermission);
+ rd, filter, broadcastPermission, userId);
} catch (RemoteException e) {
return null;
}
@@ -1676,22 +1700,52 @@
@Override
public Context createConfigurationContext(Configuration overrideConfiguration) {
+ if (overrideConfiguration == null) {
+ throw new IllegalArgumentException("overrideConfiguration must not be null");
+ }
+
ContextImpl c = new ContextImpl();
c.init(mPackageInfo, null, mMainThread);
c.mResources = mMainThread.getTopLevelResources(
- mPackageInfo.getResDir(), overrideConfiguration,
+ mPackageInfo.getResDir(),
+ getDisplayId(), overrideConfiguration,
mResources.getCompatibilityInfo());
return c;
}
@Override
+ public Context createDisplayContext(Display display) {
+ if (display == null) {
+ throw new IllegalArgumentException("display must not be null");
+ }
+
+ int displayId = display.getDisplayId();
+ CompatibilityInfo ci = CompatibilityInfo.DEFAULT_COMPATIBILITY_INFO;
+ CompatibilityInfoHolder cih = getCompatibilityInfo(displayId);
+ if (cih != null) {
+ ci = cih.get();
+ }
+
+ ContextImpl context = new ContextImpl();
+ context.init(mPackageInfo, null, mMainThread);
+ context.mDisplay = display;
+ context.mResources = mMainThread.getTopLevelResources(
+ mPackageInfo.getResDir(), displayId, null, ci);
+ return context;
+ }
+
+ private int getDisplayId() {
+ return mDisplay != null ? mDisplay.getDisplayId() : Display.DEFAULT_DISPLAY;
+ }
+
+ @Override
public boolean isRestricted() {
return mRestricted;
}
@Override
- public CompatibilityInfoHolder getCompatibilityInfo() {
- return mPackageInfo.mCompatibilityInfo;
+ public CompatibilityInfoHolder getCompatibilityInfo(int displayId) {
+ return displayId == Display.DEFAULT_DISPLAY ? mPackageInfo.mCompatibilityInfo : null;
}
private File getDataDirFile() {
@@ -1735,6 +1789,7 @@
mResources = context.mResources;
mMainThread = context.mMainThread;
mContentResolver = context.mContentResolver;
+ mDisplay = context.mDisplay;
mOuterContext = this;
}
@@ -1758,7 +1813,8 @@
" compatiblity info:" + container.getDisplayMetrics());
}
mResources = mainThread.getTopLevelResources(
- mPackageInfo.getResDir(), null, container.getCompatibilityInfo());
+ mPackageInfo.getResDir(), Display.DEFAULT_DISPLAY,
+ null, container.getCompatibilityInfo());
}
mMainThread = mainThread;
mContentResolver = new ApplicationContentResolver(this, mainThread);
diff --git a/core/java/android/app/Dialog.java b/core/java/android/app/Dialog.java
index 16112cb3..b3d99c5 100644
--- a/core/java/android/app/Dialog.java
+++ b/core/java/android/app/Dialog.java
@@ -147,15 +147,19 @@
this(context, theme, true);
}
- Dialog(Context context, int theme, boolean createContextWrapper) {
- if (theme == 0) {
- TypedValue outValue = new TypedValue();
- context.getTheme().resolveAttribute(com.android.internal.R.attr.dialogTheme,
- outValue, true);
- theme = outValue.resourceId;
+ Dialog(Context context, int theme, boolean createContextThemeWrapper) {
+ if (createContextThemeWrapper) {
+ if (theme == 0) {
+ TypedValue outValue = new TypedValue();
+ context.getTheme().resolveAttribute(com.android.internal.R.attr.dialogTheme,
+ outValue, true);
+ theme = outValue.resourceId;
+ }
+ mContext = new ContextThemeWrapper(context, theme);
+ } else {
+ mContext = context;
}
- mContext = createContextWrapper ? new ContextThemeWrapper(context, theme) : context;
mWindowManager = (WindowManager)context.getSystemService(Context.WINDOW_SERVICE);
Window w = PolicyManager.makeNewWindow(mContext);
mWindow = w;
@@ -164,7 +168,7 @@
w.setGravity(Gravity.CENTER);
mListenersHandler = new ListenersHandler(this);
}
-
+
/**
* @deprecated
* @hide
diff --git a/core/java/android/app/IActivityManager.java b/core/java/android/app/IActivityManager.java
index 70d8445..7a633ed 100644
--- a/core/java/android/app/IActivityManager.java
+++ b/core/java/android/app/IActivityManager.java
@@ -66,7 +66,7 @@
public int startActivityWithConfig(IApplicationThread caller,
Intent intent, String resolvedType, IBinder resultTo, String resultWho,
int requestCode, int startFlags, Configuration newConfig,
- Bundle options) throws RemoteException;
+ Bundle options, int userId) throws RemoteException;
public int startActivityIntentSender(IApplicationThread caller,
IntentSender intent, Intent fillInIntent, String resolvedType,
IBinder resultTo, String resultWho, int requestCode,
@@ -80,7 +80,7 @@
public boolean willActivityBeVisible(IBinder token) throws RemoteException;
public Intent registerReceiver(IApplicationThread caller, String callerPackage,
IIntentReceiver receiver, IntentFilter filter,
- String requiredPermission) throws RemoteException;
+ String requiredPermission, int userId) throws RemoteException;
public void unregisterReceiver(IIntentReceiver receiver) throws RemoteException;
public int broadcastIntent(IApplicationThread caller, Intent intent,
String resolvedType, IIntentReceiver resultTo, int resultCode,
@@ -177,13 +177,16 @@
public IIntentSender getIntentSender(int type,
String packageName, IBinder token, String resultWho,
int requestCode, Intent[] intents, String[] resolvedTypes,
- int flags, Bundle options) throws RemoteException;
+ int flags, Bundle options, int userId) throws RemoteException;
public void cancelIntentSender(IIntentSender sender) throws RemoteException;
public boolean clearApplicationUserData(final String packageName,
final IPackageDataObserver observer, int userId) throws RemoteException;
public String getPackageForIntentSender(IIntentSender sender) throws RemoteException;
public int getUidForIntentSender(IIntentSender sender) throws RemoteException;
+ public int handleIncomingUser(int callingPid, int callingUid, int userId, boolean allowAll,
+ boolean requireFull, String name, String callerPackage) throws RemoteException;
+
public void setProcessLimit(int max) throws RemoteException;
public int getProcessLimit() throws RemoteException;
@@ -272,11 +275,6 @@
public void stopAppSwitches() throws RemoteException;
public void resumeAppSwitches() throws RemoteException;
- public int startActivityInPackage(int uid,
- Intent intent, String resolvedType, IBinder resultTo,
- String resultWho, int requestCode, int startFlags, Bundle options)
- throws RemoteException;
-
public void killApplicationWithUid(String pkg, int uid) throws RemoteException;
public void closeSystemDialogs(String reason) throws RemoteException;
@@ -316,9 +314,6 @@
public int startActivities(IApplicationThread caller,
Intent[] intents, String[] resolvedTypes, IBinder resultTo,
Bundle options) throws RemoteException;
- public int startActivitiesInPackage(int uid,
- Intent[] intents, String[] resolvedTypes, IBinder resultTo,
- Bundle options) throws RemoteException;
public int getFrontActivityScreenCompatMode() throws RemoteException;
public void setFrontActivityScreenCompatMode(int mode) throws RemoteException;
@@ -551,9 +546,8 @@
int BACKUP_AGENT_CREATED_TRANSACTION = IBinder.FIRST_CALL_TRANSACTION+90;
int UNBIND_BACKUP_AGENT_TRANSACTION = IBinder.FIRST_CALL_TRANSACTION+91;
int GET_UID_FOR_INTENT_SENDER_TRANSACTION = IBinder.FIRST_CALL_TRANSACTION+92;
+ int HANDLE_INCOMING_USER_TRANSACTION = IBinder.FIRST_CALL_TRANSACTION+93;
-
- int START_ACTIVITY_IN_PACKAGE_TRANSACTION = IBinder.FIRST_CALL_TRANSACTION+94;
int KILL_APPLICATION_WITH_UID_TRANSACTION = IBinder.FIRST_CALL_TRANSACTION+95;
int CLOSE_SYSTEM_DIALOGS_TRANSACTION = IBinder.FIRST_CALL_TRANSACTION+96;
int GET_PROCESS_MEMORY_INFO_TRANSACTION = IBinder.FIRST_CALL_TRANSACTION+97;
@@ -580,7 +574,7 @@
int CHECK_GRANT_URI_PERMISSION_TRANSACTION = IBinder.FIRST_CALL_TRANSACTION+118;
int DUMP_HEAP_TRANSACTION = IBinder.FIRST_CALL_TRANSACTION+119;
int START_ACTIVITIES_TRANSACTION = IBinder.FIRST_CALL_TRANSACTION+120;
- int START_ACTIVITIES_IN_PACKAGE_TRANSACTION = IBinder.FIRST_CALL_TRANSACTION+121;
+
int ACTIVITY_SLEPT_TRANSACTION = IBinder.FIRST_CALL_TRANSACTION+122;
int GET_FRONT_ACTIVITY_SCREEN_COMPAT_MODE_TRANSACTION = IBinder.FIRST_CALL_TRANSACTION+123;
int SET_FRONT_ACTIVITY_SCREEN_COMPAT_MODE_TRANSACTION = IBinder.FIRST_CALL_TRANSACTION+124;
diff --git a/core/java/android/app/IApplicationThread.java b/core/java/android/app/IApplicationThread.java
index f60cfd6..03a26d4 100644
--- a/core/java/android/app/IApplicationThread.java
+++ b/core/java/android/app/IApplicationThread.java
@@ -65,7 +65,8 @@
void scheduleDestroyActivity(IBinder token, boolean finished,
int configChanges) throws RemoteException;
void scheduleReceiver(Intent intent, ActivityInfo info, CompatibilityInfo compatInfo,
- int resultCode, String data, Bundle extras, boolean sync) throws RemoteException;
+ int resultCode, String data, Bundle extras, boolean sync,
+ int sendingUser) throws RemoteException;
static final int BACKUP_MODE_INCREMENTAL = 0;
static final int BACKUP_MODE_FULL = 1;
static final int BACKUP_MODE_RESTORE = 2;
@@ -105,8 +106,8 @@
void dumpProvider(FileDescriptor fd, IBinder servicetoken, String[] args)
throws RemoteException;
void scheduleRegisteredReceiver(IIntentReceiver receiver, Intent intent,
- int resultCode, String data, Bundle extras, boolean ordered, boolean sticky)
- throws RemoteException;
+ int resultCode, String data, Bundle extras, boolean ordered,
+ boolean sticky, int sendingUser) throws RemoteException;
void scheduleLowMemory() throws RemoteException;
void scheduleActivityConfigurationChanged(IBinder token) throws RemoteException;
void profilerControl(boolean start, String path, ParcelFileDescriptor fd, int profileType)
diff --git a/core/java/android/app/INotificationManager.aidl b/core/java/android/app/INotificationManager.aidl
index 6f95e26..62d4962 100644
--- a/core/java/android/app/INotificationManager.aidl
+++ b/core/java/android/app/INotificationManager.aidl
@@ -24,16 +24,13 @@
/** {@hide} */
interface INotificationManager
{
- /** @deprecated use {@link #enqueueNotificationWithTag} instead */
- void enqueueNotification(String pkg, int id, in Notification notification, inout int[] idReceived);
- /** @deprecated use {@link #cancelNotificationWithTag} instead */
- void cancelNotification(String pkg, int id);
- void cancelAllNotifications(String pkg);
+ void cancelAllNotifications(String pkg, int userId);
void enqueueToast(String pkg, ITransientNotification callback, int duration);
void cancelToast(String pkg, ITransientNotification callback);
- void enqueueNotificationWithTag(String pkg, String tag, int id, in Notification notification, inout int[] idReceived);
- void cancelNotificationWithTag(String pkg, String tag, int id);
+ void enqueueNotificationWithTag(String pkg, String tag, int id,
+ in Notification notification, inout int[] idReceived, int userId);
+ void cancelNotificationWithTag(String pkg, String tag, int id, int userId);
void setNotificationsEnabledForPackage(String pkg, boolean enabled);
boolean areNotificationsEnabledForPackage(String pkg);
diff --git a/core/java/android/app/LoadedApk.java b/core/java/android/app/LoadedApk.java
index 1e89bb2..0a9ed58 100644
--- a/core/java/android/app/LoadedApk.java
+++ b/core/java/android/app/LoadedApk.java
@@ -41,6 +41,7 @@
import android.util.AndroidRuntimeException;
import android.util.Slog;
import android.view.CompatibilityInfoHolder;
+import android.view.Display;
import java.io.File;
import java.io.IOException;
@@ -139,7 +140,8 @@
ContextImpl.createSystemContext(mainThread);
ActivityThread.mSystemContext.getResources().updateConfiguration(
mainThread.getConfiguration(),
- mainThread.getDisplayMetricsLocked(compatInfo, false),
+ mainThread.getDisplayMetricsLocked(
+ Display.DEFAULT_DISPLAY, compatInfo),
compatInfo);
//Slog.i(TAG, "Created system resources "
// + mSystemContext.getResources() + ": "
@@ -471,7 +473,8 @@
public Resources getResources(ActivityThread mainThread) {
if (mResources == null) {
- mResources = mainThread.getTopLevelResources(mResDir, null, this);
+ mResources = mainThread.getTopLevelResources(mResDir,
+ Display.DEFAULT_DISPLAY, null, this);
}
return mResources;
}
@@ -667,8 +670,8 @@
mDispatcher = new WeakReference<LoadedApk.ReceiverDispatcher>(rd);
mStrongRef = strong ? rd : null;
}
- public void performReceive(Intent intent, int resultCode,
- String data, Bundle extras, boolean ordered, boolean sticky) {
+ public void performReceive(Intent intent, int resultCode, String data,
+ Bundle extras, boolean ordered, boolean sticky, int sendingUser) {
LoadedApk.ReceiverDispatcher rd = mDispatcher.get();
if (ActivityThread.DEBUG_BROADCAST) {
int seq = intent.getIntExtra("seq", -1);
@@ -677,7 +680,7 @@
}
if (rd != null) {
rd.performReceive(intent, resultCode, data, extras,
- ordered, sticky);
+ ordered, sticky, sendingUser);
} else {
// The activity manager dispatched a broadcast to a registered
// receiver in this process, but before it could be delivered the
@@ -713,10 +716,10 @@
private final boolean mOrdered;
public Args(Intent intent, int resultCode, String resultData, Bundle resultExtras,
- boolean ordered, boolean sticky) {
+ boolean ordered, boolean sticky, int sendingUser) {
super(resultCode, resultData, resultExtras,
mRegistered ? TYPE_REGISTERED : TYPE_UNREGISTERED,
- ordered, sticky, mIIntentReceiver.asBinder());
+ ordered, sticky, mIIntentReceiver.asBinder(), sendingUser);
mCurIntent = intent;
mOrdered = ordered;
}
@@ -827,14 +830,15 @@
return mUnregisterLocation;
}
- public void performReceive(Intent intent, int resultCode,
- String data, Bundle extras, boolean ordered, boolean sticky) {
+ public void performReceive(Intent intent, int resultCode, String data,
+ Bundle extras, boolean ordered, boolean sticky, int sendingUser) {
if (ActivityThread.DEBUG_BROADCAST) {
int seq = intent.getIntExtra("seq", -1);
Slog.i(ActivityThread.TAG, "Enqueueing broadcast " + intent.getAction() + " seq=" + seq
+ " to " + mReceiver);
}
- Args args = new Args(intent, resultCode, data, extras, ordered, sticky);
+ Args args = new Args(intent, resultCode, data, extras, ordered,
+ sticky, sendingUser);
if (!mActivityThread.post(args)) {
if (mRegistered && ordered) {
IActivityManager mgr = ActivityManagerNative.getDefault();
diff --git a/core/java/android/app/NotificationManager.java b/core/java/android/app/NotificationManager.java
index 69c20b0..c095280 100644
--- a/core/java/android/app/NotificationManager.java
+++ b/core/java/android/app/NotificationManager.java
@@ -21,6 +21,7 @@
import android.os.IBinder;
import android.os.RemoteException;
import android.os.ServiceManager;
+import android.os.UserHandle;
import android.util.Log;
/**
@@ -125,7 +126,27 @@
String pkg = mContext.getPackageName();
if (localLOGV) Log.v(TAG, pkg + ": notify(" + id + ", " + notification + ")");
try {
- service.enqueueNotificationWithTag(pkg, tag, id, notification, idOut);
+ service.enqueueNotificationWithTag(pkg, tag, id, notification, idOut,
+ UserHandle.myUserId());
+ if (id != idOut[0]) {
+ Log.w(TAG, "notify: id corrupted: sent " + id + ", got back " + idOut[0]);
+ }
+ } catch (RemoteException e) {
+ }
+ }
+
+ /**
+ * @hide
+ */
+ public void notifyAsUser(String tag, int id, Notification notification, UserHandle user)
+ {
+ int[] idOut = new int[1];
+ INotificationManager service = getService();
+ String pkg = mContext.getPackageName();
+ if (localLOGV) Log.v(TAG, pkg + ": notify(" + id + ", " + notification + ")");
+ try {
+ service.enqueueNotificationWithTag(pkg, tag, id, notification, idOut,
+ user.getIdentifier());
if (id != idOut[0]) {
Log.w(TAG, "notify: id corrupted: sent " + id + ", got back " + idOut[0]);
}
@@ -154,7 +175,21 @@
String pkg = mContext.getPackageName();
if (localLOGV) Log.v(TAG, pkg + ": cancel(" + id + ")");
try {
- service.cancelNotificationWithTag(pkg, tag, id);
+ service.cancelNotificationWithTag(pkg, tag, id, UserHandle.myUserId());
+ } catch (RemoteException e) {
+ }
+ }
+
+ /**
+ * @hide
+ */
+ public void cancelAsUser(String tag, int id, UserHandle user)
+ {
+ INotificationManager service = getService();
+ String pkg = mContext.getPackageName();
+ if (localLOGV) Log.v(TAG, pkg + ": cancel(" + id + ")");
+ try {
+ service.cancelNotificationWithTag(pkg, tag, id, user.getIdentifier());
} catch (RemoteException e) {
}
}
@@ -169,7 +204,7 @@
String pkg = mContext.getPackageName();
if (localLOGV) Log.v(TAG, pkg + ": cancelAll()");
try {
- service.cancelAllNotifications(pkg);
+ service.cancelAllNotifications(pkg, UserHandle.myUserId());
} catch (RemoteException e) {
}
}
diff --git a/core/java/android/app/PendingIntent.java b/core/java/android/app/PendingIntent.java
index a57c516..a3c1838 100644
--- a/core/java/android/app/PendingIntent.java
+++ b/core/java/android/app/PendingIntent.java
@@ -147,8 +147,8 @@
mWho = who;
mHandler = handler;
}
- public void performReceive(Intent intent, int resultCode,
- String data, Bundle extras, boolean serialized, boolean sticky) {
+ public void performReceive(Intent intent, int resultCode, String data,
+ Bundle extras, boolean serialized, boolean sticky, int sendingUser) {
mIntent = intent;
mResultCode = resultCode;
mResultData = data;
@@ -228,7 +228,29 @@
ActivityManager.INTENT_SENDER_ACTIVITY, packageName,
null, null, requestCode, new Intent[] { intent },
resolvedType != null ? new String[] { resolvedType } : null,
- flags, options);
+ flags, options, UserHandle.myUserId());
+ return target != null ? new PendingIntent(target) : null;
+ } catch (RemoteException e) {
+ }
+ return null;
+ }
+
+ /**
+ * @hide
+ */
+ public static PendingIntent getActivityAsUser(Context context, int requestCode,
+ Intent intent, int flags, Bundle options, UserHandle user) {
+ String packageName = context.getPackageName();
+ String resolvedType = intent != null ? intent.resolveTypeIfNeeded(
+ context.getContentResolver()) : null;
+ try {
+ intent.setAllowFds(false);
+ IIntentSender target =
+ ActivityManagerNative.getDefault().getIntentSender(
+ ActivityManager.INTENT_SENDER_ACTIVITY, packageName,
+ null, null, requestCode, new Intent[] { intent },
+ resolvedType != null ? new String[] { resolvedType } : null,
+ flags, options, user.getIdentifier());
return target != null ? new PendingIntent(target) : null;
} catch (RemoteException e) {
}
@@ -334,7 +356,8 @@
IIntentSender target =
ActivityManagerNative.getDefault().getIntentSender(
ActivityManager.INTENT_SENDER_ACTIVITY, packageName,
- null, null, requestCode, intents, resolvedTypes, flags, options);
+ null, null, requestCode, intents, resolvedTypes, flags, options,
+ UserHandle.myUserId());
return target != null ? new PendingIntent(target) : null;
} catch (RemoteException e) {
}
@@ -372,7 +395,7 @@
ActivityManager.INTENT_SENDER_BROADCAST, packageName,
null, null, requestCode, new Intent[] { intent },
resolvedType != null ? new String[] { resolvedType } : null,
- flags, null);
+ flags, null, UserHandle.myUserId());
return target != null ? new PendingIntent(target) : null;
} catch (RemoteException e) {
}
@@ -411,7 +434,7 @@
ActivityManager.INTENT_SENDER_SERVICE, packageName,
null, null, requestCode, new Intent[] { intent },
resolvedType != null ? new String[] { resolvedType } : null,
- flags, null);
+ flags, null, UserHandle.myUserId());
return target != null ? new PendingIntent(target) : null;
} catch (RemoteException e) {
}
diff --git a/core/java/android/app/Presentation.java b/core/java/android/app/Presentation.java
new file mode 100644
index 0000000..eb5a652
--- /dev/null
+++ b/core/java/android/app/Presentation.java
@@ -0,0 +1,256 @@
+/*
+ * 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 android.app;
+
+import android.content.Context;
+import android.content.res.Resources;
+import android.hardware.display.DisplayManager;
+import android.hardware.display.DisplayManager.DisplayListener;
+import android.view.ContextThemeWrapper;
+import android.view.Display;
+import android.view.Gravity;
+import android.view.WindowManagerImpl;
+import android.os.Handler;
+import android.os.Message;
+import android.util.DisplayMetrics;
+import android.util.Log;
+import android.util.TypedValue;
+
+/**
+ * Base class for presentations.
+ *
+ * A presentation is a special kind of dialog whose purpose is to present
+ * content on a secondary display. A {@link Presentation} is associated with
+ * the target {@link Display} at creation time and configures its context and
+ * resource configuration according to the display's metrics.
+ *
+ * Notably, the {@link Context} of a presentation is different from the context
+ * of its containing {@link Activity}. It is important to inflate the layout
+ * of a presentation and load other resources using the presentation's own context
+ * to ensure that assets of the correct size and density for the target display
+ * are loaded.
+ *
+ * A presentation is automatically canceled (see {@link Dialog#cancel()}) when
+ * the display to which it is attached is removed. An activity should take
+ * care of pausing and resuming whatever content is playing within the presentation
+ * whenever the activity itself is paused or resume.
+ *
+ * @see {@link DisplayManager} for information on how to enumerate displays.
+ */
+public class Presentation extends Dialog {
+ private static final String TAG = "Presentation";
+
+ private static final int MSG_CANCEL = 1;
+
+ private final Display mDisplay;
+ private final DisplayManager mDisplayManager;
+
+ /**
+ * Creates a new presentation that is attached to the specified display
+ * using the default theme.
+ *
+ * @param outerContext The context of the application that is showing the presentation.
+ * The presentation will create its own context (see {@link #getContext()}) based
+ * on this context and information about the associated display.
+ * @param display The display to which the presentation should be attached.
+ */
+ public Presentation(Context outerContext, Display display) {
+ this(outerContext, display, 0);
+ }
+
+ /**
+ * Creates a new presentation that is attached to the specified display
+ * using the optionally specified theme.
+ *
+ * @param outerContext The context of the application that is showing the presentation.
+ * The presentation will create its own context (see {@link #getContext()}) based
+ * on this context and information about the associated display.
+ * @param display The display to which the presentation should be attached.
+ * @param theme A style resource describing the theme to use for the window.
+ * See <a href="{@docRoot}guide/topics/resources/available-resources.html#stylesandthemes">
+ * Style and Theme Resources</a> for more information about defining and using
+ * styles. This theme is applied on top of the current theme in
+ * <var>outerContext</var>. If 0, the default presentation theme will be used.
+ */
+ public Presentation(Context outerContext, Display display, int theme) {
+ super(createPresentationContext(outerContext, display, theme), theme, false);
+
+ mDisplay = display;
+ mDisplayManager = (DisplayManager)getContext().getSystemService(Context.DISPLAY_SERVICE);
+
+ getWindow().setGravity(Gravity.FILL);
+ setCanceledOnTouchOutside(false);
+ }
+
+ /**
+ * Gets the {@link Display} that this presentation appears on.
+ *
+ * @return The display.
+ */
+ public Display getDisplay() {
+ return mDisplay;
+ }
+
+ /**
+ * Gets the {@link Resources} that should be used to inflate the layout of this presentation.
+ * This resources object has been configured according to the metrics of the
+ * display that the presentation appears on.
+ *
+ * @return The presentation resources object.
+ */
+ public Resources getResources() {
+ return getContext().getResources();
+ }
+
+ @Override
+ protected void onStart() {
+ super.onStart();
+ mDisplayManager.registerDisplayListener(mDisplayListener, null);
+
+ // Since we were not watching for display changes until just now, there is a
+ // chance that the display metrics have changed. If so, we will need to
+ // dismiss the presentation immediately. This case is expected
+ // to be rare but surprising, so we'll write a log message about it.
+ if (!isConfigurationStillValid()) {
+ Log.i(TAG, "Presentation is being immediately dismissed because the "
+ + "display metrics have changed since it was created.");
+ mHandler.sendEmptyMessage(MSG_CANCEL);
+ }
+ }
+
+ @Override
+ protected void onStop() {
+ mDisplayManager.unregisterDisplayListener(mDisplayListener);
+ super.onStop();
+ }
+
+ /**
+ * Called by the system when the {@link Display} to which the presentation
+ * is attached has been removed.
+ *
+ * The system automatically calls {@link #cancel} to dismiss the presentation
+ * after sending this event.
+ *
+ * @see #getDisplay
+ */
+ public void onDisplayRemoved() {
+ }
+
+ /**
+ * Called by the system when the properties of the {@link Display} to which
+ * the presentation is attached have changed.
+ *
+ * If the display metrics have changed (for example, if the display has been
+ * resized or rotated), then the system automatically calls
+ * {@link #cancel} to dismiss the presentation.
+ *
+ * @see #getDisplay
+ */
+ public void onDisplayChanged() {
+ }
+
+ private void handleDisplayRemoved() {
+ onDisplayRemoved();
+ cancel();
+ }
+
+ private void handleDisplayChanged() {
+ onDisplayChanged();
+
+ // We currently do not support configuration changes for presentations
+ // (although we could add that feature with a bit more work).
+ // If the display metrics have changed in any way then the current configuration
+ // is invalid and the application must recreate the presentation to get
+ // a new context.
+ if (!isConfigurationStillValid()) {
+ cancel();
+ }
+ }
+
+ private boolean isConfigurationStillValid() {
+ DisplayMetrics dm = new DisplayMetrics();
+ mDisplay.getMetrics(dm);
+ return dm.equals(getResources().getDisplayMetrics());
+ }
+
+ private static Context createPresentationContext(
+ Context outerContext, Display display, int theme) {
+ if (outerContext == null) {
+ throw new IllegalArgumentException("outerContext must not be null");
+ }
+ if (display == null) {
+ throw new IllegalArgumentException("display must not be null");
+ }
+
+ Context displayContext = outerContext.createDisplayContext(display);
+ if (theme == 0) {
+ TypedValue outValue = new TypedValue();
+ displayContext.getTheme().resolveAttribute(
+ com.android.internal.R.attr.presentationTheme, outValue, true);
+ theme = outValue.resourceId;
+ }
+
+ // Derive the display's window manager from the outer window manager.
+ // We do this because the outer window manager have some extra information
+ // such as the parent window, which is important if the presentation uses
+ // an application window type.
+ final WindowManagerImpl outerWindowManager =
+ (WindowManagerImpl)outerContext.getSystemService(Context.WINDOW_SERVICE);
+ final WindowManagerImpl displayWindowManager =
+ outerWindowManager.createPresentationWindowManager(display);
+ return new ContextThemeWrapper(displayContext, theme) {
+ @Override
+ public Object getSystemService(String name) {
+ if (Context.WINDOW_SERVICE.equals(name)) {
+ return displayWindowManager;
+ }
+ return super.getSystemService(name);
+ }
+ };
+ }
+
+ private final DisplayListener mDisplayListener = new DisplayListener() {
+ @Override
+ public void onDisplayAdded(int displayId) {
+ }
+
+ @Override
+ public void onDisplayRemoved(int displayId) {
+ if (displayId == mDisplay.getDisplayId()) {
+ handleDisplayRemoved();
+ }
+ }
+
+ @Override
+ public void onDisplayChanged(int displayId) {
+ if (displayId == mDisplay.getDisplayId()) {
+ handleDisplayChanged();
+ }
+ }
+ };
+
+ private final Handler mHandler = new Handler() {
+ @Override
+ public void handleMessage(Message msg) {
+ switch (msg.what) {
+ case MSG_CANCEL:
+ cancel();
+ break;
+ }
+ }
+ };
+}
diff --git a/core/java/android/app/admin/DeviceAdminInfo.java b/core/java/android/app/admin/DeviceAdminInfo.java
index 1c37414..c8062ca 100644
--- a/core/java/android/app/admin/DeviceAdminInfo.java
+++ b/core/java/android/app/admin/DeviceAdminInfo.java
@@ -50,23 +50,23 @@
*/
public final class DeviceAdminInfo implements Parcelable {
static final String TAG = "DeviceAdminInfo";
-
+
/**
* A type of policy that this device admin can use: limit the passwords
* that the user can select, via {@link DevicePolicyManager#setPasswordQuality}
* and {@link DevicePolicyManager#setPasswordMinimumLength}.
- *
+ *
* <p>To control this policy, the device admin must have a "limit-password"
* tag in the "uses-policies" section of its meta-data.
*/
public static final int USES_POLICY_LIMIT_PASSWORD = 0;
-
+
/**
* A type of policy that this device admin can use: able to watch login
* attempts from the user, via {@link DeviceAdminReceiver#ACTION_PASSWORD_FAILED},
* {@link DeviceAdminReceiver#ACTION_PASSWORD_SUCCEEDED}, and
* {@link DevicePolicyManager#getCurrentFailedPasswordAttempts}.
- *
+ *
* <p>To control this policy, the device admin must have a "watch-login"
* tag in the "uses-policies" section of its meta-data.
*/
@@ -76,7 +76,7 @@
* A type of policy that this device admin can use: able to reset the
* user's password via
* {@link DevicePolicyManager#resetPassword}.
- *
+ *
* <p>To control this policy, the device admin must have a "reset-password"
* tag in the "uses-policies" section of its meta-data.
*/
@@ -87,7 +87,7 @@
* to lock via{@link DevicePolicyManager#lockNow} or limit the
* maximum lock timeout for the device via
* {@link DevicePolicyManager#setMaximumTimeToLock}.
- *
+ *
* <p>To control this policy, the device admin must have a "force-lock"
* tag in the "uses-policies" section of its meta-data.
*/
@@ -97,7 +97,7 @@
* A type of policy that this device admin can use: able to factory
* reset the device, erasing all of the user's data, via
* {@link DevicePolicyManager#wipeData}.
- *
+ *
* <p>To control this policy, the device admin must have a "wipe-data"
* tag in the "uses-policies" section of its meta-data.
*/
@@ -138,13 +138,21 @@
*/
public static final int USES_POLICY_DISABLE_CAMERA = 8;
+ /**
+ * A type of policy that this device admin can use: disables use of keyguard widgets.
+ *
+ * <p>To control this policy, the device admin must have a "disable-keyguard-widgets"
+ * tag in the "uses-policies" section of its meta-data.
+ */
+ public static final int USES_POLICY_DISABLE_KEYGUARD_WIDGETS = 9;
+
/** @hide */
public static class PolicyInfo {
public final int ident;
final public String tag;
final public int label;
final public int description;
-
+
public PolicyInfo(int identIn, String tagIn, int labelIn, int descriptionIn) {
ident = identIn;
tag = tagIn;
@@ -152,11 +160,11 @@
description = descriptionIn;
}
}
-
+
static ArrayList<PolicyInfo> sPoliciesDisplayOrder = new ArrayList<PolicyInfo>();
static HashMap<String, Integer> sKnownPolicies = new HashMap<String, Integer>();
static SparseArray<PolicyInfo> sRevKnownPolicies = new SparseArray<PolicyInfo>();
-
+
static {
sPoliciesDisplayOrder.add(new PolicyInfo(USES_POLICY_WIPE_DATA, "wipe-data",
com.android.internal.R.string.policylab_wipeData,
@@ -185,6 +193,10 @@
sPoliciesDisplayOrder.add(new PolicyInfo(USES_POLICY_DISABLE_CAMERA, "disable-camera",
com.android.internal.R.string.policylab_disableCamera,
com.android.internal.R.string.policydesc_disableCamera));
+ sPoliciesDisplayOrder.add(new PolicyInfo(
+ USES_POLICY_DISABLE_KEYGUARD_WIDGETS, "disable-keyguard-widgets",
+ com.android.internal.R.string.policylab_disableKeyguardWidgets,
+ com.android.internal.R.string.policydesc_disableKeyguardWidgets));
for (int i=0; i<sPoliciesDisplayOrder.size(); i++) {
PolicyInfo pi = sPoliciesDisplayOrder.get(i);
@@ -192,25 +204,25 @@
sKnownPolicies.put(pi.tag, pi.ident);
}
}
-
+
/**
* The BroadcastReceiver that implements this device admin component.
*/
final ResolveInfo mReceiver;
-
+
/**
* Whether this should be visible to the user.
*/
boolean mVisible;
-
+
/**
* The policies this administrator needs access to.
*/
int mUsesPolicies;
-
+
/**
* Constructor.
- *
+ *
* @param context The Context in which we are parsing the device admin.
* @param receiver The ResolveInfo returned from the package manager about
* this device admin's component.
@@ -219,9 +231,9 @@
throws XmlPullParserException, IOException {
mReceiver = receiver;
ActivityInfo ai = receiver.activityInfo;
-
+
PackageManager pm = context.getPackageManager();
-
+
XmlResourceParser parser = null;
try {
parser = ai.loadXmlMetaData(pm, DeviceAdminReceiver.DEVICE_ADMIN_META_DATA);
@@ -229,30 +241,30 @@
throw new XmlPullParserException("No "
+ DeviceAdminReceiver.DEVICE_ADMIN_META_DATA + " meta-data");
}
-
+
Resources res = pm.getResourcesForApplication(ai.applicationInfo);
-
+
AttributeSet attrs = Xml.asAttributeSet(parser);
-
+
int type;
while ((type=parser.next()) != XmlPullParser.END_DOCUMENT
&& type != XmlPullParser.START_TAG) {
}
-
+
String nodeName = parser.getName();
if (!"device-admin".equals(nodeName)) {
throw new XmlPullParserException(
"Meta-data does not start with device-admin tag");
}
-
+
TypedArray sa = res.obtainAttributes(attrs,
com.android.internal.R.styleable.DeviceAdmin);
mVisible = sa.getBoolean(
com.android.internal.R.styleable.DeviceAdmin_visible, true);
-
+
sa.recycle();
-
+
int outerDepth = parser.getDepth();
while ((type=parser.next()) != XmlPullParser.END_DOCUMENT
&& (type != XmlPullParser.END_TAG || parser.getDepth() > outerDepth)) {
@@ -290,14 +302,14 @@
mReceiver = ResolveInfo.CREATOR.createFromParcel(source);
mUsesPolicies = source.readInt();
}
-
+
/**
* Return the .apk package that implements this device admin.
*/
public String getPackageName() {
return mReceiver.activityInfo.packageName;
}
-
+
/**
* Return the class name of the receiver component that implements
* this device admin.
@@ -321,20 +333,20 @@
return new ComponentName(mReceiver.activityInfo.packageName,
mReceiver.activityInfo.name);
}
-
+
/**
* Load the user-displayed label for this device admin.
- *
+ *
* @param pm Supply a PackageManager used to load the device admin's
* resources.
*/
public CharSequence loadLabel(PackageManager pm) {
return mReceiver.loadLabel(pm);
}
-
+
/**
* Load user-visible description associated with this device admin.
- *
+ *
* @param pm Supply a PackageManager used to load the device admin's
* resources.
*/
@@ -351,17 +363,17 @@
}
throw new NotFoundException();
}
-
+
/**
* Load the user-displayed icon for this device admin.
- *
+ *
* @param pm Supply a PackageManager used to load the device admin's
* resources.
*/
public Drawable loadIcon(PackageManager pm) {
return mReceiver.loadIcon(pm);
}
-
+
/**
* Returns whether this device admin would like to be visible to the
* user, even when it is not enabled.
@@ -369,7 +381,7 @@
public boolean isVisible() {
return mVisible;
}
-
+
/**
* Return true if the device admin has requested that it be able to use
* the given policy control. The possible policy identifier inputs are:
@@ -382,7 +394,7 @@
public boolean usesPolicy(int policyIdent) {
return (mUsesPolicies & (1<<policyIdent)) != 0;
}
-
+
/**
* Return the XML tag name for the given policy identifier. Valid identifiers
* are as per {@link #usesPolicy(int)}. If the given identifier is not
@@ -391,7 +403,7 @@
public String getTagForPolicy(int policyIdent) {
return sRevKnownPolicies.get(policyIdent).tag;
}
-
+
/** @hide */
public ArrayList<PolicyInfo> getUsedPolicies() {
ArrayList<PolicyInfo> res = new ArrayList<PolicyInfo>();
@@ -403,25 +415,25 @@
}
return res;
}
-
+
/** @hide */
public void writePoliciesToXml(XmlSerializer out)
throws IllegalArgumentException, IllegalStateException, IOException {
out.attribute(null, "flags", Integer.toString(mUsesPolicies));
}
-
+
/** @hide */
public void readPoliciesFromXml(XmlPullParser parser)
throws XmlPullParserException, IOException {
mUsesPolicies = Integer.parseInt(
parser.getAttributeValue(null, "flags"));
}
-
+
public void dump(Printer pw, String prefix) {
pw.println(prefix + "Receiver:");
mReceiver.dump(pw, prefix + " ");
}
-
+
@Override
public String toString() {
return "DeviceAdminInfo{" + mReceiver.activityInfo.name + "}";
@@ -429,7 +441,7 @@
/**
* Used to package this object into a {@link Parcel}.
- *
+ *
* @param dest The {@link Parcel} to be written.
* @param flags The flags used for parceling.
*/
diff --git a/core/java/android/app/admin/DevicePolicyManager.java b/core/java/android/app/admin/DevicePolicyManager.java
index 0b58396..4c55bb3 100755
--- a/core/java/android/app/admin/DevicePolicyManager.java
+++ b/core/java/android/app/admin/DevicePolicyManager.java
@@ -1155,6 +1155,16 @@
= "android.app.action.START_ENCRYPTION";
/**
+ * Widgets are enabled in keyguard
+ */
+ public static final int KEYGUARD_DISABLE_WIDGETS_NONE = 0;
+
+ /**
+ * Disable all keyguard widgets
+ */
+ public static final int KEYGUARD_DISABLE_WIDGETS_ALL = 0x7fffffff;
+
+ /**
* Called by an application that is administering the device to
* request that the storage system be encrypted.
*
@@ -1284,6 +1294,46 @@
}
/**
+ * Called by an application that is administering the device to disable adding widgets to
+ * keyguard. After setting this, keyguard widgets will be disabled according to the state
+ * provided.
+ *
+ * <p>The calling device admin must have requested
+ * {@link DeviceAdminInfo#USES_POLICY_DISABLE_KEYGUARD_WIDGETS} to be able to call
+ * this method; if it has not, a security exception will be thrown.
+ *
+ * @param admin Which {@link DeviceAdminReceiver} this request is associated with.
+ * @param which {@link DevicePolicyManager#KEYGUARD_DISABLE_WIDGETS_ALL} or
+ * {@link DevicePolicyManager#KEYGUARD_DISABLE_WIDGETS_NONE} (the default).
+ */
+ public void setKeyguardWidgetsDisabled(ComponentName admin, int which) {
+ if (mService != null) {
+ try {
+ mService.setKeyguardWidgetsDisabled(admin, which);
+ } catch (RemoteException e) {
+ Log.w(TAG, "Failed talking with device policy service", e);
+ }
+ }
+ }
+
+ /**
+ * Determine whether or not widgets have been disabled in keyguard either by the current
+ * admin, if specified, or all admins.
+ * @param admin The name of the admin component to check, or null to check if any admins
+ * have disabled widgets in keyguard.
+ */
+ public int getKeyguardWidgetsDisabled(ComponentName admin) {
+ if (mService != null) {
+ try {
+ return mService.getKeyguardWidgetsDisabled(admin);
+ } catch (RemoteException e) {
+ Log.w(TAG, "Failed talking with device policy service", e);
+ }
+ }
+ return KEYGUARD_DISABLE_WIDGETS_NONE;
+ }
+
+ /**
* @hide
*/
public void setActiveAdmin(ComponentName policyReceiver, boolean refreshing) {
diff --git a/core/java/android/app/admin/IDevicePolicyManager.aidl b/core/java/android/app/admin/IDevicePolicyManager.aidl
index 9419a62..0b7ec122 100644
--- a/core/java/android/app/admin/IDevicePolicyManager.aidl
+++ b/core/java/android/app/admin/IDevicePolicyManager.aidl
@@ -48,7 +48,7 @@
void setPasswordMinimumNonLetter(in ComponentName who, int length);
int getPasswordMinimumNonLetter(in ComponentName who);
-
+
void setPasswordHistoryLength(in ComponentName who, int length);
int getPasswordHistoryLength(in ComponentName who);
@@ -59,17 +59,17 @@
boolean isActivePasswordSufficient();
int getCurrentFailedPasswordAttempts();
-
+
void setMaximumFailedPasswordsForWipe(in ComponentName admin, int num);
int getMaximumFailedPasswordsForWipe(in ComponentName admin);
-
+
boolean resetPassword(String password, int flags);
-
+
void setMaximumTimeToLock(in ComponentName who, long timeMs);
long getMaximumTimeToLock(in ComponentName who);
-
+
void lockNow();
-
+
void wipeData(int flags);
ComponentName setGlobalProxy(in ComponentName admin, String proxySpec, String exclusionList);
@@ -82,6 +82,9 @@
void setCameraDisabled(in ComponentName who, boolean disabled);
boolean getCameraDisabled(in ComponentName who);
+ void setKeyguardWidgetsDisabled(in ComponentName who, int which);
+ int getKeyguardWidgetsDisabled(in ComponentName who);
+
void setActiveAdmin(in ComponentName policyReceiver, boolean refreshing);
boolean isAdminActive(in ComponentName policyReceiver);
List<ComponentName> getActiveAdmins();
@@ -89,7 +92,7 @@
void getRemoveWarning(in ComponentName policyReceiver, in RemoteCallback result);
void removeActiveAdmin(in ComponentName policyReceiver);
boolean hasGrantedPolicy(in ComponentName policyReceiver, int usesPolicy);
-
+
void setActivePasswordState(int quality, int length, int letters, int uppercase, int lowercase,
int numbers, int symbols, int nonletter);
void reportFailedPasswordAttempt();
diff --git a/core/java/android/app/backup/WallpaperBackupHelper.java b/core/java/android/app/backup/WallpaperBackupHelper.java
index a74a268..9e8ab2c 100644
--- a/core/java/android/app/backup/WallpaperBackupHelper.java
+++ b/core/java/android/app/backup/WallpaperBackupHelper.java
@@ -20,7 +20,9 @@
import android.content.Context;
import android.graphics.BitmapFactory;
import android.graphics.Point;
+import android.os.Environment;
import android.os.ParcelFileDescriptor;
+import android.os.UserHandle;
import android.util.Slog;
import android.view.Display;
import android.view.WindowManager;
@@ -39,8 +41,12 @@
// This path must match what the WallpaperManagerService uses
// TODO: Will need to change if backing up non-primary user's wallpaper
- public static final String WALLPAPER_IMAGE = "/data/system/users/0/wallpaper";
- public static final String WALLPAPER_INFO = "/data/system/users/0/wallpaper_info.xml";
+ public static final String WALLPAPER_IMAGE =
+ new File(Environment.getUserSystemDirectory(UserHandle.USER_OWNER),
+ "wallpaper").getAbsolutePath();
+ public static final String WALLPAPER_INFO =
+ new File(Environment.getUserSystemDirectory(UserHandle.USER_OWNER),
+ "wallpaper_info.xml").getAbsolutePath();
// Use old keys to keep legacy data compatibility and avoid writing two wallpapers
public static final String WALLPAPER_IMAGE_KEY =
"/data/data/com.android.settings/files/wallpaper";
@@ -50,7 +56,9 @@
// will be saved to this file from the restore stream, then renamed to the proper
// location if it's deemed suitable.
// TODO: Will need to change if backing up non-primary user's wallpaper
- private static final String STAGE_FILE = "/data/system/users/0/wallpaper-tmp";
+ private static final String STAGE_FILE =
+ new File(Environment.getUserSystemDirectory(UserHandle.USER_OWNER),
+ "wallpaper-tmp").getAbsolutePath();
Context mContext;
String[] mFiles;
diff --git a/core/java/android/content/BroadcastReceiver.java b/core/java/android/content/BroadcastReceiver.java
index 446f1af..1500b00 100644
--- a/core/java/android/content/BroadcastReceiver.java
+++ b/core/java/android/content/BroadcastReceiver.java
@@ -237,16 +237,17 @@
final boolean mOrderedHint;
final boolean mInitialStickyHint;
final IBinder mToken;
+ final int mSendingUser;
int mResultCode;
String mResultData;
Bundle mResultExtras;
boolean mAbortBroadcast;
boolean mFinished;
-
+
/** @hide */
public PendingResult(int resultCode, String resultData, Bundle resultExtras,
- int type, boolean ordered, boolean sticky, IBinder token) {
+ int type, boolean ordered, boolean sticky, IBinder token, int userId) {
mResultCode = resultCode;
mResultData = resultData;
mResultExtras = resultExtras;
@@ -254,6 +255,7 @@
mOrderedHint = ordered;
mInitialStickyHint = sticky;
mToken = token;
+ mSendingUser = userId;
}
/**
@@ -425,7 +427,12 @@
}
}
}
-
+
+ /** @hide */
+ public int getSendingUserId() {
+ return mSendingUser;
+ }
+
void checkSynchronousHint() {
// Note that we don't assert when receiving the initial sticky value,
// since that may have come from an ordered broadcast. We'll catch
@@ -733,6 +740,11 @@
return mPendingResult;
}
+ /** @hide */
+ public int getSendingUserId() {
+ return mPendingResult.mSendingUser;
+ }
+
/**
* Control inclusion of debugging help for mismatched
* calls to {@link Context#registerReceiver(BroadcastReceiver, IntentFilter)
diff --git a/core/java/android/content/Context.java b/core/java/android/content/Context.java
index c2b796a..7438ba8 100644
--- a/core/java/android/content/Context.java
+++ b/core/java/android/content/Context.java
@@ -35,6 +35,8 @@
import android.os.UserHandle;
import android.util.AttributeSet;
import android.view.CompatibilityInfoHolder;
+import android.view.Display;
+import android.view.WindowManager;
import java.io.File;
import java.io.FileInputStream;
@@ -1408,9 +1410,35 @@
* @see #unregisterReceiver
*/
public abstract Intent registerReceiver(BroadcastReceiver receiver,
- IntentFilter filter,
- String broadcastPermission,
- Handler scheduler);
+ IntentFilter filter, String broadcastPermission, Handler scheduler);
+
+ /**
+ * @hide
+ * Same as {@link #registerReceiver(BroadcastReceiver, IntentFilter, String, Handler)
+ * but for a specific user. This receiver will receiver broadcasts that
+ * are sent to the requested user. It
+ * requires holding the {@link android.Manifest.permission#INTERACT_ACROSS_USERS_FULL}
+ * permission.
+ *
+ * @param receiver The BroadcastReceiver to handle the broadcast.
+ * @param user UserHandle to send the intent to.
+ * @param filter Selects the Intent broadcasts to be received.
+ * @param broadcastPermission String naming a permissions that a
+ * broadcaster must hold in order to send an Intent to you. If null,
+ * no permission is required.
+ * @param scheduler Handler identifying the thread that will receive
+ * the Intent. If null, the main thread of the process will be used.
+ *
+ * @return The first sticky intent found that matches <var>filter</var>,
+ * or null if there are none.
+ *
+ * @see #registerReceiver(BroadcastReceiver, IntentFilter, String, Handler
+ * @see #sendBroadcast
+ * @see #unregisterReceiver
+ */
+ public abstract Intent registerReceiverAsUser(BroadcastReceiver receiver,
+ UserHandle user, IntentFilter filter, String broadcastPermission,
+ Handler scheduler);
/**
* Unregister a previously registered BroadcastReceiver. <em>All</em>
@@ -2547,7 +2575,7 @@
/**
* Return a new Context object for the current Context but whose resources
* are adjusted to match the given Configuration. Each call to this method
- * returns a new instance of a Contex object; Context objects are not
+ * returns a new instance of a Context object; Context objects are not
* shared, however common state (ClassLoader, other Resources for the
* same configuration) may be so the Context itself can be fairly lightweight.
*
@@ -2557,19 +2585,40 @@
* orientation change), the resources of this context will also change except
* for those that have been explicitly overridden with a value here.
*
- * @return A Context for the application.
+ * @return A Context with the given configuration override.
*/
public abstract Context createConfigurationContext(Configuration overrideConfiguration);
/**
+ * Return a new Context object for the current Context but whose resources
+ * are adjusted to match the metrics of the given Display. Each call to this method
+ * returns a new instance of a Context object; Context objects are not
+ * shared, however common state (ClassLoader, other Resources for the
+ * same configuration) may be so the Context itself can be fairly lightweight.
+ *
+ * The returned display Context provides a {@link WindowManager}
+ * (see {@link #getSystemService(String)}) that is configured to show windows
+ * on the given display. The WindowManager's {@link WindowManager#getDefaultDisplay}
+ * method can be used to retrieve the Display from the returned Context.
+ *
+ * @param display A {@link Display} object specifying the display
+ * for whose metrics the Context's resources should be tailored and upon which
+ * new windows should be shown.
+ *
+ * @return A Context for the display.
+ */
+ public abstract Context createDisplayContext(Display display);
+
+ /**
* Gets the compatibility info holder for this context. This information
* is provided on a per-application basis and is used to simulate lower density
* display metrics for legacy applications.
*
+ * @param displayId The display id for which to get compatibility info.
* @return The compatibility info holder, or null if not required by the application.
* @hide
*/
- public abstract CompatibilityInfoHolder getCompatibilityInfo();
+ public abstract CompatibilityInfoHolder getCompatibilityInfo(int displayId);
/**
* Indicates whether this Context is restricted.
diff --git a/core/java/android/content/ContextWrapper.java b/core/java/android/content/ContextWrapper.java
index e503388..6101f4e 100644
--- a/core/java/android/content/ContextWrapper.java
+++ b/core/java/android/content/ContextWrapper.java
@@ -36,6 +36,7 @@
import android.os.RemoteException;
import android.os.UserHandle;
import android.view.CompatibilityInfoHolder;
+import android.view.Display;
import java.io.File;
import java.io.FileInputStream;
@@ -424,6 +425,15 @@
scheduler);
}
+ /** @hide */
+ @Override
+ public Intent registerReceiverAsUser(
+ BroadcastReceiver receiver, UserHandle user, IntentFilter filter,
+ String broadcastPermission, Handler scheduler) {
+ return mBase.registerReceiverAsUser(receiver, user, filter, broadcastPermission,
+ scheduler);
+ }
+
@Override
public void unregisterReceiver(BroadcastReceiver receiver) {
mBase.unregisterReceiver(receiver);
@@ -582,13 +592,18 @@
}
@Override
+ public Context createDisplayContext(Display display) {
+ return mBase.createDisplayContext(display);
+ }
+
+ @Override
public boolean isRestricted() {
return mBase.isRestricted();
}
/** @hide */
@Override
- public CompatibilityInfoHolder getCompatibilityInfo() {
- return mBase.getCompatibilityInfo();
+ public CompatibilityInfoHolder getCompatibilityInfo(int displayId) {
+ return mBase.getCompatibilityInfo(displayId);
}
}
diff --git a/core/java/android/content/IIntentReceiver.aidl b/core/java/android/content/IIntentReceiver.aidl
index 6f2f7c4..3d92723 100755
--- a/core/java/android/content/IIntentReceiver.aidl
+++ b/core/java/android/content/IIntentReceiver.aidl
@@ -27,7 +27,7 @@
* {@hide}
*/
oneway interface IIntentReceiver {
- void performReceive(in Intent intent, int resultCode,
- String data, in Bundle extras, boolean ordered, boolean sticky);
+ void performReceive(in Intent intent, int resultCode, String data,
+ in Bundle extras, boolean ordered, boolean sticky, int sendingUser);
}
diff --git a/core/java/android/content/IntentSender.java b/core/java/android/content/IntentSender.java
index 079241a..6c3cf99 100644
--- a/core/java/android/content/IntentSender.java
+++ b/core/java/android/content/IntentSender.java
@@ -114,8 +114,8 @@
mWho = who;
mHandler = handler;
}
- public void performReceive(Intent intent, int resultCode,
- String data, Bundle extras, boolean serialized, boolean sticky) {
+ public void performReceive(Intent intent, int resultCode, String data,
+ Bundle extras, boolean serialized, boolean sticky, int sendingUser) {
mIntent = intent;
mResultCode = resultCode;
mResultData = data;
diff --git a/core/java/android/content/SyncManager.java b/core/java/android/content/SyncManager.java
index ee075b4..6b5e6e2 100644
--- a/core/java/android/content/SyncManager.java
+++ b/core/java/android/content/SyncManager.java
@@ -980,7 +980,7 @@
mSyncHandler.sendMessage(msg);
}
- boolean bindToSyncAdapter(RegisteredServicesCache.ServiceInfo info) {
+ boolean bindToSyncAdapter(RegisteredServicesCache.ServiceInfo info, int userId) {
if (Log.isLoggable(TAG, Log.VERBOSE)) {
Log.d(TAG, "bindToSyncAdapter: " + info.componentName + ", connection " + this);
}
@@ -989,8 +989,9 @@
intent.setComponent(info.componentName);
intent.putExtra(Intent.EXTRA_CLIENT_LABEL,
com.android.internal.R.string.sync_binding_label);
- intent.putExtra(Intent.EXTRA_CLIENT_INTENT, PendingIntent.getActivity(
- mContext, 0, new Intent(Settings.ACTION_SYNC_SETTINGS), 0));
+ intent.putExtra(Intent.EXTRA_CLIENT_INTENT, PendingIntent.getActivityAsUser(
+ mContext, 0, new Intent(Settings.ACTION_SYNC_SETTINGS), 0,
+ null, new UserHandle(userId)));
mBound = true;
final boolean bindResult = mContext.bindService(intent, this,
Context.BIND_AUTO_CREATE | Context.BIND_NOT_FOREGROUND
@@ -2132,7 +2133,7 @@
if (Log.isLoggable(TAG, Log.VERBOSE)) {
Log.v(TAG, "dispatchSyncOperation: starting " + activeSyncContext);
}
- if (!activeSyncContext.bindToSyncAdapter(syncAdapterInfo)) {
+ if (!activeSyncContext.bindToSyncAdapter(syncAdapterInfo, op.userId)) {
Log.e(TAG, "Bind attempt failed to " + syncAdapterInfo);
closeActiveSyncContext(activeSyncContext);
return false;
@@ -2255,10 +2256,12 @@
if (syncResult != null && syncResult.tooManyDeletions) {
installHandleTooManyDeletesNotification(syncOperation.account,
- syncOperation.authority, syncResult.stats.numDeletes);
+ syncOperation.authority, syncResult.stats.numDeletes,
+ syncOperation.userId);
} else {
- mNotificationMgr.cancel(
- syncOperation.account.hashCode() ^ syncOperation.authority.hashCode());
+ mNotificationMgr.cancelAsUser(null,
+ syncOperation.account.hashCode() ^ syncOperation.authority.hashCode(),
+ new UserHandle(syncOperation.userId));
}
if (syncResult != null && syncResult.fullSyncRequested) {
@@ -2471,7 +2474,7 @@
}
private void installHandleTooManyDeletesNotification(Account account, String authority,
- long numDeletes) {
+ long numDeletes, int userId) {
if (mNotificationMgr == null) return;
final ProviderInfo providerInfo = mContext.getPackageManager().resolveContentProvider(
@@ -2493,7 +2496,8 @@
}
final PendingIntent pendingIntent = PendingIntent
- .getActivity(mContext, 0, clickIntent, PendingIntent.FLAG_CANCEL_CURRENT);
+ .getActivityAsUser(mContext, 0, clickIntent,
+ PendingIntent.FLAG_CANCEL_CURRENT, null, new UserHandle(userId));
CharSequence tooManyDeletesDescFormat = mContext.getResources().getText(
R.string.contentServiceTooManyDeletesNotificationDesc);
@@ -2507,7 +2511,8 @@
String.format(tooManyDeletesDescFormat.toString(), authorityName),
pendingIntent);
notification.flags |= Notification.FLAG_ONGOING_EVENT;
- mNotificationMgr.notify(account.hashCode() ^ authority.hashCode(), notification);
+ mNotificationMgr.notifyAsUser(null, account.hashCode() ^ authority.hashCode(),
+ notification, new UserHandle(userId));
}
/**
diff --git a/core/java/android/content/SyncStorageEngine.java b/core/java/android/content/SyncStorageEngine.java
index 773e0fe..de97481 100644
--- a/core/java/android/content/SyncStorageEngine.java
+++ b/core/java/android/content/SyncStorageEngine.java
@@ -25,6 +25,7 @@
import android.accounts.Account;
import android.accounts.AccountAndUser;
+import android.content.res.Resources;
import android.database.Cursor;
import android.database.sqlite.SQLiteDatabase;
import android.database.sqlite.SQLiteException;
@@ -336,6 +337,7 @@
private int mNextHistoryId = 0;
private SparseArray<Boolean> mMasterSyncAutomatically = new SparseArray<Boolean>();
+ private boolean mDefaultMasterSyncAutomatically;
private OnSyncRequestListener mSyncRequestListener;
@@ -345,6 +347,9 @@
mCal = Calendar.getInstance(TimeZone.getTimeZone("GMT+0"));
+ mDefaultMasterSyncAutomatically = mContext.getResources().getBoolean(
+ com.android.internal.R.bool.config_syncstorageengine_masterSyncAutomatically);
+
File systemDir = new File(dataDir, "system");
File syncDir = new File(systemDir, "sync");
syncDir.mkdirs();
@@ -780,7 +785,7 @@
public boolean getMasterSyncAutomatically(int userId) {
synchronized (mAuthorities) {
Boolean auto = mMasterSyncAutomatically.get(userId);
- return auto == null ? true : auto;
+ return auto == null ? mDefaultMasterSyncAutomatically : auto;
}
}
diff --git a/core/java/android/content/pm/ActivityInfo.java b/core/java/android/content/pm/ActivityInfo.java
index 3035729..0b320f0 100644
--- a/core/java/android/content/pm/ActivityInfo.java
+++ b/core/java/android/content/pm/ActivityInfo.java
@@ -372,6 +372,12 @@
public static final int CONFIG_DENSITY = 0x1000;
/**
* Bit in {@link #configChanges} that indicates that the activity
+ * can itself handle the change to layout direction. Set from the
+ * {@link android.R.attr#configChanges} attribute.
+ */
+ public static final int CONFIG_LAYOUT_DIRECTION = 0x2000;
+ /**
+ * Bit in {@link #configChanges} that indicates that the activity
* can itself handle changes to the font scaling factor. Set from the
* {@link android.R.attr#configChanges} attribute. This is
* not a core resource configutation, but a higher-level value, so its
@@ -398,6 +404,7 @@
0x0200, // SCREEN SIZE
0x2000, // SMALLEST SCREEN SIZE
0x0100, // DENSITY
+ 0x4000, // LAYOUT DIRECTION
};
/** @hide
* Convert Java change bits to native.
@@ -434,8 +441,9 @@
* {@link #CONFIG_MCC}, {@link #CONFIG_MNC},
* {@link #CONFIG_LOCALE}, {@link #CONFIG_TOUCHSCREEN},
* {@link #CONFIG_KEYBOARD}, {@link #CONFIG_NAVIGATION},
- * {@link #CONFIG_ORIENTATION}, and {@link #CONFIG_SCREEN_LAYOUT}. Set from the
- * {@link android.R.attr#configChanges} attribute.
+ * {@link #CONFIG_ORIENTATION}, {@link #CONFIG_SCREEN_LAYOUT} and
+ * {@link #CONFIG_LAYOUT_DIRECTION}. Set from the {@link android.R.attr#configChanges}
+ * attribute.
*/
public int configChanges;
diff --git a/core/java/android/content/res/Configuration.java b/core/java/android/content/res/Configuration.java
index 52b6498..0b77842 100644
--- a/core/java/android/content/res/Configuration.java
+++ b/core/java/android/content/res/Configuration.java
@@ -125,7 +125,25 @@
* <a href="{@docRoot}guide/topics/resources/providing-resources.html#ScreenAspectQualifier">long</a>
* resource qualifier. */
public static final int SCREENLAYOUT_LONG_YES = 0x20;
-
+
+ /** Constant for {@link #screenLayout}: bits that encode the layout direction. */
+ public static final int SCREENLAYOUT_LAYOUTDIR_MASK = 0xC0;
+ /** Constant for {@link #screenLayout}: bits shift to get the layout direction. */
+ public static final int SCREENLAYOUT_LAYOUTDIR_SHIFT = 6;
+ /** Constant for {@link #screenLayout}: a {@link #SCREENLAYOUT_LAYOUTDIR_MASK}
+ * value indicating that no layout dir has been set. */
+ public static final int SCREENLAYOUT_LAYOUTDIR_UNDEFINED = 0x00;
+ /** Constant for {@link #screenLayout}: a {@link #SCREENLAYOUT_LAYOUTDIR_MASK}
+ * value indicating that a layout dir has been set to LTR. */
+ public static final int SCREENLAYOUT_LAYOUTDIR_LTR = 0x01 << SCREENLAYOUT_LAYOUTDIR_SHIFT;
+ /** Constant for {@link #screenLayout}: a {@link #SCREENLAYOUT_LAYOUTDIR_MASK}
+ * value indicating that a layout dir has been set to RTL. */
+ public static final int SCREENLAYOUT_LAYOUTDIR_RTL = 0x02 << SCREENLAYOUT_LAYOUTDIR_SHIFT;
+
+ /** Constant for {@link #screenLayout}: a value indicating that screenLayout is undefined */
+ public static final int SCREENLAYOUT_UNDEFINED = SCREENLAYOUT_SIZE_UNDEFINED |
+ SCREENLAYOUT_LONG_UNDEFINED | SCREENLAYOUT_LAYOUTDIR_UNDEFINED;
+
/**
* Special flag we generate to indicate that the screen layout requires
* us to use a compatibility mode for apps that are not modern layout
@@ -146,6 +164,10 @@
* is wider/taller than normal. They may be one of
* {@link #SCREENLAYOUT_LONG_NO} or {@link #SCREENLAYOUT_LONG_YES}.
*
+ * <p>The {@link #SCREENLAYOUT_LAYOUTDIR_MASK} defines whether the screen layout
+ * is either LTR or RTL. They may be one of
+ * {@link #SCREENLAYOUT_LAYOUTDIR_LTR} or {@link #SCREENLAYOUT_LAYOUTDIR_RTL}.
+ *
* <p>See <a href="{@docRoot}guide/practices/screens_support.html">Supporting
* Multiple Screens</a> for more information.
*/
@@ -442,11 +464,6 @@
public int compatSmallestScreenWidthDp;
/**
- * @hide The layout direction associated to the current Locale
- */
- public int layoutDirection;
-
- /**
* @hide Internal book-keeping.
*/
public int seq;
@@ -472,7 +489,6 @@
mnc = o.mnc;
if (o.locale != null) {
locale = (Locale) o.locale.clone();
- layoutDirection = o.layoutDirection;
}
userSetLocale = o.userSetLocale;
touchscreen = o.touchscreen;
@@ -517,10 +533,13 @@
} else {
sb.append(" ?locale");
}
- switch (layoutDirection) {
- case View.LAYOUT_DIRECTION_LTR: /* ltr not interesting */ break;
- case View.LAYOUT_DIRECTION_RTL: sb.append(" rtl"); break;
- default: sb.append(" layoutDir="); sb.append(layoutDirection); break;
+ int layoutDir = (screenLayout&SCREENLAYOUT_LAYOUTDIR_MASK);
+ switch (layoutDir) {
+ case SCREENLAYOUT_LAYOUTDIR_UNDEFINED: sb.append(" ?layoutDir"); break;
+ case SCREENLAYOUT_LAYOUTDIR_LTR: sb.append(" ltr"); break;
+ case SCREENLAYOUT_LAYOUTDIR_RTL: sb.append(" rtl"); break;
+ default: sb.append(" layoutDir=");
+ sb.append(layoutDir >> SCREENLAYOUT_LAYOUTDIR_SHIFT); break;
}
if (smallestScreenWidthDp != SMALLEST_SCREEN_WIDTH_DP_UNDEFINED) {
sb.append(" sw"); sb.append(smallestScreenWidthDp); sb.append("dp");
@@ -643,13 +662,12 @@
navigation = NAVIGATION_UNDEFINED;
navigationHidden = NAVIGATIONHIDDEN_UNDEFINED;
orientation = ORIENTATION_UNDEFINED;
- screenLayout = SCREENLAYOUT_SIZE_UNDEFINED;
+ screenLayout = SCREENLAYOUT_UNDEFINED;
uiMode = UI_MODE_TYPE_UNDEFINED;
screenWidthDp = compatScreenWidthDp = SCREEN_WIDTH_DP_UNDEFINED;
screenHeightDp = compatScreenHeightDp = SCREEN_HEIGHT_DP_UNDEFINED;
smallestScreenWidthDp = compatSmallestScreenWidthDp = SMALLEST_SCREEN_WIDTH_DP_UNDEFINED;
densityDpi = DENSITY_DPI_UNDEFINED;
- layoutDirection = View.LAYOUT_DIRECTION_LTR;
seq = 0;
}
@@ -685,7 +703,11 @@
changed |= ActivityInfo.CONFIG_LOCALE;
locale = delta.locale != null
? (Locale) delta.locale.clone() : null;
- layoutDirection = LocaleUtil.getLayoutDirectionFromLocale(locale);
+ // If locale has changed, then layout direction is also changed ...
+ changed |= ActivityInfo.CONFIG_LAYOUT_DIRECTION;
+ // ... and we need to update the layout direction (represented by the first
+ // 2 most significant bits in screenLayout).
+ setLayoutDirection(locale);
}
if (delta.userSetLocale && (!userSetLocale || ((changed & ActivityInfo.CONFIG_LOCALE) != 0)))
{
@@ -727,10 +749,17 @@
changed |= ActivityInfo.CONFIG_ORIENTATION;
orientation = delta.orientation;
}
- if (delta.screenLayout != SCREENLAYOUT_SIZE_UNDEFINED
- && screenLayout != delta.screenLayout) {
+ if (getScreenLayoutNoDirection(delta.screenLayout) !=
+ (SCREENLAYOUT_SIZE_UNDEFINED | SCREENLAYOUT_LONG_UNDEFINED)
+ && (getScreenLayoutNoDirection(screenLayout) !=
+ getScreenLayoutNoDirection(delta.screenLayout))) {
changed |= ActivityInfo.CONFIG_SCREEN_LAYOUT;
- screenLayout = delta.screenLayout;
+ // We need to preserve the previous layout dir bits if they were defined
+ if ((delta.screenLayout&SCREENLAYOUT_LAYOUTDIR_MASK) == 0) {
+ screenLayout = (screenLayout&SCREENLAYOUT_LAYOUTDIR_MASK)|delta.screenLayout;
+ } else {
+ screenLayout = delta.screenLayout;
+ }
}
if (delta.uiMode != (UI_MODE_TYPE_UNDEFINED|UI_MODE_NIGHT_UNDEFINED)
&& uiMode != delta.uiMode) {
@@ -771,7 +800,6 @@
if (delta.compatSmallestScreenWidthDp != SMALLEST_SCREEN_WIDTH_DP_UNDEFINED) {
compatSmallestScreenWidthDp = delta.compatSmallestScreenWidthDp;
}
-
if (delta.seq != 0) {
seq = delta.seq;
}
@@ -807,6 +835,8 @@
* PackageManager.ActivityInfo.CONFIG_SCREEN_SIZE}, or
* {@link android.content.pm.ActivityInfo#CONFIG_SMALLEST_SCREEN_SIZE
* PackageManager.ActivityInfo.CONFIG_SMALLEST_SCREEN_SIZE}.
+ * {@link android.content.pm.ActivityInfo#CONFIG_LAYOUT_DIRECTION
+ * PackageManager.ActivityInfo.CONFIG_LAYOUT_DIRECTION}.
*/
public int diff(Configuration delta) {
int changed = 0;
@@ -822,6 +852,7 @@
if (delta.locale != null
&& (locale == null || !locale.equals(delta.locale))) {
changed |= ActivityInfo.CONFIG_LOCALE;
+ changed |= ActivityInfo.CONFIG_LAYOUT_DIRECTION;
}
if (delta.touchscreen != TOUCHSCREEN_UNDEFINED
&& touchscreen != delta.touchscreen) {
@@ -851,8 +882,10 @@
&& orientation != delta.orientation) {
changed |= ActivityInfo.CONFIG_ORIENTATION;
}
- if (delta.screenLayout != SCREENLAYOUT_SIZE_UNDEFINED
- && screenLayout != delta.screenLayout) {
+ if (getScreenLayoutNoDirection(delta.screenLayout) !=
+ (SCREENLAYOUT_SIZE_UNDEFINED | SCREENLAYOUT_LONG_UNDEFINED)
+ && getScreenLayoutNoDirection(screenLayout) !=
+ getScreenLayoutNoDirection(delta.screenLayout)) {
changed |= ActivityInfo.CONFIG_SCREEN_LAYOUT;
}
if (delta.uiMode != (UI_MODE_TYPE_UNDEFINED|UI_MODE_NIGHT_UNDEFINED)
@@ -875,7 +908,7 @@
&& densityDpi != delta.densityDpi) {
changed |= ActivityInfo.CONFIG_DENSITY;
}
-
+
return changed;
}
@@ -963,7 +996,6 @@
dest.writeInt(compatScreenWidthDp);
dest.writeInt(compatScreenHeightDp);
dest.writeInt(compatSmallestScreenWidthDp);
- dest.writeInt(layoutDirection);
dest.writeInt(seq);
}
@@ -992,7 +1024,6 @@
compatScreenWidthDp = source.readInt();
compatScreenHeightDp = source.readInt();
compatSmallestScreenWidthDp = source.readInt();
- layoutDirection = source.readInt();
seq = source.readInt();
}
@@ -1100,4 +1131,50 @@
result = 31 * result + densityDpi;
return result;
}
+
+ /**
+ * Set the locale. This is the preferred way for setting up the locale (instead of using the
+ * direct accessor). This will also set the userLocale and layout direction according to
+ * the locale.
+ *
+ * @param loc The locale. Can be null.
+ */
+ public void setLocale(Locale loc) {
+ locale = loc;
+ userSetLocale = true;
+ setLayoutDirection(locale);
+ }
+
+ /**
+ * Return the layout direction. Will be either {@link View#LAYOUT_DIRECTION_LTR} or
+ * {@link View#LAYOUT_DIRECTION_RTL}.
+ *
+ * @return the layout direction
+ */
+ public int getLayoutDirection() {
+ // We need to substract one here as the configuration values are using "0" as undefined thus
+ // having LRT set to "1" and RTL set to "2"
+ return ((screenLayout&SCREENLAYOUT_LAYOUTDIR_MASK) >> SCREENLAYOUT_LAYOUTDIR_SHIFT) - 1;
+ }
+
+ /**
+ * Set the layout direction from the Locale.
+ *
+ * @param locale The Locale. If null will set the layout direction to
+ * {@link View#LAYOUT_DIRECTION_LTR}. If not null will set it to the layout direction
+ * corresponding to the Locale.
+ *
+ * @see {@link View#LAYOUT_DIRECTION_LTR} and {@link View#LAYOUT_DIRECTION_RTL}
+ */
+ public void setLayoutDirection(Locale locale) {
+ // There is a "1" difference between the configuration values for
+ // layout direction and View constants for layout direction, just add "1".
+ final int layoutDirection = 1 + LocaleUtil.getLayoutDirectionFromLocale(locale);
+ screenLayout = (screenLayout&~SCREENLAYOUT_LAYOUTDIR_MASK)|
+ (layoutDirection << SCREENLAYOUT_LAYOUTDIR_SHIFT);
+ }
+
+ private static int getScreenLayoutNoDirection(int screenLayout) {
+ return screenLayout&~SCREENLAYOUT_LAYOUTDIR_MASK;
+ }
}
diff --git a/core/java/android/content/res/Resources.java b/core/java/android/content/res/Resources.java
index 7559f1e..42a6bdc 100755
--- a/core/java/android/content/res/Resources.java
+++ b/core/java/android/content/res/Resources.java
@@ -1444,12 +1444,14 @@
}
if (mTmpConfig.locale == null) {
mTmpConfig.locale = Locale.getDefault();
+ mTmpConfig.setLayoutDirection(mTmpConfig.locale);
}
configChanges = mConfiguration.updateFrom(mTmpConfig);
configChanges = ActivityInfo.activityInfoConfigToNative(configChanges);
}
if (mConfiguration.locale == null) {
mConfiguration.locale = Locale.getDefault();
+ mConfiguration.setLayoutDirection(mConfiguration.locale);
}
if (mConfiguration.densityDpi != Configuration.DENSITY_DPI_UNDEFINED) {
mMetrics.densityDpi = mConfiguration.densityDpi;
diff --git a/core/java/android/hardware/display/DisplayManager.java b/core/java/android/hardware/display/DisplayManager.java
index 74996da..2814301 100644
--- a/core/java/android/hardware/display/DisplayManager.java
+++ b/core/java/android/hardware/display/DisplayManager.java
@@ -19,7 +19,6 @@
import android.content.Context;
import android.os.Handler;
import android.util.SparseArray;
-import android.view.CompatibilityInfoHolder;
import android.view.Display;
/**
@@ -92,7 +91,7 @@
Display display = mDisplays.get(displayId);
if (display == null) {
display = mGlobal.getCompatibleDisplay(displayId,
- getCompatibilityInfoForDisplayLocked(displayId));
+ mContext.getCompatibilityInfo(displayId));
if (display != null) {
mDisplays.put(displayId, display);
}
@@ -102,14 +101,6 @@
return display;
}
- private CompatibilityInfoHolder getCompatibilityInfoForDisplayLocked(int displayId) {
- CompatibilityInfoHolder cih = null;
- if (displayId == Display.DEFAULT_DISPLAY) {
- cih = mContext.getCompatibilityInfo();
- }
- return cih;
- }
-
/**
* Registers an display listener to receive notifications about when
* displays are added, removed or changed.
diff --git a/core/java/android/hardware/display/DisplayManagerGlobal.java b/core/java/android/hardware/display/DisplayManagerGlobal.java
index 69c0319..4077964 100644
--- a/core/java/android/hardware/display/DisplayManagerGlobal.java
+++ b/core/java/android/hardware/display/DisplayManagerGlobal.java
@@ -42,6 +42,16 @@
private static final String TAG = "DisplayManager";
private static final boolean DEBUG = false;
+ // True if display info and display ids should be cached.
+ //
+ // FIXME: The cache is currently disabled because it's unclear whether we have the
+ // necessary guarantees that the caches will always be flushed before clients
+ // attempt to observe their new state. For example, depending on the order
+ // in which the binder transactions take place, we might have a problem where
+ // an application could start processing a configuration change due to a display
+ // orientation change before the display info cache has actually been invalidated.
+ private static final boolean USE_CACHE = false;
+
public static final int EVENT_DISPLAY_ADDED = 1;
public static final int EVENT_DISPLAY_CHANGED = 2;
public static final int EVENT_DISPLAY_REMOVED = 3;
@@ -91,21 +101,27 @@
public DisplayInfo getDisplayInfo(int displayId) {
try {
synchronized (mLock) {
- DisplayInfo info = mDisplayInfoCache.get(displayId);
- if (info != null) {
- return info;
+ DisplayInfo info;
+ if (USE_CACHE) {
+ info = mDisplayInfoCache.get(displayId);
+ if (info != null) {
+ return info;
+ }
}
info = mDm.getDisplayInfo(displayId);
if (info == null) {
return null;
}
+
+ if (USE_CACHE) {
+ mDisplayInfoCache.put(displayId, info);
+ }
+ registerCallbackIfNeededLocked();
+
if (DEBUG) {
Log.d(TAG, "getDisplayInfo: displayId=" + displayId + ", info=" + info);
}
-
- mDisplayInfoCache.put(displayId, info);
- registerCallbackIfNeededLocked();
return info;
}
} catch (RemoteException ex) {
@@ -122,11 +138,18 @@
public int[] getDisplayIds() {
try {
synchronized (mLock) {
- if (mDisplayIdCache == null) {
- mDisplayIdCache = mDm.getDisplayIds();
- registerCallbackIfNeededLocked();
+ if (USE_CACHE) {
+ if (mDisplayIdCache != null) {
+ return mDisplayIdCache;
+ }
}
- return mDisplayIdCache;
+
+ int[] displayIds = mDm.getDisplayIds();
+ if (USE_CACHE) {
+ mDisplayIdCache = displayIds;
+ }
+ registerCallbackIfNeededLocked();
+ return displayIds;
}
} catch (RemoteException ex) {
Log.e(TAG, "Could not get display ids from display manager.", ex);
@@ -215,10 +238,12 @@
private void handleDisplayEvent(int displayId, int event) {
synchronized (mLock) {
- mDisplayInfoCache.remove(displayId);
+ if (USE_CACHE) {
+ mDisplayInfoCache.remove(displayId);
- if (event == EVENT_DISPLAY_ADDED || event == EVENT_DISPLAY_REMOVED) {
- mDisplayIdCache = null;
+ if (event == EVENT_DISPLAY_ADDED || event == EVENT_DISPLAY_REMOVED) {
+ mDisplayIdCache = null;
+ }
}
final int numListeners = mDisplayListeners.size();
diff --git a/core/java/android/nfc/NdefRecord.java b/core/java/android/nfc/NdefRecord.java
index ed1c5b3..2d9dae9 100644
--- a/core/java/android/nfc/NdefRecord.java
+++ b/core/java/android/nfc/NdefRecord.java
@@ -688,7 +688,8 @@
}
} catch (FormatException e) { }
} else if (Arrays.equals(mType, RTD_URI)) {
- return parseWktUri().normalizeScheme();
+ Uri wktUri = parseWktUri();
+ return (wktUri != null ? wktUri.normalizeScheme() : null);
}
break;
diff --git a/core/java/android/os/Binder.java b/core/java/android/os/Binder.java
index 5d40456..ea14098 100644
--- a/core/java/android/os/Binder.java
+++ b/core/java/android/os/Binder.java
@@ -73,34 +73,6 @@
public static final native int getCallingUid();
/**
- * Return the original ID of the user assigned to the process that sent you the current
- * transaction that is being processed. This uid can be used with higher-level system services
- * to determine its identity and check permissions. If the current thread is not currently
- * executing an incoming transaction, then its own uid is returned.
- * <p/>
- * This value cannot be reset by calls to {@link #clearCallingIdentity()}.
- * @hide
- */
- public static final int getOrigCallingUid() {
- if (UserHandle.MU_ENABLED) {
- return getOrigCallingUidNative();
- } else {
- return getCallingUid();
- }
- }
-
- private static final native int getOrigCallingUidNative();
-
- /**
- * Utility function to return the user id of the calling process.
- * @return userId of the calling process, extracted from the callingUid
- * @hide
- */
- public static final int getOrigCallingUser() {
- return UserHandle.getUserId(getOrigCallingUid());
- }
-
- /**
* Reset the identity of the incoming IPC on the current thread. This can
* be useful if, while handling an incoming call, you will be calling
* on interfaces of other objects that may be local to your process and
diff --git a/core/java/android/os/Environment.java b/core/java/android/os/Environment.java
index 679cf1a..2fbcf3f 100644
--- a/core/java/android/os/Environment.java
+++ b/core/java/android/os/Environment.java
@@ -104,6 +104,17 @@
}
/**
+ * Return the system directory for a user. This is for use by system services to store
+ * files relating to the user. This directory will be automatically deleted when the user
+ * is removed.
+ *
+ * @hide
+ */
+ public static File getUserSystemDirectory(int userId) {
+ return new File(new File(getSystemSecureDirectory(), "users"), Integer.toString(userId));
+ }
+
+ /**
* Returns whether the Encrypted File System feature is enabled on the device or not.
* @return <code>true</code> if Encrypted File System feature is enabled, <code>false</code>
* if disabled.
diff --git a/core/java/android/os/Handler.java b/core/java/android/os/Handler.java
index 0f9be9c..94de448 100644
--- a/core/java/android/os/Handler.java
+++ b/core/java/android/os/Handler.java
@@ -431,7 +431,12 @@
* set up a Handler thread and need to perform some initialization steps on
* it before continuing execution.
*
+ * If timeout occurs then this method returns <code>false</code> but the runnable
+ * will remain posted on the handler and may already be in progress or
+ * complete at a later time.
+ *
* @param r The Runnable that will be executed synchronously.
+ * @param timeout The timeout in milliseconds, or 0 to wait indefinitely.
*
* @return Returns true if the Runnable was successfully executed.
* Returns false on failure, usually because the
@@ -441,10 +446,13 @@
* If we ever do make it part of the API, we might want to rename it to something
* less funny like runUnsafe().
*/
- public final boolean runWithScissors(final Runnable r) {
+ public final boolean runWithScissors(final Runnable r, long timeout) {
if (r == null) {
throw new IllegalArgumentException("runnable must not be null");
}
+ if (timeout < 0) {
+ throw new IllegalArgumentException("timeout must be non-negative");
+ }
if (Looper.myLooper() == mLooper) {
r.run();
@@ -452,7 +460,7 @@
}
BlockingRunnable br = new BlockingRunnable(r);
- return br.postAndWait(this);
+ return br.postAndWait(this, timeout);
}
/**
@@ -743,16 +751,30 @@
}
}
- public boolean postAndWait(Handler handler) {
+ public boolean postAndWait(Handler handler, long timeout) {
if (!handler.post(this)) {
return false;
}
synchronized (this) {
- while (!mDone) {
- try {
- wait();
- } catch (InterruptedException ex) {
+ if (timeout > 0) {
+ final long expirationTime = SystemClock.uptimeMillis() + timeout;
+ while (!mDone) {
+ long delay = expirationTime - SystemClock.uptimeMillis();
+ if (delay <= 0) {
+ return false; // timeout
+ }
+ try {
+ wait(delay);
+ } catch (InterruptedException ex) {
+ }
+ }
+ } else {
+ while (!mDone) {
+ try {
+ wait();
+ } catch (InterruptedException ex) {
+ }
}
}
}
diff --git a/core/java/android/util/DisplayMetrics.java b/core/java/android/util/DisplayMetrics.java
index 506594b..85e4b9d 100644
--- a/core/java/android/util/DisplayMetrics.java
+++ b/core/java/android/util/DisplayMetrics.java
@@ -206,13 +206,52 @@
public void setToDefaults() {
widthPixels = 0;
heightPixels = 0;
- density = noncompatDensity = DENSITY_DEVICE / (float) DENSITY_DEFAULT;
- densityDpi = noncompatDensityDpi = DENSITY_DEVICE;
+ density = DENSITY_DEVICE / (float) DENSITY_DEFAULT;
+ densityDpi = DENSITY_DEVICE;
scaledDensity = density;
- xdpi = noncompatXdpi = DENSITY_DEVICE;
- ydpi = noncompatYdpi = DENSITY_DEVICE;
- noncompatWidthPixels = 0;
- noncompatHeightPixels = 0;
+ xdpi = DENSITY_DEVICE;
+ ydpi = DENSITY_DEVICE;
+ noncompatWidthPixels = widthPixels;
+ noncompatHeightPixels = heightPixels;
+ noncompatDensity = density;
+ noncompatDensityDpi = densityDpi;
+ noncompatScaledDensity = scaledDensity;
+ noncompatXdpi = xdpi;
+ noncompatYdpi = ydpi;
+ }
+
+ @Override
+ public boolean equals(Object o) {
+ return o instanceof DisplayMetrics && equals((DisplayMetrics)o);
+ }
+
+ /**
+ * Returns true if these display metrics equal the other display metrics.
+ *
+ * @param other The display metrics with which to compare.
+ * @return True if the display metrics are equal.
+ */
+ public boolean equals(DisplayMetrics other) {
+ return other != null
+ && widthPixels == other.widthPixels
+ && heightPixels == other.heightPixels
+ && density == other.density
+ && densityDpi == other.densityDpi
+ && scaledDensity == other.scaledDensity
+ && xdpi == other.xdpi
+ && ydpi == other.ydpi
+ && noncompatWidthPixels == other.noncompatWidthPixels
+ && noncompatHeightPixels == other.noncompatHeightPixels
+ && noncompatDensity == other.noncompatDensity
+ && noncompatDensityDpi == other.noncompatDensityDpi
+ && noncompatScaledDensity == other.noncompatScaledDensity
+ && noncompatXdpi == other.noncompatXdpi
+ && noncompatYdpi == other.noncompatYdpi;
+ }
+
+ @Override
+ public int hashCode() {
+ return widthPixels * heightPixels * densityDpi;
}
@Override
diff --git a/core/java/android/view/Display.java b/core/java/android/view/Display.java
index ec635a2..8ac84f7a 100644
--- a/core/java/android/view/Display.java
+++ b/core/java/android/view/Display.java
@@ -53,6 +53,8 @@
private final DisplayManagerGlobal mGlobal;
private final int mDisplayId;
+ private final int mLayerStack;
+ private final String mName;
private final CompatibilityInfoHolder mCompatibilityInfo;
private DisplayInfo mDisplayInfo; // never null
@@ -90,6 +92,8 @@
mGlobal = global;
mDisplayId = displayId;
mDisplayInfo = displayInfo;
+ mLayerStack = displayInfo.layerStack; // can never change as long as the display is valid
+ mName = displayInfo.name; // cannot change as long as the display is valid
mCompatibilityInfo = compatibilityInfo;
mIsValid = true;
}
@@ -146,13 +150,11 @@
* Each display has its own independent layer stack upon which surfaces
* are placed to be managed by surface flinger.
*
- * @return The layer stack number.
+ * @return The display's layer stack number.
* @hide
*/
public int getLayerStack() {
- // Note: This is the current convention but there is no requirement that
- // the display id and layer stack id be the same.
- return mDisplayId;
+ return mLayerStack;
}
/**
@@ -166,6 +168,14 @@
}
/**
+ * Gets the name of the display.
+ * @return The display's name.
+ */
+ public String getName() {
+ return mName;
+ }
+
+ /**
* Gets the size of the display, in pixels.
* <p>
* Note that this value should <em>not</em> be used for computing layouts,
diff --git a/core/java/android/view/DisplayInfo.java b/core/java/android/view/DisplayInfo.java
index 593e8c4..b728d71 100644
--- a/core/java/android/view/DisplayInfo.java
+++ b/core/java/android/view/DisplayInfo.java
@@ -21,12 +21,24 @@
import android.os.Parcelable;
import android.util.DisplayMetrics;
+import libcore.util.Objects;
+
/**
* Describes the characteristics of a particular logical display.
* @hide
*/
public final class DisplayInfo implements Parcelable {
/**
+ * The surface flinger layer stack associated with this logical display.
+ */
+ public int layerStack;
+
+ /**
+ * The human-readable name of the display.
+ */
+ public String name;
+
+ /**
* The width of the portion of the display that is available to applications, in pixels.
* Represents the size of the display minus any system decorations.
*/
@@ -147,11 +159,37 @@
}
@Override
- public int describeContents() {
- return 0;
+ public boolean equals(Object o) {
+ return o instanceof DisplayInfo && equals((DisplayInfo)o);
+ }
+
+ public boolean equals(DisplayInfo other) {
+ return other != null
+ && layerStack == other.layerStack
+ && Objects.equal(name, other.name)
+ && appWidth == other.appWidth
+ && appHeight == other.appHeight
+ && smallestNominalAppWidth == other.smallestNominalAppWidth
+ && smallestNominalAppHeight == other.smallestNominalAppHeight
+ && largestNominalAppWidth == other.largestNominalAppWidth
+ && largestNominalAppHeight == other.largestNominalAppHeight
+ && logicalWidth == other.logicalWidth
+ && logicalHeight == other.logicalHeight
+ && rotation == other.rotation
+ && refreshRate == other.refreshRate
+ && logicalDensityDpi == other.logicalDensityDpi
+ && physicalXDpi == other.physicalXDpi
+ && physicalYDpi == other.physicalYDpi;
+ }
+
+ @Override
+ public int hashCode() {
+ return 0; // don't care
}
public void copyFrom(DisplayInfo other) {
+ layerStack = other.layerStack;
+ name = other.name;
appWidth = other.appWidth;
appHeight = other.appHeight;
smallestNominalAppWidth = other.smallestNominalAppWidth;
@@ -168,6 +206,8 @@
}
public void readFromParcel(Parcel source) {
+ layerStack = source.readInt();
+ name = source.readString();
appWidth = source.readInt();
appHeight = source.readInt();
smallestNominalAppWidth = source.readInt();
@@ -185,6 +225,8 @@
@Override
public void writeToParcel(Parcel dest, int flags) {
+ dest.writeInt(layerStack);
+ dest.writeString(name);
dest.writeInt(appWidth);
dest.writeInt(appHeight);
dest.writeInt(smallestNominalAppWidth);
@@ -200,6 +242,11 @@
dest.writeFloat(physicalYDpi);
}
+ @Override
+ public int describeContents() {
+ return 0;
+ }
+
public void getAppMetrics(DisplayMetrics outMetrics, CompatibilityInfoHolder cih) {
getMetricsWithSize(outMetrics, cih, appWidth, appHeight);
}
@@ -231,13 +278,14 @@
// For debugging purposes
@Override
public String toString() {
- return "app " + appWidth + " x " + appHeight
+ return "DisplayInfo{\"" + name + "\", app " + appWidth + " x " + appHeight
+ ", real " + logicalWidth + " x " + logicalHeight
+ ", largest app " + largestNominalAppWidth + " x " + largestNominalAppHeight
+ ", smallest app " + smallestNominalAppWidth + " x " + smallestNominalAppHeight
+ ", " + refreshRate + " fps"
+ ", rotation " + rotation
+ ", density " + logicalDensityDpi
- + ", " + physicalXDpi + " x " + physicalYDpi + " dpi";
+ + ", " + physicalXDpi + " x " + physicalYDpi + " dpi"
+ + ", layerStack " + layerStack + "}";
}
}
diff --git a/core/java/android/view/ScaleGestureDetector.java b/core/java/android/view/ScaleGestureDetector.java
index dc36088..bcb8800 100644
--- a/core/java/android/view/ScaleGestureDetector.java
+++ b/core/java/android/view/ScaleGestureDetector.java
@@ -86,8 +86,8 @@
* pointers going up.
*
* Once a scale has ended, {@link ScaleGestureDetector#getFocusX()}
- * and {@link ScaleGestureDetector#getFocusY()} will return the location
- * of the pointer remaining on the screen.
+ * and {@link ScaleGestureDetector#getFocusY()} will return focal point
+ * of the pointers remaining on the screen.
*
* @param detector The detector reporting the event - use this to
* retrieve extended info about event state.
@@ -128,6 +128,7 @@
private float mCurrSpan;
private float mPrevSpan;
+ private float mInitialSpan;
private float mCurrSpanX;
private float mCurrSpanY;
private float mPrevSpanX;
@@ -135,6 +136,7 @@
private long mCurrTime;
private long mPrevTime;
private boolean mInProgress;
+ private int mSpanSlop;
/**
* Consistency verifier for debugging purposes.
@@ -146,6 +148,7 @@
public ScaleGestureDetector(Context context, OnScaleGestureListener listener) {
mContext = context;
mListener = listener;
+ mSpanSlop = ViewConfiguration.get(context).getScaledTouchSlop() * 2;
}
/**
@@ -176,6 +179,7 @@
if (mInProgress) {
mListener.onScaleEnd(this);
mInProgress = false;
+ mInitialSpan = 0;
}
if (streamComplete) {
@@ -221,18 +225,24 @@
// Dispatch begin/end events as needed.
// If the configuration changes, notify the app to reset its current state by beginning
// a fresh scale event stream.
+ final boolean wasInProgress = mInProgress;
+ mFocusX = focusX;
+ mFocusY = focusY;
if (mInProgress && (span == 0 || configChanged)) {
mListener.onScaleEnd(this);
mInProgress = false;
+ mInitialSpan = span;
}
if (configChanged) {
mPrevSpanX = mCurrSpanX = spanX;
mPrevSpanY = mCurrSpanY = spanY;
- mPrevSpan = mCurrSpan = span;
+ mInitialSpan = mPrevSpan = mCurrSpan = span;
}
- if (!mInProgress && span != 0) {
- mFocusX = focusX;
- mFocusY = focusY;
+ if (!mInProgress && span != 0 &&
+ (wasInProgress || Math.abs(span - mInitialSpan) > mSpanSlop)) {
+ mPrevSpanX = mCurrSpanX = spanX;
+ mPrevSpanY = mCurrSpanY = spanY;
+ mPrevSpan = mCurrSpan = span;
mInProgress = mListener.onScaleBegin(this);
}
@@ -241,8 +251,6 @@
mCurrSpanX = spanX;
mCurrSpanY = spanY;
mCurrSpan = span;
- mFocusX = focusX;
- mFocusY = focusY;
boolean updatePrev = true;
if (mInProgress) {
diff --git a/core/java/android/view/Surface.java b/core/java/android/view/Surface.java
index cf1767d7..db05e10 100644
--- a/core/java/android/view/Surface.java
+++ b/core/java/android/view/Surface.java
@@ -262,12 +262,8 @@
IBinder displayToken, SurfaceTexture surfaceTexture);
private static native void nativeSetDisplayLayerStack(
IBinder displayToken, int layerStack);
- private static native void nativeSetDisplayOrientation(
- IBinder displayToken, int orientation);
- private static native void nativeSetDisplayViewport(
- IBinder displayToken, Rect viewport);
- private static native void nativeSetDisplayFrame(
- IBinder displayToken, Rect frame);
+ private static native void nativeSetDisplayProjection(
+ IBinder displayToken, int orientation, Rect layerStackRect, Rect displayRect);
private static native boolean nativeGetDisplayInfo(
IBinder displayToken, PhysicalDisplayInfo outInfo);
@@ -617,33 +613,18 @@
}
/** @hide */
- public static void setDisplayOrientation(IBinder displayToken, int orientation) {
+ public static void setDisplayProjection(IBinder displayToken,
+ int orientation, Rect layerStackRect, Rect displayRect) {
if (displayToken == null) {
throw new IllegalArgumentException("displayToken must not be null");
}
- nativeSetDisplayOrientation(displayToken, orientation);
- }
-
- /** @hide */
- public static void setDisplayViewport(IBinder displayToken, Rect viewport) {
- if (displayToken == null) {
- throw new IllegalArgumentException("displayToken must not be null");
+ if (layerStackRect == null) {
+ throw new IllegalArgumentException("layerStackRect must not be null");
}
- if (viewport == null) {
- throw new IllegalArgumentException("viewport must not be null");
+ if (displayRect == null) {
+ throw new IllegalArgumentException("displayRect must not be null");
}
- nativeSetDisplayViewport(displayToken, viewport);
- }
-
- /** @hide */
- public static void setDisplayFrame(IBinder displayToken, Rect frame) {
- if (displayToken == null) {
- throw new IllegalArgumentException("displayToken must not be null");
- }
- if (frame == null) {
- throw new IllegalArgumentException("frame must not be null");
- }
- nativeSetDisplayFrame(displayToken, frame);
+ nativeSetDisplayProjection(displayToken, orientation, layerStackRect, displayRect);
}
/** @hide */
@@ -743,17 +724,59 @@
}
/**
- * Describes the properties of a physical display.
+ * Describes the properties of a physical display known to surface flinger.
* @hide
*/
public static final class PhysicalDisplayInfo {
- // TODO: redesign this
public int width;
public int height;
public float refreshRate;
public float density;
public float xDpi;
public float yDpi;
+
+ public PhysicalDisplayInfo() {
+ }
+
+ public PhysicalDisplayInfo(PhysicalDisplayInfo other) {
+ copyFrom(other);
+ }
+
+ @Override
+ public boolean equals(Object o) {
+ return o instanceof PhysicalDisplayInfo && equals((PhysicalDisplayInfo)o);
+ }
+
+ public boolean equals(PhysicalDisplayInfo other) {
+ return other != null
+ && width == other.width
+ && height == other.height
+ && refreshRate == other.refreshRate
+ && density == other.density
+ && xDpi == other.xDpi
+ && yDpi == other.yDpi;
+ }
+
+ @Override
+ public int hashCode() {
+ return 0; // don't care
+ }
+
+ public void copyFrom(PhysicalDisplayInfo other) {
+ width = other.width;
+ height = other.height;
+ refreshRate = other.refreshRate;
+ density = other.density;
+ xDpi = other.xDpi;
+ yDpi = other.yDpi;
+ }
+
+ // For debugging purposes
+ @Override
+ public String toString() {
+ return "PhysicalDisplayInfo{" + width + " x " + height + ", " + refreshRate + " fps, "
+ + "density " + density + ", " + xDpi + " x " + yDpi + " dpi}";
+ }
}
/**
diff --git a/core/java/android/view/WindowManager.java b/core/java/android/view/WindowManager.java
index f30952c..7412f39 100644
--- a/core/java/android/view/WindowManager.java
+++ b/core/java/android/view/WindowManager.java
@@ -16,6 +16,8 @@
package android.view;
+import android.app.Presentation;
+import android.content.Context;
import android.content.pm.ActivityInfo;
import android.graphics.PixelFormat;
import android.os.IBinder;
@@ -29,6 +31,17 @@
* The interface that apps use to talk to the window manager.
* <p>
* Use <code>Context.getSystemService(Context.WINDOW_SERVICE)</code> to get one of these.
+ * </p><p>
+ * Each window manager instance is bound to a particular {@link Display}.
+ * To obtain a {@link WindowManager} for a different display, use
+ * {@link Context#createDisplayContext} to obtain a {@link Context} for that
+ * display, then use <code>Context.getSystemService(Context.WINDOW_SERVICE)</code>
+ * to get the WindowManager.
+ * </p><p>
+ * The simplest way to show a window on another display is to create a
+ * {@link Presentation}. The presentation will automatically obtain a
+ * {@link WindowManager} and {@link Context} for that display.
+ * </p>
*
* @see android.content.Context#getSystemService
* @see android.content.Context#WINDOW_SERVICE
@@ -49,12 +62,24 @@
}
/**
- * Use this method to get the default Display object.
- *
- * @return default Display object
+ * Returns the {@link Display} upon which this {@link WindowManager} instance
+ * will create new windows.
+ * <p>
+ * Despite the name of this method, the display that is returned is not
+ * necessarily the primary display of the system (see {@link Display#DEFAULT_DISPLAY}).
+ * The returned display could instead be a secondary display that this
+ * window manager instance is managing. Think of it as the display that
+ * this {@link WindowManager} instance uses by default.
+ * </p><p>
+ * To create windows on a different display, you need to obtain a
+ * {@link WindowManager} for that {@link Display}. (See the {@link WindowManager}
+ * class documentation for more information.)
+ * </p>
+ *
+ * @return The display that this window manager is managing.
*/
public Display getDefaultDisplay();
-
+
/**
* Special variation of {@link #removeView} that immediately invokes
* the given view hierarchy's {@link View#onDetachedFromWindow()
diff --git a/core/java/android/view/WindowManagerImpl.java b/core/java/android/view/WindowManagerImpl.java
index aa9179f..52d79f8 100644
--- a/core/java/android/view/WindowManagerImpl.java
+++ b/core/java/android/view/WindowManagerImpl.java
@@ -16,9 +16,6 @@
package android.view;
-import android.content.Context;
-import android.hardware.display.DisplayManager;
-
/**
* Provides low-level communication with the system window manager for
* operations that are bound to a particular context, display or parent window.
@@ -47,25 +44,24 @@
*/
public final class WindowManagerImpl implements WindowManager {
private final WindowManagerGlobal mGlobal = WindowManagerGlobal.getInstance();
- private final Context mContext;
private final Display mDisplay;
private final Window mParentWindow;
- public WindowManagerImpl(Context context, int displayId) {
- DisplayManager dm = (DisplayManager)context.getSystemService(Context.DISPLAY_SERVICE);
- mContext = context;
- mDisplay = dm.getDisplay(displayId);
- mParentWindow = null;
+ public WindowManagerImpl(Display display) {
+ this(display, null);
}
- private WindowManagerImpl(Context context, Display display, Window parentWindow) {
- mContext = context;
+ private WindowManagerImpl(Display display, Window parentWindow) {
mDisplay = display;
mParentWindow = parentWindow;
}
public WindowManagerImpl createLocalWindowManager(Window parentWindow) {
- return new WindowManagerImpl(mContext, mDisplay, parentWindow);
+ return new WindowManagerImpl(mDisplay, parentWindow);
+ }
+
+ public WindowManagerImpl createPresentationWindowManager(Display display) {
+ return new WindowManagerImpl(display, mParentWindow);
}
@Override
diff --git a/core/java/android/view/WindowManagerPolicy.java b/core/java/android/view/WindowManagerPolicy.java
index 7173d1d..05838f9 100644
--- a/core/java/android/view/WindowManagerPolicy.java
+++ b/core/java/android/view/WindowManagerPolicy.java
@@ -827,32 +827,33 @@
static final int FINISH_LAYOUT_REDO_ANIM = 0x0008;
/**
- * Called when animation of the windows is about to start.
+ * Called following layout of all windows before each window has policy applied.
*
* @param displayWidth The current full width of the screen.
* @param displayHeight The current full height of the screen.
*/
- public void beginAnimationLw(int displayWidth, int displayHeight);
+ public void beginPostLayoutPolicyLw(int displayWidth, int displayHeight);
/**
- * Called each time a window is animating.
+ * Called following layout of all window to apply policy to each window.
*
* @param win The window being positioned.
* @param attrs The LayoutParams of the window.
*/
- public void animatingWindowLw(WindowState win,
+ public void applyPostLayoutPolicyLw(WindowState win,
WindowManager.LayoutParams attrs);
/**
- * Called when animation of the windows is finished. If in this function you do
- * something that may have modified the animation state of another window,
- * be sure to return true in order to perform another animation frame.
+ * Called following layout of all windows and after policy has been applied
+ * to each window. If in this function you do
+ * something that may have modified the animation state of another window,
+ * be sure to return non-zero in order to perform another pass through layout.
*
* @return Return any bit set of {@link #FINISH_LAYOUT_REDO_LAYOUT},
* {@link #FINISH_LAYOUT_REDO_CONFIG}, {@link #FINISH_LAYOUT_REDO_WALLPAPER},
* or {@link #FINISH_LAYOUT_REDO_ANIM}.
*/
- public int finishAnimationLw();
+ public int finishPostLayoutPolicyLw();
/**
* Return true if it is okay to perform animations for an app transition
diff --git a/core/java/android/view/inputmethod/InputMethodInfo.java b/core/java/android/view/inputmethod/InputMethodInfo.java
index 131f0ae..08e30aa 100644
--- a/core/java/android/view/inputmethod/InputMethodInfo.java
+++ b/core/java/android/view/inputmethod/InputMethodInfo.java
@@ -34,6 +34,7 @@
import android.os.Parcelable;
import android.util.AttributeSet;
import android.util.Printer;
+import android.util.Slog;
import android.util.Xml;
import java.io.IOException;
@@ -169,7 +170,10 @@
a.getBoolean(com.android.internal.R.styleable
.InputMethod_Subtype_isAuxiliary, false),
a.getBoolean(com.android.internal.R.styleable
- .InputMethod_Subtype_overridesImplicitlyEnabledSubtype, false));
+ .InputMethod_Subtype_overridesImplicitlyEnabledSubtype, false),
+ a.getInt(com.android.internal.R.styleable
+ .InputMethod_Subtype_subtypeId, 0 /* use Arrays.hashCode */)
+ );
if (!subtype.isAuxiliary()) {
mIsAuxIme = false;
}
@@ -194,6 +198,9 @@
final InputMethodSubtype subtype = additionalSubtypes.get(i);
if (!mSubtypes.contains(subtype)) {
mSubtypes.add(subtype);
+ } else {
+ Slog.w(TAG, "Duplicated subtype definition found: "
+ + subtype.getLocale() + ", " + subtype.getMode());
}
}
}
diff --git a/core/java/android/view/inputmethod/InputMethodSubtype.java b/core/java/android/view/inputmethod/InputMethodSubtype.java
index b7c94a3..7895e6f 100644
--- a/core/java/android/view/inputmethod/InputMethodSubtype.java
+++ b/core/java/android/view/inputmethod/InputMethodSubtype.java
@@ -55,13 +55,14 @@
private final int mSubtypeHashCode;
private final int mSubtypeIconResId;
private final int mSubtypeNameResId;
+ private final int mSubtypeId;
private final String mSubtypeLocale;
private final String mSubtypeMode;
private final String mSubtypeExtraValue;
private volatile HashMap<String, String> mExtraValueHashMapCache;
/**
- * Constructor.
+ * Constructor with no subtype ID specified, overridesImplicitlyEnabledSubtype not specified.
* @param nameId Resource ID of the subtype name string. The string resource may have exactly
* one %s in it. If there is, the %s part will be replaced with the locale's display name by
* the formatter. Please refer to {@link #getDisplayName} for details.
@@ -87,7 +88,7 @@
}
/**
- * Constructor.
+ * Constructor with no subtype ID specified.
* @param nameId Resource ID of the subtype name string. The string resource may have exactly
* one %s in it. If there is, the %s part will be replaced with the locale's display name by
* the formatter. Please refer to {@link #getDisplayName} for details.
@@ -112,6 +113,41 @@
*/
public InputMethodSubtype(int nameId, int iconId, String locale, String mode, String extraValue,
boolean isAuxiliary, boolean overridesImplicitlyEnabledSubtype) {
+ this(nameId, iconId, locale, mode, extraValue, isAuxiliary,
+ overridesImplicitlyEnabledSubtype, 0);
+ }
+
+ /**
+ * Constructor.
+ * @param nameId Resource ID of the subtype name string. The string resource may have exactly
+ * one %s in it. If there is, the %s part will be replaced with the locale's display name by
+ * the formatter. Please refer to {@link #getDisplayName} for details.
+ * @param iconId Resource ID of the subtype icon drawable.
+ * @param locale The locale supported by the subtype
+ * @param mode The mode supported by the subtype
+ * @param extraValue The extra value of the subtype. This string is free-form, but the API
+ * supplies tools to deal with a key-value comma-separated list; see
+ * {@link #containsExtraValueKey} and {@link #getExtraValueOf}.
+ * @param isAuxiliary true when this subtype is auxiliary, false otherwise. An auxiliary
+ * subtype will not be shown in the list of enabled IMEs for choosing the current IME in
+ * the Settings even when this subtype is enabled. Please note that this subtype will still
+ * be shown in the list of IMEs in the IME switcher to allow the user to tentatively switch
+ * to this subtype while an IME is shown. The framework will never switch the current IME to
+ * this subtype by {@link android.view.inputmethod.InputMethodManager#switchToLastInputMethod}.
+ * The intent of having this flag is to allow for IMEs that are invoked in a one-shot way as
+ * auxiliary input mode, and return to the previous IME once it is finished (e.g. voice input).
+ * @param overridesImplicitlyEnabledSubtype true when this subtype should be enabled by default
+ * if no other subtypes in the IME are enabled explicitly. Note that a subtype with this
+ * parameter being true will not be shown in the list of subtypes in each IME's subtype enabler.
+ * Having an "automatic" subtype is an example use of this flag.
+ * @param id The unique ID for the subtype. The input method framework keeps track of enabled
+ * subtypes by ID. When the IME package gets upgraded, enabled IDs will stay enabled even if
+ * other attributes are different. If the ID is unspecified or 0,
+ * Arrays.hashCode(new Object[] {locale, mode, extraValue,
+ * isAuxiliary, overridesImplicitlyEnabledSubtype}) will be used instead.
+ */
+ public InputMethodSubtype(int nameId, int iconId, String locale, String mode, String extraValue,
+ boolean isAuxiliary, boolean overridesImplicitlyEnabledSubtype, int id) {
mSubtypeNameResId = nameId;
mSubtypeIconResId = iconId;
mSubtypeLocale = locale != null ? locale : "";
@@ -119,8 +155,11 @@
mSubtypeExtraValue = extraValue != null ? extraValue : "";
mIsAuxiliary = isAuxiliary;
mOverridesImplicitlyEnabledSubtype = overridesImplicitlyEnabledSubtype;
- mSubtypeHashCode = hashCodeInternal(mSubtypeLocale, mSubtypeMode, mSubtypeExtraValue,
- mIsAuxiliary, mOverridesImplicitlyEnabledSubtype);
+ // If hashCode() of this subtype is 0 and you want to specify it as an id of this subtype,
+ // just specify 0 as this subtype's id. Then, this subtype's id is treated as 0.
+ mSubtypeHashCode = id != 0 ? id : hashCodeInternal(mSubtypeLocale, mSubtypeMode,
+ mSubtypeExtraValue, mIsAuxiliary, mOverridesImplicitlyEnabledSubtype);
+ mSubtypeId = id;
}
InputMethodSubtype(Parcel source) {
@@ -135,8 +174,8 @@
mSubtypeExtraValue = s != null ? s : "";
mIsAuxiliary = (source.readInt() == 1);
mOverridesImplicitlyEnabledSubtype = (source.readInt() == 1);
- mSubtypeHashCode = hashCodeInternal(mSubtypeLocale, mSubtypeMode, mSubtypeExtraValue,
- mIsAuxiliary, mOverridesImplicitlyEnabledSubtype);
+ mSubtypeHashCode = source.readInt();
+ mSubtypeId = source.readInt();
}
/**
@@ -288,6 +327,9 @@
public boolean equals(Object o) {
if (o instanceof InputMethodSubtype) {
InputMethodSubtype subtype = (InputMethodSubtype) o;
+ if (subtype.mSubtypeId != 0 || mSubtypeId != 0) {
+ return (subtype.hashCode() == hashCode());
+ }
return (subtype.hashCode() == hashCode())
&& (subtype.getNameResId() == getNameResId())
&& (subtype.getMode().equals(getMode()))
@@ -313,6 +355,8 @@
dest.writeString(mSubtypeExtraValue);
dest.writeInt(mIsAuxiliary ? 1 : 0);
dest.writeInt(mOverridesImplicitlyEnabledSubtype ? 1 : 0);
+ dest.writeInt(mSubtypeHashCode);
+ dest.writeInt(mSubtypeId);
}
public static final Parcelable.Creator<InputMethodSubtype> CREATOR
diff --git a/core/java/android/webkit/BrowserFrame.java b/core/java/android/webkit/BrowserFrame.java
index 1b23b18..6074a0c 100644
--- a/core/java/android/webkit/BrowserFrame.java
+++ b/core/java/android/webkit/BrowserFrame.java
@@ -247,7 +247,6 @@
mWebViewCore = w;
mSearchBox = new SearchBoxImpl(mWebViewCore, mCallbackProxy);
- mJavaScriptObjects.put(SearchBoxImpl.JS_INTERFACE_NAME, mSearchBox);
AssetManager am = context.getAssets();
nativeCreateFrame(w, am, proxy.getBackForwardList());
@@ -598,8 +597,6 @@
}
}
mRemovedJavaScriptObjects.clear();
-
- stringByEvaluatingJavaScriptFromString(SearchBoxImpl.JS_BRIDGE);
}
/**
diff --git a/core/java/android/webkit/WebViewClassic.java b/core/java/android/webkit/WebViewClassic.java
index 591b87f..9334036 100644
--- a/core/java/android/webkit/WebViewClassic.java
+++ b/core/java/android/webkit/WebViewClassic.java
@@ -1346,20 +1346,40 @@
private void onHandleUiTouchEvent(MotionEvent ev) {
final ScaleGestureDetector detector =
- mZoomManager.getMultiTouchGestureDetector();
+ mZoomManager.getScaleGestureDetector();
- float x = ev.getX();
- float y = ev.getY();
+ int action = ev.getActionMasked();
+ final boolean pointerUp = action == MotionEvent.ACTION_POINTER_UP;
+ final boolean configChanged =
+ action == MotionEvent.ACTION_POINTER_UP ||
+ action == MotionEvent.ACTION_POINTER_DOWN;
+ final int skipIndex = pointerUp ? ev.getActionIndex() : -1;
+
+ // Determine focal point
+ float sumX = 0, sumY = 0;
+ final int count = ev.getPointerCount();
+ for (int i = 0; i < count; i++) {
+ if (skipIndex == i) continue;
+ sumX += ev.getX(i);
+ sumY += ev.getY(i);
+ }
+ final int div = pointerUp ? count - 1 : count;
+ float x = sumX / div;
+ float y = sumY / div;
+
+ if (configChanged) {
+ mLastTouchX = Math.round(x);
+ mLastTouchY = Math.round(y);
+ mLastTouchTime = ev.getEventTime();
+ mWebView.cancelLongPress();
+ mPrivateHandler.removeMessages(SWITCH_TO_LONGPRESS);
+ }
if (detector != null) {
detector.onTouchEvent(ev);
if (detector.isInProgress()) {
mLastTouchTime = ev.getEventTime();
- x = detector.getFocusX();
- y = detector.getFocusY();
- mWebView.cancelLongPress();
- mPrivateHandler.removeMessages(SWITCH_TO_LONGPRESS);
if (!mZoomManager.supportsPanDuringZoom()) {
return;
}
@@ -1370,14 +1390,9 @@
}
}
- int action = ev.getActionMasked();
if (action == MotionEvent.ACTION_POINTER_DOWN) {
cancelTouch();
action = MotionEvent.ACTION_DOWN;
- } else if (action == MotionEvent.ACTION_POINTER_UP) {
- // set mLastTouchX/Y to the remaining points for multi-touch.
- mLastTouchX = Math.round(x);
- mLastTouchY = Math.round(y);
} else if (action == MotionEvent.ACTION_MOVE) {
// negative x or y indicate it is on the edge, skip it.
if (x < 0 || y < 0) {
@@ -4385,7 +4400,7 @@
// A multi-finger gesture can look like a long press; make sure we don't take
// long press actions if we're scaling.
- final ScaleGestureDetector detector = mZoomManager.getMultiTouchGestureDetector();
+ final ScaleGestureDetector detector = mZoomManager.getScaleGestureDetector();
if (detector != null && detector.isInProgress()) {
return false;
}
@@ -5823,7 +5838,7 @@
* and the middle point for multi-touch.
*/
private void handleTouchEventCommon(MotionEvent event, int action, int x, int y) {
- ScaleGestureDetector detector = mZoomManager.getMultiTouchGestureDetector();
+ ScaleGestureDetector detector = mZoomManager.getScaleGestureDetector();
long eventTime = event.getEventTime();
diff --git a/core/java/android/webkit/ZoomManager.java b/core/java/android/webkit/ZoomManager.java
index 8830119..80a6782 100644
--- a/core/java/android/webkit/ZoomManager.java
+++ b/core/java/android/webkit/ZoomManager.java
@@ -204,7 +204,7 @@
*/
private boolean mAllowPanAndScale;
- // use the framework's ScaleGestureDetector to handle multi-touch
+ // use the framework's ScaleGestureDetector to handle scaling gestures
private ScaleGestureDetector mScaleDetector;
private boolean mPinchToZoomAnimating = false;
@@ -768,7 +768,7 @@
return isZoomAnimating();
}
- public ScaleGestureDetector getMultiTouchGestureDetector() {
+ public ScaleGestureDetector getScaleGestureDetector() {
return mScaleDetector;
}
diff --git a/core/java/android/widget/SearchView.java b/core/java/android/widget/SearchView.java
index a30567f..9b62a51 100644
--- a/core/java/android/widget/SearchView.java
+++ b/core/java/android/widget/SearchView.java
@@ -1517,6 +1517,9 @@
// because the voice search activity will always need to insert "QUERY" into
// it anyway.
Bundle queryExtras = new Bundle();
+ if (mAppSearchData != null) {
+ queryExtras.putParcelable(SearchManager.APP_DATA, mAppSearchData);
+ }
// Now build the intent to launch the voice search. Add all necessary
// extras to launch the voice recognizer, and then all the necessary extras
diff --git a/core/java/android/widget/TextView.java b/core/java/android/widget/TextView.java
index f502de4..c9688c8 100644
--- a/core/java/android/widget/TextView.java
+++ b/core/java/android/widget/TextView.java
@@ -302,6 +302,7 @@
// The alignment to pass to Layout, or null if not resolved.
private Layout.Alignment mLayoutAlignment;
+ private int mResolvedTextAlignment;
private boolean mResolvedDrawables;
@@ -5639,9 +5640,8 @@
@Override
public void onResolvedLayoutDirectionReset() {
if (mLayoutAlignment != null) {
- int resolvedTextAlignment = getResolvedTextAlignment();
- if (resolvedTextAlignment == TEXT_ALIGNMENT_VIEW_START ||
- resolvedTextAlignment == TEXT_ALIGNMENT_VIEW_END) {
+ if (mResolvedTextAlignment == TEXT_ALIGNMENT_VIEW_START ||
+ mResolvedTextAlignment == TEXT_ALIGNMENT_VIEW_END) {
mLayoutAlignment = null;
}
}
@@ -5649,8 +5649,8 @@
private Layout.Alignment getLayoutAlignment() {
if (mLayoutAlignment == null) {
- int textAlign = getResolvedTextAlignment();
- switch (textAlign) {
+ mResolvedTextAlignment = getResolvedTextAlignment();
+ switch (mResolvedTextAlignment) {
case TEXT_ALIGNMENT_GRAVITY:
switch (mGravity & Gravity.RELATIVE_HORIZONTAL_GRAVITY_MASK) {
case Gravity.START:
diff --git a/core/java/android/widget/Toast.java b/core/java/android/widget/Toast.java
index e8bf9d9..485bd37 100644
--- a/core/java/android/widget/Toast.java
+++ b/core/java/android/widget/Toast.java
@@ -379,7 +379,7 @@
// We can resolve the Gravity here by using the Locale for getting
// the layout direction
final Configuration config = mView.getContext().getResources().getConfiguration();
- final int gravity = Gravity.getAbsoluteGravity(mGravity, config.layoutDirection);
+ final int gravity = Gravity.getAbsoluteGravity(mGravity, config.getLayoutDirection());
mParams.gravity = gravity;
if ((gravity & Gravity.HORIZONTAL_GRAVITY_MASK) == Gravity.FILL_HORIZONTAL) {
mParams.horizontalWeight = 1.0f;
diff --git a/core/java/com/android/internal/app/LocalePicker.java b/core/java/com/android/internal/app/LocalePicker.java
index 7d1231e..f173327 100644
--- a/core/java/com/android/internal/app/LocalePicker.java
+++ b/core/java/com/android/internal/app/LocalePicker.java
@@ -243,10 +243,9 @@
IActivityManager am = ActivityManagerNative.getDefault();
Configuration config = am.getConfiguration();
- config.locale = locale;
-
- // indicate this isn't some passing default - the user wants this remembered
- config.userSetLocale = true;
+ // Will set userSetLocale to indicate this isn't some passing default - the user
+ // wants this remembered
+ config.setLocale(locale);
am.updateConfiguration(config);
// Trigger the dirty bit for the Settings Provider.
diff --git a/core/java/com/android/internal/widget/LockSettingsService.java b/core/java/com/android/internal/widget/LockSettingsService.java
index 350e006..4ecbd16 100644
--- a/core/java/com/android/internal/widget/LockSettingsService.java
+++ b/core/java/com/android/internal/widget/LockSettingsService.java
@@ -23,6 +23,7 @@
import android.database.sqlite.SQLiteDatabase;
import android.database.sqlite.SQLiteOpenHelper;
import android.os.Binder;
+import android.os.Environment;
import android.os.RemoteException;
import android.os.SystemProperties;
import android.os.UserHandle;
@@ -173,7 +174,8 @@
// Leave it in the same place for user 0
return dataSystemDirectory + LOCK_PATTERN_FILE;
} else {
- return dataSystemDirectory + "users/" + userId + "/" + LOCK_PATTERN_FILE;
+ return new File(Environment.getUserSystemDirectory(userId), LOCK_PATTERN_FILE)
+ .getAbsolutePath();
}
}
@@ -185,7 +187,8 @@
// Leave it in the same place for user 0
return dataSystemDirectory + LOCK_PASSWORD_FILE;
} else {
- return dataSystemDirectory + "users/" + userId + "/" + LOCK_PASSWORD_FILE;
+ return new File(Environment.getUserSystemDirectory(userId), LOCK_PASSWORD_FILE)
+ .getAbsolutePath();
}
}
diff --git a/core/jni/android_util_Binder.cpp b/core/jni/android_util_Binder.cpp
index 04dc49f..881d9a0 100644
--- a/core/jni/android_util_Binder.cpp
+++ b/core/jni/android_util_Binder.cpp
@@ -729,11 +729,6 @@
return IPCThreadState::self()->getCallingUid();
}
-static jint android_os_Binder_getOrigCallingUid(JNIEnv* env, jobject clazz)
-{
- return IPCThreadState::self()->getOrigCallingUid();
-}
-
static jlong android_os_Binder_clearCallingIdentity(JNIEnv* env, jobject clazz)
{
return IPCThreadState::self()->clearCallingIdentity();
@@ -805,7 +800,6 @@
/* name, signature, funcPtr */
{ "getCallingPid", "()I", (void*)android_os_Binder_getCallingPid },
{ "getCallingUid", "()I", (void*)android_os_Binder_getCallingUid },
- { "getOrigCallingUidNative", "()I", (void*)android_os_Binder_getOrigCallingUid },
{ "clearCallingIdentity", "()J", (void*)android_os_Binder_clearCallingIdentity },
{ "restoreCallingIdentity", "(J)V", (void*)android_os_Binder_restoreCallingIdentity },
{ "setThreadStrictModePolicy", "(I)V", (void*)android_os_Binder_setThreadStrictModePolicy },
diff --git a/core/jni/android_view_GLES20Canvas.cpp b/core/jni/android_view_GLES20Canvas.cpp
index 9fc73a4..3538fef 100644
--- a/core/jni/android_view_GLES20Canvas.cpp
+++ b/core/jni/android_view_GLES20Canvas.cpp
@@ -46,6 +46,7 @@
#include <OpenGLRenderer.h>
#include <SkiaShader.h>
#include <SkiaColorFilter.h>
+#include <Stencil.h>
#include <Rect.h>
#include <TextLayout.h>
@@ -150,7 +151,7 @@
}
static jint android_view_GLES20Canvas_getStencilSize(JNIEnv* env, jobject clazz) {
- return OpenGLRenderer::getStencilSize();
+ return Stencil::getStencilSize();
}
// ----------------------------------------------------------------------------
diff --git a/core/jni/android_view_Surface.cpp b/core/jni/android_view_Surface.cpp
index 4d5e680..4fbfab6 100644
--- a/core/jni/android_view_Surface.cpp
+++ b/core/jni/android_view_Surface.cpp
@@ -647,38 +647,24 @@
SurfaceComposerClient::setDisplayLayerStack(token, layerStack);
}
-static void nativeSetDisplayOrientation(JNIEnv* env, jclass clazz,
- jobject tokenObj, jint orientation) {
+static void nativeSetDisplayProjection(JNIEnv* env, jclass clazz,
+ jobject tokenObj, jint orientation, jobject rect1Obj, jobject rect2Obj) {
sp<IBinder> token(ibinderForJavaObject(env, tokenObj));
if (token == NULL) return;
- SurfaceComposerClient::setDisplayOrientation(token, orientation);
-}
+ Rect rect1;
+ rect1.left = env->GetIntField(rect1Obj, gRectClassInfo.left);
+ rect1.top = env->GetIntField(rect1Obj, gRectClassInfo.top);
+ rect1.right = env->GetIntField(rect1Obj, gRectClassInfo.right);
+ rect1.bottom = env->GetIntField(rect1Obj, gRectClassInfo.bottom);
-static void nativeSetDisplayViewport(JNIEnv* env, jclass clazz,
- jobject tokenObj, jobject rectObj) {
- sp<IBinder> token(ibinderForJavaObject(env, tokenObj));
- if (token == NULL) return;
+ Rect rect2;
+ rect2.left = env->GetIntField(rect2Obj, gRectClassInfo.left);
+ rect2.top = env->GetIntField(rect2Obj, gRectClassInfo.top);
+ rect2.right = env->GetIntField(rect2Obj, gRectClassInfo.right);
+ rect2.bottom = env->GetIntField(rect2Obj, gRectClassInfo.bottom);
- Rect rect;
- rect.left = env->GetIntField(rectObj, gRectClassInfo.left);
- rect.top = env->GetIntField(rectObj, gRectClassInfo.top);
- rect.right = env->GetIntField(rectObj, gRectClassInfo.right);
- rect.bottom = env->GetIntField(rectObj, gRectClassInfo.bottom);
- SurfaceComposerClient::setDisplayViewport(token, rect);
-}
-
-static void nativeSetDisplayFrame(JNIEnv* env, jclass clazz,
- jobject tokenObj, jobject rectObj) {
- sp<IBinder> token(ibinderForJavaObject(env, tokenObj));
- if (token == NULL) return;
-
- Rect rect;
- rect.left = env->GetIntField(rectObj, gRectClassInfo.left);
- rect.top = env->GetIntField(rectObj, gRectClassInfo.top);
- rect.right = env->GetIntField(rectObj, gRectClassInfo.right);
- rect.bottom = env->GetIntField(rectObj, gRectClassInfo.bottom);
- SurfaceComposerClient::setDisplayFrame(token, rect);
+ SurfaceComposerClient::setDisplayProjection(token, orientation, rect1, rect2);
}
static jboolean nativeGetDisplayInfo(JNIEnv* env, jclass clazz,
@@ -818,12 +804,8 @@
(void*)nativeSetDisplaySurface },
{"nativeSetDisplayLayerStack", "(Landroid/os/IBinder;I)V",
(void*)nativeSetDisplayLayerStack },
- {"nativeSetDisplayOrientation", "(Landroid/os/IBinder;I)V",
- (void*)nativeSetDisplayOrientation },
- {"nativeSetDisplayViewport", "(Landroid/os/IBinder;Landroid/graphics/Rect;)V",
- (void*)nativeSetDisplayViewport },
- {"nativeSetDisplayFrame", "(Landroid/os/IBinder;Landroid/graphics/Rect;)V",
- (void*)nativeSetDisplayFrame },
+ {"nativeSetDisplayProjection", "(Landroid/os/IBinder;ILandroid/graphics/Rect;Landroid/graphics/Rect;)V",
+ (void*)nativeSetDisplayProjection },
{"nativeGetDisplayInfo", "(Landroid/os/IBinder;Landroid/view/Surface$PhysicalDisplayInfo;)Z",
(void*)nativeGetDisplayInfo },
{"nativeCopyFrom", "(Landroid/view/Surface;)V",
diff --git a/core/res/res/anim/keyguard_security_fade_in.xml b/core/res/res/anim/keyguard_security_fade_in.xml
new file mode 100644
index 0000000..7d5516a
--- /dev/null
+++ b/core/res/res/anim/keyguard_security_fade_in.xml
@@ -0,0 +1,22 @@
+<?xml version="1.0" encoding="utf-8"?>
+<!-- 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.
+-->
+
+<alpha xmlns:android="http://schemas.android.com/apk/res/android"
+ android:interpolator="@interpolator/decelerate_quad"
+ android:fromAlpha="0.0" android:toAlpha="1.0"
+ android:duration="@integer/kg_security_fade_duration" />
+
+
diff --git a/core/res/res/anim/keyguard_security_fade_out.xml b/core/res/res/anim/keyguard_security_fade_out.xml
new file mode 100644
index 0000000..caf896e
--- /dev/null
+++ b/core/res/res/anim/keyguard_security_fade_out.xml
@@ -0,0 +1,20 @@
+<?xml version="1.0" encoding="utf-8"?>
+<!-- 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.
+-->
+<alpha xmlns:android="http://schemas.android.com/apk/res/android" android:interpolator="@interpolator/accelerate_quad"
+ android:fromAlpha="1.0"
+ android:toAlpha="0.0"
+ android:duration="@integer/kg_security_fade_duration"
+/>
diff --git a/core/res/res/layout-sw600dp/keyguard_screen_password_landscape.xml b/core/res/res/layout-sw600dp/keyguard_screen_password_landscape.xml
index 4ceb907..4f94f96 100644
--- a/core/res/res/layout-sw600dp/keyguard_screen_password_landscape.xml
+++ b/core/res/res/layout-sw600dp/keyguard_screen_password_landscape.xml
@@ -154,7 +154,7 @@
<!-- Area to overlay FaceLock -->
<RelativeLayout
- android:id="@+id/faceLockAreaView"
+ android:id="@+id/face_unlock_area_view"
android:visibility="invisible"
android:layout_width="530dip"
android:layout_height="530dip"
diff --git a/core/res/res/layout-sw600dp/keyguard_screen_password_portrait.xml b/core/res/res/layout-sw600dp/keyguard_screen_password_portrait.xml
index da627b5..9a649fbb 100644
--- a/core/res/res/layout-sw600dp/keyguard_screen_password_portrait.xml
+++ b/core/res/res/layout-sw600dp/keyguard_screen_password_portrait.xml
@@ -157,7 +157,7 @@
<!-- Area to overlay FaceLock -->
<RelativeLayout
- android:id="@+id/faceLockAreaView"
+ android:id="@+id/face_unlock_area_view"
android:visibility="invisible"
android:layout_width="440dip"
android:layout_height="440dip"
diff --git a/core/res/res/layout-sw600dp/keyguard_screen_status_land.xml b/core/res/res/layout-sw600dp/keyguard_screen_status_land.xml
index df999f0..c6ddd1b 100644
--- a/core/res/res/layout-sw600dp/keyguard_screen_status_land.xml
+++ b/core/res/res/layout-sw600dp/keyguard_screen_status_land.xml
@@ -109,7 +109,7 @@
/>
<TextView
- android:id="@+id/propertyOf"
+ android:id="@+id/owner_info"
android:lineSpacingExtra="8dip"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
diff --git a/core/res/res/layout-sw600dp/keyguard_screen_status_port.xml b/core/res/res/layout-sw600dp/keyguard_screen_status_port.xml
index 565785b..765dc95 100644
--- a/core/res/res/layout-sw600dp/keyguard_screen_status_port.xml
+++ b/core/res/res/layout-sw600dp/keyguard_screen_status_port.xml
@@ -109,7 +109,7 @@
/>
<TextView
- android:id="@+id/propertyOf"
+ android:id="@+id/owner_info"
android:lineSpacingExtra="8dip"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
diff --git a/core/res/res/layout-sw600dp/keyguard_screen_unlock_landscape.xml b/core/res/res/layout-sw600dp/keyguard_screen_unlock_landscape.xml
index 51b946a..a71ef30 100644
--- a/core/res/res/layout-sw600dp/keyguard_screen_unlock_landscape.xml
+++ b/core/res/res/layout-sw600dp/keyguard_screen_unlock_landscape.xml
@@ -126,7 +126,7 @@
<!-- Area to overlay FaceLock -->
<RelativeLayout
- android:id="@+id/faceLockAreaView"
+ android:id="@+id/face_unlock_area_view"
android:visibility="invisible"
android:layout_width="530dip"
android:layout_height="530dip"
diff --git a/core/res/res/layout-sw600dp/keyguard_screen_unlock_portrait.xml b/core/res/res/layout-sw600dp/keyguard_screen_unlock_portrait.xml
index 1d6b6a9..0c4a2c7 100644
--- a/core/res/res/layout-sw600dp/keyguard_screen_unlock_portrait.xml
+++ b/core/res/res/layout-sw600dp/keyguard_screen_unlock_portrait.xml
@@ -119,7 +119,7 @@
<!-- Area to overlay FaceLock -->
<RelativeLayout
- android:id="@+id/faceLockAreaView"
+ android:id="@+id/face_unlock_area_view"
android:visibility="invisible"
android:layout_width="440dip"
android:layout_height="440dip"
diff --git a/core/res/res/layout/keyguard_face_unlock_view.xml b/core/res/res/layout/keyguard_face_unlock_view.xml
index 572c013..60f0492 100644
--- a/core/res/res/layout/keyguard_face_unlock_view.xml
+++ b/core/res/res/layout/keyguard_face_unlock_view.xml
@@ -17,15 +17,45 @@
*/
-->
-<!-- This is the screen that shows the 9 circle unlock widget and instructs
- the user how to unlock their device, or make an emergency call. This
- is the portrait layout. -->
+<!-- This is the screen that allows the user to unlock by showing their face. -->
<com.android.internal.policy.impl.keyguard.KeyguardFaceUnlockView
xmlns:android="http://schemas.android.com/apk/res/android"
android:id="@+id/keyguard_face_unlock_view"
+ android:orientation="vertical"
android:layout_width="match_parent"
android:layout_height="match_parent">
- <!-- TODO -->
+ <include layout="@layout/keyguard_navigation"/>
+
+ <Space
+ android:layout_width="match_parent"
+ android:layout_height="0dip"
+ android:layout_weight="1" />
+
+ <RelativeLayout
+ android:id="@+id/face_unlock_area_view"
+ android:layout_width="match_parent"
+ android:layout_height="@dimen/face_unlock_height"
+ android:background="@drawable/intro_bg"
+ android:gravity="center">
+
+ <View
+ android:id="@+id/spotlightMask"
+ android:layout_width="match_parent"
+ android:layout_height="match_parent"
+ android:background="@color/facelock_spotlight_mask"
+ />
+
+ <ImageView
+ android:id="@+id/cancel_button"
+ android:layout_width="wrap_content"
+ android:layout_height="wrap_content"
+ android:padding="5dip"
+ android:layout_alignParentTop="true"
+ android:layout_alignParentEnd="true"
+ android:src="@drawable/ic_facial_backup"
+ />
+
+ </RelativeLayout>
</com.android.internal.policy.impl.keyguard.KeyguardFaceUnlockView>
diff --git a/core/res/res/layout/keyguard_navigation.xml b/core/res/res/layout/keyguard_navigation.xml
index f9e3ef8..a033101 100644
--- a/core/res/res/layout/keyguard_navigation.xml
+++ b/core/res/res/layout/keyguard_navigation.xml
@@ -31,11 +31,11 @@
android:padding="10dip"
android:clickable="true">
- <TextView
- android:layout_width="20dip"
- android:layout_height="wrap_content"
- android:textSize="28dp"
- android:text="@string/kg_temp_back_string" />
+ <ImageView
+ android:src="?android:attr/homeAsUpIndicator"
+ android:layout_gravity="center_vertical|start"
+ android:layout_width="wrap_content"
+ android:layout_height="wrap_content"/>
<!-- message area for security screen -->
<TextView
@@ -43,6 +43,7 @@
android:layout_width="0dip"
android:layout_height="wrap_content"
android:layout_weight="1"
+ android:layout_gravity="end"
android:singleLine="true"
android:ellipsize="marquee"
android:layout_marginEnd="6dip"
diff --git a/core/res/res/layout/keyguard_screen_password_landscape.xml b/core/res/res/layout/keyguard_screen_password_landscape.xml
index 71fb363..e0a3ce3 100644
--- a/core/res/res/layout/keyguard_screen_password_landscape.xml
+++ b/core/res/res/layout/keyguard_screen_password_landscape.xml
@@ -209,7 +209,7 @@
<!-- Area to overlay FaceLock -->
<RelativeLayout
- android:id="@+id/faceLockAreaView"
+ android:id="@+id/face_unlock_area_view"
android:visibility="invisible"
android:layout_row="0"
android:layout_column="2"
diff --git a/core/res/res/layout/keyguard_screen_password_portrait.xml b/core/res/res/layout/keyguard_screen_password_portrait.xml
index 486c7fe..0212f73 100644
--- a/core/res/res/layout/keyguard_screen_password_portrait.xml
+++ b/core/res/res/layout/keyguard_screen_password_portrait.xml
@@ -200,7 +200,7 @@
<!-- Area to overlay FaceLock -->
<RelativeLayout
- android:id="@+id/faceLockAreaView"
+ android:id="@+id/face_unlock_area_view"
android:visibility="invisible"
android:layout_row="3"
android:layout_column="0"
diff --git a/core/res/res/layout/keyguard_screen_unlock_landscape.xml b/core/res/res/layout/keyguard_screen_unlock_landscape.xml
index edd74e2..27f6629 100644
--- a/core/res/res/layout/keyguard_screen_unlock_landscape.xml
+++ b/core/res/res/layout/keyguard_screen_unlock_landscape.xml
@@ -162,7 +162,7 @@
<!-- Area to overlay FaceLock -->
<RelativeLayout
- android:id="@+id/faceLockAreaView"
+ android:id="@+id/face_unlock_area_view"
android:visibility="invisible"
android:layout_row="0"
android:layout_column="1"
diff --git a/core/res/res/layout/keyguard_screen_unlock_portrait.xml b/core/res/res/layout/keyguard_screen_unlock_portrait.xml
index f20cbcd..de94451 100644
--- a/core/res/res/layout/keyguard_screen_unlock_portrait.xml
+++ b/core/res/res/layout/keyguard_screen_unlock_portrait.xml
@@ -171,7 +171,7 @@
<!-- Area to overlay FaceLock -->
<RelativeLayout
- android:id="@+id/faceLockAreaView"
+ android:id="@+id/face_unlock_area_view"
android:visibility="invisible"
android:layout_row="4"
android:layout_column="0"
diff --git a/core/res/res/layout/keyguard_status_view.xml b/core/res/res/layout/keyguard_status_view.xml
index f8d05b7..37e6779 100644
--- a/core/res/res/layout/keyguard_status_view.xml
+++ b/core/res/res/layout/keyguard_status_view.xml
@@ -94,7 +94,7 @@
</LinearLayout>
<TextView
- android:id="@*android:id/status1"
+ android:id="@+id/status1"
android:layout_gravity="end"
android:layout_marginEnd="@*android:dimen/keyguard_lockscreen_status_line_font_right_margin"
android:singleLine="true"
@@ -105,7 +105,18 @@
/>
<TextView
- android:id="@*android:id/carrier"
+ android:id="@+id/owner_info"
+ android:layout_gravity="end"
+ android:layout_marginEnd="@*android:dimen/keyguard_lockscreen_status_line_font_right_margin"
+ android:singleLine="true"
+ android:ellipsize="marquee"
+ android:textAppearance="?android:attr/textAppearanceMedium"
+ android:textSize="@*android:dimen/keyguard_lockscreen_status_line_font_size"
+ android:drawablePadding="4dip"
+ />
+
+ <TextView
+ android:id="@+id/carrier"
android:layout_gravity="end"
android:layout_marginEnd="@*android:dimen/keyguard_lockscreen_status_line_font_right_margin"
android:singleLine="true"
diff --git a/core/res/res/layout/overlay_display_window.xml b/core/res/res/layout/overlay_display_window.xml
index 25c792a..36b4a0d 100644
--- a/core/res/res/layout/overlay_display_window.xml
+++ b/core/res/res/layout/overlay_display_window.xml
@@ -16,7 +16,8 @@
<FrameLayout xmlns:android="http://schemas.android.com/apk/res/android"
android:layout_width="match_parent"
- android:layout_height="match_parent">
+ android:layout_height="match_parent"
+ android:background="#000000">
<TextureView android:id="@+id/overlay_display_window_texture"
android:layout_width="0px"
android:layout_height="0px" />
diff --git a/core/res/res/values-af/strings.xml b/core/res/res/values-af/strings.xml
index 4f67789..2eaf87a 100644
--- a/core/res/res/values-af/strings.xml
+++ b/core/res/res/values-af/strings.xml
@@ -547,6 +547,10 @@
<string name="permdesc_sdcardWrite" product="default" msgid="4337417790936632090">"Laat die program toe om na die SD-kaart te skryf."</string>
<string name="permlab_mediaStorageWrite" product="default" msgid="6859839199706879015">"verander/vee uit interne mediabergingsinhoud"</string>
<string name="permdesc_mediaStorageWrite" product="default" msgid="8189160597698529185">"Laat die program toe om die inhoud van die interne mediaberging te verander."</string>
+ <!-- no translation found for permlab_sdcardAccessAll (8150613823900460576) -->
+ <skip />
+ <!-- no translation found for permdesc_sdcardAccessAll (3215208357415891320) -->
+ <skip />
<string name="permlab_cache_filesystem" msgid="5656487264819669824">"lees die kaslêerstelsel"</string>
<string name="permdesc_cache_filesystem" msgid="5578967642265550955">"Laat die program toe om die kaslêerstelsel te lees en skryf."</string>
<string name="permlab_use_sip" msgid="5986952362795870502">"maak/ontvang internetoproepe"</string>
@@ -1167,14 +1171,10 @@
<string name="vpn_title_long" msgid="6400714798049252294">"VPN is geaktiveer deur <xliff:g id="APP">%s</xliff:g>"</string>
<string name="vpn_text" msgid="3011306607126450322">"Raak om die netwerk te bestuur."</string>
<string name="vpn_text_long" msgid="6407351006249174473">"Gekoppel aan <xliff:g id="SESSION">%s</xliff:g>. Raak om die netwerk te bestuur."</string>
- <!-- no translation found for vpn_lockdown_connecting (6443438964440960745) -->
- <skip />
- <!-- no translation found for vpn_lockdown_connected (8202679674819213931) -->
- <skip />
- <!-- no translation found for vpn_lockdown_error (6009249814034708175) -->
- <skip />
- <!-- no translation found for vpn_lockdown_reset (5365010427963548932) -->
- <skip />
+ <string name="vpn_lockdown_connecting" msgid="6443438964440960745">"Altydaan-VPN koppel tans..."</string>
+ <string name="vpn_lockdown_connected" msgid="8202679674819213931">"Altydaan-VPN gekoppel"</string>
+ <string name="vpn_lockdown_error" msgid="6009249814034708175">"Altydaan-VPN se fout"</string>
+ <string name="vpn_lockdown_reset" msgid="5365010427963548932">"Raak om verbinding terug te stel"</string>
<string name="upload_file" msgid="2897957172366730416">"Kies lêer"</string>
<string name="no_file_chosen" msgid="6363648562170759465">"Geen lêer gekies nie"</string>
<string name="reset" msgid="2448168080964209908">"Stel terug"</string>
diff --git a/core/res/res/values-am/strings.xml b/core/res/res/values-am/strings.xml
index b85ad6a..bbe666a 100644
--- a/core/res/res/values-am/strings.xml
+++ b/core/res/res/values-am/strings.xml
@@ -547,6 +547,10 @@
<string name="permdesc_sdcardWrite" product="default" msgid="4337417790936632090">"መተግበሪያውን ወደ SD ካርድ ለመፃፍ ይፈቅዳል።"</string>
<string name="permlab_mediaStorageWrite" product="default" msgid="6859839199706879015">"የውስጥ ማህደረ መረጃ ማከማቻ ይዘቶችን ቀይር/ሰርዝ"</string>
<string name="permdesc_mediaStorageWrite" product="default" msgid="8189160597698529185">"የውስጥ ማህደረ መረጃ ማከማቻ ይዘትን ለመቀየር ለመተግበሪያው ይፈቅዳሉ፡፡"</string>
+ <!-- no translation found for permlab_sdcardAccessAll (8150613823900460576) -->
+ <skip />
+ <!-- no translation found for permdesc_sdcardAccessAll (3215208357415891320) -->
+ <skip />
<string name="permlab_cache_filesystem" msgid="5656487264819669824">"የመሸጎጫ ስርዓተ ፋይል ድረስ"</string>
<string name="permdesc_cache_filesystem" msgid="5578967642265550955">"መሸጎጫ ስርዓተ ፋይል ለማንበብ እና ለመፃፍ ለመተግበሪያው ይፈቅዳሉ።"</string>
<string name="permlab_use_sip" msgid="5986952362795870502">"የበይነ መረብ ጥሪዎች አድርግ/ተቀበል"</string>
diff --git a/core/res/res/values-ar/strings.xml b/core/res/res/values-ar/strings.xml
index bf00a81..9b44dcd 100644
--- a/core/res/res/values-ar/strings.xml
+++ b/core/res/res/values-ar/strings.xml
@@ -547,6 +547,10 @@
<string name="permdesc_sdcardWrite" product="default" msgid="4337417790936632090">"للسماح للتطبيق بالكتابة إلى بطاقة SD."</string>
<string name="permlab_mediaStorageWrite" product="default" msgid="6859839199706879015">"تعديل/حذف محتويات وحدة تخزين الوسائط الداخلية"</string>
<string name="permdesc_mediaStorageWrite" product="default" msgid="8189160597698529185">"للسماح للتطبيق بتعديل محتويات وحدة تخزين الوسائط الداخلية."</string>
+ <!-- no translation found for permlab_sdcardAccessAll (8150613823900460576) -->
+ <skip />
+ <!-- no translation found for permdesc_sdcardAccessAll (3215208357415891320) -->
+ <skip />
<string name="permlab_cache_filesystem" msgid="5656487264819669824">"الدخول إلى نظام ملفات ذاكرة التخزين المؤقت"</string>
<string name="permdesc_cache_filesystem" msgid="5578967642265550955">"للسماح للتطبيق بقراءة نظام ملفات ذاكرة التخزين المؤقت والكتابة به."</string>
<string name="permlab_use_sip" msgid="5986952362795870502">"إجراء/تلقي مكالمات عبر الإنترنت"</string>
@@ -1167,14 +1171,10 @@
<string name="vpn_title_long" msgid="6400714798049252294">"تم تنشيط VPN بواسطة <xliff:g id="APP">%s</xliff:g>"</string>
<string name="vpn_text" msgid="3011306607126450322">"المس لإدارة الشبكة."</string>
<string name="vpn_text_long" msgid="6407351006249174473">"تم الاتصال بـ <xliff:g id="SESSION">%s</xliff:g>. المس لإدارة الشبكة."</string>
- <!-- no translation found for vpn_lockdown_connecting (6443438964440960745) -->
- <skip />
- <!-- no translation found for vpn_lockdown_connected (8202679674819213931) -->
- <skip />
- <!-- no translation found for vpn_lockdown_error (6009249814034708175) -->
- <skip />
- <!-- no translation found for vpn_lockdown_reset (5365010427963548932) -->
- <skip />
+ <string name="vpn_lockdown_connecting" msgid="6443438964440960745">"جارٍ الاتصال بشبكة ظاهرية خاصة (VPN) دائمة التشغيل..."</string>
+ <string name="vpn_lockdown_connected" msgid="8202679674819213931">"تم الاتصال بشبكة ظاهرية خاصة (VPN) دائمة التشغيل"</string>
+ <string name="vpn_lockdown_error" msgid="6009249814034708175">"خطأ بشبكة ظاهرية خاصة (VPN) دائمة التشغيل"</string>
+ <string name="vpn_lockdown_reset" msgid="5365010427963548932">"المس لإعادة تعيين الاتصال"</string>
<string name="upload_file" msgid="2897957172366730416">"اختيار ملف"</string>
<string name="no_file_chosen" msgid="6363648562170759465">"لم يتم اختيار أي ملف"</string>
<string name="reset" msgid="2448168080964209908">"إعادة تعيين"</string>
diff --git a/core/res/res/values-be/strings.xml b/core/res/res/values-be/strings.xml
index 04053f6..7d8c3f3 100644
--- a/core/res/res/values-be/strings.xml
+++ b/core/res/res/values-be/strings.xml
@@ -547,6 +547,10 @@
<string name="permdesc_sdcardWrite" product="default" msgid="4337417790936632090">"Дазваляе прыкладанням запісваць на SD-карту."</string>
<string name="permlab_mediaStorageWrite" product="default" msgid="6859839199706879015">"змяніць/выдаліць унутраныя носьбіты змесціва"</string>
<string name="permdesc_mediaStorageWrite" product="default" msgid="8189160597698529185">"Дазваляе прыкладанням змяняць змесціва ўнутранай памяці."</string>
+ <!-- no translation found for permlab_sdcardAccessAll (8150613823900460576) -->
+ <skip />
+ <!-- no translation found for permdesc_sdcardAccessAll (3215208357415891320) -->
+ <skip />
<string name="permlab_cache_filesystem" msgid="5656487264819669824">"доступ да файлавай сістэмы кэша"</string>
<string name="permdesc_cache_filesystem" msgid="5578967642265550955">"Дазваляе прыкладанню счытваць і выконваць запіс у файлавую сістэму кэш-памяці."</string>
<string name="permlab_use_sip" msgid="5986952362795870502">"рабіць/прымаць iнтэрнэт-выклікі"</string>
diff --git a/core/res/res/values-bg/strings.xml b/core/res/res/values-bg/strings.xml
index 0813df0..85a2c9e 100644
--- a/core/res/res/values-bg/strings.xml
+++ b/core/res/res/values-bg/strings.xml
@@ -547,6 +547,10 @@
<string name="permdesc_sdcardWrite" product="default" msgid="4337417790936632090">"Разрешава на приложението да записва върху SD картата."</string>
<string name="permlab_mediaStorageWrite" product="default" msgid="6859839199706879015">"пром./изтр. на съдърж. на вътр. мултим. хранил."</string>
<string name="permdesc_mediaStorageWrite" product="default" msgid="8189160597698529185">"Разрешава на приложението да променя съдържанието на вътрешното мултимедийно хранилище."</string>
+ <!-- no translation found for permlab_sdcardAccessAll (8150613823900460576) -->
+ <skip />
+ <!-- no translation found for permdesc_sdcardAccessAll (3215208357415891320) -->
+ <skip />
<string name="permlab_cache_filesystem" msgid="5656487264819669824">"достъп до файловата система на кеша"</string>
<string name="permdesc_cache_filesystem" msgid="5578967642265550955">"Разрешава на приложението да чете и записва във файловата система на кеша."</string>
<string name="permlab_use_sip" msgid="5986952362795870502">"извършване/получаване на интернет обаждания"</string>
@@ -1167,14 +1171,10 @@
<string name="vpn_title_long" msgid="6400714798049252294">"VPN е активирана от <xliff:g id="APP">%s</xliff:g>"</string>
<string name="vpn_text" msgid="3011306607126450322">"Докоснете за управление на мрежата."</string>
<string name="vpn_text_long" msgid="6407351006249174473">"Свързана със: <xliff:g id="SESSION">%s</xliff:g>. Докоснете, за да управлявате мрежата."</string>
- <!-- no translation found for vpn_lockdown_connecting (6443438964440960745) -->
- <skip />
- <!-- no translation found for vpn_lockdown_connected (8202679674819213931) -->
- <skip />
- <!-- no translation found for vpn_lockdown_error (6009249814034708175) -->
- <skip />
- <!-- no translation found for vpn_lockdown_reset (5365010427963548932) -->
- <skip />
+ <string name="vpn_lockdown_connecting" msgid="6443438964440960745">"Установява се връзка с винаги включената виртуална частна мрежа (VPN)…"</string>
+ <string name="vpn_lockdown_connected" msgid="8202679674819213931">"Установена е връзка с винаги включената виртуална частна мрежа (VPN)"</string>
+ <string name="vpn_lockdown_error" msgid="6009249814034708175">"Грешка във винаги включената виртуална частна мрежа (VPN)"</string>
+ <string name="vpn_lockdown_reset" msgid="5365010427963548932">"Докоснете, за да възстановите връзката"</string>
<string name="upload_file" msgid="2897957172366730416">"Избор на файл"</string>
<string name="no_file_chosen" msgid="6363648562170759465">"Няма избран файл"</string>
<string name="reset" msgid="2448168080964209908">"Повторно задаване"</string>
diff --git a/core/res/res/values-ca/strings.xml b/core/res/res/values-ca/strings.xml
index 5924751..86c9f36 100644
--- a/core/res/res/values-ca/strings.xml
+++ b/core/res/res/values-ca/strings.xml
@@ -547,6 +547,10 @@
<string name="permdesc_sdcardWrite" product="default" msgid="4337417790936632090">"Permet a l\'aplicació escriure a la targeta SD."</string>
<string name="permlab_mediaStorageWrite" product="default" msgid="6859839199706879015">"Canvia/esborra emmagatz. intern"</string>
<string name="permdesc_mediaStorageWrite" product="default" msgid="8189160597698529185">"Permet que l\'aplicació modifiqui el contingut de l\'emmagatzematge multimèdia intern."</string>
+ <!-- no translation found for permlab_sdcardAccessAll (8150613823900460576) -->
+ <skip />
+ <!-- no translation found for permdesc_sdcardAccessAll (3215208357415891320) -->
+ <skip />
<string name="permlab_cache_filesystem" msgid="5656487264819669824">"accedir al sistema de fitxers de la memòria cau"</string>
<string name="permdesc_cache_filesystem" msgid="5578967642265550955">"Permet que l\'aplicació llegeixi el sistema de fitxers de la memòria cau i que hi escrigui."</string>
<string name="permlab_use_sip" msgid="5986952362795870502">"fer/rebre trucades per Internet"</string>
diff --git a/core/res/res/values-cs/strings.xml b/core/res/res/values-cs/strings.xml
index 9395aec..5264a43 100644
--- a/core/res/res/values-cs/strings.xml
+++ b/core/res/res/values-cs/strings.xml
@@ -547,6 +547,10 @@
<string name="permdesc_sdcardWrite" product="default" msgid="4337417790936632090">"Umožňuje aplikaci zapisovat na kartu SD."</string>
<string name="permlab_mediaStorageWrite" product="default" msgid="6859839199706879015">"Upravit/smazat interní úlož."</string>
<string name="permdesc_mediaStorageWrite" product="default" msgid="8189160597698529185">"Umožňuje aplikaci upravovat obsah interního úložiště médií."</string>
+ <!-- no translation found for permlab_sdcardAccessAll (8150613823900460576) -->
+ <skip />
+ <!-- no translation found for permdesc_sdcardAccessAll (3215208357415891320) -->
+ <skip />
<string name="permlab_cache_filesystem" msgid="5656487264819669824">"přistupovat do souborového systému mezipaměti"</string>
<string name="permdesc_cache_filesystem" msgid="5578967642265550955">"Umožňuje aplikaci číst a zapisovat do souborového systému mezipaměti."</string>
<string name="permlab_use_sip" msgid="5986952362795870502">"uskutečňovat a přijímat internetové hovory"</string>
@@ -1167,14 +1171,10 @@
<string name="vpn_title_long" msgid="6400714798049252294">"Aplikace <xliff:g id="APP">%s</xliff:g> aktivovala síť VPN"</string>
<string name="vpn_text" msgid="3011306607126450322">"Dotykem zobrazíte správu sítě."</string>
<string name="vpn_text_long" msgid="6407351006249174473">"Připojeno k relaci <xliff:g id="SESSION">%s</xliff:g>. Dotykem můžete síť spravovat."</string>
- <!-- no translation found for vpn_lockdown_connecting (6443438964440960745) -->
- <skip />
- <!-- no translation found for vpn_lockdown_connected (8202679674819213931) -->
- <skip />
- <!-- no translation found for vpn_lockdown_error (6009249814034708175) -->
- <skip />
- <!-- no translation found for vpn_lockdown_reset (5365010427963548932) -->
- <skip />
+ <string name="vpn_lockdown_connecting" msgid="6443438964440960745">"Připojování k trvalé síti VPN…"</string>
+ <string name="vpn_lockdown_connected" msgid="8202679674819213931">"Je připojena trvalá síť VPN"</string>
+ <string name="vpn_lockdown_error" msgid="6009249814034708175">"Chyba trvalé sítě VPN"</string>
+ <string name="vpn_lockdown_reset" msgid="5365010427963548932">"Klepnutím resetujete připojení"</string>
<string name="upload_file" msgid="2897957172366730416">"Zvolit soubor"</string>
<string name="no_file_chosen" msgid="6363648562170759465">"Není vybrán žádný soubor"</string>
<string name="reset" msgid="2448168080964209908">"Resetovat"</string>
diff --git a/core/res/res/values-da/strings.xml b/core/res/res/values-da/strings.xml
index 050eca8..0aeef97 100644
--- a/core/res/res/values-da/strings.xml
+++ b/core/res/res/values-da/strings.xml
@@ -547,6 +547,10 @@
<string name="permdesc_sdcardWrite" product="default" msgid="4337417790936632090">"Tillader, at appen kan skrive til SD-kortet."</string>
<string name="permlab_mediaStorageWrite" product="default" msgid="6859839199706879015">"Rediger/slet internt medielagringsindhold"</string>
<string name="permdesc_mediaStorageWrite" product="default" msgid="8189160597698529185">"Tillader, appen kan ændre indholdet af det interne medielager."</string>
+ <!-- no translation found for permlab_sdcardAccessAll (8150613823900460576) -->
+ <skip />
+ <!-- no translation found for permdesc_sdcardAccessAll (3215208357415891320) -->
+ <skip />
<string name="permlab_cache_filesystem" msgid="5656487264819669824">"få adgang til cache-filsystemet"</string>
<string name="permdesc_cache_filesystem" msgid="5578967642265550955">"Tillader, at appen kan læse og skrive i cachefilsystemet."</string>
<string name="permlab_use_sip" msgid="5986952362795870502">"foretage/modtage internetopkald"</string>
diff --git a/core/res/res/values-de/strings.xml b/core/res/res/values-de/strings.xml
index 4fdc411..8b1e8bd 100644
--- a/core/res/res/values-de/strings.xml
+++ b/core/res/res/values-de/strings.xml
@@ -547,6 +547,10 @@
<string name="permdesc_sdcardWrite" product="default" msgid="4337417790936632090">"Ermöglicht der App, auf die SD-Karte zu schreiben"</string>
<string name="permlab_mediaStorageWrite" product="default" msgid="6859839199706879015">"Intern. Mediensp. änd./löschen"</string>
<string name="permdesc_mediaStorageWrite" product="default" msgid="8189160597698529185">"Ermöglicht der App, den Inhalt des internen Medienspeichers zu ändern"</string>
+ <!-- no translation found for permlab_sdcardAccessAll (8150613823900460576) -->
+ <skip />
+ <!-- no translation found for permdesc_sdcardAccessAll (3215208357415891320) -->
+ <skip />
<string name="permlab_cache_filesystem" msgid="5656487264819669824">"Zugriff auf das Cache-Dateisystem"</string>
<string name="permdesc_cache_filesystem" msgid="5578967642265550955">"Ermöglicht der App Lese- und Schreibzugriff auf das Cache-Dateisystem"</string>
<string name="permlab_use_sip" msgid="5986952362795870502">"Internetanrufe tätigen/annehmen"</string>
diff --git a/core/res/res/values-el/strings.xml b/core/res/res/values-el/strings.xml
index 181dc3f..a299dab 100644
--- a/core/res/res/values-el/strings.xml
+++ b/core/res/res/values-el/strings.xml
@@ -547,6 +547,10 @@
<string name="permdesc_sdcardWrite" product="default" msgid="4337417790936632090">"Επιτρέπει στην εφαρμογή την εγγραφή στην κάρτα SD."</string>
<string name="permlab_mediaStorageWrite" product="default" msgid="6859839199706879015">"τροπ./διαγ. περ. απ. εσ. μνήμ."</string>
<string name="permdesc_mediaStorageWrite" product="default" msgid="8189160597698529185">"Επιτρέπει στην εφαρμογή να τροποποιήσει τα περιεχόμενα των εσωτερικών μέσων αποθήκευσης."</string>
+ <!-- no translation found for permlab_sdcardAccessAll (8150613823900460576) -->
+ <skip />
+ <!-- no translation found for permdesc_sdcardAccessAll (3215208357415891320) -->
+ <skip />
<string name="permlab_cache_filesystem" msgid="5656487264819669824">"πρόσβαση στο σύστημα αρχείων προσωρινής μνήμης"</string>
<string name="permdesc_cache_filesystem" msgid="5578967642265550955">"Επιτρέπει στην εφαρμογή την ανάγνωση και την εγγραφή του συστήματος αρχείων προσωρινής μνήμης."</string>
<string name="permlab_use_sip" msgid="5986952362795870502">"πραγματοποίηση/λήψη κλήσεων μέσω Διαδικτύου"</string>
diff --git a/core/res/res/values-en-rGB/strings.xml b/core/res/res/values-en-rGB/strings.xml
index 2dd96c7..2d96b9a 100644
--- a/core/res/res/values-en-rGB/strings.xml
+++ b/core/res/res/values-en-rGB/strings.xml
@@ -547,6 +547,10 @@
<string name="permdesc_sdcardWrite" product="default" msgid="4337417790936632090">"Allows the app to write to the SD card."</string>
<string name="permlab_mediaStorageWrite" product="default" msgid="6859839199706879015">"modify/delete internal media storage contents"</string>
<string name="permdesc_mediaStorageWrite" product="default" msgid="8189160597698529185">"Allows the app to modify the contents of the internal media storage."</string>
+ <!-- no translation found for permlab_sdcardAccessAll (8150613823900460576) -->
+ <skip />
+ <!-- no translation found for permdesc_sdcardAccessAll (3215208357415891320) -->
+ <skip />
<string name="permlab_cache_filesystem" msgid="5656487264819669824">"access the cache file system"</string>
<string name="permdesc_cache_filesystem" msgid="5578967642265550955">"Allows the app to read and write the cache file system."</string>
<string name="permlab_use_sip" msgid="5986952362795870502">"make/receive Internet calls"</string>
diff --git a/core/res/res/values-es-rUS/strings.xml b/core/res/res/values-es-rUS/strings.xml
index 3e9694c..bd41ffc 100644
--- a/core/res/res/values-es-rUS/strings.xml
+++ b/core/res/res/values-es-rUS/strings.xml
@@ -547,6 +547,10 @@
<string name="permdesc_sdcardWrite" product="default" msgid="4337417790936632090">"Admite que la aplicación escriba en la tarjeta SD."</string>
<string name="permlab_mediaStorageWrite" product="default" msgid="6859839199706879015">"modificar/eliminar los contenidos del almacenamientos de medios internos"</string>
<string name="permdesc_mediaStorageWrite" product="default" msgid="8189160597698529185">"Permite que la aplicación modifique el contenido del almacenamiento de medios interno."</string>
+ <!-- no translation found for permlab_sdcardAccessAll (8150613823900460576) -->
+ <skip />
+ <!-- no translation found for permdesc_sdcardAccessAll (3215208357415891320) -->
+ <skip />
<string name="permlab_cache_filesystem" msgid="5656487264819669824">"Acceder al sistema de archivos caché"</string>
<string name="permdesc_cache_filesystem" msgid="5578967642265550955">"Permite que la aplicación lea y escriba el sistema de archivos almacenado en caché."</string>
<string name="permlab_use_sip" msgid="5986952362795870502">"realizar o recibir llamadas por Internet"</string>
@@ -1167,14 +1171,10 @@
<string name="vpn_title_long" msgid="6400714798049252294">"VPN está activado por <xliff:g id="APP">%s</xliff:g>"</string>
<string name="vpn_text" msgid="3011306607126450322">"Toca para administrar la red."</string>
<string name="vpn_text_long" msgid="6407351006249174473">"Conectado a <xliff:g id="SESSION">%s</xliff:g>. Toca para administrar la red."</string>
- <!-- no translation found for vpn_lockdown_connecting (6443438964440960745) -->
- <skip />
- <!-- no translation found for vpn_lockdown_connected (8202679674819213931) -->
- <skip />
- <!-- no translation found for vpn_lockdown_error (6009249814034708175) -->
- <skip />
- <!-- no translation found for vpn_lockdown_reset (5365010427963548932) -->
- <skip />
+ <string name="vpn_lockdown_connecting" msgid="6443438964440960745">"Estableciendo conexión con VPN activada ininterrumpidamente..."</string>
+ <string name="vpn_lockdown_connected" msgid="8202679674819213931">"Se estableció conexión con la VPN activada ininterrumpidamente."</string>
+ <string name="vpn_lockdown_error" msgid="6009249814034708175">"Se produjo un error al establecer conexión con la VPN activada ininterrumpidamente."</string>
+ <string name="vpn_lockdown_reset" msgid="5365010427963548932">"Toca para restablecer la conexión."</string>
<string name="upload_file" msgid="2897957172366730416">"Elegir archivo"</string>
<string name="no_file_chosen" msgid="6363648562170759465">"No se seleccionó un archivo."</string>
<string name="reset" msgid="2448168080964209908">"Restablecer"</string>
diff --git a/core/res/res/values-es/strings.xml b/core/res/res/values-es/strings.xml
index 32f7e8d..c962de0 100644
--- a/core/res/res/values-es/strings.xml
+++ b/core/res/res/values-es/strings.xml
@@ -547,6 +547,10 @@
<string name="permdesc_sdcardWrite" product="default" msgid="4337417790936632090">"Permite que la aplicación escriba en la tarjeta SD."</string>
<string name="permlab_mediaStorageWrite" product="default" msgid="6859839199706879015">"modificar o eliminar el contenido del almacenamiento de medios interno"</string>
<string name="permdesc_mediaStorageWrite" product="default" msgid="8189160597698529185">"Permite que la aplicación modifique el contenido del almacenamiento multimedia interno."</string>
+ <!-- no translation found for permlab_sdcardAccessAll (8150613823900460576) -->
+ <skip />
+ <!-- no translation found for permdesc_sdcardAccessAll (3215208357415891320) -->
+ <skip />
<string name="permlab_cache_filesystem" msgid="5656487264819669824">"acceder al sistema de archivos almacenado en caché"</string>
<string name="permdesc_cache_filesystem" msgid="5578967642265550955">"Permite que la aplicación lea y escriba el sistema de archivos almacenado en caché."</string>
<string name="permlab_use_sip" msgid="5986952362795870502">"realizar/recibir llamadas por Internet"</string>
@@ -1167,14 +1171,10 @@
<string name="vpn_title_long" msgid="6400714798049252294">"VPN activada por <xliff:g id="APP">%s</xliff:g>"</string>
<string name="vpn_text" msgid="3011306607126450322">"Toca para administrar la red."</string>
<string name="vpn_text_long" msgid="6407351006249174473">"Conectado a <xliff:g id="SESSION">%s</xliff:g>. Toca para administrar la red."</string>
- <!-- no translation found for vpn_lockdown_connecting (6443438964440960745) -->
- <skip />
- <!-- no translation found for vpn_lockdown_connected (8202679674819213931) -->
- <skip />
- <!-- no translation found for vpn_lockdown_error (6009249814034708175) -->
- <skip />
- <!-- no translation found for vpn_lockdown_reset (5365010427963548932) -->
- <skip />
+ <string name="vpn_lockdown_connecting" msgid="6443438964440960745">"Conectando VPN siempre activada…"</string>
+ <string name="vpn_lockdown_connected" msgid="8202679674819213931">"VPN siempre activada conectada"</string>
+ <string name="vpn_lockdown_error" msgid="6009249814034708175">"Error de VPN siempre activada"</string>
+ <string name="vpn_lockdown_reset" msgid="5365010427963548932">"Toca para restablecer la conexión."</string>
<string name="upload_file" msgid="2897957172366730416">"Seleccionar archivo"</string>
<string name="no_file_chosen" msgid="6363648562170759465">"Archivo no seleccionado"</string>
<string name="reset" msgid="2448168080964209908">"Restablecer"</string>
diff --git a/core/res/res/values-et/strings.xml b/core/res/res/values-et/strings.xml
index 32f6ade..d8a0ec5 100644
--- a/core/res/res/values-et/strings.xml
+++ b/core/res/res/values-et/strings.xml
@@ -547,6 +547,10 @@
<string name="permdesc_sdcardWrite" product="default" msgid="4337417790936632090">"Võimaldab rakendusel kirjutada SD-kaardile."</string>
<string name="permlab_mediaStorageWrite" product="default" msgid="6859839199706879015">"sisemälu sisu muutm./kustut."</string>
<string name="permdesc_mediaStorageWrite" product="default" msgid="8189160597698529185">"Võimaldab rakendusel muuta sisemise andmekandja sisu."</string>
+ <!-- no translation found for permlab_sdcardAccessAll (8150613823900460576) -->
+ <skip />
+ <!-- no translation found for permdesc_sdcardAccessAll (3215208357415891320) -->
+ <skip />
<string name="permlab_cache_filesystem" msgid="5656487264819669824">"juurdepääs vahemälu failisüsteemile"</string>
<string name="permdesc_cache_filesystem" msgid="5578967642265550955">"Võimaldab rakendusel vahemälu failisüsteemi lugeda ja kirjutada."</string>
<string name="permlab_use_sip" msgid="5986952362795870502">"Interneti-kõnede tegemine/vastuvõtmine"</string>
@@ -1167,14 +1171,10 @@
<string name="vpn_title_long" msgid="6400714798049252294">"VPN-i aktiveeris <xliff:g id="APP">%s</xliff:g>"</string>
<string name="vpn_text" msgid="3011306607126450322">"Võrgu haldamiseks puudutage."</string>
<string name="vpn_text_long" msgid="6407351006249174473">"Ühendatud seansiga <xliff:g id="SESSION">%s</xliff:g>. Võrgu haldamiseks puudutage."</string>
- <!-- no translation found for vpn_lockdown_connecting (6443438964440960745) -->
- <skip />
- <!-- no translation found for vpn_lockdown_connected (8202679674819213931) -->
- <skip />
- <!-- no translation found for vpn_lockdown_error (6009249814034708175) -->
- <skip />
- <!-- no translation found for vpn_lockdown_reset (5365010427963548932) -->
- <skip />
+ <string name="vpn_lockdown_connecting" msgid="6443438964440960745">"Ühendamine alati sees VPN-iga …"</string>
+ <string name="vpn_lockdown_connected" msgid="8202679674819213931">"Ühendatud alati sees VPN-iga"</string>
+ <string name="vpn_lockdown_error" msgid="6009249814034708175">"Alati see VPN-i viga"</string>
+ <string name="vpn_lockdown_reset" msgid="5365010427963548932">"Ühenduse lähtestamiseks puudutage"</string>
<string name="upload_file" msgid="2897957172366730416">"Valige fail"</string>
<string name="no_file_chosen" msgid="6363648562170759465">"Ühtegi faili pole valitud"</string>
<string name="reset" msgid="2448168080964209908">"Lähtesta"</string>
diff --git a/core/res/res/values-fa/strings.xml b/core/res/res/values-fa/strings.xml
index a466354..fb9a1ff 100644
--- a/core/res/res/values-fa/strings.xml
+++ b/core/res/res/values-fa/strings.xml
@@ -85,7 +85,7 @@
<string name="serviceClassFAX" msgid="5566624998840486475">"نمابر"</string>
<string name="serviceClassSMS" msgid="2015460373701527489">"پیامک"</string>
<string name="serviceClassDataAsync" msgid="4523454783498551468">"غیر همگام"</string>
- <string name="serviceClassDataSync" msgid="7530000519646054776">"همگام سازی"</string>
+ <string name="serviceClassDataSync" msgid="7530000519646054776">"همگامسازی"</string>
<string name="serviceClassPacket" msgid="6991006557993423453">"بسته"</string>
<string name="serviceClassPAD" msgid="3235259085648271037">"PAD"</string>
<string name="roamingText0" msgid="7170335472198694945">"نشانگر رومینگ روشن"</string>
@@ -126,8 +126,8 @@
<string name="httpErrorFileNotFound" msgid="6203856612042655084">"فایل درخواستی پیدا نشد."</string>
<string name="httpErrorTooManyRequests" msgid="1235396927087188253">"درخواستهای زیادی در حال پردازش است. بعداً دوباره امتحان کنید."</string>
<string name="notification_title" msgid="8967710025036163822">"خطای ورود به سیستم برای <xliff:g id="ACCOUNT">%1$s</xliff:g>"</string>
- <string name="contentServiceSync" msgid="8353523060269335667">"همگام سازی"</string>
- <string name="contentServiceSyncNotificationTitle" msgid="397743349191901458">"همگام سازی"</string>
+ <string name="contentServiceSync" msgid="8353523060269335667">"همگامسازی"</string>
+ <string name="contentServiceSyncNotificationTitle" msgid="397743349191901458">"همگامسازی"</string>
<string name="contentServiceTooManyDeletesNotificationDesc" msgid="8100981435080696431">"تعداد موارد حذف شده <xliff:g id="CONTENT_TYPE">%s</xliff:g> بسیار زیاد است."</string>
<string name="low_memory" product="tablet" msgid="6494019234102154896">"حافظه رایانهٔ لوحی پر است! برخی از فایلها را حذف کنید تا فضا آزاد شود."</string>
<string name="low_memory" product="default" msgid="3475999286680000541">"حافظه تلفن پر است. بعضی از فایلها را حذف کنید تا فضا آزاد شود."</string>
@@ -464,8 +464,8 @@
<string name="permdesc_devicePower" product="tablet" msgid="6689862878984631831">"به برنامه اجازه میدهد رایانهٔ لوحی را روشن یا خاموش کند."</string>
<string name="permdesc_devicePower" product="default" msgid="6037057348463131032">"به برنامه اجازه میدهد گوشی را روشن یا خاموش کند."</string>
<string name="permlab_factoryTest" msgid="3715225492696416187">"اجرا در حالت تست کارخانه"</string>
- <string name="permdesc_factoryTest" product="tablet" msgid="3952059318359653091">"اجرا به عنوان تست سازنده سطح پایین، امکان دسترسی کامل به سختافزار رایانهٔ لوحی شما را فراهم میآورد. فقط زمانی که رایانهٔ لوحی در حالت تست سازنده در حال اجراست قابل دسترسی است."</string>
- <string name="permdesc_factoryTest" product="default" msgid="8136644990319244802">"اجرا به عنوان تست سازنده سطح پایین، امکان دسترسی کامل به سختافزار تلفن شما را فراهم میآورد. فقط زمانی که تلفن در حالت تست سازنده در حال اجراست قابل دسترسی است."</string>
+ <string name="permdesc_factoryTest" product="tablet" msgid="3952059318359653091">"اجرا بهعنوان تست سازنده سطح پایین، امکان دسترسی کامل به سختافزار رایانهٔ لوحی شما را فراهم میآورد. فقط زمانی که رایانهٔ لوحی در حالت تست سازنده در حال اجراست قابل دسترسی است."</string>
+ <string name="permdesc_factoryTest" product="default" msgid="8136644990319244802">"اجرا بهعنوان تست سازنده سطح پایین، امکان دسترسی کامل به سختافزار تلفن شما را فراهم میآورد. فقط زمانی که تلفن در حالت تست سازنده در حال اجراست قابل دسترسی است."</string>
<string name="permlab_setWallpaper" msgid="6627192333373465143">"تنظیم تصویر زمینه"</string>
<string name="permdesc_setWallpaper" msgid="7373447920977624745">"به برنامه اجازه میدهد تا تصویر زمینه سیستم را تنظیم کند."</string>
<string name="permlab_setWallpaperHints" msgid="3278608165977736538">"تنظیم اندازه تصویر زمینه"</string>
@@ -478,7 +478,7 @@
<string name="permlab_setTimeZone" msgid="2945079801013077340">"تنظیم منطقهٔ زمانی"</string>
<string name="permdesc_setTimeZone" product="tablet" msgid="1676983712315827645">"به برنامه اجازه میدهد تا منطقهٔ زمانی رایانهٔ لوحی را تغییر دهد."</string>
<string name="permdesc_setTimeZone" product="default" msgid="4499943488436633398">"به برنامه اجازه میدهد تا منطقهٔ زمانی تلفن را تغییر دهد."</string>
- <string name="permlab_accountManagerService" msgid="4829262349691386986">"عملکرد به عنوان AccountManagerService"</string>
+ <string name="permlab_accountManagerService" msgid="4829262349691386986">"عملکرد بهعنوان AccountManagerService"</string>
<string name="permdesc_accountManagerService" msgid="1948455552333615954">"به برنامه اجازه میدهد با AccountAuthenticators تماس برقرار کند."</string>
<string name="permlab_getAccounts" msgid="1086795467760122114">"یافتن حسابها در دستگاه"</string>
<string name="permdesc_getAccounts" product="tablet" msgid="2741496534769660027">"به برنامه اجازه میدهد به لیست حسابهای شناخته شده توسط رایانهٔ لوحی دسترسی پیدا کند. این ممکن است حسابهای ایجاد شده توسط برنامههایی را که نصب کردهاید، شامل شود."</string>
@@ -502,7 +502,7 @@
<string name="permlab_changeBackgroundDataSetting" msgid="1400666012671648741">"تغییر تنظیمات میزان استفاده داده در پسزمینه"</string>
<string name="permdesc_changeBackgroundDataSetting" msgid="5347729578468744379">"به برنامه اجازه میدهد تا تنظیم کاربرد دادههای پسزمینه را تغییر دهد."</string>
<string name="permlab_accessWifiState" msgid="5202012949247040011">"مشاهدهٔ اتصالات Wi-Fi"</string>
- <string name="permdesc_accessWifiState" msgid="5002798077387803726">"به برنامه امکان میدهد اطلاعات مربوط به شبکه Wi-Fi را مشاهده کند، به عنوان مثال فعال بودن Wi-Fi و نام دستگاههای Wi-Fi متصل."</string>
+ <string name="permdesc_accessWifiState" msgid="5002798077387803726">"به برنامه امکان میدهد اطلاعات مربوط به شبکه Wi-Fi را مشاهده کند، بهعنوان مثال فعال بودن Wi-Fi و نام دستگاههای Wi-Fi متصل."</string>
<string name="permlab_changeWifiState" msgid="6550641188749128035">"اتصال به Wi-Fi و قطع اتصال از آن"</string>
<string name="permdesc_changeWifiState" msgid="7137950297386127533">"به برنامه اجازه میدهد تا به نقاط دسترسی Wi-Fi وصل شود و ارتباط خود را با آنها قطع کند و تغییراتی را در پیکربندی دستگاه برای شبکههای Wi-Fi ایجاد کند."</string>
<string name="permlab_changeWifiMulticastState" msgid="1368253871483254784">"دریافت چندگانه Wi-Fi را مجاز میکند"</string>
@@ -522,12 +522,12 @@
<string name="permlab_nfc" msgid="4423351274757876953">"کنترل ارتباط راه نزدیک"</string>
<string name="permdesc_nfc" msgid="7120611819401789907">"به برنامه اجازه میدهد تا با تگهای ارتباط میدان نزدیک (NFC)، کارتها و فایل خوان ارتباط برقرار کند."</string>
<string name="permlab_disableKeyguard" msgid="3598496301486439258">"غیرفعال کردن قفل صفحه شما"</string>
- <string name="permdesc_disableKeyguard" msgid="6034203065077122992">"به برنامه امکان میدهد قفل کلید و هر گونه امنیت گذرواژه مرتبط را غیرفعال کند. به عنوان مثال تلفن هنگام دریافت یک تماس تلفنی ورودی قفل کلید را غیرفعال میکند و بعد از پایان تماس، قفل کلید را دوباره فعال میکند."</string>
- <string name="permlab_readSyncSettings" msgid="6201810008230503052">"خواندن تنظیمات همگام سازی"</string>
- <string name="permdesc_readSyncSettings" msgid="2706745674569678644">"به برنامه اجازه میدهد تنظیمات را برای یک حساب بخواند. به عنوان مثال، این ویژگی میتواند تعیین کند آیا حساب «افراد» شما با یک حساب همگامسازی شده است."</string>
+ <string name="permdesc_disableKeyguard" msgid="6034203065077122992">"به برنامه امکان میدهد قفل کلید و هر گونه امنیت گذرواژه مرتبط را غیرفعال کند. بهعنوان مثال تلفن هنگام دریافت یک تماس تلفنی ورودی قفل کلید را غیرفعال میکند و بعد از پایان تماس، قفل کلید را دوباره فعال میکند."</string>
+ <string name="permlab_readSyncSettings" msgid="6201810008230503052">"خواندن تنظیمات همگامسازی"</string>
+ <string name="permdesc_readSyncSettings" msgid="2706745674569678644">"به برنامه اجازه میدهد تنظیمات را برای یک حساب بخواند. بهعنوان مثال، این ویژگی میتواند تعیین کند آیا حساب «افراد» شما با یک حساب همگامسازی شده است."</string>
<string name="permlab_writeSyncSettings" msgid="5408694875793945314">"تغییر وضعیت همگامسازی بین فعال و غیرفعال"</string>
- <string name="permdesc_writeSyncSettings" msgid="8956262591306369868">"به یک برنامه اجازه میدهد تنظیمات همگامسازی را برای یک حساب اصلاح کند. به عنوان مثال، از این ویژگی میتوان برای فعال کردن همگامسازی برنامه «افراد» با یک حساب استفاده کرد."</string>
- <string name="permlab_readSyncStats" msgid="7396577451360202448">"خواندن اطلاعات آماری همگام سازی"</string>
+ <string name="permdesc_writeSyncSettings" msgid="8956262591306369868">"به یک برنامه اجازه میدهد تنظیمات همگامسازی را برای یک حساب اصلاح کند. بهعنوان مثال، از این ویژگی میتوان برای فعال کردن همگامسازی برنامه «افراد» با یک حساب استفاده کرد."</string>
+ <string name="permlab_readSyncStats" msgid="7396577451360202448">"خواندن اطلاعات آماری همگامسازی"</string>
<string name="permdesc_readSyncStats" msgid="1510143761757606156">"به یک برنامه اجازه میدهد وضعیت همگامسازی یک حساب را بخواند، از جمله سابقه رویدادهای همگامسازی و میزان دادههای همگامسازی شده."</string>
<string name="permlab_subscribedFeedsRead" msgid="4756609637053353318">"خواندن فیدهای مشترک"</string>
<string name="permdesc_subscribedFeedsRead" msgid="5557058907906144505">"به برنامه اجازه میدهد تا جزئیات مربوط به فیدهای همگام شده کنونی را دریافت کند."</string>
@@ -547,6 +547,10 @@
<string name="permdesc_sdcardWrite" product="default" msgid="4337417790936632090">"به برنامه اجازه میدهد تا در کارت SD بنویسد."</string>
<string name="permlab_mediaStorageWrite" product="default" msgid="6859839199706879015">"تغییر/حذف محتواهای حافظه رسانه داخلی"</string>
<string name="permdesc_mediaStorageWrite" product="default" msgid="8189160597698529185">"به برنامه اجازه میدهد تا محتویات حافظه رسانه داخلی را تغییر دهد."</string>
+ <!-- no translation found for permlab_sdcardAccessAll (8150613823900460576) -->
+ <skip />
+ <!-- no translation found for permdesc_sdcardAccessAll (3215208357415891320) -->
+ <skip />
<string name="permlab_cache_filesystem" msgid="5656487264819669824">"دسترسی به سیستم فایل حافظهٔ پنهان"</string>
<string name="permdesc_cache_filesystem" msgid="5578967642265550955">"به برنامه اجازه میدهد تا سیستم فایل حافظهٔ پنهان را بخواند و بنویسد."</string>
<string name="permlab_use_sip" msgid="5986952362795870502">"علامتگذاری/دریافت تماسهای اینترنتی"</string>
@@ -1089,9 +1093,9 @@
<string name="dlg_confirm_kill_storage_users_text" msgid="5100428757107469454">"در صورت فعال کردن حافظهٔ USB، برخی از برنامههایی که از آنها استفاده میکنید متوقف میشوند و تا زمانی که حافظهٔ USB را غیرفعال نکنید امکان استفاده از آنها وجود نخواهد داشت."</string>
<string name="dlg_error_title" msgid="7323658469626514207">"راهاندازی USB ناموفق بود."</string>
<string name="dlg_ok" msgid="7376953167039865701">"تأیید"</string>
- <string name="usb_mtp_notification_title" msgid="3699913097391550394">"متصل شده به عنوان دستگاه رسانهای"</string>
- <string name="usb_ptp_notification_title" msgid="1960817192216064833">"متصل شده به عنوان دوربین"</string>
- <string name="usb_cd_installer_notification_title" msgid="6774712827892090754">"متصل شده به عنوان نصب کننده"</string>
+ <string name="usb_mtp_notification_title" msgid="3699913097391550394">"متصل شده بهعنوان دستگاه رسانهای"</string>
+ <string name="usb_ptp_notification_title" msgid="1960817192216064833">"متصل شده بهعنوان دوربین"</string>
+ <string name="usb_cd_installer_notification_title" msgid="6774712827892090754">"متصل شده بهعنوان نصب کننده"</string>
<string name="usb_accessory_notification_title" msgid="7848236974087653666">"به یک وسیله جانبی USB وصل شده است"</string>
<string name="usb_notification_message" msgid="2290859399983720271">"برای سایر گزینههای USB لمس کنید."</string>
<string name="extmedia_format_title" product="nosdcard" msgid="9020092196061007262">"حافظهٔ USB فرمت شود؟"</string>
@@ -1159,7 +1163,7 @@
<string name="permission_request_notification_title" msgid="6486759795926237907">"مجوز درخواست شد"</string>
<string name="permission_request_notification_with_subtitle" msgid="8530393139639560189">"مجوز"\n"برای حساب <xliff:g id="ACCOUNT">%s</xliff:g> درخواست شد."</string>
<string name="input_method_binding_label" msgid="1283557179944992649">"روش ورودی"</string>
- <string name="sync_binding_label" msgid="3687969138375092423">"همگام سازی"</string>
+ <string name="sync_binding_label" msgid="3687969138375092423">"همگامسازی"</string>
<string name="accessibility_binding_label" msgid="4148120742096474641">"قابلیت دسترسی"</string>
<string name="wallpaper_binding_label" msgid="1240087844304687662">"تصویر زمینه"</string>
<string name="chooser_wallpaper" msgid="7873476199295190279">"تغییر تصویر زمینه"</string>
@@ -1167,14 +1171,10 @@
<string name="vpn_title_long" msgid="6400714798049252294">"VPN توسط <xliff:g id="APP">%s</xliff:g> فعال شده است"</string>
<string name="vpn_text" msgid="3011306607126450322">"برای مدیریت شبکه لمس کنید."</string>
<string name="vpn_text_long" msgid="6407351006249174473">"به <xliff:g id="SESSION">%s</xliff:g> وصل شد. برای مدیریت شبکه لمس کنید."</string>
- <!-- no translation found for vpn_lockdown_connecting (6443438964440960745) -->
- <skip />
- <!-- no translation found for vpn_lockdown_connected (8202679674819213931) -->
- <skip />
- <!-- no translation found for vpn_lockdown_error (6009249814034708175) -->
- <skip />
- <!-- no translation found for vpn_lockdown_reset (5365010427963548932) -->
- <skip />
+ <string name="vpn_lockdown_connecting" msgid="6443438964440960745">"در حال اتصال VPN همیشه فعال…"</string>
+ <string name="vpn_lockdown_connected" msgid="8202679674819213931">"VPN همیشه فعال متصل شد"</string>
+ <string name="vpn_lockdown_error" msgid="6009249814034708175">"خطای VPN همیشه فعال"</string>
+ <string name="vpn_lockdown_reset" msgid="5365010427963548932">"برای بازنشانی اتصال لمس کنید"</string>
<string name="upload_file" msgid="2897957172366730416">"انتخاب فایل"</string>
<string name="no_file_chosen" msgid="6363648562170759465">"هیچ فایلی انتخاب نشد"</string>
<string name="reset" msgid="2448168080964209908">"بازنشانی"</string>
diff --git a/core/res/res/values-fi/strings.xml b/core/res/res/values-fi/strings.xml
index ce355ca..b9d6c06 100644
--- a/core/res/res/values-fi/strings.xml
+++ b/core/res/res/values-fi/strings.xml
@@ -547,6 +547,10 @@
<string name="permdesc_sdcardWrite" product="default" msgid="4337417790936632090">"Antaa sovelluksen kirjoittaa SD-kortille."</string>
<string name="permlab_mediaStorageWrite" product="default" msgid="6859839199706879015">"muokkaa/poista sisäisen säilytystilan sisältöä"</string>
<string name="permdesc_mediaStorageWrite" product="default" msgid="8189160597698529185">"Antaa sovelluksen muokata sisäisen tallennustilan sisältöä."</string>
+ <!-- no translation found for permlab_sdcardAccessAll (8150613823900460576) -->
+ <skip />
+ <!-- no translation found for permdesc_sdcardAccessAll (3215208357415891320) -->
+ <skip />
<string name="permlab_cache_filesystem" msgid="5656487264819669824">"käytä välimuistin tiedostojärjestelmää"</string>
<string name="permdesc_cache_filesystem" msgid="5578967642265550955">"Antaa sovelluksen lukea välimuistin tiedostojärjestelmää ja kirjoittaa siihen."</string>
<string name="permlab_use_sip" msgid="5986952362795870502">"soita/vastaanota internetpuheluita"</string>
@@ -1167,14 +1171,10 @@
<string name="vpn_title_long" msgid="6400714798049252294">"<xliff:g id="APP">%s</xliff:g> on aktivoinut VPN-yhteyden"</string>
<string name="vpn_text" msgid="3011306607126450322">"Voit hallinnoida verkkoa koskettamalla."</string>
<string name="vpn_text_long" msgid="6407351006249174473">"Yhdistetty: <xliff:g id="SESSION">%s</xliff:g>. Hallinnoi verkkoa koskettamalla."</string>
- <!-- no translation found for vpn_lockdown_connecting (6443438964440960745) -->
- <skip />
- <!-- no translation found for vpn_lockdown_connected (8202679674819213931) -->
- <skip />
- <!-- no translation found for vpn_lockdown_error (6009249814034708175) -->
- <skip />
- <!-- no translation found for vpn_lockdown_reset (5365010427963548932) -->
- <skip />
+ <string name="vpn_lockdown_connecting" msgid="6443438964440960745">"Yhdistetään aina käytössä olevaan VPN-verkkoon..."</string>
+ <string name="vpn_lockdown_connected" msgid="8202679674819213931">"Yhdistetty aina käytössä olevaan VPN-verkkoon"</string>
+ <string name="vpn_lockdown_error" msgid="6009249814034708175">"Aina käytössä oleva VPN: virhe"</string>
+ <string name="vpn_lockdown_reset" msgid="5365010427963548932">"Nollaa yhteys koskettamalla"</string>
<string name="upload_file" msgid="2897957172366730416">"Valitse tiedosto"</string>
<string name="no_file_chosen" msgid="6363648562170759465">"Ei valittua tiedostoa"</string>
<string name="reset" msgid="2448168080964209908">"Palauta"</string>
diff --git a/core/res/res/values-fr/strings.xml b/core/res/res/values-fr/strings.xml
index f9250ce..e5f7978 100644
--- a/core/res/res/values-fr/strings.xml
+++ b/core/res/res/values-fr/strings.xml
@@ -547,6 +547,10 @@
<string name="permdesc_sdcardWrite" product="default" msgid="4337417790936632090">"Permet à l\'application de modifier le contenu de la carte SD."</string>
<string name="permlab_mediaStorageWrite" product="default" msgid="6859839199706879015">"Modifier/Supprimer contenu mémoire interne"</string>
<string name="permdesc_mediaStorageWrite" product="default" msgid="8189160597698529185">"Permet à l\'application de modifier le contenu de la mémoire de stockage multimédia interne."</string>
+ <!-- no translation found for permlab_sdcardAccessAll (8150613823900460576) -->
+ <skip />
+ <!-- no translation found for permdesc_sdcardAccessAll (3215208357415891320) -->
+ <skip />
<string name="permlab_cache_filesystem" msgid="5656487264819669824">"accéder au système de fichiers en cache"</string>
<string name="permdesc_cache_filesystem" msgid="5578967642265550955">"Permet à l\'application d\'obtenir des droits en lecture/écriture concernant le système de fichiers du cache."</string>
<string name="permlab_use_sip" msgid="5986952362795870502">"effectuer/recevoir des appels Internet"</string>
diff --git a/core/res/res/values-hi/strings.xml b/core/res/res/values-hi/strings.xml
index faca09d..fcb0783 100644
--- a/core/res/res/values-hi/strings.xml
+++ b/core/res/res/values-hi/strings.xml
@@ -547,6 +547,10 @@
<string name="permdesc_sdcardWrite" product="default" msgid="4337417790936632090">"एप्लिकेशन को SD कार्ड पर लिखने देता है."</string>
<string name="permlab_mediaStorageWrite" product="default" msgid="6859839199706879015">"आंतरिक मीडिया संग्रहण सामग्रियों को बदलें/हटाएं"</string>
<string name="permdesc_mediaStorageWrite" product="default" msgid="8189160597698529185">"एप्लिकेशन को आंतरिक मीडिया संग्रहण की सामग्री को संशोधित करने देता है."</string>
+ <!-- no translation found for permlab_sdcardAccessAll (8150613823900460576) -->
+ <skip />
+ <!-- no translation found for permdesc_sdcardAccessAll (3215208357415891320) -->
+ <skip />
<string name="permlab_cache_filesystem" msgid="5656487264819669824">"कैश फ़ाइल सिस्टम में पहंचे"</string>
<string name="permdesc_cache_filesystem" msgid="5578967642265550955">"एप्लिकेशन को संचय फ़ाइल सिस्टम पढ़ने और लिखने देता है."</string>
<string name="permlab_use_sip" msgid="5986952362795870502">"इंटरनेट कॉल करें/प्राप्त करें"</string>
@@ -1167,14 +1171,10 @@
<string name="vpn_title_long" msgid="6400714798049252294">"VPN को <xliff:g id="APP">%s</xliff:g> द्वारा सक्रिय किया गया है"</string>
<string name="vpn_text" msgid="3011306607126450322">"नेटवर्क प्रबंधित करने के लिए स्पर्श करें."</string>
<string name="vpn_text_long" msgid="6407351006249174473">"<xliff:g id="SESSION">%s</xliff:g> से कनेक्ट किया गया. नेटवर्क प्रबंधित करने के लिए स्पर्श करें."</string>
- <!-- no translation found for vpn_lockdown_connecting (6443438964440960745) -->
- <skip />
- <!-- no translation found for vpn_lockdown_connected (8202679674819213931) -->
- <skip />
- <!-- no translation found for vpn_lockdown_error (6009249814034708175) -->
- <skip />
- <!-- no translation found for vpn_lockdown_reset (5365010427963548932) -->
- <skip />
+ <string name="vpn_lockdown_connecting" msgid="6443438964440960745">"हमेशा-चालू VPN कनेक्ट हो रहा है…"</string>
+ <string name="vpn_lockdown_connected" msgid="8202679674819213931">"हमेशा-चालू VPN कनेक्ट है"</string>
+ <string name="vpn_lockdown_error" msgid="6009249814034708175">"हमेशा-चालू VPN त्रुटि"</string>
+ <string name="vpn_lockdown_reset" msgid="5365010427963548932">"कनेक्शन रीसेट करने के लिए स्पर्श करें"</string>
<string name="upload_file" msgid="2897957172366730416">"फ़ाइल चुनें"</string>
<string name="no_file_chosen" msgid="6363648562170759465">"कोई फ़ाइल चुनी नहीं गई"</string>
<string name="reset" msgid="2448168080964209908">"रीसेट करें"</string>
diff --git a/core/res/res/values-hr/strings.xml b/core/res/res/values-hr/strings.xml
index 928761e..01e6557 100644
--- a/core/res/res/values-hr/strings.xml
+++ b/core/res/res/values-hr/strings.xml
@@ -547,6 +547,10 @@
<string name="permdesc_sdcardWrite" product="default" msgid="4337417790936632090">"Aplikaciji omogućuje pisanje na SD karticu."</string>
<string name="permlab_mediaStorageWrite" product="default" msgid="6859839199706879015">"izmijeni/izbriši sadržaj pohranjen na internim medijima"</string>
<string name="permdesc_mediaStorageWrite" product="default" msgid="8189160597698529185">"Aplikaciji omogućuje izmjenu sadržaja pohranjenog na internim medijima."</string>
+ <!-- no translation found for permlab_sdcardAccessAll (8150613823900460576) -->
+ <skip />
+ <!-- no translation found for permdesc_sdcardAccessAll (3215208357415891320) -->
+ <skip />
<string name="permlab_cache_filesystem" msgid="5656487264819669824">"pristup sustavu datoteka predmemorije"</string>
<string name="permdesc_cache_filesystem" msgid="5578967642265550955">"Aplikaciji omogućuje čitanje i pisanje u datotečnom sustavu privremene memorije."</string>
<string name="permlab_use_sip" msgid="5986952362795870502">"zovi/primaj internetske pozive"</string>
@@ -1167,14 +1171,10 @@
<string name="vpn_title_long" msgid="6400714798049252294">"Aplikacija <xliff:g id="APP">%s</xliff:g> aktivirala je VPN"</string>
<string name="vpn_text" msgid="3011306607126450322">"Dodirnite za upravljanje mrežom."</string>
<string name="vpn_text_long" msgid="6407351006249174473">"Povezan sa sesijom <xliff:g id="SESSION">%s</xliff:g>. Dodirnite za upravljanje mrežom."</string>
- <!-- no translation found for vpn_lockdown_connecting (6443438964440960745) -->
- <skip />
- <!-- no translation found for vpn_lockdown_connected (8202679674819213931) -->
- <skip />
- <!-- no translation found for vpn_lockdown_error (6009249814034708175) -->
- <skip />
- <!-- no translation found for vpn_lockdown_reset (5365010427963548932) -->
- <skip />
+ <string name="vpn_lockdown_connecting" msgid="6443438964440960745">"Povezivanje s uvijek uključenom VPN mrežom…"</string>
+ <string name="vpn_lockdown_connected" msgid="8202679674819213931">"Povezan s uvijek uključenom VPN mrežom"</string>
+ <string name="vpn_lockdown_error" msgid="6009249814034708175">"Pogreška uvijek uključene VPN mreže"</string>
+ <string name="vpn_lockdown_reset" msgid="5365010427963548932">"Dodirnite za poništavanje veze"</string>
<string name="upload_file" msgid="2897957172366730416">"Odaberite datoteku"</string>
<string name="no_file_chosen" msgid="6363648562170759465">"Nema odabranih datoteka"</string>
<string name="reset" msgid="2448168080964209908">"Ponovo postavi"</string>
diff --git a/core/res/res/values-hu/strings.xml b/core/res/res/values-hu/strings.xml
index fce9c5b..a48129d 100644
--- a/core/res/res/values-hu/strings.xml
+++ b/core/res/res/values-hu/strings.xml
@@ -547,6 +547,10 @@
<string name="permdesc_sdcardWrite" product="default" msgid="4337417790936632090">"Lehetővé teszi az alkalmazás számára, hogy írjon az SD-kártyára."</string>
<string name="permlab_mediaStorageWrite" product="default" msgid="6859839199706879015">"belső tár tartalmának módosítása/törlése"</string>
<string name="permdesc_mediaStorageWrite" product="default" msgid="8189160597698529185">"Lehetővé teszi az alkalmazás számára, hogy módosítsa a belső háttértár tartalmát."</string>
+ <!-- no translation found for permlab_sdcardAccessAll (8150613823900460576) -->
+ <skip />
+ <!-- no translation found for permdesc_sdcardAccessAll (3215208357415891320) -->
+ <skip />
<string name="permlab_cache_filesystem" msgid="5656487264819669824">"hozzáférés a gyorsítótár fájlrendszeréhez"</string>
<string name="permdesc_cache_filesystem" msgid="5578967642265550955">"Lehetővé teszi az alkalmazás számára a gyorsítótár-fájlrendszer olvasását és írását."</string>
<string name="permlab_use_sip" msgid="5986952362795870502">"internetes hívások kezdeményezése és fogadása"</string>
@@ -1167,14 +1171,10 @@
<string name="vpn_title_long" msgid="6400714798049252294">"A(z) <xliff:g id="APP">%s</xliff:g> aktiválta a VPN-t"</string>
<string name="vpn_text" msgid="3011306607126450322">"Érintse meg a hálózat kezeléséhez."</string>
<string name="vpn_text_long" msgid="6407351006249174473">"Csatlakozva ide: <xliff:g id="SESSION">%s</xliff:g>. Érintse meg a hálózat kezeléséhez."</string>
- <!-- no translation found for vpn_lockdown_connecting (6443438964440960745) -->
- <skip />
- <!-- no translation found for vpn_lockdown_connected (8202679674819213931) -->
- <skip />
- <!-- no translation found for vpn_lockdown_error (6009249814034708175) -->
- <skip />
- <!-- no translation found for vpn_lockdown_reset (5365010427963548932) -->
- <skip />
+ <string name="vpn_lockdown_connecting" msgid="6443438964440960745">"Csatlakozás a mindig bekapcsolt VPN-hez..."</string>
+ <string name="vpn_lockdown_connected" msgid="8202679674819213931">"Csatlakozva a mindig bekapcsolt VPN-hez"</string>
+ <string name="vpn_lockdown_error" msgid="6009249814034708175">"Hiba a mindig bekapcsolt VPN-nel"</string>
+ <string name="vpn_lockdown_reset" msgid="5365010427963548932">"Érintse meg a kapcsolat alaphelyzetbe állításához."</string>
<string name="upload_file" msgid="2897957172366730416">"Fájl kiválasztása"</string>
<string name="no_file_chosen" msgid="6363648562170759465">"Nincs fájl kiválasztva"</string>
<string name="reset" msgid="2448168080964209908">"Alaphelyzet"</string>
diff --git a/core/res/res/values-in/strings.xml b/core/res/res/values-in/strings.xml
index 0c1ff65..77333cd 100644
--- a/core/res/res/values-in/strings.xml
+++ b/core/res/res/values-in/strings.xml
@@ -547,6 +547,10 @@
<string name="permdesc_sdcardWrite" product="default" msgid="4337417790936632090">"Memungkinkan apl menulis ke kartu SD."</string>
<string name="permlab_mediaStorageWrite" product="default" msgid="6859839199706879015">"ubah/hapus konten penyimpanan media internal"</string>
<string name="permdesc_mediaStorageWrite" product="default" msgid="8189160597698529185">"Mengizinkan apl memodifikasi konten penyimpanan media internal."</string>
+ <!-- no translation found for permlab_sdcardAccessAll (8150613823900460576) -->
+ <skip />
+ <!-- no translation found for permdesc_sdcardAccessAll (3215208357415891320) -->
+ <skip />
<string name="permlab_cache_filesystem" msgid="5656487264819669824">"akses sistem file cache."</string>
<string name="permdesc_cache_filesystem" msgid="5578967642265550955">"Mengizinkan apl membaca dan menulis pada sistem file cache."</string>
<string name="permlab_use_sip" msgid="5986952362795870502">"lakukan//terima panggilan internet"</string>
@@ -1167,14 +1171,10 @@
<string name="vpn_title_long" msgid="6400714798049252294">"VPN diaktifkan oleh <xliff:g id="APP">%s</xliff:g>"</string>
<string name="vpn_text" msgid="3011306607126450322">"Sentuh untuk mengelola jaringan."</string>
<string name="vpn_text_long" msgid="6407351006249174473">"Tersambung ke <xliff:g id="SESSION">%s</xliff:g>. Sentuh untuk mengelola jaringan."</string>
- <!-- no translation found for vpn_lockdown_connecting (6443438964440960745) -->
- <skip />
- <!-- no translation found for vpn_lockdown_connected (8202679674819213931) -->
- <skip />
- <!-- no translation found for vpn_lockdown_error (6009249814034708175) -->
- <skip />
- <!-- no translation found for vpn_lockdown_reset (5365010427963548932) -->
- <skip />
+ <string name="vpn_lockdown_connecting" msgid="6443438964440960745">"Menyambungkan VPN selalu aktif..."</string>
+ <string name="vpn_lockdown_connected" msgid="8202679674819213931">"VPN selalu aktif tersambung"</string>
+ <string name="vpn_lockdown_error" msgid="6009249814034708175">"Kesalahan VPN selalu aktif"</string>
+ <string name="vpn_lockdown_reset" msgid="5365010427963548932">"Sentuh untuk menyetel ulang sambungan"</string>
<string name="upload_file" msgid="2897957172366730416">"Pilih file"</string>
<string name="no_file_chosen" msgid="6363648562170759465">"Tidak ada file yang dipilih"</string>
<string name="reset" msgid="2448168080964209908">"Setel ulang"</string>
diff --git a/core/res/res/values-it/strings.xml b/core/res/res/values-it/strings.xml
index 432f2e4..9f1ee1c 100644
--- a/core/res/res/values-it/strings.xml
+++ b/core/res/res/values-it/strings.xml
@@ -547,6 +547,10 @@
<string name="permdesc_sdcardWrite" product="default" msgid="4337417790936632090">"Consente all\'applicazione di scrivere sulla scheda SD."</string>
<string name="permlab_mediaStorageWrite" product="default" msgid="6859839199706879015">"modifica/eliminaz. contenuti archivio media int."</string>
<string name="permdesc_mediaStorageWrite" product="default" msgid="8189160597698529185">"Consente all\'applicazione di modificare i contenuti dell\'archivio media interno."</string>
+ <!-- no translation found for permlab_sdcardAccessAll (8150613823900460576) -->
+ <skip />
+ <!-- no translation found for permdesc_sdcardAccessAll (3215208357415891320) -->
+ <skip />
<string name="permlab_cache_filesystem" msgid="5656487264819669824">"accesso al filesystem nella cache"</string>
<string name="permdesc_cache_filesystem" msgid="5578967642265550955">"Consente all\'applicazione di leggere e scrivere il filesystem nella cache."</string>
<string name="permlab_use_sip" msgid="5986952362795870502">"effettuazione/ricezione chiamate Internet"</string>
diff --git a/core/res/res/values-iw/strings.xml b/core/res/res/values-iw/strings.xml
index ff17945..d7ce2e1 100644
--- a/core/res/res/values-iw/strings.xml
+++ b/core/res/res/values-iw/strings.xml
@@ -547,6 +547,10 @@
<string name="permdesc_sdcardWrite" product="default" msgid="4337417790936632090">"מאפשר ליישום לכתוב לכרטיס ה-SD."</string>
<string name="permlab_mediaStorageWrite" product="default" msgid="6859839199706879015">"שנה/מחק תוכן של אחסון מדיה פנימי"</string>
<string name="permdesc_mediaStorageWrite" product="default" msgid="8189160597698529185">"מאפשר ליישום לשנות את התוכן של אמצעי האחסון הפנימי למדיה."</string>
+ <!-- no translation found for permlab_sdcardAccessAll (8150613823900460576) -->
+ <skip />
+ <!-- no translation found for permdesc_sdcardAccessAll (3215208357415891320) -->
+ <skip />
<string name="permlab_cache_filesystem" msgid="5656487264819669824">"גישה למערכת הקבצים בקובץ השמור"</string>
<string name="permdesc_cache_filesystem" msgid="5578967642265550955">"מאפשר ליישום לקרוא ולכתוב במערכת הקבצים של הקבצים השמורים."</string>
<string name="permlab_use_sip" msgid="5986952362795870502">"בצע/קבל שיחות אינטרנט"</string>
diff --git a/core/res/res/values-ja/strings.xml b/core/res/res/values-ja/strings.xml
index 1b2be12..0e1b9eb 100644
--- a/core/res/res/values-ja/strings.xml
+++ b/core/res/res/values-ja/strings.xml
@@ -547,6 +547,10 @@
<string name="permdesc_sdcardWrite" product="default" msgid="4337417790936632090">"SDカードへの書き込みをアプリに許可します。"</string>
<string name="permlab_mediaStorageWrite" product="default" msgid="6859839199706879015">"内部メディアストレージの内容の変更/削除"</string>
<string name="permdesc_mediaStorageWrite" product="default" msgid="8189160597698529185">"内部メディアストレージの内容を変更することをアプリに許可します。"</string>
+ <!-- no translation found for permlab_sdcardAccessAll (8150613823900460576) -->
+ <skip />
+ <!-- no translation found for permdesc_sdcardAccessAll (3215208357415891320) -->
+ <skip />
<string name="permlab_cache_filesystem" msgid="5656487264819669824">"キャッシュファイルシステムにアクセス"</string>
<string name="permdesc_cache_filesystem" msgid="5578967642265550955">"キャッシュファイルシステムの読み書きをアプリに許可します。"</string>
<string name="permlab_use_sip" msgid="5986952362795870502">"インターネット通話の発着信"</string>
diff --git a/core/res/res/values-ko/strings.xml b/core/res/res/values-ko/strings.xml
index 5549489..ae9b0c0 100644
--- a/core/res/res/values-ko/strings.xml
+++ b/core/res/res/values-ko/strings.xml
@@ -547,6 +547,10 @@
<string name="permdesc_sdcardWrite" product="default" msgid="4337417790936632090">"앱이 SD 카드에 쓸 수 있도록 허용합니다."</string>
<string name="permlab_mediaStorageWrite" product="default" msgid="6859839199706879015">"내부 미디어 저장소 콘텐츠 수정/삭제"</string>
<string name="permdesc_mediaStorageWrite" product="default" msgid="8189160597698529185">"앱이 내부 미디어 저장소의 콘텐츠를 수정할 수 있도록 허용합니다."</string>
+ <!-- no translation found for permlab_sdcardAccessAll (8150613823900460576) -->
+ <skip />
+ <!-- no translation found for permdesc_sdcardAccessAll (3215208357415891320) -->
+ <skip />
<string name="permlab_cache_filesystem" msgid="5656487264819669824">"캐시 파일시스템 액세스"</string>
<string name="permdesc_cache_filesystem" msgid="5578967642265550955">"앱이 캐시 파일 시스템을 읽고 쓸 수 있도록 허용합니다."</string>
<string name="permlab_use_sip" msgid="5986952362795870502">"인터넷 전화 걸기/받기"</string>
@@ -1167,14 +1171,10 @@
<string name="vpn_title_long" msgid="6400714798049252294">"VPN이 <xliff:g id="APP">%s</xliff:g>에 의해 활성화됨"</string>
<string name="vpn_text" msgid="3011306607126450322">"네트워크를 관리하려면 터치하세요."</string>
<string name="vpn_text_long" msgid="6407351006249174473">"<xliff:g id="SESSION">%s</xliff:g>에 연결되어 있습니다. 네트워크를 관리하려면 터치하세요."</string>
- <!-- no translation found for vpn_lockdown_connecting (6443438964440960745) -->
- <skip />
- <!-- no translation found for vpn_lockdown_connected (8202679674819213931) -->
- <skip />
- <!-- no translation found for vpn_lockdown_error (6009249814034708175) -->
- <skip />
- <!-- no translation found for vpn_lockdown_reset (5365010427963548932) -->
- <skip />
+ <string name="vpn_lockdown_connecting" msgid="6443438964440960745">"연결 유지 VPN에 연결하는 중…"</string>
+ <string name="vpn_lockdown_connected" msgid="8202679674819213931">"연결 유지 VPN에 연결됨"</string>
+ <string name="vpn_lockdown_error" msgid="6009249814034708175">"연결 유지 VPN 오류"</string>
+ <string name="vpn_lockdown_reset" msgid="5365010427963548932">"터치하여 연결 재설정"</string>
<string name="upload_file" msgid="2897957172366730416">"파일 선택"</string>
<string name="no_file_chosen" msgid="6363648562170759465">"파일을 선택하지 않았습니다."</string>
<string name="reset" msgid="2448168080964209908">"초기화"</string>
diff --git a/core/res/res/values-lt/strings.xml b/core/res/res/values-lt/strings.xml
index 37b2f83..94c5825 100644
--- a/core/res/res/values-lt/strings.xml
+++ b/core/res/res/values-lt/strings.xml
@@ -547,6 +547,10 @@
<string name="permdesc_sdcardWrite" product="default" msgid="4337417790936632090">"Leidžiama programai rašyti į SD kortelę."</string>
<string name="permlab_mediaStorageWrite" product="default" msgid="6859839199706879015">"keisti / ištr. vid. med. atm. tur."</string>
<string name="permdesc_mediaStorageWrite" product="default" msgid="8189160597698529185">"Leidžiama programai keisti vidinės medijos saugyklos turinį."</string>
+ <!-- no translation found for permlab_sdcardAccessAll (8150613823900460576) -->
+ <skip />
+ <!-- no translation found for permdesc_sdcardAccessAll (3215208357415891320) -->
+ <skip />
<string name="permlab_cache_filesystem" msgid="5656487264819669824">"pasiekti talpyklos failų sistemą"</string>
<string name="permdesc_cache_filesystem" msgid="5578967642265550955">"Leidžiama programai skaityti talpyklos failų sistemą ir į ją rašyti."</string>
<string name="permlab_use_sip" msgid="5986952362795870502">"skambinti / priimti skambučius internetu"</string>
@@ -1167,14 +1171,10 @@
<string name="vpn_title_long" msgid="6400714798049252294">"VPN suaktyvino „<xliff:g id="APP">%s</xliff:g>“"</string>
<string name="vpn_text" msgid="3011306607126450322">"Palieskite, kad valdytumėte tinklą."</string>
<string name="vpn_text_long" msgid="6407351006249174473">"Prisijungta prie <xliff:g id="SESSION">%s</xliff:g>. Jei norite valdyti tinklą, palieskite."</string>
- <!-- no translation found for vpn_lockdown_connecting (6443438964440960745) -->
- <skip />
- <!-- no translation found for vpn_lockdown_connected (8202679674819213931) -->
- <skip />
- <!-- no translation found for vpn_lockdown_error (6009249814034708175) -->
- <skip />
- <!-- no translation found for vpn_lockdown_reset (5365010427963548932) -->
- <skip />
+ <string name="vpn_lockdown_connecting" msgid="6443438964440960745">"Prisijungiama prie visada įjungto VPN…"</string>
+ <string name="vpn_lockdown_connected" msgid="8202679674819213931">"Prisijungta prie visada įjungto VPN"</string>
+ <string name="vpn_lockdown_error" msgid="6009249814034708175">"Visada įjungto VPN klaida"</string>
+ <string name="vpn_lockdown_reset" msgid="5365010427963548932">"Palieskite, kad iš naujo nustatytumėte ryšį"</string>
<string name="upload_file" msgid="2897957172366730416">"Pasirinkti failą"</string>
<string name="no_file_chosen" msgid="6363648562170759465">"Nepasirinktas joks failas"</string>
<string name="reset" msgid="2448168080964209908">"Atstatyti"</string>
diff --git a/core/res/res/values-lv/strings.xml b/core/res/res/values-lv/strings.xml
index 38f8047..91b414b 100644
--- a/core/res/res/values-lv/strings.xml
+++ b/core/res/res/values-lv/strings.xml
@@ -547,6 +547,10 @@
<string name="permdesc_sdcardWrite" product="default" msgid="4337417790936632090">"Ļauj lietotnei rakstīt SD kartē."</string>
<string name="permlab_mediaStorageWrite" product="default" msgid="6859839199706879015">"pārv./dz.datu n.iekš.atm.sat."</string>
<string name="permdesc_mediaStorageWrite" product="default" msgid="8189160597698529185">"Ļauj lietotnei modificēt datu nesēja iekšējās atmiņas saturu."</string>
+ <!-- no translation found for permlab_sdcardAccessAll (8150613823900460576) -->
+ <skip />
+ <!-- no translation found for permdesc_sdcardAccessAll (3215208357415891320) -->
+ <skip />
<string name="permlab_cache_filesystem" msgid="5656487264819669824">"piekļūt kešatmiņas failu sistēmai"</string>
<string name="permdesc_cache_filesystem" msgid="5578967642265550955">"Ļauj lietotnei lasīt un rakstīt kešatmiņas failu sistēmā."</string>
<string name="permlab_use_sip" msgid="5986952362795870502">"veikt/saņemt interneta zvanus"</string>
@@ -1167,14 +1171,10 @@
<string name="vpn_title_long" msgid="6400714798049252294">"Lietojumprogramma <xliff:g id="APP">%s</xliff:g> aktivizēja VPN."</string>
<string name="vpn_text" msgid="3011306607126450322">"Pieskarieties, lai pārvaldītu tīklu."</string>
<string name="vpn_text_long" msgid="6407351006249174473">"Ir izveidots savienojums ar <xliff:g id="SESSION">%s</xliff:g>. Pieskarieties, lai pārvaldītu tīklu."</string>
- <!-- no translation found for vpn_lockdown_connecting (6443438964440960745) -->
- <skip />
- <!-- no translation found for vpn_lockdown_connected (8202679674819213931) -->
- <skip />
- <!-- no translation found for vpn_lockdown_error (6009249814034708175) -->
- <skip />
- <!-- no translation found for vpn_lockdown_reset (5365010427963548932) -->
- <skip />
+ <string name="vpn_lockdown_connecting" msgid="6443438964440960745">"Notiek savienojuma izveide ar vienmēr ieslēgtu VPN…"</string>
+ <string name="vpn_lockdown_connected" msgid="8202679674819213931">"Izveidots savienojums ar vienmēr ieslēgtu VPN."</string>
+ <string name="vpn_lockdown_error" msgid="6009249814034708175">"Kļūda saistībā ar vienmēr ieslēgtu VPN"</string>
+ <string name="vpn_lockdown_reset" msgid="5365010427963548932">"Pieskarieties, lai atiestatītu savienojumu."</string>
<string name="upload_file" msgid="2897957172366730416">"Izvēlēties failu"</string>
<string name="no_file_chosen" msgid="6363648562170759465">"Neviens fails nav izvēlēts"</string>
<string name="reset" msgid="2448168080964209908">"Atiestatīt"</string>
diff --git a/core/res/res/values-ms/strings.xml b/core/res/res/values-ms/strings.xml
index a56f6cf..b9fca83 100644
--- a/core/res/res/values-ms/strings.xml
+++ b/core/res/res/values-ms/strings.xml
@@ -547,6 +547,10 @@
<string name="permdesc_sdcardWrite" product="default" msgid="4337417790936632090">"Membenarkan apl menulis ke kad SD."</string>
<string name="permlab_mediaStorageWrite" product="default" msgid="6859839199706879015">"ubh suai/pdm kdg strn mdia dlm"</string>
<string name="permdesc_mediaStorageWrite" product="default" msgid="8189160597698529185">"Membenarkan apl mengubah suai kandungan storan media dalaman."</string>
+ <!-- no translation found for permlab_sdcardAccessAll (8150613823900460576) -->
+ <skip />
+ <!-- no translation found for permdesc_sdcardAccessAll (3215208357415891320) -->
+ <skip />
<string name="permlab_cache_filesystem" msgid="5656487264819669824">"akses sistem fail cache"</string>
<string name="permdesc_cache_filesystem" msgid="5578967642265550955">"Membenarkan apl membaca dan menulis cache sistem fail."</string>
<string name="permlab_use_sip" msgid="5986952362795870502">"membuat/menerima panggilan Internet"</string>
@@ -1167,14 +1171,10 @@
<string name="vpn_title_long" msgid="6400714798049252294">"VPN diaktifkan oleh <xliff:g id="APP">%s</xliff:g>"</string>
<string name="vpn_text" msgid="3011306607126450322">"Sentuh untuk mengurus rangkaian."</string>
<string name="vpn_text_long" msgid="6407351006249174473">"Bersambung ke <xliff:g id="SESSION">%s</xliff:g>. Sentuh untuk mengurus rangkaian."</string>
- <!-- no translation found for vpn_lockdown_connecting (6443438964440960745) -->
- <skip />
- <!-- no translation found for vpn_lockdown_connected (8202679674819213931) -->
- <skip />
- <!-- no translation found for vpn_lockdown_error (6009249814034708175) -->
- <skip />
- <!-- no translation found for vpn_lockdown_reset (5365010427963548932) -->
- <skip />
+ <string name="vpn_lockdown_connecting" msgid="6443438964440960745">"VPN sentiasa hidup sedang disambungkan..."</string>
+ <string name="vpn_lockdown_connected" msgid="8202679674819213931">"VPN sentiasa hidup telah disambungkan"</string>
+ <string name="vpn_lockdown_error" msgid="6009249814034708175">"Ralat VPN sentiasa hidup"</string>
+ <string name="vpn_lockdown_reset" msgid="5365010427963548932">"Sentuh untuk menetapkan semula sambungan"</string>
<string name="upload_file" msgid="2897957172366730416">"Pilih fail"</string>
<string name="no_file_chosen" msgid="6363648562170759465">"Tiada fail dipilih"</string>
<string name="reset" msgid="2448168080964209908">"Tetapkan semula"</string>
diff --git a/core/res/res/values-nb/strings.xml b/core/res/res/values-nb/strings.xml
index a18e63c..ef6a305 100644
--- a/core/res/res/values-nb/strings.xml
+++ b/core/res/res/values-nb/strings.xml
@@ -547,6 +547,10 @@
<string name="permdesc_sdcardWrite" product="default" msgid="4337417790936632090">"Lar appen skrive til SD-kortet."</string>
<string name="permlab_mediaStorageWrite" product="default" msgid="6859839199706879015">"endre eller slette innhold på interne medier"</string>
<string name="permdesc_mediaStorageWrite" product="default" msgid="8189160597698529185">"Lar appen endre innholdet i det interne lagringsmediet."</string>
+ <!-- no translation found for permlab_sdcardAccessAll (8150613823900460576) -->
+ <skip />
+ <!-- no translation found for permdesc_sdcardAccessAll (3215208357415891320) -->
+ <skip />
<string name="permlab_cache_filesystem" msgid="5656487264819669824">"tilgang til bufrede filer"</string>
<string name="permdesc_cache_filesystem" msgid="5578967642265550955">"Lar appen lese og skrive til det bufrede filsystemet."</string>
<string name="permlab_use_sip" msgid="5986952362795870502">"foreta/motta Internett-anrop"</string>
diff --git a/core/res/res/values-nl/strings.xml b/core/res/res/values-nl/strings.xml
index 3ac4f86..2ac476d 100644
--- a/core/res/res/values-nl/strings.xml
+++ b/core/res/res/values-nl/strings.xml
@@ -547,6 +547,10 @@
<string name="permdesc_sdcardWrite" product="default" msgid="4337417790936632090">"Hiermee kan de app schrijven naar de SD-kaart."</string>
<string name="permlab_mediaStorageWrite" product="default" msgid="6859839199706879015">"inh. mediaopsl. wijz./verw."</string>
<string name="permdesc_mediaStorageWrite" product="default" msgid="8189160597698529185">"Hiermee kan de app de inhoud van de interne mediaopslag aanpassen."</string>
+ <!-- no translation found for permlab_sdcardAccessAll (8150613823900460576) -->
+ <skip />
+ <!-- no translation found for permdesc_sdcardAccessAll (3215208357415891320) -->
+ <skip />
<string name="permlab_cache_filesystem" msgid="5656487264819669824">"het cachebestandssysteem openen"</string>
<string name="permdesc_cache_filesystem" msgid="5578967642265550955">"Hiermee kan de app het cachebestandssysteem lezen en schrijven."</string>
<string name="permlab_use_sip" msgid="5986952362795870502">"internetoproepen starten/ontvangen"</string>
diff --git a/core/res/res/values-pl/strings.xml b/core/res/res/values-pl/strings.xml
index b3cb4f8..bdea11f 100644
--- a/core/res/res/values-pl/strings.xml
+++ b/core/res/res/values-pl/strings.xml
@@ -547,6 +547,10 @@
<string name="permdesc_sdcardWrite" product="default" msgid="4337417790936632090">"Pozwala aplikacji na zapis na karcie SD."</string>
<string name="permlab_mediaStorageWrite" product="default" msgid="6859839199706879015">"modyfikowanie/usuwanie zawartości pamięci wew."</string>
<string name="permdesc_mediaStorageWrite" product="default" msgid="8189160597698529185">"Pozwala aplikacji na modyfikowanie zawartości pamięci wewnętrznej."</string>
+ <!-- no translation found for permlab_sdcardAccessAll (8150613823900460576) -->
+ <skip />
+ <!-- no translation found for permdesc_sdcardAccessAll (3215208357415891320) -->
+ <skip />
<string name="permlab_cache_filesystem" msgid="5656487264819669824">"dostęp do systemu plików pamięci podręcznej"</string>
<string name="permdesc_cache_filesystem" msgid="5578967642265550955">"Pozwala aplikacji na odczyt i zapis w systemie plików pamięci podręcznej."</string>
<string name="permlab_use_sip" msgid="5986952362795870502">"nawiązywanie/odbieranie połączeń przez internet"</string>
diff --git a/core/res/res/values-pt-rPT/strings.xml b/core/res/res/values-pt-rPT/strings.xml
index aab8ca9c..390faaa 100644
--- a/core/res/res/values-pt-rPT/strings.xml
+++ b/core/res/res/values-pt-rPT/strings.xml
@@ -547,6 +547,10 @@
<string name="permdesc_sdcardWrite" product="default" msgid="4337417790936632090">"Permite que a aplicação escreva no cartão SD."</string>
<string name="permlab_mediaStorageWrite" product="default" msgid="6859839199706879015">"modif./elim. armaz. interno"</string>
<string name="permdesc_mediaStorageWrite" product="default" msgid="8189160597698529185">"Permite que a aplicação modifique o conteúdo de armazenamento de suportes internos."</string>
+ <!-- no translation found for permlab_sdcardAccessAll (8150613823900460576) -->
+ <skip />
+ <!-- no translation found for permdesc_sdcardAccessAll (3215208357415891320) -->
+ <skip />
<string name="permlab_cache_filesystem" msgid="5656487264819669824">"aceder ao sistema de ficheiros da cache"</string>
<string name="permdesc_cache_filesystem" msgid="5578967642265550955">"Permite à aplicação ler e escrever no sistema de ficheiros da cache."</string>
<string name="permlab_use_sip" msgid="5986952362795870502">"fazer/receber chamadas pela internet"</string>
diff --git a/core/res/res/values-pt/strings.xml b/core/res/res/values-pt/strings.xml
index 575630f..5ec23f9 100644
--- a/core/res/res/values-pt/strings.xml
+++ b/core/res/res/values-pt/strings.xml
@@ -547,6 +547,10 @@
<string name="permdesc_sdcardWrite" product="default" msgid="4337417790936632090">"Permite que o aplicativo grave em seu cartão SD."</string>
<string name="permlab_mediaStorageWrite" product="default" msgid="6859839199706879015">"modificar/excluir conteúdos de armazenamento de mídia internos"</string>
<string name="permdesc_mediaStorageWrite" product="default" msgid="8189160597698529185">"Permite que o aplicativo modifique o conteúdo da mídia de armazenamento interno."</string>
+ <!-- no translation found for permlab_sdcardAccessAll (8150613823900460576) -->
+ <skip />
+ <!-- no translation found for permdesc_sdcardAccessAll (3215208357415891320) -->
+ <skip />
<string name="permlab_cache_filesystem" msgid="5656487264819669824">"acessar o sistema de arquivos de cache"</string>
<string name="permdesc_cache_filesystem" msgid="5578967642265550955">"Permite que o aplicativo leia e grave o sistema de arquivos cache."</string>
<string name="permlab_use_sip" msgid="5986952362795870502">"fazer/receber chamadas pela internet"</string>
diff --git a/core/res/res/values-rm/strings.xml b/core/res/res/values-rm/strings.xml
index eafb3b4..40fad47 100644
--- a/core/res/res/values-rm/strings.xml
+++ b/core/res/res/values-rm/strings.xml
@@ -891,6 +891,10 @@
<skip />
<!-- no translation found for permdesc_mediaStorageWrite (8189160597698529185) -->
<skip />
+ <!-- no translation found for permlab_sdcardAccessAll (8150613823900460576) -->
+ <skip />
+ <!-- no translation found for permdesc_sdcardAccessAll (3215208357415891320) -->
+ <skip />
<string name="permlab_cache_filesystem" msgid="5656487264819669824">"access al sistem da datotecas da cache"</string>
<!-- no translation found for permdesc_cache_filesystem (5578967642265550955) -->
<skip />
diff --git a/core/res/res/values-ro/strings.xml b/core/res/res/values-ro/strings.xml
index 969d3a1..52c0c42 100644
--- a/core/res/res/values-ro/strings.xml
+++ b/core/res/res/values-ro/strings.xml
@@ -547,6 +547,10 @@
<string name="permdesc_sdcardWrite" product="default" msgid="4337417790936632090">"Permite aplicaţiei să scrie pe cardul SD."</string>
<string name="permlab_mediaStorageWrite" product="default" msgid="6859839199706879015">"modif./şterg. conţinutul media stocat intern"</string>
<string name="permdesc_mediaStorageWrite" product="default" msgid="8189160597698529185">"Permite aplicaţiei să modifice conţinutul stocării media interne."</string>
+ <!-- no translation found for permlab_sdcardAccessAll (8150613823900460576) -->
+ <skip />
+ <!-- no translation found for permdesc_sdcardAccessAll (3215208357415891320) -->
+ <skip />
<string name="permlab_cache_filesystem" msgid="5656487264819669824">"accesare sistem de fişiere cache"</string>
<string name="permdesc_cache_filesystem" msgid="5578967642265550955">"Permite aplicaţiei să scrie şi să citească sistemul de fişiere cache."</string>
<string name="permlab_use_sip" msgid="5986952362795870502">"efectuare/primire apeluri prin internet"</string>
@@ -1167,14 +1171,10 @@
<string name="vpn_title_long" msgid="6400714798049252294">"VPN este activată de <xliff:g id="APP">%s</xliff:g>"</string>
<string name="vpn_text" msgid="3011306607126450322">"Atingeţi pentru a gestiona reţeaua."</string>
<string name="vpn_text_long" msgid="6407351006249174473">"Conectat la <xliff:g id="SESSION">%s</xliff:g>. Atingeţi pentru a gestiona reţeaua."</string>
- <!-- no translation found for vpn_lockdown_connecting (6443438964440960745) -->
- <skip />
- <!-- no translation found for vpn_lockdown_connected (8202679674819213931) -->
- <skip />
- <!-- no translation found for vpn_lockdown_error (6009249814034708175) -->
- <skip />
- <!-- no translation found for vpn_lockdown_reset (5365010427963548932) -->
- <skip />
+ <string name="vpn_lockdown_connecting" msgid="6443438964440960745">"Se efectuează conectarea la reţeaua VPN activată permanent…"</string>
+ <string name="vpn_lockdown_connected" msgid="8202679674819213931">"Conectat(ă) la reţeaua VPN activată permanent"</string>
+ <string name="vpn_lockdown_error" msgid="6009249814034708175">"Eroare de reţea VPN activată permanent"</string>
+ <string name="vpn_lockdown_reset" msgid="5365010427963548932">"Atingeţi pentru a reseta conexiunea"</string>
<string name="upload_file" msgid="2897957172366730416">"Alegeţi un fişier"</string>
<string name="no_file_chosen" msgid="6363648562170759465">"Nu au fost găsite fişiere"</string>
<string name="reset" msgid="2448168080964209908">"Resetaţi"</string>
diff --git a/core/res/res/values-ru/strings.xml b/core/res/res/values-ru/strings.xml
index a7df6c1..a9a0d0c 100644
--- a/core/res/res/values-ru/strings.xml
+++ b/core/res/res/values-ru/strings.xml
@@ -547,6 +547,10 @@
<string name="permdesc_sdcardWrite" product="default" msgid="4337417790936632090">"Приложение сможет записывать данные на SD-карту."</string>
<string name="permlab_mediaStorageWrite" product="default" msgid="6859839199706879015">"изм./удал. данных мультимедиа"</string>
<string name="permdesc_mediaStorageWrite" product="default" msgid="8189160597698529185">"Приложение сможет изменять контент внутреннего хранилища мультимедиа."</string>
+ <!-- no translation found for permlab_sdcardAccessAll (8150613823900460576) -->
+ <skip />
+ <!-- no translation found for permdesc_sdcardAccessAll (3215208357415891320) -->
+ <skip />
<string name="permlab_cache_filesystem" msgid="5656487264819669824">"получать доступ к кэшу файловой системы"</string>
<string name="permdesc_cache_filesystem" msgid="5578967642265550955">"Приложение сможет выполнять чтение и запись в файловую систему кэша."</string>
<string name="permlab_use_sip" msgid="5986952362795870502">"Осуществление/прием интернет-вызовов"</string>
@@ -1167,14 +1171,10 @@
<string name="vpn_title_long" msgid="6400714798049252294">"Сеть VPN активирована приложением <xliff:g id="APP">%s</xliff:g>"</string>
<string name="vpn_text" msgid="3011306607126450322">"Нажмите, чтобы открыть настройки."</string>
<string name="vpn_text_long" msgid="6407351006249174473">"Сеть VPN подключена: <xliff:g id="SESSION">%s</xliff:g>. Нажмите, чтобы открыть настройки."</string>
- <!-- no translation found for vpn_lockdown_connecting (6443438964440960745) -->
- <skip />
- <!-- no translation found for vpn_lockdown_connected (8202679674819213931) -->
- <skip />
- <!-- no translation found for vpn_lockdown_error (6009249814034708175) -->
- <skip />
- <!-- no translation found for vpn_lockdown_reset (5365010427963548932) -->
- <skip />
+ <string name="vpn_lockdown_connecting" msgid="6443438964440960745">"Подключение…"</string>
+ <string name="vpn_lockdown_connected" msgid="8202679674819213931">"Подключено"</string>
+ <string name="vpn_lockdown_error" msgid="6009249814034708175">"Ошибка"</string>
+ <string name="vpn_lockdown_reset" msgid="5365010427963548932">"Нажмите, чтобы сбросить соединение"</string>
<string name="upload_file" msgid="2897957172366730416">"Выбрать файл"</string>
<string name="no_file_chosen" msgid="6363648562170759465">"Не выбран файл"</string>
<string name="reset" msgid="2448168080964209908">"Сбросить"</string>
diff --git a/core/res/res/values-sk/strings.xml b/core/res/res/values-sk/strings.xml
index 28bfa5e..2bfa219 100644
--- a/core/res/res/values-sk/strings.xml
+++ b/core/res/res/values-sk/strings.xml
@@ -547,6 +547,10 @@
<string name="permdesc_sdcardWrite" product="default" msgid="4337417790936632090">"Umožňuje aplikácii zápis na kartu SD."</string>
<string name="permlab_mediaStorageWrite" product="default" msgid="6859839199706879015">"úprava alebo odstránenie obsahu interného ukladacieho priestoru média"</string>
<string name="permdesc_mediaStorageWrite" product="default" msgid="8189160597698529185">"Umožňuje aplikácii zmeniť obsah interného ukladacieho priestoru média."</string>
+ <!-- no translation found for permlab_sdcardAccessAll (8150613823900460576) -->
+ <skip />
+ <!-- no translation found for permdesc_sdcardAccessAll (3215208357415891320) -->
+ <skip />
<string name="permlab_cache_filesystem" msgid="5656487264819669824">"pristupovať do súborového systému vyrovnávacej pamäte"</string>
<string name="permdesc_cache_filesystem" msgid="5578967642265550955">"Umožňuje aplikácii čítať a zapisovať do súborového systému vyrovnávacej pamäte."</string>
<string name="permlab_use_sip" msgid="5986952362795870502">"uskutočňovať a prijímať internetové hovory"</string>
@@ -1167,14 +1171,10 @@
<string name="vpn_title_long" msgid="6400714798049252294">"Aplikáciu <xliff:g id="APP">%s</xliff:g> aktivovala sieť VPN"</string>
<string name="vpn_text" msgid="3011306607126450322">"Dotykom môžete spravovať sieť."</string>
<string name="vpn_text_long" msgid="6407351006249174473">"Pripojené k relácii <xliff:g id="SESSION">%s</xliff:g>. Po dotyku môžete sieť spravovať."</string>
- <!-- no translation found for vpn_lockdown_connecting (6443438964440960745) -->
- <skip />
- <!-- no translation found for vpn_lockdown_connected (8202679674819213931) -->
- <skip />
- <!-- no translation found for vpn_lockdown_error (6009249814034708175) -->
- <skip />
- <!-- no translation found for vpn_lockdown_reset (5365010427963548932) -->
- <skip />
+ <string name="vpn_lockdown_connecting" msgid="6443438964440960745">"Prebieha pripájanie k vždy zapnutej sieti VPN..."</string>
+ <string name="vpn_lockdown_connected" msgid="8202679674819213931">"Pripojenie k vždy zapnutej sieti VPN"</string>
+ <string name="vpn_lockdown_error" msgid="6009249814034708175">"Chyba vždy zapnutej siete VPN"</string>
+ <string name="vpn_lockdown_reset" msgid="5365010427963548932">"Dotykom obnovíte pripojenie"</string>
<string name="upload_file" msgid="2897957172366730416">"Zvoliť súbor"</string>
<string name="no_file_chosen" msgid="6363648562170759465">"Nie je vybratý žiadny súbor"</string>
<string name="reset" msgid="2448168080964209908">"Obnoviť"</string>
diff --git a/core/res/res/values-sl/strings.xml b/core/res/res/values-sl/strings.xml
index 5a9f299..78c2806 100644
--- a/core/res/res/values-sl/strings.xml
+++ b/core/res/res/values-sl/strings.xml
@@ -547,6 +547,10 @@
<string name="permdesc_sdcardWrite" product="default" msgid="4337417790936632090">"Programu omogoča pisanje na kartico SD."</string>
<string name="permlab_mediaStorageWrite" product="default" msgid="6859839199706879015">"spreminjanje/brisanje vsebine notranje shrambe nosilca podatkov"</string>
<string name="permdesc_mediaStorageWrite" product="default" msgid="8189160597698529185">"Programu omogoča spreminjanje vsebine notranje shrambe nosilca podatkov."</string>
+ <!-- no translation found for permlab_sdcardAccessAll (8150613823900460576) -->
+ <skip />
+ <!-- no translation found for permdesc_sdcardAccessAll (3215208357415891320) -->
+ <skip />
<string name="permlab_cache_filesystem" msgid="5656487264819669824">"dostop do datotečnega sistema predpomnilnika"</string>
<string name="permdesc_cache_filesystem" msgid="5578967642265550955">"Programu omogoča branje in pisanje v datotečni sistem predpomnilnika."</string>
<string name="permlab_use_sip" msgid="5986952362795870502">"opravljanje/sprejemanje internetnih klicev"</string>
@@ -1167,14 +1171,10 @@
<string name="vpn_title_long" msgid="6400714798049252294">"VPN je aktiviral program <xliff:g id="APP">%s</xliff:g>"</string>
<string name="vpn_text" msgid="3011306607126450322">"Dotaknite se, če želite upravljati omrežje."</string>
<string name="vpn_text_long" msgid="6407351006249174473">"Vzpostavljena povezava s sejo <xliff:g id="SESSION">%s</xliff:g>. Dotaknite se, če želite upravljati omrežje."</string>
- <!-- no translation found for vpn_lockdown_connecting (6443438964440960745) -->
- <skip />
- <!-- no translation found for vpn_lockdown_connected (8202679674819213931) -->
- <skip />
- <!-- no translation found for vpn_lockdown_error (6009249814034708175) -->
- <skip />
- <!-- no translation found for vpn_lockdown_reset (5365010427963548932) -->
- <skip />
+ <string name="vpn_lockdown_connecting" msgid="6443438964440960745">"Povezovanje v stalno vklopljeno navidezno zasebno omrežje ..."</string>
+ <string name="vpn_lockdown_connected" msgid="8202679674819213931">"Stalno vklopljeno navidezno zasebno omrežje povezano"</string>
+ <string name="vpn_lockdown_error" msgid="6009249814034708175">"Napaka stalno vklopljenega navideznega zasebnega omrežja"</string>
+ <string name="vpn_lockdown_reset" msgid="5365010427963548932">"Dotaknite se, da ponastavite povezavo"</string>
<string name="upload_file" msgid="2897957172366730416">"Izberi datoteko"</string>
<string name="no_file_chosen" msgid="6363648562170759465">"Nobena datoteka ni izbrana"</string>
<string name="reset" msgid="2448168080964209908">"Ponastavi"</string>
diff --git a/core/res/res/values-sr/strings.xml b/core/res/res/values-sr/strings.xml
index eeeeb30..e7c11b6 100644
--- a/core/res/res/values-sr/strings.xml
+++ b/core/res/res/values-sr/strings.xml
@@ -547,6 +547,10 @@
<string name="permdesc_sdcardWrite" product="default" msgid="4337417790936632090">"Дозвољава апликацији да уписује податке на SD картицу."</string>
<string name="permlab_mediaStorageWrite" product="default" msgid="6859839199706879015">"измена/брисање интерне меморије медија"</string>
<string name="permdesc_mediaStorageWrite" product="default" msgid="8189160597698529185">"Дозвољава апликацији да мења садржај интерне меморије медијума."</string>
+ <!-- no translation found for permlab_sdcardAccessAll (8150613823900460576) -->
+ <skip />
+ <!-- no translation found for permdesc_sdcardAccessAll (3215208357415891320) -->
+ <skip />
<string name="permlab_cache_filesystem" msgid="5656487264819669824">"приступ систему датотека кеша"</string>
<string name="permdesc_cache_filesystem" msgid="5578967642265550955">"Дозвољава апликацији да чита систем датотека кеша и уписује податке у њега."</string>
<string name="permlab_use_sip" msgid="5986952362795870502">"упућивање/пријем Интернет позива"</string>
@@ -1167,14 +1171,10 @@
<string name="vpn_title_long" msgid="6400714798049252294">"Апликација <xliff:g id="APP">%s</xliff:g> је активирала VPN"</string>
<string name="vpn_text" msgid="3011306607126450322">"Додирните да бисте управљали мрежом."</string>
<string name="vpn_text_long" msgid="6407351006249174473">"Повезано са сесијом <xliff:g id="SESSION">%s</xliff:g>. Додирните да бисте управљали мрежом."</string>
- <!-- no translation found for vpn_lockdown_connecting (6443438964440960745) -->
- <skip />
- <!-- no translation found for vpn_lockdown_connected (8202679674819213931) -->
- <skip />
- <!-- no translation found for vpn_lockdown_error (6009249814034708175) -->
- <skip />
- <!-- no translation found for vpn_lockdown_reset (5365010427963548932) -->
- <skip />
+ <string name="vpn_lockdown_connecting" msgid="6443438964440960745">"Повезивање стално укљученог VPN-а..."</string>
+ <string name="vpn_lockdown_connected" msgid="8202679674819213931">"Стално укључени VPN је повезан"</string>
+ <string name="vpn_lockdown_error" msgid="6009249814034708175">"Грешка стално укљученог VPN-а"</string>
+ <string name="vpn_lockdown_reset" msgid="5365010427963548932">"Додирните да бисте ресетовали везу"</string>
<string name="upload_file" msgid="2897957172366730416">"Одабери датотеку"</string>
<string name="no_file_chosen" msgid="6363648562170759465">"Није изабрана ниједна датотека"</string>
<string name="reset" msgid="2448168080964209908">"Поново постави"</string>
diff --git a/core/res/res/values-sv/strings.xml b/core/res/res/values-sv/strings.xml
index 761b8df3..b1009e2 100644
--- a/core/res/res/values-sv/strings.xml
+++ b/core/res/res/values-sv/strings.xml
@@ -547,6 +547,10 @@
<string name="permdesc_sdcardWrite" product="default" msgid="4337417790936632090">"Tillåter att appen skriver till SD-kortet."</string>
<string name="permlab_mediaStorageWrite" product="default" msgid="6859839199706879015">"ändra/ta bort innehåll"</string>
<string name="permdesc_mediaStorageWrite" product="default" msgid="8189160597698529185">"Tillåter att appen ändrar innehållet på den interna lagringsenheten."</string>
+ <!-- no translation found for permlab_sdcardAccessAll (8150613823900460576) -->
+ <skip />
+ <!-- no translation found for permdesc_sdcardAccessAll (3215208357415891320) -->
+ <skip />
<string name="permlab_cache_filesystem" msgid="5656487264819669824">"åtkomst till cachefilsystemet"</string>
<string name="permdesc_cache_filesystem" msgid="5578967642265550955">"Tillåter att appen läser och skriver till cachefilsystemet."</string>
<string name="permlab_use_sip" msgid="5986952362795870502">"ringa/ta emot Internetsamtal"</string>
diff --git a/core/res/res/values-sw/strings.xml b/core/res/res/values-sw/strings.xml
index dcd2d81..cf489c2 100644
--- a/core/res/res/values-sw/strings.xml
+++ b/core/res/res/values-sw/strings.xml
@@ -547,6 +547,10 @@
<string name="permdesc_sdcardWrite" product="default" msgid="4337417790936632090">"Inaruhusu programu kuandikia kadi ya SD."</string>
<string name="permlab_mediaStorageWrite" product="default" msgid="6859839199706879015">"badilisha/futa maudhui ya hifadhi ya media ya ndani."</string>
<string name="permdesc_mediaStorageWrite" product="default" msgid="8189160597698529185">"Inaruhusu programu kurekebisha maudhui ya hifadhi ya ndani vyombo vya habari."</string>
+ <!-- no translation found for permlab_sdcardAccessAll (8150613823900460576) -->
+ <skip />
+ <!-- no translation found for permdesc_sdcardAccessAll (3215208357415891320) -->
+ <skip />
<string name="permlab_cache_filesystem" msgid="5656487264819669824">"fikia faili za mfumo za kache"</string>
<string name="permdesc_cache_filesystem" msgid="5578967642265550955">"Inaruhusu programu kusoma na kuandika mfumo wa faili wa kache."</string>
<string name="permlab_use_sip" msgid="5986952362795870502">"piga/pokea simu za mtandao"</string>
diff --git a/core/res/res/values-th/strings.xml b/core/res/res/values-th/strings.xml
index b9ebf43..9358870 100644
--- a/core/res/res/values-th/strings.xml
+++ b/core/res/res/values-th/strings.xml
@@ -547,6 +547,10 @@
<string name="permdesc_sdcardWrite" product="default" msgid="4337417790936632090">"อนุญาตให้แอปพลิเคชันเขียนลงบนการ์ด SD"</string>
<string name="permlab_mediaStorageWrite" product="default" msgid="6859839199706879015">"แก้/ลบเนื้อหาข้อมูลสื่อภายใน"</string>
<string name="permdesc_mediaStorageWrite" product="default" msgid="8189160597698529185">"อนุญาตให้แอปพลิเคชันแก้ไขเนื้อหาของที่เก็บข้อมูลสื่อภายใน"</string>
+ <!-- no translation found for permlab_sdcardAccessAll (8150613823900460576) -->
+ <skip />
+ <!-- no translation found for permdesc_sdcardAccessAll (3215208357415891320) -->
+ <skip />
<string name="permlab_cache_filesystem" msgid="5656487264819669824">"เข้าถึงระบบไฟล์แคช"</string>
<string name="permdesc_cache_filesystem" msgid="5578967642265550955">"อนุญาตให้แอปพลิเคชันอ่านและเขียนระบบไฟล์แคช"</string>
<string name="permlab_use_sip" msgid="5986952362795870502">"โทรออก/รับสายอินเทอร์เน็ต"</string>
@@ -1167,14 +1171,10 @@
<string name="vpn_title_long" msgid="6400714798049252294">"เปิดใช้งาน VPN โดย <xliff:g id="APP">%s</xliff:g>"</string>
<string name="vpn_text" msgid="3011306607126450322">"แตะเพื่อจัดการเครือข่าย"</string>
<string name="vpn_text_long" msgid="6407351006249174473">"เชื่อมต่อกับ <xliff:g id="SESSION">%s</xliff:g> แตะเพื่อจัดการเครือข่าย"</string>
- <!-- no translation found for vpn_lockdown_connecting (6443438964440960745) -->
- <skip />
- <!-- no translation found for vpn_lockdown_connected (8202679674819213931) -->
- <skip />
- <!-- no translation found for vpn_lockdown_error (6009249814034708175) -->
- <skip />
- <!-- no translation found for vpn_lockdown_reset (5365010427963548932) -->
- <skip />
+ <string name="vpn_lockdown_connecting" msgid="6443438964440960745">"กำลังเชื่อมต่อ VPN แบบเปิดตลอดเวลา…"</string>
+ <string name="vpn_lockdown_connected" msgid="8202679674819213931">"เชื่อมต่อ VPN แบบเปิดตลอดเวลาแล้ว"</string>
+ <string name="vpn_lockdown_error" msgid="6009249814034708175">"ข้อผิดพลาดของ VPN แบบเปิดตลอดเวลา"</string>
+ <string name="vpn_lockdown_reset" msgid="5365010427963548932">"แตะเพื่อรีเซ็ตการเชื่อมต่อ"</string>
<string name="upload_file" msgid="2897957172366730416">"เลือกไฟล์"</string>
<string name="no_file_chosen" msgid="6363648562170759465">"ไม่ได้เลือกไฟล์ไว้"</string>
<string name="reset" msgid="2448168080964209908">"รีเซ็ต"</string>
diff --git a/core/res/res/values-tl/strings.xml b/core/res/res/values-tl/strings.xml
index 4f222b7..1147059 100644
--- a/core/res/res/values-tl/strings.xml
+++ b/core/res/res/values-tl/strings.xml
@@ -547,6 +547,10 @@
<string name="permdesc_sdcardWrite" product="default" msgid="4337417790936632090">"Pinapayagan ang app na magsulat sa SD card."</string>
<string name="permlab_mediaStorageWrite" product="default" msgid="6859839199706879015">"baguhin/tanggalin ang mga nilalaman ng panloob na imbakan ng media"</string>
<string name="permdesc_mediaStorageWrite" product="default" msgid="8189160597698529185">"Pinapayagan ang app na baguhin ang mga nilalaman ng panloob na media storage."</string>
+ <!-- no translation found for permlab_sdcardAccessAll (8150613823900460576) -->
+ <skip />
+ <!-- no translation found for permdesc_sdcardAccessAll (3215208357415891320) -->
+ <skip />
<string name="permlab_cache_filesystem" msgid="5656487264819669824">"i-access ang cache filesystem"</string>
<string name="permdesc_cache_filesystem" msgid="5578967642265550955">"Pinapayagan ang app na basahin at isulat ang cache filesystem."</string>
<string name="permlab_use_sip" msgid="5986952362795870502">"gumawa/tumanggap ng mga tawag sa Internet"</string>
@@ -1167,14 +1171,10 @@
<string name="vpn_title_long" msgid="6400714798049252294">"Isinaaktibo ang VPN ng <xliff:g id="APP">%s</xliff:g>"</string>
<string name="vpn_text" msgid="3011306607126450322">"Pindutin upang pamahalaan ang network."</string>
<string name="vpn_text_long" msgid="6407351006249174473">"Nakakonekta sa <xliff:g id="SESSION">%s</xliff:g>. Pindutin upang pamahalaan ang network."</string>
- <!-- no translation found for vpn_lockdown_connecting (6443438964440960745) -->
- <skip />
- <!-- no translation found for vpn_lockdown_connected (8202679674819213931) -->
- <skip />
- <!-- no translation found for vpn_lockdown_error (6009249814034708175) -->
- <skip />
- <!-- no translation found for vpn_lockdown_reset (5365010427963548932) -->
- <skip />
+ <string name="vpn_lockdown_connecting" msgid="6443438964440960745">"Kumukonekta ang Always-on VPN…"</string>
+ <string name="vpn_lockdown_connected" msgid="8202679674819213931">"Nakakonekta ang Always-on VPN"</string>
+ <string name="vpn_lockdown_error" msgid="6009249814034708175">"Error sa Always-on VPN"</string>
+ <string name="vpn_lockdown_reset" msgid="5365010427963548932">"Pindutin upang i-reset ang koneksyon"</string>
<string name="upload_file" msgid="2897957172366730416">"Pumili ng file"</string>
<string name="no_file_chosen" msgid="6363648562170759465">"Walang napiling file"</string>
<string name="reset" msgid="2448168080964209908">"I-reset"</string>
diff --git a/core/res/res/values-tr/strings.xml b/core/res/res/values-tr/strings.xml
index fe80c3e..a4e7d4d 100644
--- a/core/res/res/values-tr/strings.xml
+++ b/core/res/res/values-tr/strings.xml
@@ -547,6 +547,10 @@
<string name="permdesc_sdcardWrite" product="default" msgid="4337417790936632090">"Uygulamaya, SD karta yazma izni verir."</string>
<string name="permlab_mediaStorageWrite" product="default" msgid="6859839199706879015">"dahili medya depolama birimi içeriğini değiştir/sil"</string>
<string name="permdesc_mediaStorageWrite" product="default" msgid="8189160597698529185">"Uygulamaya, dahili medya depolama içeriğini değiştirme izni verir."</string>
+ <!-- no translation found for permlab_sdcardAccessAll (8150613823900460576) -->
+ <skip />
+ <!-- no translation found for permdesc_sdcardAccessAll (3215208357415891320) -->
+ <skip />
<string name="permlab_cache_filesystem" msgid="5656487264819669824">"önbellek dosya sistemine eriş"</string>
<string name="permdesc_cache_filesystem" msgid="5578967642265550955">"Uygulamaya, önbellek dosya sisteminde okuma ve yazma yapma izni verir."</string>
<string name="permlab_use_sip" msgid="5986952362795870502">"İnternet çağrılar yap/alma"</string>
@@ -1167,14 +1171,10 @@
<string name="vpn_title_long" msgid="6400714798049252294">"VPN, <xliff:g id="APP">%s</xliff:g> tarafından etkinleştirildi"</string>
<string name="vpn_text" msgid="3011306607126450322">"Ağı yönetmek için dokunun."</string>
<string name="vpn_text_long" msgid="6407351006249174473">"<xliff:g id="SESSION">%s</xliff:g> oturumuna bağlandı. Ağı yönetmek için dokunun."</string>
- <!-- no translation found for vpn_lockdown_connecting (6443438964440960745) -->
- <skip />
- <!-- no translation found for vpn_lockdown_connected (8202679674819213931) -->
- <skip />
- <!-- no translation found for vpn_lockdown_error (6009249814034708175) -->
- <skip />
- <!-- no translation found for vpn_lockdown_reset (5365010427963548932) -->
- <skip />
+ <string name="vpn_lockdown_connecting" msgid="6443438964440960745">"Her zaman açık VPN\'ye bağlanılıyor…"</string>
+ <string name="vpn_lockdown_connected" msgid="8202679674819213931">"Her zaman açık VPN\'ye bağlanıldı"</string>
+ <string name="vpn_lockdown_error" msgid="6009249814034708175">"Her zaman açık VPN hatası"</string>
+ <string name="vpn_lockdown_reset" msgid="5365010427963548932">"Bağlantıyı sıfırlamak için dokunun"</string>
<string name="upload_file" msgid="2897957172366730416">"Dosya seç"</string>
<string name="no_file_chosen" msgid="6363648562170759465">"Seçili dosya yok"</string>
<string name="reset" msgid="2448168080964209908">"Sıfırla"</string>
diff --git a/core/res/res/values-uk/strings.xml b/core/res/res/values-uk/strings.xml
index 730c384..5c4a1b1 100644
--- a/core/res/res/values-uk/strings.xml
+++ b/core/res/res/values-uk/strings.xml
@@ -547,6 +547,10 @@
<string name="permdesc_sdcardWrite" product="default" msgid="4337417790936632090">"Дозволяє програмі записувати на карту SD."</string>
<string name="permlab_mediaStorageWrite" product="default" msgid="6859839199706879015">"змінювати/видаляти вміст внутр. сховища даних"</string>
<string name="permdesc_mediaStorageWrite" product="default" msgid="8189160597698529185">"Дозволяє програмі змінювати вміст внутрішнього сховища даних."</string>
+ <!-- no translation found for permlab_sdcardAccessAll (8150613823900460576) -->
+ <skip />
+ <!-- no translation found for permdesc_sdcardAccessAll (3215208357415891320) -->
+ <skip />
<string name="permlab_cache_filesystem" msgid="5656487264819669824">"отр. дост. до файл. сист. кешу"</string>
<string name="permdesc_cache_filesystem" msgid="5578967642265550955">"Дозволяє програмі читати з файлової системи кеш-пам’яті та писати в неї."</string>
<string name="permlab_use_sip" msgid="5986952362795870502">"здійсн./отрим. Інтернет-дзвін."</string>
@@ -1167,14 +1171,10 @@
<string name="vpn_title_long" msgid="6400714798049252294">"Мережу VPN активовано програмою <xliff:g id="APP">%s</xliff:g>"</string>
<string name="vpn_text" msgid="3011306607126450322">"Торкніться, щоб керувати мережею."</string>
<string name="vpn_text_long" msgid="6407351006249174473">"Під’єднано до сеансу <xliff:g id="SESSION">%s</xliff:g>. Торкніться, щоб керувати мережею."</string>
- <!-- no translation found for vpn_lockdown_connecting (6443438964440960745) -->
- <skip />
- <!-- no translation found for vpn_lockdown_connected (8202679674819213931) -->
- <skip />
- <!-- no translation found for vpn_lockdown_error (6009249814034708175) -->
- <skip />
- <!-- no translation found for vpn_lockdown_reset (5365010427963548932) -->
- <skip />
+ <string name="vpn_lockdown_connecting" msgid="6443438964440960745">"Під’єднання до постійної мережі VPN…"</string>
+ <string name="vpn_lockdown_connected" msgid="8202679674819213931">"Під’єднано до постійної мережі VPN"</string>
+ <string name="vpn_lockdown_error" msgid="6009249814034708175">"Помилка постійної мережі VPN"</string>
+ <string name="vpn_lockdown_reset" msgid="5365010427963548932">"Торкніться, щоб скинути з’єднання"</string>
<string name="upload_file" msgid="2897957172366730416">"Виберіть файл"</string>
<string name="no_file_chosen" msgid="6363648562170759465">"Не вибрано файл"</string>
<string name="reset" msgid="2448168080964209908">"Віднов."</string>
diff --git a/core/res/res/values-vi/strings.xml b/core/res/res/values-vi/strings.xml
index 6ffc84e..66d0f31 100644
--- a/core/res/res/values-vi/strings.xml
+++ b/core/res/res/values-vi/strings.xml
@@ -547,6 +547,10 @@
<string name="permdesc_sdcardWrite" product="default" msgid="4337417790936632090">"Cho phép ứng dụng ghi vào thẻ SD."</string>
<string name="permlab_mediaStorageWrite" product="default" msgid="6859839199706879015">"sửa đổi/xóa nội dung trên bộ nhớ phương tiện cục bộ"</string>
<string name="permdesc_mediaStorageWrite" product="default" msgid="8189160597698529185">"Cho phép ứng dụng sửa đổi nội dung của bộ lưu trữ phương tiện nội bộ."</string>
+ <!-- no translation found for permlab_sdcardAccessAll (8150613823900460576) -->
+ <skip />
+ <!-- no translation found for permdesc_sdcardAccessAll (3215208357415891320) -->
+ <skip />
<string name="permlab_cache_filesystem" msgid="5656487264819669824">"truy cập hệ thống tệp bộ nhớ cache"</string>
<string name="permdesc_cache_filesystem" msgid="5578967642265550955">"Cho phép ứng dụng đọc và ghi hệ thống tệp bộ nhớ cache."</string>
<string name="permlab_use_sip" msgid="5986952362795870502">"thực hiện/nhận cuộc gọi qua Internet"</string>
@@ -1167,14 +1171,10 @@
<string name="vpn_title_long" msgid="6400714798049252294">"VPN được <xliff:g id="APP">%s</xliff:g> kích hoạt"</string>
<string name="vpn_text" msgid="3011306607126450322">"Chạm để quản lý mạng."</string>
<string name="vpn_text_long" msgid="6407351006249174473">"Đã kết nối với <xliff:g id="SESSION">%s</xliff:g>. Chạm để quản lý mạng."</string>
- <!-- no translation found for vpn_lockdown_connecting (6443438964440960745) -->
- <skip />
- <!-- no translation found for vpn_lockdown_connected (8202679674819213931) -->
- <skip />
- <!-- no translation found for vpn_lockdown_error (6009249814034708175) -->
- <skip />
- <!-- no translation found for vpn_lockdown_reset (5365010427963548932) -->
- <skip />
+ <string name="vpn_lockdown_connecting" msgid="6443438964440960745">"Đang kết nối VPN luôn bật…"</string>
+ <string name="vpn_lockdown_connected" msgid="8202679674819213931">"Đã kết nối VPN luôn bật"</string>
+ <string name="vpn_lockdown_error" msgid="6009249814034708175">"Lỗi VPN luôn bật"</string>
+ <string name="vpn_lockdown_reset" msgid="5365010427963548932">"Chạm để đặt lại kết nối"</string>
<string name="upload_file" msgid="2897957172366730416">"Chọn tệp"</string>
<string name="no_file_chosen" msgid="6363648562170759465">"Không có tệp nào được chọn"</string>
<string name="reset" msgid="2448168080964209908">"Đặt lại"</string>
diff --git a/core/res/res/values-zh-rCN/strings.xml b/core/res/res/values-zh-rCN/strings.xml
index 12de741..4c42e4a 100644
--- a/core/res/res/values-zh-rCN/strings.xml
+++ b/core/res/res/values-zh-rCN/strings.xml
@@ -547,6 +547,10 @@
<string name="permdesc_sdcardWrite" product="default" msgid="4337417790936632090">"允许应用写入 SD 卡。"</string>
<string name="permlab_mediaStorageWrite" product="default" msgid="6859839199706879015">"修改/删除内部媒体存储设备的内容"</string>
<string name="permdesc_mediaStorageWrite" product="default" msgid="8189160597698529185">"允许应用修改内部媒体存储设备的内容。"</string>
+ <!-- no translation found for permlab_sdcardAccessAll (8150613823900460576) -->
+ <skip />
+ <!-- no translation found for permdesc_sdcardAccessAll (3215208357415891320) -->
+ <skip />
<string name="permlab_cache_filesystem" msgid="5656487264819669824">"访问缓存文件系统"</string>
<string name="permdesc_cache_filesystem" msgid="5578967642265550955">"允许应用读取和写入缓存文件系统。"</string>
<string name="permlab_use_sip" msgid="5986952362795870502">"拨打/接听互联网通话"</string>
diff --git a/core/res/res/values-zh-rTW/strings.xml b/core/res/res/values-zh-rTW/strings.xml
index 84725cf..53f3d39 100644
--- a/core/res/res/values-zh-rTW/strings.xml
+++ b/core/res/res/values-zh-rTW/strings.xml
@@ -547,6 +547,10 @@
<string name="permdesc_sdcardWrite" product="default" msgid="4337417790936632090">"允許應用程式寫入 SD 卡。"</string>
<string name="permlab_mediaStorageWrite" product="default" msgid="6859839199706879015">"修改/刪除內部媒體儲存裝置內容"</string>
<string name="permdesc_mediaStorageWrite" product="default" msgid="8189160597698529185">"允許應用程式修改內部媒體儲存空間的內容。"</string>
+ <!-- no translation found for permlab_sdcardAccessAll (8150613823900460576) -->
+ <skip />
+ <!-- no translation found for permdesc_sdcardAccessAll (3215208357415891320) -->
+ <skip />
<string name="permlab_cache_filesystem" msgid="5656487264819669824">"存取快取檔案系統"</string>
<string name="permdesc_cache_filesystem" msgid="5578967642265550955">"允許應用程式讀取及寫入快取檔案系統。"</string>
<string name="permlab_use_sip" msgid="5986952362795870502">"撥打/接聽網路電話"</string>
@@ -1167,14 +1171,10 @@
<string name="vpn_title_long" msgid="6400714798049252294">"<xliff:g id="APP">%s</xliff:g> 已啟用 VPN"</string>
<string name="vpn_text" msgid="3011306607126450322">"輕觸即可管理網路。"</string>
<string name="vpn_text_long" msgid="6407351006249174473">"已連線至 <xliff:g id="SESSION">%s</xliff:g>,輕觸即可管理網路。"</string>
- <!-- no translation found for vpn_lockdown_connecting (6443438964440960745) -->
- <skip />
- <!-- no translation found for vpn_lockdown_connected (8202679674819213931) -->
- <skip />
- <!-- no translation found for vpn_lockdown_error (6009249814034708175) -->
- <skip />
- <!-- no translation found for vpn_lockdown_reset (5365010427963548932) -->
- <skip />
+ <string name="vpn_lockdown_connecting" msgid="6443438964440960745">"正在連線至永久連線的 VPN…"</string>
+ <string name="vpn_lockdown_connected" msgid="8202679674819213931">"已連線至永久連線的 VPN"</string>
+ <string name="vpn_lockdown_error" msgid="6009249814034708175">"永久連線的 VPN 發生錯誤"</string>
+ <string name="vpn_lockdown_reset" msgid="5365010427963548932">"輕觸即可重設連線"</string>
<string name="upload_file" msgid="2897957172366730416">"選擇檔案"</string>
<string name="no_file_chosen" msgid="6363648562170759465">"未選擇任何檔案"</string>
<string name="reset" msgid="2448168080964209908">"重設"</string>
diff --git a/core/res/res/values-zu/strings.xml b/core/res/res/values-zu/strings.xml
index e8eaaa6..232c933 100644
--- a/core/res/res/values-zu/strings.xml
+++ b/core/res/res/values-zu/strings.xml
@@ -547,6 +547,10 @@
<string name="permdesc_sdcardWrite" product="default" msgid="4337417790936632090">"Ivumela insiza ukuthi ibhalele ekhadini le-SD."</string>
<string name="permlab_mediaStorageWrite" product="default" msgid="6859839199706879015">"guqula/susa okuqukethwe kwisitoreji semidiya yangaphakathi"</string>
<string name="permdesc_mediaStorageWrite" product="default" msgid="8189160597698529185">"Ivumela insiza ukuthi iguqule okuqukethwe kokulondoloza imidiya yangaphakathi."</string>
+ <!-- no translation found for permlab_sdcardAccessAll (8150613823900460576) -->
+ <skip />
+ <!-- no translation found for permdesc_sdcardAccessAll (3215208357415891320) -->
+ <skip />
<string name="permlab_cache_filesystem" msgid="5656487264819669824">"finyelela kunqolobane yesistimu yefayela"</string>
<string name="permdesc_cache_filesystem" msgid="5578967642265550955">"Ivumela insiza ukuthi ifunde futhi ibhale isistimu yokufayila amafayela esikhashana."</string>
<string name="permlab_use_sip" msgid="5986952362795870502">"yena/thola amakholi e-Inthanethi"</string>
diff --git a/core/res/res/values/attrs.xml b/core/res/res/values/attrs.xml
index 209fff0..9601ad4 100755
--- a/core/res/res/values/attrs.xml
+++ b/core/res/res/values/attrs.xml
@@ -768,11 +768,15 @@
<attr name="dialogCustomTitleDecorLayout" format="reference" />
<!-- Window decor layout to use in dialog mode with title only -->
<attr name="dialogTitleDecorLayout" format="reference" />
+
<!-- Theme to use for alert dialogs spawned from this theme. -->
<attr name="alertDialogTheme" format="reference" />
<!-- Icon drawable to use for alerts -->
<attr name="alertDialogIcon" format="reference" />
+ <!-- Theme to use for presentations spawned from this theme. -->
+ <attr name="presentationTheme" format="reference" />
+
<!-- Drawable to use for generic vertical dividers. -->
<attr name="dividerVertical" format="reference" />
@@ -2370,6 +2374,12 @@
<!-- The extra value of the subtype. This string can be any string and will be passed to
the IME when the framework calls the IME with the subtype. -->
<attr name="imeSubtypeExtraValue" format="string" />
+ <!-- The unique id for the subtype. The input method framework keeps track of enabled
+ subtypes by ID. When the IME package gets upgraded, enabled IDs will stay enabled even
+ if other attributes are different. If the ID is unspecified (by calling the other
+ constructor or 0. Arrays.hashCode(new Object[] {locale, mode, extraValue,
+ isAuxiliary, overridesImplicitlyEnabledSubtype}) will be used instead. -->
+ <attr name="subtypeId" format="integer"/>
</declare-styleable>
<!-- Use <code>spell-checker</code> as the root tag of the XML resource that
diff --git a/core/res/res/values/attrs_manifest.xml b/core/res/res/values/attrs_manifest.xml
index 1c3318d..3a69937 100644
--- a/core/res/res/values/attrs_manifest.xml
+++ b/core/res/res/values/attrs_manifest.xml
@@ -639,6 +639,8 @@
physical screen size has changed such as switching to an external
display. -->
<flag name="smallestScreenSize" value="0x0800" />
+ <!-- The layout direction has changed. For example going from LTR to RTL. -->
+ <flag name="layoutDirection" value="0x2000" />
<!-- The font scaling factor has changed, that is the user has
selected a new global font size. -->
<flag name="fontScale" value="0x40000000" />
diff --git a/core/res/res/values/config.xml b/core/res/res/values/config.xml
index 86d03ad..bba2252 100755
--- a/core/res/res/values/config.xml
+++ b/core/res/res/values/config.xml
@@ -471,6 +471,9 @@
Also, the battery stats are flushed to disk when we hit this level. -->
<integer name="config_criticalBatteryWarningLevel">4</integer>
+ <!-- Shutdown if the battery temperature exceeds (this value * 0.1) Celsius. -->
+ <integer name="config_shutdownBatteryTemperature">680</integer>
+
<!-- Display low battery warning when battery level dips to this value -->
<!-- Display low battery warning when battery level dips to this value -->
<integer name="config_lowBatteryWarningLevel">15</integer>
@@ -479,7 +482,7 @@
<integer name="config_lowBatteryCloseWarningLevel">20</integer>
<!-- Default color for notification LED. -->
- <color name="config_defaultNotificationColor">#ff00ff00</color>
+ <color name="config_defaultNotificationColor">#ffffffff</color>
<!-- Default LED on time for notification LED in milliseconds. -->
<integer name="config_defaultNotificationLedOn">500</integer>
@@ -916,6 +919,9 @@
<!-- Set to true to add links to Cell Broadcast app from Settings and MMS app. -->
<bool name="config_cellBroadcastAppLinks">false</bool>
+ <!-- The default value if the SyncStorageEngine should sync automatically or not -->
+ <bool name="config_syncstorageengine_masterSyncAutomatically">true</bool>
+
<!-- Maximum number of supported users -->
<integer name="config_multiuserMaximumUsers">10</integer>
</resources>
diff --git a/core/res/res/values/dimens.xml b/core/res/res/values/dimens.xml
index 372a1ee..48c52cc 100644
--- a/core/res/res/values/dimens.xml
+++ b/core/res/res/values/dimens.xml
@@ -193,6 +193,9 @@
<!-- Padding on left margin of PIN text entry field to center it when del button is showing -->
<dimen name="keyguard_lockscreen_pin_margin_left">40dip</dimen>
+ <!-- Height of FaceUnlock widget in keyguard -->
+ <dimen name="face_unlock_height">330dip</dimen>
+
<!-- Minimum popup width for selecting an activity in ActivityChooserDialog/ActivityChooserView. -->
<dimen name="activity_chooser_popup_min_width">200dip</dimen>
diff --git a/core/res/res/values/integers.xml b/core/res/res/values/integers.xml
index 859eefc..7834997 100644
--- a/core/res/res/values/integers.xml
+++ b/core/res/res/values/integers.xml
@@ -18,4 +18,5 @@
-->
<resources>
<integer name="kg_security_flip_duration">150</integer>
+ <integer name="kg_security_fade_duration">150</integer>
</resources>
diff --git a/core/res/res/values/public.xml b/core/res/res/values/public.xml
index 4dede0b..307fc81 100644
--- a/core/res/res/values/public.xml
+++ b/core/res/res/values/public.xml
@@ -255,6 +255,7 @@
<java-symbol type="bool" name="config_sms_capable" />
<java-symbol type="bool" name="config_sms_utf8_support" />
<java-symbol type="bool" name="config_swipeDisambiguation" />
+ <java-symbol type="bool" name="config_syncstorageengine_masterSyncAutomatically" />
<java-symbol type="bool" name="config_telephony_use_own_number_for_voicemail" />
<java-symbol type="bool" name="config_ui_enableFadingMarquee" />
<java-symbol type="bool" name="config_use_strict_phone_number_comparation" />
@@ -482,6 +483,7 @@
<java-symbol type="string" name="default_text_encoding" />
<java-symbol type="string" name="description_target_unlock_tablet" />
<java-symbol type="string" name="display_manager_built_in_display_name" />
+ <java-symbol type="string" name="display_manager_hdmi_display_name" />
<java-symbol type="string" name="display_manager_overlay_display_name" />
<java-symbol type="string" name="display_manager_overlay_display_title" />
<java-symbol type="string" name="double_tap_toast" />
@@ -709,6 +711,7 @@
<java-symbol type="string" name="policydesc_setGlobalProxy" />
<java-symbol type="string" name="policydesc_watchLogin" />
<java-symbol type="string" name="policydesc_wipeData" />
+ <java-symbol type="string" name="policydesc_disableKeyguardWidgets" />
<java-symbol type="string" name="policylab_disableCamera" />
<java-symbol type="string" name="policylab_encryptedStorage" />
<java-symbol type="string" name="policylab_expirePassword" />
@@ -718,6 +721,7 @@
<java-symbol type="string" name="policylab_setGlobalProxy" />
<java-symbol type="string" name="policylab_watchLogin" />
<java-symbol type="string" name="policylab_wipeData" />
+ <java-symbol type="string" name="policylab_disableKeyguardWidgets" />
<java-symbol type="string" name="postalTypeCustom" />
<java-symbol type="string" name="postalTypeHome" />
<java-symbol type="string" name="postalTypeOther" />
@@ -1210,6 +1214,8 @@
<java-symbol type="anim" name="dock_right_exit" />
<java-symbol type="anim" name="keyguard_security_animate_in" />
<java-symbol type="anim" name="keyguard_security_animate_out" />
+ <java-symbol type="anim" name="keyguard_security_fade_in" />
+ <java-symbol type="anim" name="keyguard_security_fade_out" />
<java-symbol type="array" name="config_keyboardTapVibePattern" />
<java-symbol type="array" name="config_longPressVibePattern" />
<java-symbol type="array" name="config_safeModeDisabledVibePattern" />
@@ -1269,7 +1275,7 @@
<java-symbol type="id" name="date" />
<java-symbol type="id" name="eight" />
<java-symbol type="id" name="emergencyCallButton" />
- <java-symbol type="id" name="faceLockAreaView" />
+ <java-symbol type="id" name="face_unlock_area_view" />
<java-symbol type="id" name="five" />
<java-symbol type="id" name="forgotPatternButton" />
<java-symbol type="id" name="four" />
@@ -1292,7 +1298,7 @@
<java-symbol type="id" name="passwordEntry" />
<java-symbol type="id" name="pinDel" />
<java-symbol type="id" name="pinDisplay" />
- <java-symbol type="id" name="propertyOf" />
+ <java-symbol type="id" name="owner_info" />
<java-symbol type="id" name="pukDel" />
<java-symbol type="id" name="pukDisplay" />
<java-symbol type="id" name="right_icon" />
@@ -1543,6 +1549,7 @@
<java-symbol type="integer" name="config_screenBrightnessSettingMaximum" />
<java-symbol type="integer" name="config_screenBrightnessSettingDefault" />
<java-symbol type="integer" name="config_screenBrightnessDim" />
+ <java-symbol type="integer" name="config_shutdownBatteryTemperature" />
<java-symbol type="integer" name="config_virtualKeyQuietTimeMillis" />
<java-symbol type="layout" name="am_compat_mode_dialog" />
<java-symbol type="layout" name="launch_warning" />
@@ -3759,5 +3766,7 @@
<public type="attr" name="listPreferredItemPaddingStart" />
<public type="attr" name="listPreferredItemPaddingEnd" />
<public type="attr" name="singleUser" />
+ <public type="attr" name="presentationTheme" />
+ <public type="attr" name="subtypeId"/>
</resources>
diff --git a/core/res/res/values/strings.xml b/core/res/res/values/strings.xml
index 894e272..d2951bf 100755
--- a/core/res/res/values/strings.xml
+++ b/core/res/res/values/strings.xml
@@ -1693,6 +1693,10 @@
<string name="policylab_disableCamera">Disable cameras</string>
<!-- Description of policy access to disable all device cameras [CHAR LIMIT=110]-->
<string name="policydesc_disableCamera">Prevent use of all device cameras.</string>
+ <!-- Title of policy access to disable all device cameras [CHAR LIMIT=30]-->
+ <string name="policylab_disableKeyguardWidgets">Disable widgets on keyguard</string>
+ <!-- Description of policy access to disable all device cameras [CHAR LIMIT=110]-->
+ <string name="policydesc_disableKeyguardWidgets">Prevent use of some or all widgets on keyguard.</string>
<!-- The order of these is important, don't reorder without changing Contacts.java --> <skip />
<!-- Phone number types from android.provider.Contacts. This could be used when adding a new phone number for a contact, for example. -->
@@ -3658,11 +3662,14 @@
<!-- Name of the built-in display. [CHAR LIMIT=50] -->
<string name="display_manager_built_in_display_name">Built-in Screen</string>
+ <!-- Name of the HDMI display. [CHAR LIMIT=50] -->
+ <string name="display_manager_hdmi_display_name">HDMI Screen</string>
+
<!-- Name of the N'th overlay display for testing. [CHAR LIMIT=50] -->
<string name="display_manager_overlay_display_name">Overlay #<xliff:g id="id">%1$d</xliff:g></string>
<!-- Title text to show within the overlay. [CHAR LIMIT=50] -->
- <string name="display_manager_overlay_display_title">Overlay #<xliff:g id="id">%1$d</xliff:g>: <xliff:g id="width">%2$d</xliff:g>x<xliff:g id="height">%3$d</xliff:g>, <xliff:g id="dpi">%4$d</xliff:g> dpi</string>
+ <string name="display_manager_overlay_display_title"><xliff:g id="name">%1$s</xliff:g>: <xliff:g id="width">%2$d</xliff:g>x<xliff:g id="height">%3$d</xliff:g>, <xliff:g id="dpi">%4$d</xliff:g> dpi</string>
<!-- Keyguard strings -->
<!-- Label shown on emergency call button in keyguard -->
diff --git a/core/res/res/values/styles_device_defaults.xml b/core/res/res/values/styles_device_defaults.xml
index 28fed45..d465356 100644
--- a/core/res/res/values/styles_device_defaults.xml
+++ b/core/res/res/values/styles_device_defaults.xml
@@ -673,7 +673,6 @@
</style>
-
<!-- Animation Styles -->
<style name="Animation.DeviceDefault.Activity" parent="Animation.Holo.Activity">
diff --git a/core/res/res/values/themes.xml b/core/res/res/values/themes.xml
index 2f93335..215551b 100644
--- a/core/res/res/values/themes.xml
+++ b/core/res/res/values/themes.xml
@@ -186,17 +186,24 @@
<item name="windowFixedHeightMinor">0dp</item>
<!-- Dialog attributes -->
- <item name="alertDialogStyle">@android:style/AlertDialog</item>
<item name="dialogTheme">@android:style/Theme.Dialog</item>
<item name="dialogTitleIconsDecorLayout">@layout/dialog_title_icons</item>
<item name="dialogCustomTitleDecorLayout">@layout/dialog_custom_title</item>
<item name="dialogTitleDecorLayout">@layout/dialog_title</item>
+
+ <!-- AlertDialog attributes -->
<item name="alertDialogTheme">@android:style/Theme.Dialog.Alert</item>
+ <item name="alertDialogStyle">@android:style/AlertDialog</item>
<item name="alertDialogCenterButtons">true</item>
<item name="alertDialogIcon">@android:drawable/ic_dialog_alert</item>
+ <!-- Presentation attributes (introduced after API level 10 so does not
+ have a special old-style theme. -->
+ <item name="presentationTheme">@android:style/Theme.DeviceDefault.Dialog.Presentation</item>
+
+ <!-- Toast attributes -->
<item name="toastFrameBackground">@android:drawable/toast_frame</item>
-
+
<!-- Panel attributes -->
<item name="panelBackground">@android:drawable/menu_background</item>
<item name="panelFullBackground">@android:drawable/menu_background_fill_parent_width</item>
@@ -1012,17 +1019,23 @@
<item name="windowActionModeOverlay">false</item>
<!-- Dialog attributes -->
- <item name="alertDialogStyle">@android:style/AlertDialog.Holo</item>
<item name="dialogTheme">@android:style/Theme.Holo.Dialog</item>
<item name="dialogTitleIconsDecorLayout">@layout/dialog_title_icons_holo</item>
<item name="dialogCustomTitleDecorLayout">@layout/dialog_custom_title_holo</item>
<item name="dialogTitleDecorLayout">@layout/dialog_title_holo</item>
+
+ <!-- AlertDialog attributes -->
<item name="alertDialogTheme">@android:style/Theme.Holo.Dialog.Alert</item>
+ <item name="alertDialogStyle">@android:style/AlertDialog.Holo</item>
<item name="alertDialogCenterButtons">false</item>
<item name="alertDialogIcon">@android:drawable/ic_dialog_alert_holo_dark</item>
+ <!-- Presentation attributes -->
+ <item name="presentationTheme">@android:style/Theme.Holo.Dialog.Presentation</item>
+
+ <!-- Toast attributes -->
<item name="toastFrameBackground">@android:drawable/toast_frame_holo</item>
-
+
<!-- Panel attributes -->
<item name="panelBackground">@android:drawable/menu_hardkey_panel_holo_dark</item>
<item name="panelFullBackground">@android:drawable/menu_background_fill_parent_width</item>
@@ -1319,17 +1332,23 @@
<item name="windowActionModeOverlay">false</item>
<!-- Dialog attributes -->
- <item name="alertDialogStyle">@android:style/AlertDialog.Holo.Light</item>
<item name="dialogTheme">@android:style/Theme.Holo.Light.Dialog</item>
<item name="dialogTitleIconsDecorLayout">@layout/dialog_title_icons_holo</item>
<item name="dialogCustomTitleDecorLayout">@layout/dialog_custom_title_holo</item>
<item name="dialogTitleDecorLayout">@layout/dialog_title_holo</item>
- <item name="alertDialogCenterButtons">false</item>
+
+ <!-- AlertDialog attributes -->
<item name="alertDialogTheme">@android:style/Theme.Holo.Light.Dialog.Alert</item>
+ <item name="alertDialogStyle">@android:style/AlertDialog.Holo.Light</item>
+ <item name="alertDialogCenterButtons">false</item>
<item name="alertDialogIcon">@android:drawable/ic_dialog_alert_holo_light</item>
+ <!-- Presentation attributes -->
+ <item name="presentationTheme">@android:style/Theme.Holo.Light.Dialog.Presentation</item>
+
+ <!-- Toast attributes -->
<item name="toastFrameBackground">@android:drawable/toast_frame_holo</item>
-
+
<!-- Panel attributes -->
<item name="panelBackground">@android:drawable/menu_hardkey_panel_holo_light</item>
<item name="panelFullBackground">@android:drawable/menu_background_fill_parent_width</item>
@@ -1663,6 +1682,10 @@
<style name="Theme.Holo.DialogWhenLarge.NoActionBar" parent="@android:style/Theme.Holo.NoActionBar">
</style>
+ <!-- Theme for a presentation window on a secondary display. -->
+ <style name="Theme.Holo.Dialog.Presentation" parent="@android:style/Theme.Holo.NoActionBar.Fullscreen">
+ </style>
+
<!-- Light holo dialog themes -->
<!-- Holo light theme for dialog windows and activities, which is used by the
@@ -1760,6 +1783,10 @@
<item name="android:windowMinWidthMinor">@android:dimen/dialog_min_width_minor</item>
</style>
+ <!-- Theme for a presentation window on a secondary display. -->
+ <style name="Theme.Holo.Light.Dialog.Presentation" parent="@android:style/Theme.Holo.Light.NoActionBar.Fullscreen" >
+ </style>
+
<!-- Default holographic (dark) for windows that want to have the user's selected
wallpaper appear behind them. -->
<style name="Theme.Holo.Wallpaper">
diff --git a/core/res/res/values/themes_device_defaults.xml b/core/res/res/values/themes_device_defaults.xml
index eef831f..2a2b9e0 100644
--- a/core/res/res/values/themes_device_defaults.xml
+++ b/core/res/res/values/themes_device_defaults.xml
@@ -87,9 +87,14 @@
<item name="android:windowAnimationStyle">@android:style/Animation.DeviceDefault.Activity</item>
<!-- Dialog attributes -->
- <item name="alertDialogStyle">@android:style/AlertDialog.DeviceDefault</item>
<item name="dialogTheme">@android:style/Theme.DeviceDefault.Dialog</item>
+
+ <!-- AlertDialog attributes -->
<item name="alertDialogTheme">@android:style/Theme.DeviceDefault.Dialog.Alert</item>
+ <item name="alertDialogStyle">@android:style/AlertDialog.DeviceDefault</item>
+
+ <!-- Presentation attributes -->
+ <item name="presentationTheme">@android:style/Theme.DeviceDefault.Dialog.Presentation</item>
<!-- Text selection handle attributes -->
<item name="textSelectHandleWindowStyle">@android:style/Widget.DeviceDefault.TextSelectHandle</item>
@@ -239,9 +244,14 @@
<item name="android:windowAnimationStyle">@android:style/Animation.DeviceDefault.Activity</item>
<!-- Dialog attributes -->
- <item name="alertDialogStyle">@android:style/AlertDialog.DeviceDefault.Light</item>
<item name="dialogTheme">@android:style/Theme.DeviceDefault.Light.Dialog</item>
+
+ <!-- AlertDialog attributes -->
<item name="alertDialogTheme">@android:style/Theme.DeviceDefault.Light.Dialog.Alert</item>
+ <item name="alertDialogStyle">@android:style/AlertDialog.DeviceDefault.Light</item>
+
+ <!-- Presentation attributes -->
+ <item name="presentationTheme">@android:style/Theme.DeviceDefault.Light.Dialog.Presentation</item>
<!-- Text selection handle attributes -->
<item name="textSelectHandleWindowStyle">@android:style/Widget.DeviceDefault.TextSelectHandle</item>
@@ -460,6 +470,15 @@
<style name="Theme.DeviceDefault.Light.DialogWhenLarge.NoActionBar" parent="Theme.Holo.Light.DialogWhenLarge.NoActionBar" >
</style>
+
+ <!-- DeviceDefault theme for a presentation window on a secondary display. -->
+ <style name="Theme.DeviceDefault.Dialog.Presentation" parent="Theme.Holo.Dialog.Presentation">
+ </style>
+
+ <!-- DeviceDefault light theme for a presentation window on a secondary display. -->
+ <style name="Theme.DeviceDefault.Light.Dialog.Presentation" parent="Theme.Holo.Light.Dialog.Presentation">
+ </style>
+
<!-- DeviceDefault theme for panel windows. This removes all extraneous window
decorations, so you basically have an empty rectangle in which to place your content. It makes
the window floating, with a transparent background, and turns off dimming behind the window. -->
diff --git a/core/tests/coretests/src/android/app/activity/BroadcastTest.java b/core/tests/coretests/src/android/app/activity/BroadcastTest.java
index 7f551b0..f28ba7e 100644
--- a/core/tests/coretests/src/android/app/activity/BroadcastTest.java
+++ b/core/tests/coretests/src/android/app/activity/BroadcastTest.java
@@ -305,7 +305,7 @@
Intent intent = new Intent(LaunchpadActivity.BROADCAST_STICKY1, null);
intent.putExtra("test", LaunchpadActivity.DATA_1);
ActivityManagerNative.getDefault().unbroadcastIntent(null, intent,
- Binder.getOrigCallingUser());
+ UserHandle.myUserId());
ActivityManagerNative.broadcastStickyIntent(intent, null, UserHandle.myUserId());
addIntermediate("finished-broadcast");
@@ -323,7 +323,7 @@
ActivityManagerNative.getDefault().unbroadcastIntent(
null, new Intent(LaunchpadActivity.BROADCAST_STICKY1, null),
- Binder.getOrigCallingUser());
+ UserHandle.myUserId());
addIntermediate("finished-unbroadcast");
IntentFilter filter = new IntentFilter(LaunchpadActivity.BROADCAST_STICKY1);
diff --git a/docs/html/about/dashboards/index.jd b/docs/html/about/dashboards/index.jd
index 4a75b91..c3da3bf 100644
--- a/docs/html/about/dashboards/index.jd
+++ b/docs/html/about/dashboards/index.jd
@@ -20,44 +20,43 @@
<p>The following pie chart and table is based on the number of Android devices that have accessed
Google Play within a 14-day period ending on the data collection date noted below.</p>
-<div class="col-6" style="margin-left:0">
+<div class="col-5" style="margin-left:0">
<table>
<tr>
<th>Version</th>
<th>Codename</th>
- <th>API Level</th>
+ <th>API</th>
<th>Distribution</th>
</tr>
<tr><td><a href="/about/versions/android-1.5.html">1.5</a></td><td>Cupcake</td> <td>3</td><td>0.2%</td></tr>
-<tr><td><a href="/about/versions/android-1.6.html">1.6</a></td><td>Donut</td> <td>4</td><td>0.5%</td></tr>
-<tr><td><a href="/about/versions/android-2.1.html">2.1</a></td><td>Eclair</td> <td>7</td><td>4.2%</td></tr>
-<tr><td><a href="/about/versions/android-2.2.html">2.2</a></td><td>Froyo</td> <td>8</td><td>15.5%</td></tr>
+<tr><td><a href="/about/versions/android-1.6.html">1.6</a></td><td>Donut</td> <td>4</td><td>0.4%</td></tr>
+<tr><td><a href="/about/versions/android-2.1.html">2.1</a></td><td>Eclair</td> <td>7</td><td>3.7%</td></tr>
+<tr><td><a href="/about/versions/android-2.2.html">2.2</a></td><td>Froyo</td> <td>8</td><td>14%</td></tr>
<tr><td><a href="/about/versions/android-2.3.html">2.3 - 2.3.2</a>
</td><td rowspan="2">Gingerbread</td> <td>9</td><td>0.3%</td></tr>
<tr><td><a href="/about/versions/android-2.3.3.html">2.3.3 - 2.3.7
- </a></td><!-- Gingerbread --> <td>10</td><td>60.3%</td></tr>
+ </a></td><!-- Gingerbread --> <td>10</td><td>57.2%</td></tr>
<tr><td><a href="/about/versions/android-3.1.html">3.1</a></td>
<td rowspan="2">Honeycomb</td> <td>12</td><td>0.5%</td></tr>
-<tr><td><a href="/about/versions/android-3.2.html">3.2</a></td> <!-- Honeycomb --><td>13</td><td>1.8%</td></tr>
+<tr><td><a href="/about/versions/android-3.2.html">3.2</a></td> <!-- Honeycomb --><td>13</td><td>1.6%</td></tr>
<tr><td><a href="/about/versions/android-4.0.html">4.0 - 4.0.2</a></td>
<td rowspan="2">Ice Cream Sandwich</td><td>14</td><td>0.1%</td></tr>
<tr><td><a href="/about/versions/android-4.0.3.html">4.0.3 - 4.0.4</a></td>
- <!-- ICS --> <td>15</td><td>15.8%</td></tr>
-<tr><td><a href="/about/versions/android-4.1.html">4.1</a></td> <td>Jelly Bean</td><td>16</td><td>0.8%</td></tr>
+ <!-- ICS --> <td>15</td><td>20.8%</td></tr>
+<tr><td><a href="/about/versions/android-4.1.html">4.1</a></td> <td>Jelly Bean</td><td>16</td><td>1.2%</td></tr>
</table>
-
</div>
-<div class="col-7" style="margin-right:0">
+<div class="col-8" style="margin-right:0">
<img alt=""
-src="http://chart.apis.google.com/chart?&cht=p&chs=460x310&chd=t:0.2,0.5,4.2,15.5,0.3,60.3,0.5,1.8,0.1,15.8,0.8&chl=Android%201.5|Android%201.6|Android%202.1|Android%202.2|Android%202.3|Android%202.3.3|Android%203.1|Android%203.2|Android%204.0|Android%204.0.3|Android%204.1&chco=c4df9b,6fad0c&chf=bg,s,00000000" />
+src="http://chart.apis.google.com/chart?&cht=p&chs=460x245&chd=t:4.3,14,57.5,2.1,20.9,1.2&chl=Eclair%20%26%20older|Froyo|Gingerbread|Honeycomb|Ice%20Cream%20Sandwich|Jelly%20Bean&chco=c4df9b,6fad0c&chf=bg,s,00000000" />
</div><!-- end dashboard-panel -->
-<p style="clear:both"><em>Data collected during a 14-day period ending on August 1, 2012</em></p>
+<p style="clear:both"><em>Data collected during a 14-day period ending on September 4, 2012</em></p>
<!--
<p style="font-size:.9em">* <em>Other: 0.1% of devices running obsolete versions</em></p>
-->
@@ -82,9 +81,9 @@
Google Play within a 14-day period ending on the date indicated on the x-axis.</p>
<img alt="" height="250" width="660"
-src="http://chart.apis.google.com/chart?&cht=lc&chs=660x250&chxt=x,x,y,r&chf=bg,s,00000000&chxr=0,0,12|1,0,12|2,0,100|3,0,100&chxl=0%3A%7C02/01%7C02/15%7C03/01%7C03/15%7C04/01%7C04/15%7C05/01%7C05/15%7C06/01%7C06/15%7C07/01%7C07/15%7C08/01%7C1%3A%7C2012%7C%7C%7C%7C%7C%7C%7C%7C%7C%7C%7C%7C2012%7C2%3A%7C0%25%7C25%25%7C50%25%7C75%25%7C100%25%7C3%3A%7C0%25%7C25%25%7C50%25%7C75%25%7C100%25&chxp=0,0,1,2,3,4,5,6,7,8,9,10,11,12&chxtc=0,5&chd=t:98.6,98.4,98.4,98.6,98.5,98.6,98.8,98.7,98.9,99.1,99.1,99.2,98.6|97.6,97.5,97.6,97.8,97.8,97.9,98.1,98.1,98.3,98.5,98.6,98.7,98.1|89.9,90.3,90.8,91.4,91.8,92.1,92.5,92.7,93.1,93.5,93.9,94.2,93.9|62.0,63.7,65.2,66.8,68.6,69.9,71.5,72.6,74.0,75.2,76.5,77.8,78.4|4.0,4.1,4.3,4.6,5.5,6.5,7.6,8.2,9.4,11.0,12.8,15.6,18.1|2.6,3.0,3.2,3.5,4.5,5.5,6.6,7.4,8.7,10.4,12.3,15.1,17.6|0.7,0.8,1.1,1.3,2.3,3.3,4.4,5.3,6.7,8.4,10.4,13.2,15.8&chm=b,c3df9b,0,1,0|b,b6dc7d,1,2,0|tAndroid%202.2,5b831d,2,0,15,,t::-5|b,aadb5e,2,3,0|tAndroid%202.3.3,496c13,3,0,15,,t::-5|b,9ddb3d,3,4,0|b,91da1e,4,5,0|b,80c414,5,6,0|tAndroid%204.0.3,131d02,6,10,15,,t::-5|B,6fad0c,6,7,0&chg=7,25&chdl=Android%201.6|Android%202.1|Android%202.2|Android%202.3.3|Android%203.1|Android%203.2|Android%204.0.3&chco=add274,a0d155,94d134,84c323,73ad18,62960f,507d08" />
+src="http://chart.apis.google.com/chart?&cht=lc&chs=660x250&chxt=x,x,y,r&chf=bg,s,00000000&chxr=0,0,12|1,0,12|2,0,100|3,0,100&chxl=0%3A%7C03/01%7C03/15%7C04/01%7C04/15%7C05/01%7C05/15%7C06/01%7C06/15%7C07/01%7C07/15%7C08/01%7C08/15%7C09/01%7C1%3A%7C2012%7C%7C%7C%7C%7C%7C%7C%7C%7C%7C%7C%7C2012%7C2%3A%7C0%25%7C25%25%7C50%25%7C75%25%7C100%25%7C3%3A%7C0%25%7C25%25%7C50%25%7C75%25%7C100%25&chxp=0,0,1,2,3,4,5,6,7,8,9,10,11,12&chxtc=0,5&chd=t:97.6,97.8,97.8,97.9,98.1,98.1,98.3,98.5,98.6,98.7,98.9,98.9,99.0|90.8,91.4,91.8,92.1,92.5,92.7,93.1,93.5,93.9,94.2,94.7,94.9,95.3|65.2,66.8,68.6,69.9,71.5,72.6,74.0,75.2,76.5,77.8,79.2,80.1,81.1|4.3,4.6,5.5,6.5,7.6,8.2,9.4,11.0,12.8,15.6,18.9,21.2,23.7|3.2,3.5,4.5,5.5,6.6,7.4,8.7,10.4,12.3,15.1,18.4,20.7,23.2|1.1,1.3,2.3,3.3,4.4,5.3,6.7,8.4,10.4,13.2,16.6,19.0,21.5|0.0,0.0,0.0,0.0,0.0,0.0,0.0,0.0,0.0,0.0,0.8,0.9,1.1&chm=b,c3df9b,0,1,0|tAndroid%202.2,6c9729,1,0,15,,t::-5|b,b6dc7d,1,2,0|tAndroid%202.3.3,5b831d,2,0,15,,t::-5|b,aadb5e,2,3,0|b,9ddb3d,3,4,0|b,91da1e,4,5,0|tAndroid%204.0.3,253a06,5,8,15,,t::-5|b,80c414,5,6,0|B,6fad0c,6,7,0&chg=7,25&chdl=Android%202.1|Android%202.2|Android%202.3.3|Android%203.1|Android%203.2|Android%204.0.3|Android%204.1&chco=add274,a0d155,94d134,84c323,73ad18,62960f,507d08" />
-<p><em>Last historical dataset collected during a 14-day period ending on August 1, 2012</em></p>
+<p><em>Last historical dataset collected during a 14-day period ending on September 1, 2012</em></p>
@@ -111,6 +110,14 @@
<h2 id="Screens">Screen Sizes and Densities</h2>
+
+<img alt="" style="float:right;"
+src="http://chart.googleapis.com/chart?cht=p&chs=400x250&chf=bg,s,00000000&chco=c4df9b,6fad0c&chl=Xlarge%7CLarge%7CNormal%7CSmall&chd=t%3A4.7,6.5,86,2.8" />
+
+
+<img alt="" style="float:right;clear:right"
+src="http://chart.googleapis.com/chart?cht=p&chs=400x250&chf=bg,s,00000000&chco=c4df9b,6fad0c&chl=ldpi%7Cmdpi%7Chdpi%7Cxhdpi&chd=t%3A1.6,18.6,53.6,26.2" />
+
<p>This section provides data about the relative number of active devices that have a particular
screen configuration, defined by a combination of screen size and density. To simplify the way that
you design your user interfaces for different screen configurations, Android divides the range of
@@ -132,10 +139,7 @@
ending on the data collection date noted below.</p>
-<div class="col-6" style="margin-left:0">
-
-
-<table>
+<table style="width:350px">
<tr>
<th></th>
<th scope="col">ldpi</th>
@@ -144,22 +148,22 @@
<th scope="col">xhdpi</th>
</tr>
<tr><th scope="row">small</th>
-<td>1.5%</td> <!-- small/ldpi -->
+<td>1.1%</td> <!-- small/ldpi -->
<td></td> <!-- small/mdpi -->
-<td>1.2%</td> <!-- small/hdpi -->
+<td>1.7%</td> <!-- small/hdpi -->
<td></td> <!-- small/xhdpi -->
</tr>
<tr><th scope="row">normal</th>
-<td>0.5%</td> <!-- normal/ldpi -->
-<td>12.1%</td> <!-- normal/mdpi -->
-<td>55.3%</td> <!-- normal/hdpi -->
-<td>17.4%</td> <!-- normal/xhdpi -->
+<td>0.4%</td> <!-- normal/ldpi -->
+<td>11.4%</td> <!-- normal/mdpi -->
+<td>51.9%</td> <!-- normal/hdpi -->
+<td>22.3%</td> <!-- normal/xhdpi -->
</tr>
<tr><th scope="row">large</th>
<td>0.1%</td> <!-- large/ldpi -->
-<td>2.7%</td> <!-- large/mdpi -->
+<td>2.5%</td> <!-- large/mdpi -->
<td></td> <!-- large/hdpi -->
-<td>4.5%</td> <!-- large/xhdpi -->
+<td>3.9%</td> <!-- large/xhdpi -->
</tr>
<tr><th scope="row">xlarge</th>
<td></td> <!-- xlarge/ldpi -->
@@ -169,16 +173,7 @@
</tr>
</table>
-
-</div>
-
-<div class="col-7" style="margin-right:0">
-<img alt=""
-src="http://chart.googleapis.com/chart?cht=p&chs=400x250&chf=bg,s,00000000&chco=c4df9b,6fad0c&chl=Xlarge%20/%20mdpi|Large%20/%20ldpi|Large%20/%20mdpi|Large%20/%20xhdpi|Normal%20/%20hdpi|Normal%20/%20ldpi|Normal%20/%20mdpi|Normal%20/%20xhdpi|Small%20/%20hdpi|Small%20/%20ldpi&chd=t%3A4.7,0.1,2.7,4.5,55.3,0.5,12.1,17.4,1.2,1.5" />
-
-</div>
-
-<p style="clear:both"><em>Data collected during a 7-day period ending on August 1, 2012</em></p>
+<p style="clear:both"><em>Data collected during a 7-day period ending on September 4, 2012</em></p>
@@ -196,6 +191,10 @@
support for any lower version (for example, support for version 2.0 also implies support for
1.1).</p>
+
+<img alt="" style="float:right"
+src="http://chart.googleapis.com/chart?cht=p&chs=400x250&chco=c4df9b,6fad0c&chl=GL%201.1%20only|GL%202.0%20%26%201.1&chd=t%3A9.2,90.8&chf=bg,s,00000000" />
+
<p>To declare which version of OpenGL ES your application requires, you should use the {@code
android:glEsVersion} attribute of the <a
href="{@docRoot}guide/topics/manifest/uses-feature-element.html">{@code <uses-feature>}</a>
@@ -209,28 +208,21 @@
ending on the data collection date noted below.</p>
-<div class="col-6" style="margin-left:0">
-<table>
+<table style="width:350px">
<tr>
<th scope="col">OpenGL ES Version</th>
<th scope="col">Distribution</th>
</tr>
<tr>
<td>1.1 only</th>
-<td>9.3%</td>
+<td>9.2%</td>
</tr>
<tr>
<td>2.0 & 1.1</th>
-<td>90.7%</td>
+<td>90.8%</td>
</tr>
</table>
-</div>
-
-<div class="col-7" style="margin-right:0">
-<img alt=""
-src="http://chart.googleapis.com/chart?cht=p&chs=400x250&chco=c4df9b,6fad0c&chl=GL%201.1%20only|GL%202.0%20%26%201.1&chd=t%3A9.3,90.7&chf=bg,s,00000000" />
-
-</div>
-<p style="clear:both"><em>Data collected during a 7-day period ending on August 1, 2012</em></p>
+
+<p style="clear:both"><em>Data collected during a 7-day period ending on September 4, 2012</em></p>
diff --git a/docs/html/guide/guide_toc.cs b/docs/html/guide/guide_toc.cs
index 9465f18..e812ccb 100644
--- a/docs/html/guide/guide_toc.cs
+++ b/docs/html/guide/guide_toc.cs
@@ -8,7 +8,7 @@
<ul id="nav">
<!-- Walkthrough for Developers -- quick overview of what it's like to develop on Android -->
<!--<li style="color:red">Overview</li> -->
-
+
<li class="nav-section">
<div class="nav-section-header"><a href="<?cs var:toroot ?>guide/components/index.html">
<span class="en">App Components</span>
@@ -223,7 +223,7 @@
<li><a href="<?cs var:toroot ?>guide/topics/search/adding-custom-suggestions.html">Adding Custom Suggestions</a></li>
<li><a href="<?cs var:toroot ?>guide/topics/search/searchable-config.html">Searchable Configuration</a></li>
</ul>
- </li>
+ </li>
<li><a href="<?cs var:toroot ?>guide/topics/ui/drag-drop.html">
<span class="en">Drag and Drop</span>
</a></li>
@@ -235,6 +235,9 @@
<li><a href="<?cs var:toroot ?>guide/topics/ui/accessibility/apps.html">
<span class="en">Making Applications Accessible</span>
</a></li>
+ <li><a href="<?cs var:toroot ?>guide/topics/ui/accessibility/checklist.html">
+ <span class="en">Accessibility Developer Checklist</span>
+ </a></li>
<li><a href="<?cs var:toroot ?>guide/topics/ui/accessibility/services.html">
<span class="en">Building Accessibility Services</span>
</a></li>
@@ -382,9 +385,9 @@
</a></li>
</ul>
</li><!-- end of location and sensors -->
-
-
+
+
<li class="nav-section">
<div class="nav-section-header"><a href="<?cs var:toroot ?>guide/topics/connectivity/index.html">
<span class="en">Connectivity</span>
@@ -419,10 +422,10 @@
<span class="en">SIP</span>
</a>
</li>
-
+
</ul>
</li><!-- end of connectivity -->
-
+
<li class="nav-section">
<div class="nav-section-header"><a href="<?cs var:toroot ?>guide/topics/text/index.html">
<span class="en">Text and Input</span>
@@ -439,7 +442,7 @@
</a></li>
</ul>
</li><!-- end of text and input -->
-
+
<li class="nav-section">
<div class="nav-section-header"><a href="<?cs var:toroot ?>guide/topics/data/index.html">
<span class="en">Data Storage</span>
@@ -457,7 +460,7 @@
</ul>
</li><!-- end of data storage -->
-
+
<li class="nav-section">
<div class="nav-section-header"><a href="<?cs var:toroot?>guide/topics/admin/index.html">
<span class="en">Administration</span>
@@ -475,7 +478,7 @@
-->
</ul>
</li><!-- end of administration -->
-
+
<li class="nav-section">
<div class="nav-section-header"><a href="<?cs var:toroot ?>guide/webapps/index.html">
<span class="en">Web Apps</span>
@@ -498,7 +501,7 @@
</a></li>
</ul>
</li><!-- end of web apps -->
-
+
<li class="nav-section">
<div class="nav-section-header"><a href="<?cs var:toroot ?>guide/practices/index.html">
<span class="en">Best Practices</span>
@@ -555,13 +558,13 @@
</ul>
</li>
-
-
+
+
<li class="nav-section">
<div class="nav-section-header"><a href="<?cs var:toroot ?>guide/google/index.html">
<span class="en">Google Services</span>
</a></div>
- <ul>
+ <ul>
<li class="nav-section">
<div class="nav-section-header"><a href="<?cs var:toroot?>guide/google/play/billing/index.html">
@@ -623,7 +626,7 @@
<li><a href="<?cs var:toroot ?>guide/google/play/expansion-files.html">
<span class="en">APK Expansion Files</span></a>
</li>
-
+
<li class="nav-section">
<div class="nav-section-header"><a href="<?cs var:toroot ?>guide/google/gcm/index.html">
<span class="en">Google Cloud Messaging</span></a>
@@ -649,9 +652,9 @@
</ul>
</li><!-- end Google Play -->
-
-
-
+
+
+
<!-- this needs to move
<li class="nav-section">
<div class="nav-section-header"><a href="<?cs var:toroot ?>guide/practices/ui_guidelines/index.html">
@@ -691,9 +694,9 @@
</a></div>
</li>
</ul>
- </li>
+ </li>
</ul> -->
-
+
<!-- Remove
<li class="nav-section">
<div class="nav-section-header"><a href="<?cs var:toroot ?>guide/appendix/index.html">
@@ -710,7 +713,7 @@
<li><a href="<?cs var:toroot ?>guide/appendix/g-app-intents.html">
<span class="en">Intents List: Google Apps</span>
</a></li>
-
+
<li><a href="<?cs var:toroot ?>guide/appendix/glossary.html">
<span class="en">Glossary</span>
diff --git a/docs/html/guide/topics/ui/accessibility/apps.jd b/docs/html/guide/topics/ui/accessibility/apps.jd
index d23512b..13b4538 100644
--- a/docs/html/guide/topics/ui/accessibility/apps.jd
+++ b/docs/html/guide/topics/ui/accessibility/apps.jd
@@ -21,14 +21,11 @@
<li><a href="#accessibility-methods">Implementing accessibility API methods</a></li>
<li><a href="#send-events">Sending accessibility events</a></li>
<li><a href="#populate-events">Populating accessibility events</a></li>
+ <li><a href="#virtual-hierarchy">Providing a customized accessibility context</a></li>
+ <li><a href="#custom-touch-events">Handling custom touch events</a></li>
</ol>
</li>
- <li><a href="#test">Testing Accessibility</a>
- <ol>
- <li><a href="#test-audibles">Testing audible feedback</a></li>
- <li><a href="#test-navigation">Testing focus navigation</a></li>
- </ol>
- </li>
+ <li><a href="#test">Testing Accessibility</a></li>
</ol>
<h2>Key classes</h2>
@@ -42,60 +39,79 @@
<h2>See also</h2>
<ol>
- <li><a href="{@docRoot}training/accessibility/index.html">Implementing Accessibility</a></li>
- <li><a href="{@docRoot}training/design-navigation/index.html">Designing Effective Navigation</a>
- </li>
- <li><a href="{@docRoot}design/index.html">Android Design</a></li>
+ <li><a href="checklist.html">Accessibility Developer Checklist</a><li>
+ <li><a href="{@docRoot}tools/testing/testing_accessibility.html">Accessibility Testing Checklist</a><li>
+ <li><a href="{@docRoot}design/patterns/accessibility.html">Android Design: Accessibility</a></li>
+ <li><a href="{@docRoot}training/design-navigation/index.html">Designing Effective Navigation</a></li>
+ <li><a href="{@docRoot}training/accessibility/index.html">Training: Implementing Accessibility</a></li>
</ol>
</div>
</div>
-<p>Applications built for Android are accessible to users with visual, physical or age-related
-disabilities when they activate accessibility features and services on a device. By default,
-these services make your application more accessible. However, there are further steps you should
-take to optimize the accessibility of your application and ensure a pleasant experience for all your
-users.</p>
+<p>Applications built for Android are more accessible to users with visual, physical or age-related
+limitations when those users activate accessibility services and features on a device. These
+services make your application more accessible even if you do not make any accessibility changes
+to your code. However, there are steps you should take to optimize the accessibility of your
+application and ensure a pleasant experience for all your users.</p>
-<p>Making sure your application is accessible to all users is relatively easy, particularly when you
-use framework-provided user interface components. If you only use these standard components for your
-application, there are just a few steps required to ensure your application is accessible:</p>
+<p>Making sure your application is accessible to all users requires only a few steps, particularly
+when you create your user interface with the components provided by the Android framework. If you
+use only the standard components for your application, the steps are:</p>
<ol>
- <li>Label your {@link android.widget.ImageButton}, {@link android.widget.ImageView}, {@link
-android.widget.EditText}, {@link android.widget.CheckBox} and other user interface controls using
-the <a href="{@docRoot}reference/android/view/View.html#attr_android:contentDescription">
- {@code android:contentDescription}</a> attribute.</li>
- <li>Make all of your user interface elements accessible with a directional controller,
- such as a trackball or D-pad.</li>
- <li>Test your application by turning on accessibility services like TalkBack and Explore by
- Touch, and try using your application using only directional controls.</li>
+ <li>Add descriptive text to user interface controls in your application using the
+ <a href="{@docRoot}reference/android/view/View.html#attr_android:contentDescription">
+ {@code android:contentDescription}</a> attribute. Pay particular attention to
+ {@link android.widget.ImageButton}, {@link android.widget.ImageView}
+ and {@link android.widget.CheckBox}.</li>
+ <li>Make sure that all user interface elements that can accept input (touches or typing) can be
+ reached with a directional controller, such as a trackball, D-pad (physical or virtual) or
+ navigation <a href="http://support.google.com/android/bin/topic.py?hl=en&topic=2492346">gestures
+ </a>.</li>
+ <li>Make sure that audio prompts are always accompanied by another visual prompt or notification,
+ to assist users who are deaf or hard of hearing.</li>
+ <li>Test your application using only accessibility navigation services and features. Turn on
+ <a href="{@docRoot}tools/testing/testing_accessibility.html#testing-talkback">TalkBack</a> and
+ <a href="{@docRoot}tools/testing/testing_accessibility.html#testing-ebt">Explore by Touch</a>,
+ and then try using your application using only directional controls. For more information on
+ testing for accessibility, see the <a href="{@docRoot}tools/testing/testing_accessibility.html">
+ Accessibility Testing Checklist</a>.</li>
</ol>
-<p>Developers who create custom controls that extend from the {@link android.view.View} class have
-some additional responsibilities for making sure their components are accessible for users. This
-document also discusses how to make custom view controls compatible with accessibility services.</p>
+<p>If you build custom controls that extend the {@link android.view.View} class, you must complete
+some additional work to make sure your components are accessible. This document discusses how to
+make custom view controls compatible with accessibility services.</p>
+
+<p class="note">
+<strong>Note:</strong> The implementation steps in this document describe the requirements for
+making your application accessible for users with blindness or low-vision. Be sure to review the
+requirements for serving users who are deaf and hard of hearing in the
+<a href="{@docRoot}guide/topics/ui/accessibility/checklist.html">Accessibility Developer
+Checklist</a></p>.
+
<h2 id="label-ui">Labeling User Interface Elements</h2>
-<p>Many user interface controls rely on visual cues to inform users of their meaning. For
+<p>Many user interface controls depend on visual cues to indicate their meaning and usage. For
example, a note-taking application might use an {@link android.widget.ImageButton} with a
-picture of a plus sign to indicate that the user can add a new note. Or, an {@link
-android.widget.EditText} component may have a label near it that indicates its purpose. When a user
-with impaired vision accesses your application, these visual cues are often useless.</p>
+picture of a plus sign to indicate that the user can add a new note. An {@link
+android.widget.EditText} component may have a label near it that indicates its purpose. A user
+with impaired vision can't see these cues well enough to follow them, which makes them useless.</p>
-<p>To provide textual information about interface controls (as an alternative to the visual cues),
-use the <a href="{@docRoot}reference/android/view/View.html#attr_android:contentDescription">
-{@code android:contentDescription}</a> attribute. The text you provide in this attribute is not
-visible on the screen, but if a user has enabled accessibility services that provide audible
-prompts, then the description in this attribute is read aloud to the user.</p>
+<p>You can make these controls more accessible with the
+<a href="{@docRoot}reference/android/view/View.html#attr_android:contentDescription">
+{@code android:contentDescription}</a> XML layout attribute. The text in this attribute does not
+appear on screen, but if the user enables accessibility services that provide audible prompts, then
+when the user navigates to that control, the text is spoken.</p>
-<p>Set the <a href="{@docRoot}reference/android/view/View.html#attr_android:contentDescription">
+<p>For this reason, set the
+<a href="{@docRoot}reference/android/view/View.html#attr_android:contentDescription">
{@code android:contentDescription}</a> attribute for every {@link android.widget.ImageButton},
-{@link android.widget.ImageView}, {@link android.widget.EditText}, {@link android.widget.CheckBox}
-in your application's user interface, and on any other input controls that might require additional
-information for users who are not able to see it.</p>
+{@link android.widget.ImageView}, {@link android.widget.CheckBox}
+in your application's user interface, and add descriptions to any other input controls that might
+require additional information for users who are not able to see it.</p>
<p>For example, the following {@link android.widget.ImageButton} sets the content description for
the plus button to the {@code add_note} string resource, which could be defined as “Add note" for an
@@ -108,32 +124,38 @@
android:contentDescription=”@string/add_note”/>
</pre>
-<p>By including the description, speech-based accessibility services can announce "Add note" when a
-user moves focus to this button or hovers over it.</p>
+<p>By including the description, an accessibility service that provides spoken feedback can announce
+"Add note" when a user moves focus to this button or hovers over it.</p>
<p class="note"><strong>Note:</strong> For {@link android.widget.EditText} fields, provide an
<a href="{@docRoot}reference/android/widget/TextView.html#attr_android:hint">android:hint</a>
-attribute to help users understand what content is expected.</p>
+attribute <em>instead</em> of a content description, to help users understand what content is
+expected when the text field is empty. When the field is filled, TalkBack reads the entered
+content to the user, instead of the hint text.</p>
+
<h2 id="focus-nav">Enabling Focus Navigation</h2>
<p>Focus navigation allows users with disabilities to step through user interface controls using a
-directional controller. Directional controllers can be physical, such as a clickable trackball,
-directional pad (D-pad) or arrow keys, tab key navigation with an attached keyboard or a software
-application, such as the
+directional controller. Directional controllers can be physical, such as a trackball, directional
+pad (D-pad) or arrow keys, or virtual, such as the
<a href="https://play.google.com/store/apps/details?id=com.googlecode.eyesfree.inputmethod.latin">
-Eyes-Free Keyboard</a>, that provides an on-screen directional control.</p>
+Eyes-Free Keyboard</a>, or the gestures navigation mode available in Android 4.1 and higher.
+Directional controllers are a primary means of navigation for many Android users.
+</p>
-<p>A directional controller is a primary means of navigation for many users.
-Verify that all user interface (UI) controls in your application are accessible
-without using the touchscreen and that clicking with the center button (or OK button) of a
-directional controller has the same effect as touching the controls on the touchscreen. For
-information on testing directional controls, see <a href="#test-navigation">Testing focus
-navigation</a>.</p>
+<p>To ensure that users can navigate your application using only a directional controller, verify
+that all user interface (UI) input controls in your application can be reached and activated
+without using the touchscreen. You should also verify that clicking with the center button (or OK
+button) of a directional controller has the same effect as touching a control that already has
+focus. For information on testing directional controls, see
+<a href="{@docRoot}tools/testing/testing_accessibility.html#test-navigation">Testing
+focus navigation</a>.</p>
+
<h3 id="focus-enable">Enabling view focus</h3>
-<p>A user interface element is accessible using directional controls when its
+<p>A user interface element is reachable using directional controls when its
<a href="{@docRoot}reference/android/view/View.html#attr_android:focusable">
{@code android:focusable}</a> attribute is set to {@code true}. This setting allows users to focus
on the element using the directional controls and then interact with it. The user interface controls
@@ -149,44 +171,44 @@
<li>{@link android.view.View#requestFocus requestFocus()}</li>
</ul>
-<p>When working with a view that is not focusable by default, you can make it focusable from the XML
-layout file by setting the
-<a href="{@docRoot}reference/android/view/View.html#attr_android:focusable">
-{@code android:focusable}</a> attribute to {@code true} or by using the {@link
+<p>If a view is not focusable by default, you can make it focusable in your layout file by setting
+the <a href="{@docRoot}reference/android/view/View.html#attr_android:focusable">
+{@code android:focusable}</a> attribute to {@code true} or by calling the its {@link
android.view.View#setFocusable setFocusable()} method.</p>
+
<h3 id="focus-order">Controlling focus order</h3>
<p>When users navigate in any direction using directional controls, focus is passed from one
-user interface element (View) to another, as determined by the focus ordering. The ordering of the
-focus movement is based on an algorithm that finds the nearest neighbor in a given direction. In
-rare cases, the default algorithm may not match the order that you intended for your UI. In these
-situations, you can provide explicit overrides to the ordering using the following XML attributes in
-the layout file:</p>
+user interface element (view) to another, as determined by the focus order. This order is based on
+an algorithm that finds the nearest neighbor in a given direction. In rare cases, the algorithm may
+not match the order that you intended or may not be logical for users. In these situations, you can
+provide explicit overrides to the ordering using the following XML attributes in your layout file:
+</p>
<dl>
- <dt><a href="{@docRoot}reference/android/view/View.html#attr_android:nextFocusDown"
->{@code android:nextFocusDown}</a></dt>
- <dd>Defines the next view to receive focus when the user navigates down.</dd>
- <a><a href="{@docRoot}reference/android/view/View.html#attr_android:nextFocusLeft"
->{@code android:nextFocusLeft}</a></dt>
- <dd>Defines the next view to receive focus when the user navigates left.</dd>
- <dt><a href="{@docRoot}reference/android/view/View.html#attr_android:nextFocusRight"
->{@code android:nextFocusRight}</a></dt>
- <dd>Defines the next view to receive focus when the user navigates right.</dd>
- <dt><a href="{@docRoot}reference/android/view/View.html#attr_android:nextFocusUp"
->{@code android:nextFocusUp}</a></dt>
- <dd>Defines the next view to receive focus when the user navigates up.</dd>
+ <dt><a href="{@docRoot}reference/android/view/View.html#attr_android:nextFocusDown">
+ {@code android:nextFocusDown}</a></dt>
+ <dd>Defines the next view to receive focus when the user navigates down.</dd>
+ <dt><a href="{@docRoot}reference/android/view/View.html#attr_android:nextFocusLeft">
+ {@code android:nextFocusLeft}</a></dt>
+ <dd>Defines the next view to receive focus when the user navigates left.</dd>
+ <dt><a href="{@docRoot}reference/android/view/View.html#attr_android:nextFocusRight">
+ {@code android:nextFocusRight}</a></dt>
+ <dd>Defines the next view to receive focus when the user navigates right.</dd>
+ <dt><a href="{@docRoot}reference/android/view/View.html#attr_android:nextFocusUp">
+ {@code android:nextFocusUp}</a></dt>
+ <dd>Defines the next view to receive focus when the user navigates up.</dd>
</dl>
-<p>The following example XML layout shows two focusable user interface elements where the <a
-href="{@docRoot}reference/android/view/View.html#attr_android:nextFocusDown"
->{@code android:nextFocusDown}</a> and <a
-href="{@docRoot}reference/android/view/View.html#attr_android:nextFocusUp"
->{@code android:nextFocusUp}</a> attributes have been explicitly set. The {@link android.widget.TextView} is
+<p>The following example XML layout shows two focusable user interface elements where the
+<a href="{@docRoot}reference/android/view/View.html#attr_android:nextFocusDown">{@code
+android:nextFocusDown}</a> and
+<a href="{@docRoot}reference/android/view/View.html#attr_android:nextFocusUp">{@code
+android:nextFocusUp}</a> attributes have been explicitly set. The {@link android.widget.TextView} is
located to the right of the {@link android.widget.EditText}. However, since these properties have
been set, the {@link android.widget.TextView} element can now be reached by pressing the down arrow
-when focus is on the {@link android.widget.EditText} element: </p>
+when focus is on the {@link android.widget.EditText} element:</p>
<pre>
<LinearLayout android:orientation="horizontal"
@@ -218,8 +240,9 @@
<ul>
<li>Handle directional controller clicks</li>
- <li>Implement Accessibility API methods</li>
- <li>Send {@link android.view.accessibility.AccessibilityEvent} objects specific to your custom view</li>
+ <li>Implement accessibility API methods</li>
+ <li>Send {@link android.view.accessibility.AccessibilityEvent} objects specific to your custom
+ view</li>
<li>Populate {@link android.view.accessibility.AccessibilityEvent} and {@link
android.view.accessibility.AccessibilityNodeInfo} for your view</li>
</ul>
@@ -243,9 +266,9 @@
<p>Accessibility events are messages about users interaction with visual interface components in
your application. These messages are handled by <a href="services.html">Accessibility Services</a>,
-which use the information in these events to produce supplemental feedback and prompts when users
-have enabled accessibility services. As of Android 4.0 (API Level 14) and higher, the methods for
-generating accessibility events have been expanded to provide more detailed information beyond the
+which use the information in these events to produce supplemental feedback and prompts. In
+Android 4.0 (API Level 14) and higher, the methods for
+generating accessibility events have been expanded to provide more detailed information than the
{@link android.view.accessibility.AccessibilityEventSource} interface introduced in Android 1.6 (API
Level 4). The expanded accessibility methods are part of the {@link android.view.View} class as well
as the {@link android.view.View.AccessibilityDelegate} class. The methods are as follows:</p>
@@ -262,12 +285,12 @@
<dd>(API Level 4) This method is used when the calling code needs to directly control the check
for accessibility being enabled on the device ({@link
android.view.accessibility.AccessibilityManager#isEnabled AccessibilityManager.isEnabled()}). If
-you do implement this method, you must assume that the calling method has already checked that
-accessibility is enabled and the result is {@code true}. You typically do not need to implement this
-method for a custom view.</dd>
+you do implement this method, you must perform the call as if accessibility is enabled, regardless
+of the actual system setting. You typically do not need to implement this method for a custom view.
+</dd>
<dt>{@link android.view.View#dispatchPopulateAccessibilityEvent
-dispatchPopulateAccessibilityEvent()} </dt>
+dispatchPopulateAccessibilityEvent()}</dt>
<dd>(API Level 4) The system calls this method when your custom view generates an
accessibility event. As of API Level 14, the default implementation of this method calls {@link
android.view.View#onPopulateAccessibilityEvent onPopulateAccessibilityEvent()} for this view and
@@ -276,21 +299,21 @@
accessibility services on revisions of Android <em>prior</em> to 4.0 (API Level 14) you
<em>must</em> override this method and populate {@link
android.view.accessibility.AccessibilityEvent#getText} with descriptive text for your custom
-view.</dd>
+view, which is spoken by accessibility services, such as TalkBack.</dd>
<dt>{@link android.view.View#onPopulateAccessibilityEvent onPopulateAccessibilityEvent()}</dt>
- <dd>(API Level 14) This method sets the text output of an {@link
+ <dd>(API Level 14) This method sets the spoken text prompt of the {@link
android.view.accessibility.AccessibilityEvent} for your view. This method is also called if the
view is a child of a view which generates an accessibility event.
<p class="note"><strong>Note:</strong> Modifying additional attributes beyond the text within
-this method potentially overwrites properties set by other methods. So, while you are able modify
+this method potentially overwrites properties set by other methods. While you can modify
attributes of the accessibility event with this method, you should limit these changes
-to text content only and use the {@link android.view.View#onInitializeAccessibilityEvent
+to text content, and use the {@link android.view.View#onInitializeAccessibilityEvent
onInitializeAccessibilityEvent()} method to modify other properties of the event.</p>
- <p class="note"><strong>Note:</strong> If your implementation of this event calls for completely
-overiding the output text without allowing other parts of your layout to modify its content, then
+ <p class="note"><strong>Note:</strong> If your implementation of this event completely
+overrides the output text without allowing other parts of your layout to modify its content, then
do not call the super implementation of this method in your code.</p>
</dd>
@@ -306,7 +329,7 @@
<dt>{@link android.view.View#onInitializeAccessibilityNodeInfo
onInitializeAccessibilityNodeInfo()}</dt>
<dd>(API Level 14) This method provides accessibility services with information about the state of
-the view. The default {@link android.view.View} implementation sets a standard set of view
+the view. The default {@link android.view.View} implementation has a standard set of view
properties, but if your custom view provides interactive control beyond a simple {@link
android.widget.TextView} or {@link android.widget.Button}, you should override this method and set
the additional information about your view into the {@link
@@ -315,7 +338,7 @@
<dt>{@link android.view.ViewGroup#onRequestSendAccessibilityEvent
onRequestSendAccessibilityEvent()}</dt>
<dd>(API Level 14) The system calls this method when a child of your view has generated an
-{@link android.view.accessibility.AccessibilityEvent}. This step allows the the parent view to amend
+{@link android.view.accessibility.AccessibilityEvent}. This step allows the parent view to amend
the accessibility event with additional information. You should implement this method only if your
custom view can have child views and if the parent view can provide context information to the
accessibility event that would be useful to accessibility services.</dd>
@@ -333,7 +356,7 @@
{@link android.support.v4.view.ViewCompat#setAccessibilityDelegate
ViewCompat.setAccessibilityDelegate()} method to implement the accessibility methods
above. For an example of this approach, see the Android Support Library (revision 5 or higher)
-sample {@code AccessibilityDelegateSupportActivity} in
+sample {@code AccessibilityDelegateSupportActivity} in
({@code <sdk>/extras/android/support/v4/samples/Support4Demos/})
</li>
</ul>
@@ -446,20 +469,20 @@
}
</pre>
-<p>On Android 4.0 (API Level 14) and higher, the {@link
+<p>For Android 4.0 (API Level 14) and higher, use the {@link
android.view.View#onPopulateAccessibilityEvent onPopulateAccessibilityEvent()} and
{@link android.view.View#onInitializeAccessibilityEvent onInitializeAccessibilityEvent()}
-methods are the recommended way to populate or modify the information in an {@link
-android.view.accessibility.AccessibilityEvent}. Use the
+methods to populate or modify the information in an {@link
+android.view.accessibility.AccessibilityEvent}. Use the
{@link android.view.View#onPopulateAccessibilityEvent onPopulateAccessibilityEvent()} method
specifically for adding or modifying the text content of the event, which is turned into audible
prompts by accessibility services such as TalkBack. Use the
{@link android.view.View#onInitializeAccessibilityEvent onInitializeAccessibilityEvent()} method for
populating additional information about the event, such as the selection state of the view.</p>
-<p>In addition, you should also implement the
+<p>In addition, implement the
{@link android.view.View#onInitializeAccessibilityNodeInfo onInitializeAccessibilityNodeInfo()}
-method. {@link android.view.accessibility.AccessibilityNodeInfo} objects populated by this method
+method. The {@link android.view.accessibility.AccessibilityNodeInfo} objects populated by this method
are used by accessibility services to investigate the view hierarchy that generated an accessibility
event after receiving that event, to obtain a more detailed context information and provide
appropriate feedback to users.</p>
@@ -467,8 +490,8 @@
<p>The example code below shows how override these three methods by using
{@link android.support.v4.view.ViewCompat#setAccessibilityDelegate
ViewCompat.setAccessibilityDelegate()}. Note that this sample code requires that the Android
-<a href="{@docRoot}tools/extras/support-library.html">Support Library</a> for API Level 4 (revision 5
-or higher) is added to your project.</p>
+<a href="{@docRoot}tools/extras/support-library.html">Support Library</a> for API Level 4 (revision
+5 or higher) is added to your project.</p>
<pre>
ViewCompat.setAccessibilityDelegate(new AccessibilityDelegateCompat() {
@@ -509,10 +532,10 @@
}
</pre>
-<p>On applications targeting Android 4.0 (API Level 14) and higher, these methods can be implemented
+<p>In applications targeting Android 4.0 (API Level 14) and higher, you can implement these methods
directly in your custom view class. For another example of this approach, see the Android
-<a href="{@docRoot}tools/extras/support-library.html">Support Library</a> (revision 5 or higher) sample
-{@code AccessibilityDelegateSupportActivity} in
+<a href="{@docRoot}tools/extras/support-library.html">Support Library</a> (revision 5 or higher)
+sample {@code AccessibilityDelegateSupportActivity} in
({@code <sdk>/extras/android/support/v4/samples/Support4Demos/}).</p>
<p class="note"><strong>Note:</strong> You may find information on implementing accessibility for
@@ -525,50 +548,141 @@
methods.</p>
+<h3 id="virtual-hierarchy">Providing a customized accessibility context</h3>
+
+<p>In Android 4.0 (API Level 14), the framework was enhanced to allow accessibility services to
+ inspect the containing view hierarchy of a user interface component that generates an
+ accessibility event. This enhancement allows accessibility services to provide a much richer set
+ of contextual information with which to aid users.</p>
+
+<p>There are some cases where accessibility services cannot get adequate information
+ from the view hierarchy. An example of this is a custom interface control that has two or more
+ separately clickable areas, such as a calendar control. In this case, the services cannot get
+ adequate information because the clickable subsections are not part of the view hierarchy.</p>
+
+<img src="calendar.png" alt="" id="figure1" />
+<p class="img-caption">
+ <strong>Figure 1.</strong> A custom calendar view with selectable day elements.
+</p>
+
+<p>In the example shown in Figure 1, the entire calendar is implemented as a single view, so if you
+ do not do anything else, accessibility services do not receive enough information about the
+ content of the view and the user's selection within the view. For example, if a user clicks on the
+ day containing <strong>17</strong>, the accessibility framework only receives the description
+ information for the whole calendar control. In this case, the TalkBack accessibility service would
+ simply announce "Calendar" or, only slightly better, "April Calendar" and the user would be left
+ to wonder what day was selected.</p>
+
+<p>To provide adequate context information for accessibility services in situations like this,
+ the framework provides a way to specify a virtual view hierarchy. A <em>virtual view
+ hierarchy</em> is a way for application developers to provide a complementary view hierarchy
+ to accessibility services that more closely matches the actual information on screen. This
+ approach allows accessibility services to provide more useful context information to users.</p>
+
+<p>Another situation where a virtual view hierarchy may be needed is a user interface containing
+ a set of controls (views) that have closely related functions, where an action on one control
+ affects the contents of one or more elements, such as a number picker with separate up and down
+ buttons. In this case, accessibility services cannot get adequate information because action on
+ one control changes content in another and the relationship of those controls may not be apparent
+ to the service. To handle this situation, group the related controls with a containing view and
+ provide a virtual view hierarchy from this container to clearly represent the information and
+ behavior provided by the controls.</p>
+
+<p>In order to provide a virtual view hierarchy for a view, override the {@link
+ android.view.View#getAccessibilityNodeProvider} method in your custom view or view group and
+ return an implementation of {@link android.view.accessibility.AccessibilityNodeProvider}. For an
+ example implementation of this accessibility feature, see
+ {@code AccessibilityNodeProviderActivity} in the ApiDemos sample project. You can implement a
+ virtual view hierarchy that is compatible with Android 1.6 and later by using the
+ <a href="{@docRoot}tools/extras/support-library.html">Support Library</a> with the
+ {@link android.support.v4.view.ViewCompat#getAccessibilityNodeProvider
+ ViewCompat.getAccessibilityNodeProvider()} method and providing an implementation with
+ {@link android.support.v4.view.accessibility.AccessibilityNodeProviderCompat}.</p>
+
+
+<h3 id="custom-touch-events">Handling custom touch events</h3>
+
+<p>Custom view controls may require non-standard touch event behavior. For example, a custom
+control may use the {@link android.view.View#onTouchEvent} listener method to detect the
+{@link android.view.MotionEvent#ACTION_DOWN} and {@link android.view.MotionEvent#ACTION_UP} events
+and trigger a special click event. In order to maintain compatibility with accessibility services,
+the code that handles this custom click event must do the following:</p>
+
+<ol>
+ <li>Generate an appropriate {@link android.view.accessibility.AccessibilityEvent} for the
+ interpreted click action.</li>
+ <li>Enable accessibility services to perform the custom click action for users who are not able to
+ use a touch screen.</li>
+</ol>
+
+<p>To handle these requirements in an efficient way, your code should override the
+{@link android.view.View#performClick} method, which must call the super implementation of this
+method and then execute whatever actions are required by the click event. When the custom click
+action is detected, that code should then call your {@code performClick()} method. The following
+code example demonstrates this pattern.</p>
+
+<pre>
+class CustomTouchView extends View {
+
+ public CustomTouchView(Context context) {
+ super(context);
+ }
+
+ boolean mDownTouch = false;
+
+ @Override
+ public boolean onTouchEvent(MotionEvent event) {
+ super.onTouchEvent(event);
+
+ // Listening for the down and up touch events
+ switch (event.getAction()) {
+ case MotionEvent.ACTION_DOWN:
+ mDownTouch = true;
+ return true;
+
+ case MotionEvent.ACTION_UP:
+ if (mDownTouch) {
+ mDownTouch = false;
+ performClick(); // Call this method to handle the response, and
+ // thereby enable accessibility services to
+ // perform this action for a user who cannot
+ // click the touchscreen.
+ return true;
+ }
+ }
+ return false; // Return false for other touch events
+ }
+
+ @Override
+ public boolean performClick() {
+ // Calls the super implementation, which generates an AccessibilityEvent
+ // and calls the onClick() listener on the view, if any
+ super.performClick();
+
+ // Handle the action for the custom click here
+
+ return true;
+ }
+}
+</pre>
+
+<p>The pattern shown above makes sure that the custom click event is compatible with
+accessibility services by using the {@link android.view.View#performClick} method to both generate
+an accessibility event and provide an entry point for accessibility services to act on behalf of a
+user to perform this custom click event.</p>
+
+<p class="note"><strong>Note:</strong> If your custom view has distinct clickable regions, such as
+a custom calendar view, you must implement a <a href="#virtual-hierarchy">virtual view
+hierarchy</a> by overriding {@link android.view.View#getAccessibilityNodeProvider} in your custom
+view in order to be compatible with accessibility services.</p>
+
+
<h2 id="test">Testing Accessibility</h2>
<p>Testing the accessibility of your application is an important part of ensuring your users have a
-great experience. You can test the most important parts of accessibility by testing your application
-with audible feedback enabled and testing navigation within your application using directional
-controls.</p>
+great experience. You can test the most important accessibility features by using your application
+with audible feedback enabled and navigating within your application using only directional
+controls. For more information on testing accessibility in your application, see the
+<a href="{@docRoot}tools/testing/testing_accessibility.html">Accessibility Testing Checklist</a>.
+</p>
-<h3 id="test-audibles">Testing audible feedback</h3>
-<p>You can simulate the experience for many users by enabling an accessibility service that speaks
-as you move around the screen. The Explore by Touch accessibility service, which is available on
-devices with Android 4.0 and later. The <a
-href="https://play.google.com/store/apps/details?id=com.google.android.marvin.talkback">TalkBack</a>
-accessibility service, by the <a href="http://code.google.com/p/eyes-free/">Eyes-Free
-Project</a> comes preinstalled on many Android devices.</p>
-
-<p>To enable TalkBack on revisions of Android prior to Android 4.0:</p>
-<ol>
- <li>Launch the Settings application.</li>
- <li>Navigate to the <strong>Accessibility</strong> category and select it.</li>
- <li>Select <strong>Accessibility</strong> to enable it.</li>
- <li>Select <strong>TalkBack</strong> to enable it.</li>
-</ol>
-
-<p class="note"><strong>Note:</strong> If the TalkBack accessibility service is not available, you
-can install it for free from <a href="http://play.google.com">Google Play</a>.</p>
-
-<p>To enable Explore by Touch on Android 4.0 and later:</p>
-<ol>
- <li>Launch the Settings application.</li>
- <li>Navigate to the <strong>Accessibility</strong> category and select it.</li>
- <li>Select the <strong>TalkBack</strong> to enable it.</li>
- <li>Return to the <strong>Accessibility</strong> category and select <strong>Explore by
-Touch</strong> to enable it.
- <p class="note"><strong>Note:</strong> You must turn on TalkBack <em>first</em>, otherwise this
-option is not available.</p>
- </li>
-</ol>
-
-<h3 id="test-navigation">Testing focus navigation</h3>
-
-<p>As part of your accessibility testing, you can test navigation of your application using focus,
-even if your test devices does not have a directional controller. The <a
-href="{@docRoot}tools/help/emulator.html">Android Emulator</a> provides a
-simulated directional controller that you can easily use to test navigation. You can also use a
-software-based directional controller, such as the one provided by the
-<a href="https://play.google.com/store/apps/details?id=com.googlecode.eyesfree.inputmethod.latin">
-Eyes-Free Keyboard</a> to simulate use of a D-pad.</p>
diff --git a/docs/html/guide/topics/ui/accessibility/calendar.png b/docs/html/guide/topics/ui/accessibility/calendar.png
new file mode 100644
index 0000000..ca5c44f
--- /dev/null
+++ b/docs/html/guide/topics/ui/accessibility/calendar.png
Binary files differ
diff --git a/docs/html/guide/topics/ui/accessibility/checklist.jd b/docs/html/guide/topics/ui/accessibility/checklist.jd
new file mode 100644
index 0000000..9473d1b
--- /dev/null
+++ b/docs/html/guide/topics/ui/accessibility/checklist.jd
@@ -0,0 +1,173 @@
+page.title=Accessibility Developer Checklist
+parent.title=Accessibility
+parent.link=index.html
+@jd:body
+
+<div id="qv-wrapper">
+<div id="qv">
+
+ <h2>In this document</h2>
+ <ol>
+ <li><a href="#requirements">Accessibility Requirements</a></li>
+ <li><a href="#recommendations">Accessibility Recommendations</a></li>
+ <li><a href="#special-cases">Special Cases and Considerations</a></li>
+ </ol>
+
+ <h2>See also</h2>
+ <ol>
+ <li><a href="{@docRoot}design/patterns/accessibility.html">Android Design: Accessibility</a></li>
+ <li><a href="{@docRoot}tools/testing/testing_accessibility.html">Accessibility Testing Checklist</a></li>
+ <li><a href="{@docRoot}training/accessibility/index.html">Training: Implementing Accessibility</a></li>
+ <li><a href="{@docRoot}training/design-navigation/index.html">Designing Effective Navigation</a></li>
+ </ol>
+
+</div>
+</div>
+
+<p>Making an application accessible is about a deep commitment to usability, getting the
+details right and delighting your users. This document provides a checklist of accessibility
+requirements, recommendations and considerations to help you make sure your application is
+accessible. Following this checklist does not guarantee your application is accessible, but it's a
+good place to start.</p>
+
+<p>Creating an accessible application is not just the responsibility of developers. Involve your
+design and testing folks as well, and make them are aware of the guidelines for these other stages
+of development:</p>
+
+<ul>
+ <li><a href="{@docRoot}design/patterns/accessibility.html">Android Design: Accessibility</a></li>
+ <li><a href="{@docRoot}tools/testing/testing_accessibility.html">Accessibility Testing
+ Checklist</a></li>
+</ul>
+
+<p>In most cases, creating an accessible Android application does not require extensive code
+restructuring. Rather, it means working through the subtle details of how users interact with your
+application, so you can provide them with feedback they can sense and understand. This checklist
+helps you focus on the key development issues to get the details of accessibility right.</p>
+
+
+<h2 id="requirements">Accessibility Requirements</h2>
+
+<p>The following steps must be completed in order to ensure a minimum level of application
+ accessibility.</p>
+
+<ol>
+ <li><strong>Describe user interface controls:</strong> Provide content
+ <a href="{@docRoot}guide/topics/ui/accessibility/apps.html#label-ui">descriptions</a> for user
+ interface components that do not have visible text, particularly
+ {@link android.widget.ImageButton}, {@link android.widget.ImageView}
+ and {@link android.widget.CheckBox} components. Use the
+ <a href="{@docRoot}reference/android/view/View.html#attr_android:contentDescription">
+ {@code android:contentDescription}</a> XML layout attribute or the {@link
+ android.view.View#setContentDescription} method to provide this information for accessibility
+ services. (Exception: <a href="#decorative">decorative graphics</a>)</li>
+ <li><strong>Enable focus-based navigation:</strong> Make sure
+ <a href="{@docRoot}guide/topics/ui/accessibility/apps.html#focus-nav">users can navigate</a>
+ your screen layouts using hardware-based or software directional controls (D-pads, trackballs,
+ keyboards and navigation gestures). In a few cases, you may need to make user interface components
+ <a href="{@docRoot}reference/android/view/View.html#attr_android:focusable">focusable</a>
+ or change the <a href="{@docRoot}guide/topics/ui/accessibility/apps.html#focus-order">focus
+ order</a> to be more logical for user actions.</li>
+ <li><strong>Custom view controls:</strong> If you build
+ <a href="{@docRoot}guide/topics/ui/custom-components.html">custom interface controls</a> for
+ your application, <a href="{@docRoot}guide/topics/ui/accessibility/apps.html#custom-views">
+ implement accessibility interfaces</a> for your custom views and provide content descriptions.
+ For custom controls that are intended to be compatible with versions of Android back to 1.6,
+ use the <a href="{@docRoot}tools/extras/support-library.html">Support Library</a> to implement
+ the latest accessibility features.</li>
+ <li><strong>No audio-only feedback:</strong> Audio feedback must always have a secondary
+ feedback mechanism to support users who are deaf or hard of hearing. For example, a sound alert
+ for the arrival of a message must be accompanied by a system
+ {@link android.app.Notification}, haptic feedback (if available) or other visual alert.</li>
+ <li><strong>Test:</strong> Test accessibility by navigating your application
+ using directional controls, and using eyes-free navigation with TalkBack enabled.
+ For more accessibility testing information, see the
+ <a href="{@docRoot}tools/testing/testing_accessibility.html">Accessibility Testing
+ Checklist</a>.</li>
+</ol>
+
+
+<h2 id="recommendations">Accessibility Recommendations</h2>
+
+<p>The following steps are recommended for ensuring the accessibility of your application. If you
+ do not take these actions, it may impact the overall accessibility and quality of your
+ application.</p>
+
+<ol>
+ <li><strong>Android Design Accessibility Guidelines:</strong> Before building your layouts,
+ review and follow the accessibility guidelines provided in the
+ <a href="{@docRoot}design/patterns/accessibility.html">Design guidelines</a>.</li>
+ <li><strong>Framework-provided controls:</strong> Use Android's built-in user interface
+ controls whenever possible, as these components provide accessibility support by default.</li>
+ <li><strong>Temporary or self-hiding controls and notifications:</strong> Avoid having user
+ interface controls that fade out or disappear after a certain amount of time. If this behavior
+ is important to your application, provide an alternative interface for these functions.</li>
+</ol>
+
+
+<h2 id="special-cases">Special Cases and Considerations</h2>
+
+<p>The following list describes specific situations where action should be taken to ensure an
+ accessible app. Review this list to see if any of these special cases and considerations apply to
+ your application, and take the appropriate action.</p>
+
+<ol>
+ <li><strong>Text field hints:</strong> For {@link android.widget.EditText} fields, provide an
+ <a href="{@docRoot}reference/android/widget/TextView.html#attr_android:hint">android:hint</a>
+ attribute <em>instead</em> of a content description, to help users understand what content is
+ expected when the text field is empty and allow the contents of the field to be spoken when
+ it is filled.</li>
+ <li><strong>Custom controls with high visual context:</strong> If your application contains a
+ <a href="{@docRoot}guide/topics/ui/custom-components.html">custom control</a> with a high degree
+ of visual context (such as a calendar control), default accessibility services processing may
+ not provide adequate descriptions for users, and you should consider providing a
+ <a href="{@docRoot}guide/topics/ui/accessibility/apps.html#virtual-hierarchy">virtual
+ view hierarchy</a> for your control using
+ {@link android.view.accessibility.AccessibilityNodeProvider}.</li>
+ <li><strong>Custom controls and click handling:</strong> If a custom control in your
+ application performs specific handling of user touch interaction, such as listening with
+ {@link android.view.View#onTouchEvent} for {@link android.view.MotionEvent#ACTION_DOWN
+ MotionEvent.ACTION_DOWN} and {@link android.view.MotionEvent#ACTION_UP MotionEvent.ACTION_UP}
+ and treating it as a click event, you must trigger an {@link
+ android.view.accessibility.AccessibilityEvent} equivalent to a click and provide a way for
+ accessibility services to perform this action for users. For more information, see
+ <a href="{@docRoot}guide/topics/ui/accessibility/apps.html#custom-touch-events">Handling custom
+ touch events</a>.</li>
+ <li><strong>Controls that change function:</strong> If you have buttons or other controls
+ that change function during the normal activity of a user in your application (for example, a
+ button that changes from <strong>Play</strong> to <strong>Pause</strong>), make sure you also
+ change the <a href="{@docRoot}reference/android/view/View.html#attr_android:contentDescription">
+ {@code android:contentDescription}</a> of the button appropriately.</li>
+ <li><strong>Prompts for related controls:</strong> Make sure sets of controls which provide a
+ single function, such as the {@link android.widget.DatePicker}, provide useful audio feedback
+ when an user interacts with the individual controls.</li>
+ <li><strong>Video playback and captioning:</strong> If your application provides video
+ playback, it must support captioning and subtitles to assist users who are deaf or hard of
+ hearing. Your video playback controls must also clearly indicate if captioning is available for
+ a video and provide a clear way of enabling captions.</li>
+ <li><strong>Supplemental accessibility audio feedback:</strong> Use only the Android accessibility
+ framework to provide accessibility audio feedback for your app. Accessibility services such as
+ <a href="https://play.google.com/store/apps/details?id=com.google.android.marvin.talkback"
+ >TalkBack</a> should be the only way your application provides accessibility audio prompts to
+ users. Provide the prompting information with a
+ <a href="{@docRoot}reference/android/view/View.html#attr_android:contentDescription">{@code
+ android:contentDescription}</a> XML layout attribute or dynamically add it using accessibility
+ framework APIs. For example, if your application takes action that you want to announce to a
+ user, such as automatically turning the page of a book, use the {@link
+ android.view.View#announceForAccessibility} method to have accessibility services speak this
+ information to the user.</li>
+ <li><strong>Custom controls with complex visual interactions:</strong> For custom controls that
+ provide complex or non-standard visual interactions, provide a
+ <a href="{@docRoot}guide/topics/ui/accessibility/apps.html#virtual-hierarchy">virtual view
+ hierarchy</a> for your control using {@link android.view.accessibility.AccessibilityNodeProvider}
+ that allows accessibility services to provide a simplified interaction model for the user. If
+ this approach is not feasible, consider providing an alternate view that is accessible.</li>
+ <li><strong>Sets of small controls:</strong> If you have controls that are smaller than
+ the minimum recommended touch size in your application screens, consider grouping these controls
+ together using a {@link android.view.ViewGroup} and providing a
+ <a href="{@docRoot}reference/android/view/View.html#attr_android:contentDescription">{@code
+ android:contentDescription}</a> for the group.</li>
+ <li id="decorative"><strong>Decorative images and graphics:</strong> Elements in application
+ screens that are purely decorative and do not provide any content or enable a user action should
+ not have accessibility content descriptions.</li>
+</ol>
diff --git a/docs/html/guide/topics/ui/accessibility/index.jd b/docs/html/guide/topics/ui/accessibility/index.jd
index 6fd71e2..6cdbde4 100644
--- a/docs/html/guide/topics/ui/accessibility/index.jd
+++ b/docs/html/guide/topics/ui/accessibility/index.jd
@@ -8,47 +8,67 @@
<h2>Topics</h2>
<ol>
- <li><a href="{@docRoot}guide/topics/ui/accessibility/apps.html">Making Applications Accessible</a>
- </li>
- <li><a href="{@docRoot}guide/topics/ui/accessibility/services.html">Building Accessibility
- Services</a></li>
+ <li><a href="apps.html">
+ Making Applications Accessible</a></li>
+ <li><a href="checklist.html">
+ Accessibility Developer Checklist</a></li>
+ <li><a href="services.html">
+ Building Accessibility Services</a></li>
</ol>
- <h2>Key classes</h2>
- <ol>
- <li>{@link android.view.accessibility.AccessibilityEvent}</li>
- <li>{@link android.accessibilityservice.AccessibilityService}</li>
- </ol>
-
<h2>See also</h2>
<ol>
- <li><a href="{@docRoot}training/accessibility/index.html">Implementing Accessibility</a></li>
+ <li><a href="{@docRoot}design/patterns/accessibility.html">Android Design: Accessibility</a></li>
+ <li><a href="{@docRoot}training/accessibility/index.html">Training: Implementing Accessibility</a></li>
+ <li><a href="{@docRoot}tools/testing/testing_accessibility.html">Accessibility Testing Checklist</a></li>
+ </ol>
+
+ <h2>Related Videos</h2>
+ <ol>
+ <li>
+ <iframe title="Google I/O 2012 - Making Android Apps Accessible"
+ width="210" height="160"
+ src="http://www.youtube.com/embed/q3HliaMjL38?rel=0&hd=1"
+ frameborder="0" allowfullscreen>
+ </iframe>
+ <li>
</ol>
</div>
</div>
-<p>Many Android users have disabilities that require them to interact with their Android devices in
-different ways. These include users who have visual, physical or age-related disabilities that
-prevent them from fully seeing or using a touchscreen.</p>
+<p>Many Android users have different abilities that require them to interact with their Android
+devices in different ways. These include users who have visual, physical or age-related limitations
+that prevent them from fully seeing or using a touchscreen, and users with hearing loss who may not
+be able to perceive audible information and alerts.</p>
<p>Android provides accessibility features and services for helping these users navigate their
-devices more easily, including text-to-speech, haptic feedback, trackball and D-pad navigation that
-augment their experience. Android application developers can take advantage of these services to
-make their applications more accessible and also build their own accessibility services.</p>
+devices more easily, including text-to-speech, haptic feedback, gesture navigation, trackball and
+directional-pad navigation. Android application developers can take advantage of these services to
+make their applications more accessible.</p>
+
+<p>Android developers can also build their own accessibility services, which can provide
+enhanced usability features such as audio prompting, physical feedback, and alternative navigation
+modes. Accessibility services can provide these enhancements for all applications, a set of
+applications or just a single app.</p>
<p>The following topics show you how to use the Android framework to make applications more
accessible.</p>
<dl>
- <dt><strong><a href="{@docRoot}guide/topics/ui/accessibility/apps.html">Making Applications
-Accessible</a></strong>
+ <dt><strong><a href="{@docRoot}guide/topics/ui/accessibility/apps.html">
+ Making Applications Accessible</a></strong>
</dt>
<dd>Development practices and API features to ensure your application is accessible to users with
disabilities.</dd>
- <dt><strong><a href="{@docRoot}guide/topics/ui/accessibility/services.html">Building Accessibility
-Services</a></strong>
+ <dt><strong><a href="{@docRoot}guide/topics/ui/accessibility/checklist.html">
+ Accessibility Developer Checklist</a></strong>
+ </dt>
+ <dd>A checklist to help developers ensure that their applications are accessible.</dd>
+
+ <dt><strong><a href="{@docRoot}guide/topics/ui/accessibility/services.html">
+ Building Accessibility Services</a></strong>
</dt>
<dd>How to use API features to build services that make other applications more accessible for
users.</dd>
diff --git a/docs/html/guide/topics/ui/accessibility/services.jd b/docs/html/guide/topics/ui/accessibility/services.jd
index 7d36181..2a6fe7a 100644
--- a/docs/html/guide/topics/ui/accessibility/services.jd
+++ b/docs/html/guide/topics/ui/accessibility/services.jd
@@ -14,8 +14,16 @@
<li><a href="#service-config">Accessibility service configuration</a></li>
</ol>
</li>
+ <li><a href="#register">Registering for Accessibility Events</a></li>
<li><a href="#methods">AccessibilityService Methods</a></li>
<li><a href="#event-details">Getting Event Details</a></li>
+ <li><a href="#act-for-users">Taking Action for Users</a>
+ <ol>
+ <li><a href="#detect-gestures">Listening for gestures</a></li>
+ <li><a href="#using-actions">Using accessibility actions</a></li>
+ <li><a href="#focus-types">Using focus types</a></li>
+ </ol>
+ </li>
<li><a href="#examples">Example Code</a></li>
</ol>
@@ -30,7 +38,7 @@
<h2>See also</h2>
<ol>
- <li><a href="{@docRoot}training/accessibility/index.html">Implementing Accessibility</a></li>
+ <li><a href="{@docRoot}training/accessibility/index.html">Training: Implementing Accessibility</a></li>
</ol>
</div>
@@ -45,20 +53,20 @@
create and distribute their own services. This document explains the basics of building an
accessibility service.</p>
-<p>The ability for you to build and deploy accessibility services was introduced with Android
-1.6 (API Level 4) and received significant improvements with Android 4.0 (API Level 14). The Android
-Support Library was also updated with the release of Android 4.0 to provide support for these
-enhanced accessibility features back to Android 1.6. Developers aiming for widely compatible
-accessibility services are encouraged to use the
-<a href="{@docRoot}tools/extras/support-library.html">Support Library</a> and develop for the more
-advanced accessibility features introduced in Android 4.0.</p>
+<p>The ability for you to build and deploy accessibility services was introduced with Android 1.6
+ (API Level 4) and received significant improvements with Android 4.0 (API Level 14). The Android
+ <a href="{@docRoot}tools/extras/support-library.html">Support Library</a> was also updated with
+ the release of Android 4.0 to provide support for these enhanced accessibility features back to
+ Android 1.6. Developers aiming for widely compatible accessibility services are encouraged to use
+ the Support Library and develop for the more advanced accessibility features introduced in
+ Android 4.0.</p>
<h2 id="manifest">Manifest Declarations and Permissions</h2>
<p>Applications that provide accessibility services must include specific declarations in their
- application manifests in order to be treated as an accessibility service by an Android system.
- This section explains the required and optional settings for accessibility services.</p>
+ application manifests to be treated as an accessibility service by the Android system. This
+ section explains the required and optional settings for accessibility services.</p>
<h3 id="service-declaration">Accessibility service declaration</h3>
@@ -66,7 +74,9 @@
<p>In order to be treated as an accessibility service, your application must include the
{@code service} element (rather than the {@code activity} element) within the {@code application}
element in its manifest. In addition, within the {@code service} element, you must also include an
-accessibility service intent filter, as shown in the following sample:</p>
+accessibility service intent filter. For compatiblity with Android 4.1 and higher, the manifest
+must also request the {@link android.Manifest.permission#BIND_ACCESSIBILITY_SERVICE} permission
+as shown in the following sample:</p>
<pre>
<application>
@@ -76,6 +86,7 @@
<action android:name="android.accessibilityservice.AccessibilityService" />
</intent-filter>
</service>
+ <uses-permission android:name="android.permission.BIND_ACCESSIBILITY_SERVICE" />
</application>
</pre>
@@ -123,27 +134,6 @@
/>
</pre>
-<p>One of the most important functions of the accessibility service configuration parameters is to
-allow you to specify what types of accessibility events your service can handle. Being able to
-specify this information enables accessibility services to cooperate with each other, and allows you
-as a developer the flexibility to handle only specific events types from specific applications. The
-event filtering can include the following criteria:</p>
-
-<ul>
- <li><strong>Package Names</strong> - Specify the package names of applications whose accessibility
-events you want your service to handle. If this parameter is omitted, your accessibility service is
-considered available to service accessibility events for any application. This parameter can be set
-in the accessibility service configuration files with the {@code android:packageNames} attribute as
-a comma-separated list, or set using the {@link
-android.accessibilityservice.AccessibilityServiceInfo#packageNames
-AccessibilityServiceInfo.packageNames} member.</li>
- <li><strong>Event Types</strong> - Specify the types of accessibility events you want your service
-to handle. This parameter can be set in the accessibility service configuration files with the
-{@code android:accessibilityEventTypes} attribute as a comma-separated list, or set using the
-{@link android.accessibilityservice.AccessibilityServiceInfo#eventTypes
-AccessibilityServiceInfo.eventTypes} member. </li>
-</ul>
-
<p>For more information about the XML attributes which can be used in the accessibility service
configuration file, follow these links to the reference documentation:</p>
@@ -162,9 +152,45 @@
the {@link android.accessibilityservice.AccessibilityServiceInfo} reference documentation.</p>
+<h2 id="register">Registering for Accessibility Events</h2>
+
+<p>One of the most important functions of the accessibility service configuration parameters is to
+allow you to specify what types of accessibility events your service can handle. Being able to
+specify this information enables accessibility services to cooperate with each other, and allows you
+as a developer the flexibility to handle only specific events types from specific applications. The
+event filtering can include the following criteria:</p>
+
+<ul>
+ <li><strong>Package Names</strong> - Specify the package names of applications whose accessibility
+events you want your service to handle. If this parameter is omitted, your accessibility service is
+considered available to service accessibility events for any application. This parameter can be set
+in the accessibility service configuration files with the {@code android:packageNames} attribute as
+a comma-separated list, or set using the {@link
+android.accessibilityservice.AccessibilityServiceInfo#packageNames
+AccessibilityServiceInfo.packageNames} member.</li>
+ <li><strong>Event Types</strong> - Specify the types of accessibility events you want your service
+to handle. This parameter can be set in the accessibility service configuration files with the
+{@code android:accessibilityEventTypes} attribute as a list separated by the {@code |} character
+(for example {@code accessibilityEventTypes="typeViewClicked|typeViewFocused"}), or set using the
+{@link android.accessibilityservice.AccessibilityServiceInfo#eventTypes
+AccessibilityServiceInfo.eventTypes} member. </li>
+</ul>
+
+<p>When setting up your accessibility service, carefully consider what events your service is able
+to handle and only register for those events. Since users can activate more than one accessibility
+services at a time, your service must not consume events that it is not able to handle. Remember
+that other services may handle those events in order to improve a user's experience.</p>
+
+<p class="note"><strong>Note:</strong> The Android framework dispatches accessibility events to
+more than one accessibility service if the services provide different
+<a href="{@docRoot}reference/android/R.styleable.html#AccessibilityService_accessibilityFeedbackType">
+feedback types</a>. However, if two or more services provide the same feedback type, then only the
+first registered service receives the event.</p>
+
+
<h2 id="methods">AccessibilityService Methods</h2>
-<p>An application that provides accessibility service must extend the {@link
+<p>An accessibility service must extend the {@link
android.accessibilityservice.AccessibilityService} class and override the following methods from
that class. These methods are presented in the order in which they are called by the Android system,
from when the service is started
@@ -188,15 +214,15 @@
{@link android.view.accessibility.AccessibilityEvent} that matches the event filtering parameters
specified by your accessibility service. For example, when the user clicks a button or focuses on a
user interface control in an application for which your accessibility service is providing feedback.
-When this happens, the system calls this method of your service with the associated {@link
-android.view.accessibility.AccessibilityEvent}, which you can then interpret and provide feedback to
-the user. This method may be called many times over the lifecycle of your service.</li>
+When this happens, the system calls this method, passing the associated {@link
+android.view.accessibility.AccessibilityEvent}, which the service can then interpret and use to
+provide feedback to the user. This method may be called many times over the lifecycle of your
+service.</li>
<li>{@link android.accessibilityservice.AccessibilityService#onInterrupt onInterrupt()} -
(required) This method is called when the system wants to interrupt the feedback your service is
-providing, usually in response to a user taking action, such as moving focus to a different user
-interface control than the one for which you are currently providing feedback. This method may be
-called many times over the lifecycle of your service.</li>
+providing, usually in response to a user action such as moving focus to a different control. This
+method may be called many times over the lifecycle of your service.</li>
<li>{@link android.accessibilityservice.AccessibilityService#onUnbind onUnbind()} - (optional)
This method is called when the system is about to shutdown the accessibility service. Use this
@@ -206,7 +232,9 @@
<p>These callback methods provide the basic structure for your accessibility service. It is up to
you to decide on how to process data provided by the Android system in the form of {@link
-android.view.accessibility.AccessibilityEvent} objects and provide feedback to the user.</p>
+android.view.accessibility.AccessibilityEvent} objects and provide feedback to the user. For more
+information about getting information from an accessibility event, see the
+<a href="{@docRoot}training/accessibility/service.html">Implementing Accessibility</a> training.</p>
<h2 id="event-details">Getting Event Details</h2>
@@ -214,15 +242,15 @@
<p>The Android system provides information to accessibility services about the user interface
interaction through {@link android.view.accessibility.AccessibilityEvent} objects. Prior to Android
4.0, the information available in an accessibility event, while providing a significant amount of
-detail about a user interface control selected by the user, typically provided limited contextual
+detail about a user interface control selected by the user, offered limited contextual
information. In many cases, this missing context information might be critical to understanding the
meaning of the selected control.</p>
-<p>A typical example of an interface where context is of critical importance is a calendar or day
-planner. If a user selects a 4:00 PM time slot in a Monday to Friday day list and the accessibility
-service announces “4 PM”, but fails to indicate this is a Friday a Monday, the month or day, this is
-hardly ideal feedback for the user. In this case, the context of a user interface control is of
-critical importance to a user who wants to schedule a meeting.</p>
+<p>An example of an interface where context is critical is a calendar or day planner. If the
+user selects a 4:00 PM time slot in a Monday to Friday day list and the accessibility service
+announces “4 PM”, but does not announce the weekday name, the day of the month, or the month name,
+the resulting feedback is confusing. In this case, the context of a user interface control is
+critical to a user who wants to schedule a meeting.</p>
<p>Android 4.0 significantly extends the amount of information that an accessibility service can
obtain about an user interface interaction by composing accessibility events based on the view
@@ -245,26 +273,167 @@
AccessibilityEvent.getRecordCount()} and {@link
android.view.accessibility.AccessibilityEvent#getRecord getRecord(int)} - These methods allow you to
retrieve the set of {@link android.view.accessibility.AccessibilityRecord} objects which contributed
-to the {@link android.view.accessibility.AccessibilityEvent} passed to you by the system, which can
-provide more context for your accessibility service.</li>
+to the {@link android.view.accessibility.AccessibilityEvent} passed to you by the system. This level
+of detail provides more context for the event that triggered your accessibility service.</li>
<li>{@link android.view.accessibility.AccessibilityEvent#getSource
AccessibilityEvent.getSource()} - This method returns an {@link
-android.view.accessibility.AccessibilityNodeInfo} object. This object allows you to request the
-parents and children of the component that originated the accessibility event and investigate their
-contents and state in order to provide
+android.view.accessibility.AccessibilityNodeInfo} object. This object allows you to request view
+layout hierarchy (parents and children) of the component that originated the accessibility event.
+This feature allows an accessibility service to investigate the full context of an event, including
+the content and state of any enclosing views or child views.
- <p class="caution"><strong>Important:</strong> The ability to investigate the full view
+<p class="caution"><strong>Important:</strong> The ability to investigate the view
hierarchy from an {@link android.view.accessibility.AccessibilityEvent} potentially exposes private
user information to your accessibility service. For this reason, your service must request this
level of access through the accessibility <a href="#service-config">service configuration XML</a>
file, by including the {@code canRetrieveWindowContent} attribute and setting it to {@code true}. If
you do not include this setting in your service configuration xml file, calls to {@link
android.view.accessibility.AccessibilityEvent#getSource getSource()} fail.</p>
+
+<p class="note"><strong>Note:</strong> In Android 4.1 (API Level 16) and higher, the
+{@link android.view.accessibility.AccessibilityEvent#getSource getSource()} method,
+as well as {@link android.view.accessibility.AccessibilityNodeInfo#getChild
+AccessibilityNodeInfo.getChild()} and
+{@link android.view.accessibility.AccessibilityNodeInfo#getParent getParent()}, return only
+view objects that are considered important for accessibility (views that draw content or respond to
+user actions). If your service requires all views, it can request them by setting the
+{@link android.accessibilityservice.AccessibilityServiceInfo#flags flags} member of the service's
+{@link android.accessibilityservice.AccessibilityServiceInfo} instance to
+{@link android.accessibilityservice.AccessibilityServiceInfo#FLAG_INCLUDE_NOT_IMPORTANT_VIEWS}.</p>
</li>
</ul>
+<h2 id="act-for-users">Taking Action for Users</h2>
+
+<p>Starting with Android 4.0 (API Level 14), accessibility services can act on behalf
+ of users, including changing the input focus and selecting (activating) user interface elements.
+ In Android 4.1 (API Level 16) the range of actions has been expanded to include scrolling lists
+ and interacting with text fields. Accessibility services can
+ also take global actions, such as navigating to the Home screen, pressing the Back button, opening
+ the notifications screen and recent applications list. Android 4.1 also includes a new type of
+ focus, <em>Accessibilty Focus</em>, which makes all visible elements selectable by an
+ accessibility service.</p>
+
+<p>These new capabilities make it possible for developers of accessibility services to create
+ alternative navigation modes such as
+ <a href="{@docRoot}tools/testing/testing_accessibility.html#test-gestures">gesture navigation</a>,
+ and give users with disabilities improved control of their Android devices.</p>
+
+
+<h3 id="detect-gestures">Listening for gestures</h3>
+
+<p>Accessibility services can listen for specific gestures and respond by taking action on behalf
+ of a user. This feature, added in Android 4.1 (API Level 16), and requires that your
+ accessibility service request activation of the Explore by Touch feature. Your service can
+ request this activation by setting the
+ {@link android.accessibilityservice.AccessibilityServiceInfo#flags flags} member of the service’s
+ {@link android.accessibilityservice.AccessibilityServiceInfo} instance to
+ {@link android.accessibilityservice.AccessibilityServiceInfo#FLAG_REQUEST_TOUCH_EXPLORATION_MODE},
+ as shown in the following example.
+ </p>
+
+<pre>
+public class MyAccessibilityService extends AccessibilityService {
+ @Override
+ public void onCreate() {
+ getServiceInfo().flags = AccessibilityServiceInfo.FLAG_REQUEST_TOUCH_EXPLORATION_MODE;
+ }
+ ...
+}
+</pre>
+
+<p>Once your service has requested activation of Explore by Touch, the user must allow the
+ feature to be turned on, if it is not already active. When this feature is active, your service
+ receives notification of accessibility gestures through your service's
+ {@link android.accessibilityservice.AccessibilityService#onGesture onGesture()} callback method
+ and can respond by taking actions for the user.</p>
+
+
+<h3 id="using-actions">Using accessibility actions</h3>
+
+<p>Accessibility services can take action on behalf of users to make interacting with applications
+ simpler and more productive. The ability of accessibility services to perform actions was added
+ in Android 4.0 (API Level 14) and significantly expanded with Android 4.1 (API Level 16).</p>
+
+<p>In order to take actions on behalf of users, your accessibility service must
+ <a href="#register">register</a> to receive events from a few or many applications and request
+ permission to view the content of applications by setting the
+ <a href="{@docRoot}reference/android/R.styleable.html#AccessibilityService_canRetrieveWindowContent">
+ {@code android:canRetrieveWindowContent}</a> to {@code true} in the
+ <a href="#service-config">service configuration file</a>. When events are received by your
+ service, it can then retrieve the
+ {@link android.view.accessibility.AccessibilityNodeInfo} object from the event using
+ {@link android.view.accessibility.AccessibilityEvent#getSource getSource()}.
+ With the {@link android.view.accessibility.AccessibilityNodeInfo} object, your service can then
+ explore the view hierarchy to determine what action to take and then act for the user using
+ {@link android.view.accessibility.AccessibilityNodeInfo#performAction performAction()}.</p>
+
+<pre>
+public class MyAccessibilityService extends AccessibilityService {
+
+ @Override
+ public void onAccessibilityEvent(AccessibilityEvent event) {
+ // get the source node of the event
+ AccessibilityNodeInfo nodeInfo = event.getSource();
+
+ // Use the event and node information to determine
+ // what action to take
+
+ // take action on behalf of the user
+ nodeInfo.performAction(AccessibilityNodeInfo.ACTION_SCROLL_FORWARD);
+
+ // recycle the nodeInfo object
+ nodeInfo.recycle();
+ }
+ ...
+}
+</pre>
+
+<p>The {@link android.view.accessibility.AccessibilityNodeInfo#performAction performAction()} method
+ allows your service to take action within an application. If your service needs to perform a
+ global action such as navigating to the Home screen, pressing the Back button, opening the
+ notifications screen or recent applications list, then use the
+ {@link android.accessibilityservice.AccessibilityService#performGlobalAction performGlobalAction()}
+ method.</p>
+
+
+<h3 id="focus-types">Using focus types</h3>
+
+<p>Android 4.1 (API Level 16) introduces a new type of user interface focus called <em>Accessibility
+ Focus</em>. This type of focus can be used by accessibility services to select any visible user
+ interface element and act on it. This focus type is different from the more well known <em>Input
+ Focus</em>, which determines what on-screen user interface element receives input when a user
+ types characters, presses <strong>Enter</strong> on a keyboard or pushes the center button of a
+ D-pad control.</p>
+
+<p>Accessibility Focus is completely separate and independent from Input Focus. In fact, it is
+ possible for one element in a user interface to have Input Focus while another element has
+ Accessibility Focus. The purpose of Accessibility Focus is to provide accessibility services with
+ a method of interacting with any visible element on a screen, regardless of whether or not the
+ element is input-focusable from a system perspective. You can see accessibility focus in action by
+ testing accessibility gestures. For more information about testing this feature, see
+ <a href="{@docRoot}tools/testing/testing_accessibility.html#test-gestures">Testing gesture
+ navigation</a>.</p>
+
+<p class="note">
+ <strong>Note:</strong> Accessibility services that use Accessibility Focus are responsible for
+ synchronizing the current Input Focus when an element is capable of this type of focus. Services
+ that do not synchronize Input Focus with Accessibility Focus run the risk of causing problems in
+ applications that expect input focus to be in a specific location when certain actions are taken.
+ </p>
+
+<p>An accessibility service can determine what user interface element has Input Focus or
+ Accessibility Focus using the {@link android.view.accessibility.AccessibilityNodeInfo#findFocus
+ AccessibilityNodeInfo.findFocus()} method. You can also search for elements that can be selected
+ with Input Focus using the
+ {@link android.view.accessibility.AccessibilityNodeInfo#focusSearch focusSearch()} method.
+ Finally, your accessibility service can set Accessibility Focus using the
+ {@link android.view.accessibility.AccessibilityNodeInfo#performAction
+ performAction(AccessibilityNodeInfo.ACTION_SET_ACCESSIBILITY_FOCUS)} method.</p>
+
+
<h2 id="examples">Example Code</h2>
<p>The API Demo project contains two samples which can be used as a starting point for generating
diff --git a/docs/html/tools/testing/testing_accessibility.jd b/docs/html/tools/testing/testing_accessibility.jd
new file mode 100644
index 0000000..daf9b36
--- /dev/null
+++ b/docs/html/tools/testing/testing_accessibility.jd
@@ -0,0 +1,257 @@
+page.title=Accessibility Testing Checklist
+parent.title=Testing
+parent.link=index.html
+@jd:body
+
+<div id="qv-wrapper">
+ <div id="qv">
+ <h2>In this document</h2>
+ <ol>
+ <li><a href="#goals">Testing Goals</a></li>
+ <li><a href="#requirements">Testing Requirements</a></li>
+ <li><a href="#recommendations">Testing Recommendations</a></li>
+ <li><a href="#special-cases">Special Cases and Considerations</a></li>
+ <li><a href="#how-to">Testing Accessibility Features</a>
+ <ol>
+ <li><a href="#test-audibles">Testing audible feedback</a></li>
+ <li><a href="#test-navigation">Testing focus navigation</a></li>
+ <li><a href="#test-gestures">Testing gesture navigation</a></li>
+ </ol>
+ </li>
+ </ol>
+
+ <h2>See Also</h2>
+ <ol>
+ <li>
+ <a href="{@docRoot}guide/topics/ui/accessibility/checklist.html">
+ Accessibility Developer Checklist</a>
+ </li>
+ <li>
+ <a href="{@docRoot}design/patterns/accessibility.html">
+ Android Design: Accessibility</a>
+ </li>
+ <li>
+ <a href="{@docRoot}guide/topics/ui/accessibility/apps.html">
+ Making Applications Accessible</a>
+ </li>
+ </ol>
+ </div>
+</div>
+<p>
+ Testing is an important part of making your application accessible to users with varying
+ abilities. Following <a href="{@docRoot}design/patterns/accessibility.html">design</a> and
+ <a href="{@docRoot}guide/topics/ui/accessibility/checklist.html">development</a> guidelines for
+ accessibility are important steps toward that goal, but testing for accessibility can uncover
+ problems with user interaction that are not obvious during design and development.</p>
+
+<p>This accessibility testing checklist guides you through the important aspects of
+ accessibility testing, including overall goals, required testing steps, recommended testing and
+ special considerations. This document also discusses how to enable accessibility features on
+ Android devices for testing purposes.</p>
+
+
+<h2 id="goals">Testing Goals</h2>
+
+<p>Your accessibility testing should have the following, high level goals:</p>
+
+<ul>
+ <li>Set up and use the application without sighted assistance</li>
+ <li>All task workflows in the application can be easily navigated using directional controls and
+ provide clear and appropriate feedback</li>
+</ul>
+
+
+<h2 id="requirements">Testing Requirements</h2>
+
+<p>The following tests must be completed in order to ensure a minimum level of application
+ accessibility.</p>
+
+<ol>
+ <li><strong>Directional controls:</strong> Verify that the application can be operated
+ without the use of a touch screen. Attempt to use only directional controls to accomplish the
+ primary tasks in the application. Use the keyboard and directional-pad (D-Pad) controls in the
+ Android <a href="{@docRoot}tools/devices/emulator.html">Emulator</a> or use
+ <a href="http://support.google.com/nexus/bin/answer.py?hl=en&answer=2700718">gesture
+ navigation</a> on devices with Android 4.1 (API Level 16) or higher.
+ <p class="note"><strong>Note:</strong> Keyboards and D-pads provide different navigation paths
+ than accessibility gestures. While gestures allow users to focus on nearly any on-screen
+ content, keyboard and D-pad navigation only allow focus on input fields and buttons.</p>
+ </li>
+ <li><strong>TalkBack audio prompts:</strong> Verify that user interface controls that provide
+ information (graphics or text) or allow user action have clear and accurate audio descriptions
+ when <a href="#testing-talkback">TalkBack is enabled</a> and controls are focused. Use
+ directional controls to move focus between application layout elements.</li>
+ <li><strong>Explore by Touch prompts:</strong> Verify that user interface controls that
+ provide information (graphics or text) or allow user action have appropriate audio descriptions
+ when <a href="#testing-ebt">Explore by Touch is enabled</a>. There should be no
+ regions where contents or controls do not provide an audio description.</li>
+ <li><strong>Touchable control sizes:</strong> All controls where a user can select or take an
+ action must be a minimum of 48 dp (approximately 9mm) in length and width, as recommended by
+ <a href="{@docRoot}design/patterns/accessibility.html">Android Design</a>.</li>
+ <li><strong>Gestures work with TalkBack enabled:</strong> Verify that app-specific gestures,
+ such as zooming images, scrolling lists, swiping between pages or navigating carousel controls
+ continue to work when <a href="#testing-talkback">TalkBack is enabled</a>. If these gestures do
+ not function, then an alternative interface for these actions must be provided.</li>
+ <li><strong>No audio-only feedback:</strong> Audio feedback must always have a secondary
+ feedback mechanism to support users who are deaf or hard of hearing, for example: A sound alert
+ for the arrival of a message should also be accompanied by a system
+ {@link android.app.Notification}, haptic feedback (if available) or another visual alert.</li>
+</ol>
+
+
+<h2 id="recommendations">Testing Recommendations</h2>
+
+<p>The following tests are recommended for ensuring the accessibility of your application. If you
+ do not test these items, it may impact the overall accessibility and quality of your
+ application.</p>
+
+<ol>
+ <li><strong>Repetitive audio prompting:</strong> Check that closely related controls (such as
+ items with multiple components in a list) do not simply repeat the same audio prompt. For
+ example, in a contacts list that contains a contact picture, written name and title, the prompts
+ should not simply repeat “Bob Smith” for each item.</li>
+ <li><strong>Audio prompt overloading or underloading:</strong> Check that closely related
+ controls provide an appropriate level of audio information that enables users to understand and
+ act on a screen element. Too little or too much prompting can make it difficult to understand
+ and use a control.</li>
+</ol>
+
+
+<h2 id="special-cases">Special Cases and Considerations</h2>
+
+<p>The following list describes specific situations that should be tested to ensure an
+ accessible app. Some, none or all of the cases described here may apply to your application. Be
+ sure to review this list to find out if these special cases apply and take appropriate action.</p>
+
+<ol>
+ <li><strong>Review developer special cases and considerations:</strong> Review the list of
+ <a href="{@docRoot}guide/topics/ui/accessibility/checklist.html#special-cases">special cases</a>
+ for accessibility development and test your application for the cases that apply.</li>
+ <li><strong>Prompts for controls that change function:</strong> Buttons or other controls
+ that change function due to application context or workflow must provide audio prompts
+ appropriate to their current function. For example, a button that changes function from play
+ video to pause video should provide an audio prompt which is appropriate to its current state.</li>
+ <li><strong>Video playback and captioning:</strong> If the application provides video
+ playback, verify that it supports captioning and subtitles to assist users who are deaf or hard
+ of hearing. The video playback controls must clearly indicate if captioning is available for a
+ video and provide a clear way of enabling captions.</li>
+</ol>
+
+
+<h2 id="how-to">Testing Accessibility Features</h2>
+
+<p>Testing of accessibility features such as TalkBack, Explore by Touch and accessibility Gestures
+requires setup of your testing device. This section describes how to enable these features for
+accessibility testing.</p>
+
+
+<h3 id="test-audibles">Testing audible feedback</h3>
+
+<p>Audible accessibility feedback features on Android devices provide audio prompts that speaks
+ the screen content as you move around an application. By enabling these features on an Android
+ device, you can test the experience of users with blindness or low-vision using your application.
+</p>
+
+<p>Audible feedback for users on Android is typically provided by TalkBack accessibility service and
+the Explore by Touch system feature. The TalkBack accessibility service comes preinstalled on most
+Android devices and can also be downloaded for free from
+<a href="https://play.google.com/store/apps/details?id=com.google.android.marvin.talkback">Google
+Play</a>. The Explore by Touch system feature is available on devices running Android 4.0 and later.
+</p>
+
+<h4 id="testing-talkback">Testing with TalkBack</h4>
+
+<p>The <em>TalkBack</em> accessibility service works by speaking the contents of user interface
+controls as the user moves focus onto controls. This service should be enabled as part of testing
+focus navigation and audible prompts.</p>
+
+<p>To enable the TalkBack accessibility service:</p>
+<ol>
+ <li>Launch the <strong>Settings</strong> application.</li>
+ <li>Navigate to the <strong>Accessibility</strong> category and select it.</li>
+ <li>Select <strong>Accessibility</strong> to enable it.</li>
+ <li>Select <strong>TalkBack</strong> to enable it.</li>
+</ol>
+
+<p class="note">
+ <strong>Note:</strong> While TalkBack is the most available Android accessibility service for
+ users with disabilities, other accessibility services are available and may be installed by users.
+</p>
+
+<p>For more information about using TalkBack, see
+<a href="http://support.google.com/nexus/bin/answer.py?hl=en&answer=2700928">Use TalkBack</a>.</p>
+
+<h4 id="testing-ebt">Testing with Explore by Touch</h4>
+
+<p>The <em>Explore by Touch</em> system feature is available on devices running Android 4.0 and
+ later, and works by enabling a special accessibility mode that allows users to drag a finger
+ around the interface of an application and hear the contents of the screen spoken. This feature
+ does not require screen elements to be focused using an directional controller, but listens for
+ hover events over user interface controls.
+</p>
+
+<p>To enable Explore by Touch on Android 4.0 and later:</p>
+<ol>
+ <li>Launch the <strong>Settings</strong> application.</li>
+ <li>Navigate to the <strong>Accessibility</strong> category and select it.</li>
+ <li>Select the <strong>TalkBack</strong> to enable it.
+ <p class="note"><strong>Note:</strong> On Android 4.1 (API Level 16) and higher, the system
+ provides a popup message to enable Explore by Touch. On older versions, you must follow the
+ step below.</p>
+ </li>
+ <li>Return to the <strong>Accessibility</strong> category and select <strong>Explore by
+Touch</strong> to enable it.
+ <p class="note"><strong>Note:</strong> You must turn on TalkBack <em>first</em>, otherwise this
+option is not available.</p>
+ </li>
+</ol>
+
+<p>For more information about using the Explore by Touch features, see
+<a href="http://support.google.com/nexus/bin/answer.py?hl=en&answer=2700722">Use Explore by
+Touch</a>.</p>
+
+<h3 id="test-navigation">Testing focus navigation</h3>
+
+<p>Focus navigation is the use of directional controls to navigate between the individual user
+ interface elements of an application in order to operate it. Users with limited vision or limited
+ manual dexterity often use this mode of navigation instead of touch navigation. As part of
+ accessibility testing, you should verify that your application can be operated using only
+ directional controls.</p>
+
+<p>You can test navigation of your application using only focus controls, even if your test devices
+ does not have a directional controller. The <a href="{@docRoot}tools/help/emulator.html">Android
+ Emulator</a> provides a simulated directional controller that you can use to test navigation. You
+ can also use a software-based directional controller, such as the one provided by the
+ <a href="https://play.google.com/store/apps/details?id=com.googlecode.eyesfree.inputmethod.latin"
+ >Eyes-Free Keyboard</a> to simulate use of a D-pad on a test device that does not have a physical
+ D-pad.</p>
+
+
+<h3 id="test-gestures">Testing gesture navigation</h3>
+
+<p>Gesture navigation is an accessibility navigation mode that allows users to navigate Android
+ devices and applications using specific
+ <a href="http://support.google.com/nexus/bin/answer.py?hl=en&answer=2700718">gestures</a>. This
+ navigation mode is available on Android 4.1 (API Level 16) and higher.</p>
+
+<p class="note"><strong>Note:</strong> Accessibility gestures provide a different navigation path
+than keyboards and D-pads. While gestures allow users to focus on nearly any on-screen
+content, keyboard and D-pad navigation only allow focus on input fields and buttons.</p>
+
+<p>To enable gesture navigation on Android 4.1 and later:</p>
+<ul>
+ <li>Enable both TalkBack and the Explore by Touch feature as described in the
+ <a href="#testing-ebt">Testing with Explore by Touch</a>. When <em>both</em> of these
+ features are enabled, accessibility gestures are automatically enabled.</li>
+ <li>You can change gesture settings using <strong>Settings > Accessibility > TalkBack >
+ Settings > Manage shortcut gestures</strong>.
+</ul>
+
+<p>For more information about using Explore by Touch accessibility gestures, see
+<a href="http://support.google.com/android/bin/topic.py?hl=en&topic=2492346">Accessibility
+gestures</a>.</p>
+
+<p class="note">
+ <strong>Note:</strong> Accessibility services other than TalkBack may map accessibility gestures
+ to different user actions. If gestures are not producing the expected actions during testing, try
+ disabling other accessibility services before proceeding.</p>
\ No newline at end of file
diff --git a/docs/html/tools/tools_toc.cs b/docs/html/tools/tools_toc.cs
index 850e0ec..f3936b8 100644
--- a/docs/html/tools/tools_toc.cs
+++ b/docs/html/tools/tools_toc.cs
@@ -5,7 +5,7 @@
<a href="<?cs var:toroot ?>tools/index.html"><span class="en">Developer Tools</span></a>
</div>
</li>
-
+
<li class="nav-section">
<div class="nav-section-header"><a href="<?cs var:toroot
?>sdk/index.html"><span class="en">Download</span></a></div>
@@ -29,7 +29,7 @@
</li>
</ul>
</li>
-
+
<li class="nav-section">
<div class="nav-section-header">
<a href="/tools/workflow/index.html"><span class="en">Workflow</span></a>
@@ -39,8 +39,8 @@
<div class="nav-section-header"><a href="/tools/devices/index.html"><span class="en">Setting Up Virtual Devices</span></a></div>
<ul>
<li><a href="/tools/devices/managing-avds.html"><span class="en">With AVD Manager</span></a></li>
- <li><a href="/tools/devices/managing-avds-cmdline.html"><span class="en">From the Command Line</span></a></li>
- <li><a href="/tools/devices/emulator.html"><span class="en">Using the Android Emulator</span></a></li>
+ <li><a href="/tools/devices/managing-avds-cmdline.html"><span class="en">From the Command Line</span></a></li>
+ <li><a href="/tools/devices/emulator.html"><span class="en">Using the Android Emulator</span></a></li>
</ul>
</li>
<li><a href="/tools/device.html"><span class="en">Using Hardware Devices</span></a></li>
@@ -48,16 +48,16 @@
<div class="nav-section-header"><a href="/tools/projects/index.html"><span class="en">Setting Up Projects</span></a></div>
<ul>
<li><a href="/tools/projects/projects-eclipse.html"><span class="en">From Eclipse with ADT</span></a></li>
- <li><a href="/tools/projects/projects-cmdline.html"><span class="en">From the Command Line</span></a></li>
+ <li><a href="/tools/projects/projects-cmdline.html"><span class="en">From the Command Line</span></a></li>
</ul>
</li>
-
+
<li class="nav-section">
<div class="nav-section-header"><a href="/tools/building/index.html"><span class="en">Building and Running</span></a></div>
<ul>
<li><a href="/tools/building/building-eclipse.html"><span class="en">From Eclipse with ADT</span></a></li>
- <li><a href="/tools/building/building-cmdline.html"><span class="en">From the Command Line</span></a></li>
+ <li><a href="/tools/building/building-cmdline.html"><span class="en">From the Command Line</span></a></li>
</ul>
</li>
@@ -76,7 +76,7 @@
</li>
<li><a href="<?cs var:toroot ?>tools/testing/testing_otheride.html">
<span class="en">From Other IDEs</span></a>
- </li>
+ </li>
<li>
<a href="<?cs var:toroot?>tools/testing/activity_testing.html">
<span class="en">Activity Testing</span></a>
@@ -90,6 +90,10 @@
<span class="en">Content Provider Testing</span></a>
</li>
<li>
+ <a href="<?cs var:toroot?>tools/testing/testing_accessibility.html">
+ <span class="en">Accessibility Testing</span></a>
+ </li>
+ <li>
<a href="<?cs var:toroot ?>tools/testing/what_to_test.html">
<span class="en">What To Test</span></a>
</li>
@@ -160,7 +164,7 @@
<li><a href="<?cs var:toroot ?>tools/help/zipalign.html">zipalign</a></li>
</ul>
</li>
-
+
<li class="nav-section">
<div class="nav-section-header"><a href="<?cs var:toroot
?>tools/revisions/index.html"><span class="en">Revisions</span></a></div>
@@ -178,8 +182,8 @@
class="en">Platforms</span></a></li>
</ul>
</li>
-
-
+
+
<li class="nav-section">
<div class="nav-section-header"><a href="<?cs var:toroot
?>tools/extras/index.html"><span class="en">Extras</span></a></div>
@@ -192,13 +196,13 @@
</ul>
</li>
-
+
<li class="nav-section">
<div class="nav-section-header empty"><a href="<?cs var:toroot
?>tools/samples/index.html"><span class="en">Samples</span></a></div>
</li>
-
+
<li class="nav-section">
<div class="nav-section-header">
<a href="<?cs var:toroot ?>tools/adk/index.html">
@@ -217,7 +221,7 @@
</li>
</ul>
</li>
-
+
</ul><!-- nav -->
<script type="text/javascript">
diff --git a/docs/html/training/basics/activity-lifecycle/recreating.jd b/docs/html/training/basics/activity-lifecycle/recreating.jd
index 3bbf0bb..8c7126a 100644
--- a/docs/html/training/basics/activity-lifecycle/recreating.jd
+++ b/docs/html/training/basics/activity-lifecycle/recreating.jd
@@ -51,7 +51,7 @@
the foreground activity because the screen configuration has changed and your activity might need to
load alternative resources (such as the layout).</p>
-<p>By default, the system uses the {@link android.os.Bundle} instance state to saves information
+<p>By default, the system uses the {@link android.os.Bundle} instance state to save information
about each {@link android.view.View} object in your activity layout (such as the text value entered
into an {@link android.widget.EditText} object). So, if your activity instance is destroyed and
recreated, the state of the layout is automatically restored to its previous state. However, your
diff --git a/graphics/java/android/renderscript/ScriptIntrinsicColorMatrix.java b/graphics/java/android/renderscript/ScriptIntrinsicColorMatrix.java
new file mode 100644
index 0000000..41e7e00
--- /dev/null
+++ b/graphics/java/android/renderscript/ScriptIntrinsicColorMatrix.java
@@ -0,0 +1,68 @@
+/*
+ * 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 android.renderscript;
+
+import android.content.Context;
+import android.content.res.Resources;
+import android.util.Log;
+
+import java.io.File;
+import java.io.IOException;
+import java.io.InputStream;
+import java.util.Map.Entry;
+import java.util.HashMap;
+
+
+/**
+ * @hide
+ **/
+public class ScriptIntrinsicColorMatrix extends ScriptIntrinsic {
+ private Matrix4f mMatrix = new Matrix4f();
+ private Allocation mInput;
+
+ ScriptIntrinsicColorMatrix(int id, RenderScript rs) {
+ super(id, rs);
+ }
+
+ /**
+ * Supported elements types are float, float4, uchar, uchar4
+ *
+ *
+ * @param rs
+ * @param e
+ *
+ * @return ScriptIntrinsicColorMatrix
+ */
+ public static ScriptIntrinsicColorMatrix create(RenderScript rs, Element e) {
+ int id = rs.nScriptIntrinsicCreate(2, e.getID(rs));
+ return new ScriptIntrinsicColorMatrix(id, rs);
+
+ }
+
+ public void setColorMatrix(Matrix4f m) {
+ mMatrix.load(m);
+ FieldPacker fp = new FieldPacker(16*4);
+ fp.addMatrix(m);
+ setVar(0, fp);
+ }
+
+ public void forEach(Allocation ain, Allocation aout) {
+ forEach(0, ain, aout, null);
+ }
+
+}
+
diff --git a/include/androidfw/ResourceTypes.h b/include/androidfw/ResourceTypes.h
index 23bca3e..48f5bf3 100644
--- a/include/androidfw/ResourceTypes.h
+++ b/include/androidfw/ResourceTypes.h
@@ -957,6 +957,13 @@
SCREENLONG_ANY = ACONFIGURATION_SCREENLONG_ANY << SHIFT_SCREENLONG,
SCREENLONG_NO = ACONFIGURATION_SCREENLONG_NO << SHIFT_SCREENLONG,
SCREENLONG_YES = ACONFIGURATION_SCREENLONG_YES << SHIFT_SCREENLONG,
+
+ // screenLayout bits for layout direction.
+ MASK_LAYOUTDIR = 0xC0,
+ SHIFT_LAYOUTDIR = 6,
+ LAYOUTDIR_ANY = ACONFIGURATION_LAYOUTDIR_ANY << SHIFT_LAYOUTDIR,
+ LAYOUTDIR_LTR = ACONFIGURATION_LAYOUTDIR_LTR << SHIFT_LAYOUTDIR,
+ LAYOUTDIR_RTL = ACONFIGURATION_LAYOUTDIR_RTL << SHIFT_LAYOUTDIR,
};
enum {
@@ -1020,7 +1027,8 @@
CONFIG_SMALLEST_SCREEN_SIZE = ACONFIGURATION_SMALLEST_SCREEN_SIZE,
CONFIG_VERSION = ACONFIGURATION_VERSION,
CONFIG_SCREEN_LAYOUT = ACONFIGURATION_SCREEN_LAYOUT,
- CONFIG_UI_MODE = ACONFIGURATION_UI_MODE
+ CONFIG_UI_MODE = ACONFIGURATION_UI_MODE,
+ CONFIG_LAYOUTDIR = ACONFIGURATION_LAYOUTDIR,
};
// Compare two configuration, returning CONFIG_* flags set for each value
@@ -1061,7 +1069,7 @@
* There should be one of these chunks for each resource type.
*
* This structure is followed by an array of integers providing the set of
- * configuation change flags (ResTable_config::CONFIG_*) that have multiple
+ * configuration change flags (ResTable_config::CONFIG_*) that have multiple
* resources for that configuration. In addition, the high bit is set if that
* resource has been made public.
*/
diff --git a/keystore/java/android/security/AndroidKeyStore.java b/keystore/java/android/security/AndroidKeyStore.java
index 7b6e540..aabfcae 100644
--- a/keystore/java/android/security/AndroidKeyStore.java
+++ b/keystore/java/android/security/AndroidKeyStore.java
@@ -90,6 +90,10 @@
@Override
public Certificate[] engineGetCertificateChain(String alias) {
+ if (alias == null) {
+ throw new NullPointerException("alias == null");
+ }
+
final X509Certificate leaf = (X509Certificate) engineGetCertificate(alias);
if (leaf == null) {
return null;
@@ -119,6 +123,10 @@
@Override
public Certificate engineGetCertificate(String alias) {
+ if (alias == null) {
+ throw new NullPointerException("alias == null");
+ }
+
byte[] certificate = mKeyStore.get(Credentials.USER_CERTIFICATE + alias);
if (certificate != null) {
return toCertificate(certificate);
@@ -166,6 +174,10 @@
@Override
public Date engineGetCreationDate(String alias) {
+ if (alias == null) {
+ throw new NullPointerException("alias == null");
+ }
+
Date d = getModificationDate(Credentials.USER_PRIVATE_KEY + alias);
if (d != null) {
return d;
@@ -325,7 +337,7 @@
@Override
public void engineSetKeyEntry(String alias, byte[] userKey, Certificate[] chain)
throws KeyStoreException {
- throw new RuntimeException("Operation not supported because key encoding is unknown");
+ throw new KeyStoreException("Operation not supported because key encoding is unknown");
}
@Override
@@ -334,6 +346,11 @@
throw new KeyStoreException("Entry exists and is not a trusted certificate");
}
+ // We can't set something to null.
+ if (cert == null) {
+ throw new NullPointerException("cert == null");
+ }
+
final byte[] encoded;
try {
encoded = cert.getEncoded();
@@ -348,6 +365,10 @@
@Override
public void engineDeleteEntry(String alias) throws KeyStoreException {
+ if (!isKeyEntry(alias) && !isCertificateEntry(alias)) {
+ return;
+ }
+
if (!Credentials.deleteAllTypesForAlias(mKeyStore, alias)) {
throw new KeyStoreException("No such entry " + alias);
}
@@ -380,6 +401,10 @@
@Override
public boolean engineContainsAlias(String alias) {
+ if (alias == null) {
+ throw new NullPointerException("alias == null");
+ }
+
return mKeyStore.contains(Credentials.USER_PRIVATE_KEY + alias)
|| mKeyStore.contains(Credentials.USER_CERTIFICATE + alias)
|| mKeyStore.contains(Credentials.CA_CERTIFICATE + alias);
@@ -396,12 +421,24 @@
}
private boolean isKeyEntry(String alias) {
+ if (alias == null) {
+ throw new NullPointerException("alias == null");
+ }
+
return mKeyStore.contains(Credentials.USER_PRIVATE_KEY + alias);
}
+ private boolean isCertificateEntry(String alias) {
+ if (alias == null) {
+ throw new NullPointerException("alias == null");
+ }
+
+ return mKeyStore.contains(Credentials.CA_CERTIFICATE + alias);
+ }
+
@Override
public boolean engineIsCertificateEntry(String alias) {
- return !isKeyEntry(alias) && mKeyStore.contains(Credentials.CA_CERTIFICATE + alias);
+ return !isKeyEntry(alias) && isCertificateEntry(alias);
}
@Override
diff --git a/libs/androidfw/ResourceTypes.cpp b/libs/androidfw/ResourceTypes.cpp
index 8cce191..069dfa3 100644
--- a/libs/androidfw/ResourceTypes.cpp
+++ b/libs/androidfw/ResourceTypes.cpp
@@ -1470,6 +1470,9 @@
if (country[1] != o.country[1]) {
return country[1] < o.country[1] ? -1 : 1;
}
+ if ((screenLayout & MASK_LAYOUTDIR) != (o.screenLayout & MASK_LAYOUTDIR)) {
+ return (screenLayout & MASK_LAYOUTDIR) < (o.screenLayout & MASK_LAYOUTDIR) ? -1 : 1;
+ }
if (smallestScreenWidthDp != o.smallestScreenWidthDp) {
return smallestScreenWidthDp < o.smallestScreenWidthDp ? -1 : 1;
}
@@ -1558,6 +1561,13 @@
}
}
+ if (screenLayout || o.screenLayout) {
+ if (((screenLayout^o.screenLayout) & MASK_LAYOUTDIR) != 0) {
+ if (!(screenLayout & MASK_LAYOUTDIR)) return false;
+ if (!(o.screenLayout & MASK_LAYOUTDIR)) return true;
+ }
+ }
+
if (smallestScreenWidthDp || o.smallestScreenWidthDp) {
if (smallestScreenWidthDp != o.smallestScreenWidthDp) {
if (!smallestScreenWidthDp) return false;
@@ -1683,6 +1693,15 @@
}
}
+ if (screenLayout || o.screenLayout) {
+ if (((screenLayout^o.screenLayout) & MASK_LAYOUTDIR) != 0
+ && (requested->screenLayout & MASK_LAYOUTDIR)) {
+ int myLayoutDir = screenLayout & MASK_LAYOUTDIR;
+ int oLayoutDir = o.screenLayout & MASK_LAYOUTDIR;
+ return (myLayoutDir > oLayoutDir);
+ }
+ }
+
if (smallestScreenWidthDp || o.smallestScreenWidthDp) {
// The configuration closest to the actual size is best.
// We assume that larger configs have already been filtered
@@ -1906,6 +1925,12 @@
}
}
if (screenConfig != 0) {
+ const int layoutDir = screenLayout&MASK_LAYOUTDIR;
+ const int setLayoutDir = settings.screenLayout&MASK_LAYOUTDIR;
+ if (layoutDir != 0 && layoutDir != setLayoutDir) {
+ return false;
+ }
+
const int screenSize = screenLayout&MASK_SCREENSIZE;
const int setScreenSize = settings.screenLayout&MASK_SCREENSIZE;
// Any screen sizes for larger screens than the setting do not
@@ -2032,6 +2057,21 @@
if (res.size() > 0) res.append("-");
res.append(country, 2);
}
+ if ((screenLayout&MASK_LAYOUTDIR) != 0) {
+ if (res.size() > 0) res.append("-");
+ switch (screenLayout&ResTable_config::MASK_LAYOUTDIR) {
+ case ResTable_config::LAYOUTDIR_LTR:
+ res.append("ltr");
+ break;
+ case ResTable_config::LAYOUTDIR_RTL:
+ res.append("rtl");
+ break;
+ default:
+ res.appendFormat("layoutDir=%d",
+ dtohs(screenLayout&ResTable_config::MASK_LAYOUTDIR));
+ break;
+ }
+ }
if (smallestScreenWidthDp != 0) {
if (res.size() > 0) res.append("-");
res.appendFormat("sw%ddp", dtohs(smallestScreenWidthDp));
diff --git a/libs/hwui/Android.mk b/libs/hwui/Android.mk
index 1947c32..c0f79df 100644
--- a/libs/hwui/Android.mk
+++ b/libs/hwui/Android.mk
@@ -6,6 +6,8 @@
ifeq ($(USE_OPENGL_RENDERER),true)
LOCAL_SRC_FILES:= \
utils/SortedListImpl.cpp \
+ font/CacheTexture.cpp \
+ font/Font.cpp \
FontRenderer.cpp \
GammaFontRenderer.cpp \
Caches.cpp \
@@ -28,6 +30,7 @@
SkiaColorFilter.cpp \
SkiaShader.cpp \
Snapshot.cpp \
+ Stencil.cpp \
TextureCache.cpp \
TextDropShadowCache.cpp
diff --git a/libs/hwui/Caches.h b/libs/hwui/Caches.h
index 23d9018..6d27d6e 100644
--- a/libs/hwui/Caches.h
+++ b/libs/hwui/Caches.h
@@ -38,6 +38,7 @@
#include "TextDropShadowCache.h"
#include "FboCache.h"
#include "ResourceCache.h"
+#include "Stencil.h"
#include "Dither.h"
namespace android {
@@ -253,10 +254,14 @@
TextDropShadowCache dropShadowCache;
FboCache fboCache;
ResourceCache resourceCache;
- Dither dither;
GammaFontRenderer* fontRenderer;
+ Dither dither;
+#if STENCIL_BUFFER_SIZE
+ Stencil stencil;
+#endif
+
// Debug methods
PFNGLINSERTEVENTMARKEREXTPROC eventMark;
PFNGLPUSHGROUPMARKEREXTPROC startMark;
diff --git a/libs/hwui/FontRenderer.cpp b/libs/hwui/FontRenderer.cpp
index 6b08e7f..86667ee 100644
--- a/libs/hwui/FontRenderer.cpp
+++ b/libs/hwui/FontRenderer.cpp
@@ -25,584 +25,12 @@
#include "Caches.h"
#include "Debug.h"
#include "FontRenderer.h"
-#include "Caches.h"
+#include "Rect.h"
namespace android {
namespace uirenderer {
///////////////////////////////////////////////////////////////////////////////
-// Defines
-///////////////////////////////////////////////////////////////////////////////
-
-#define DEFAULT_TEXT_CACHE_WIDTH 1024
-#define DEFAULT_TEXT_CACHE_HEIGHT 256
-#define MAX_TEXT_CACHE_WIDTH 2048
-#define CACHE_BLOCK_ROUNDING_SIZE 4
-
-#define AUTO_KERN(prev, next) (((next) - (prev) + 32) >> 6 << 16)
-
-///////////////////////////////////////////////////////////////////////////////
-// CacheBlock
-///////////////////////////////////////////////////////////////////////////////
-
-/**
- * Insert new block into existing linked list of blocks. Blocks are sorted in increasing-width
- * order, except for the final block (the remainder space at the right, since we fill from the
- * left).
- */
-CacheBlock* CacheBlock::insertBlock(CacheBlock* head, CacheBlock *newBlock) {
-#if DEBUG_FONT_RENDERER
- ALOGD("insertBlock: this, x, y, w, h = %p, %d, %d, %d, %d",
- newBlock, newBlock->mX, newBlock->mY,
- newBlock->mWidth, newBlock->mHeight);
-#endif
- CacheBlock *currBlock = head;
- CacheBlock *prevBlock = NULL;
- while (currBlock && currBlock->mY != TEXTURE_BORDER_SIZE) {
- if (newBlock->mWidth < currBlock->mWidth) {
- newBlock->mNext = currBlock;
- newBlock->mPrev = prevBlock;
- currBlock->mPrev = newBlock;
- if (prevBlock) {
- prevBlock->mNext = newBlock;
- return head;
- } else {
- return newBlock;
- }
- }
- prevBlock = currBlock;
- currBlock = currBlock->mNext;
- }
- // new block larger than all others - insert at end (but before the remainder space, if there)
- newBlock->mNext = currBlock;
- newBlock->mPrev = prevBlock;
- if (currBlock) {
- currBlock->mPrev = newBlock;
- }
- if (prevBlock) {
- prevBlock->mNext = newBlock;
- return head;
- } else {
- return newBlock;
- }
-}
-
-CacheBlock* CacheBlock::removeBlock(CacheBlock* head, CacheBlock *blockToRemove) {
-#if DEBUG_FONT_RENDERER
- ALOGD("removeBlock: this, x, y, w, h = %p, %d, %d, %d, %d",
- blockToRemove, blockToRemove->mX, blockToRemove->mY,
- blockToRemove->mWidth, blockToRemove->mHeight);
-#endif
- CacheBlock* newHead = head;
- CacheBlock* nextBlock = blockToRemove->mNext;
- CacheBlock* prevBlock = blockToRemove->mPrev;
- if (prevBlock) {
- prevBlock->mNext = nextBlock;
- } else {
- newHead = nextBlock;
- }
- if (nextBlock) {
- nextBlock->mPrev = prevBlock;
- }
- delete blockToRemove;
- return newHead;
-}
-
-///////////////////////////////////////////////////////////////////////////////
-// CacheTexture
-///////////////////////////////////////////////////////////////////////////////
-
-bool CacheTexture::fitBitmap(const SkGlyph& glyph, uint32_t *retOriginX, uint32_t *retOriginY) {
- if (glyph.fHeight + TEXTURE_BORDER_SIZE > mHeight) {
- return false;
- }
-
- uint16_t glyphW = glyph.fWidth + TEXTURE_BORDER_SIZE;
- uint16_t glyphH = glyph.fHeight + TEXTURE_BORDER_SIZE;
- // roundedUpW equals glyphW to the next multiple of CACHE_BLOCK_ROUNDING_SIZE.
- // This columns for glyphs that are close but not necessarily exactly the same size. It trades
- // off the loss of a few pixels for some glyphs against the ability to store more glyphs
- // of varying sizes in one block.
- uint16_t roundedUpW =
- (glyphW + CACHE_BLOCK_ROUNDING_SIZE - 1) & -CACHE_BLOCK_ROUNDING_SIZE;
- CacheBlock *cacheBlock = mCacheBlocks;
- while (cacheBlock) {
- // Store glyph in this block iff: it fits the block's remaining space and:
- // it's the remainder space (mY == 0) or there's only enough height for this one glyph
- // or it's within ROUNDING_SIZE of the block width
- if (roundedUpW <= cacheBlock->mWidth && glyphH <= cacheBlock->mHeight &&
- (cacheBlock->mY == TEXTURE_BORDER_SIZE ||
- (cacheBlock->mWidth - roundedUpW < CACHE_BLOCK_ROUNDING_SIZE))) {
- if (cacheBlock->mHeight - glyphH < glyphH) {
- // Only enough space for this glyph - don't bother rounding up the width
- roundedUpW = glyphW;
- }
- *retOriginX = cacheBlock->mX;
- *retOriginY = cacheBlock->mY;
- // If this is the remainder space, create a new cache block for this column. Otherwise,
- // adjust the info about this column.
- if (cacheBlock->mY == TEXTURE_BORDER_SIZE) {
- uint16_t oldX = cacheBlock->mX;
- // Adjust remainder space dimensions
- cacheBlock->mWidth -= roundedUpW;
- cacheBlock->mX += roundedUpW;
- if (mHeight - glyphH >= glyphH) {
- // There's enough height left over to create a new CacheBlock
- CacheBlock *newBlock = new CacheBlock(oldX, glyphH + TEXTURE_BORDER_SIZE,
- roundedUpW, mHeight - glyphH - TEXTURE_BORDER_SIZE);
-#if DEBUG_FONT_RENDERER
- ALOGD("fitBitmap: Created new block: this, x, y, w, h = %p, %d, %d, %d, %d",
- newBlock, newBlock->mX, newBlock->mY,
- newBlock->mWidth, newBlock->mHeight);
-#endif
- mCacheBlocks = CacheBlock::insertBlock(mCacheBlocks, newBlock);
- }
- } else {
- // Insert into current column and adjust column dimensions
- cacheBlock->mY += glyphH;
- cacheBlock->mHeight -= glyphH;
-#if DEBUG_FONT_RENDERER
- ALOGD("fitBitmap: Added to existing block: this, x, y, w, h = %p, %d, %d, %d, %d",
- cacheBlock, cacheBlock->mX, cacheBlock->mY,
- cacheBlock->mWidth, cacheBlock->mHeight);
-#endif
- }
- if (cacheBlock->mHeight < fmin(glyphH, glyphW)) {
- // If remaining space in this block is too small to be useful, remove it
- mCacheBlocks = CacheBlock::removeBlock(mCacheBlocks, cacheBlock);
- }
- mDirty = true;
-#if DEBUG_FONT_RENDERER
- ALOGD("fitBitmap: current block list:");
- mCacheBlocks->output();
-#endif
- ++mNumGlyphs;
- return true;
- }
- cacheBlock = cacheBlock->mNext;
- }
-#if DEBUG_FONT_RENDERER
- ALOGD("fitBitmap: returning false for glyph of size %d, %d", glyphW, glyphH);
-#endif
- return false;
-}
-
-///////////////////////////////////////////////////////////////////////////////
-// Font
-///////////////////////////////////////////////////////////////////////////////
-
-Font::Font(FontRenderer* state, uint32_t fontId, float fontSize,
- int flags, uint32_t italicStyle, uint32_t scaleX,
- SkPaint::Style style, uint32_t strokeWidth) :
- mState(state), mFontId(fontId), mFontSize(fontSize),
- mFlags(flags), mItalicStyle(italicStyle), mScaleX(scaleX),
- mStyle(style), mStrokeWidth(mStrokeWidth) {
-}
-
-
-Font::~Font() {
- for (uint32_t ct = 0; ct < mState->mActiveFonts.size(); ct++) {
- if (mState->mActiveFonts[ct] == this) {
- mState->mActiveFonts.removeAt(ct);
- break;
- }
- }
-
- for (uint32_t i = 0; i < mCachedGlyphs.size(); i++) {
- delete mCachedGlyphs.valueAt(i);
- }
-}
-
-void Font::invalidateTextureCache(CacheTexture *cacheTexture) {
- for (uint32_t i = 0; i < mCachedGlyphs.size(); i++) {
- CachedGlyphInfo* cachedGlyph = mCachedGlyphs.valueAt(i);
- if (cacheTexture == NULL || cachedGlyph->mCacheTexture == cacheTexture) {
- cachedGlyph->mIsValid = false;
- }
- }
-}
-
-void Font::measureCachedGlyph(CachedGlyphInfo *glyph, int x, int y,
- uint8_t* bitmap, uint32_t bitmapW, uint32_t bitmapH, Rect* bounds, const float* pos) {
- int nPenX = x + glyph->mBitmapLeft;
- int nPenY = y + glyph->mBitmapTop;
-
- int width = (int) glyph->mBitmapWidth;
- int height = (int) glyph->mBitmapHeight;
-
- if (bounds->bottom > nPenY) {
- bounds->bottom = nPenY;
- }
- if (bounds->left > nPenX) {
- bounds->left = nPenX;
- }
- if (bounds->right < nPenX + width) {
- bounds->right = nPenX + width;
- }
- if (bounds->top < nPenY + height) {
- bounds->top = nPenY + height;
- }
-}
-
-void Font::drawCachedGlyph(CachedGlyphInfo* glyph, int x, int y,
- uint8_t* bitmap, uint32_t bitmapW, uint32_t bitmapH, Rect* bounds, const float* pos) {
- int nPenX = x + glyph->mBitmapLeft;
- int nPenY = y + glyph->mBitmapTop + glyph->mBitmapHeight;
-
- float u1 = glyph->mBitmapMinU;
- float u2 = glyph->mBitmapMaxU;
- float v1 = glyph->mBitmapMinV;
- float v2 = glyph->mBitmapMaxV;
-
- int width = (int) glyph->mBitmapWidth;
- int height = (int) glyph->mBitmapHeight;
-
- mState->appendMeshQuad(nPenX, nPenY, u1, v2,
- nPenX + width, nPenY, u2, v2,
- nPenX + width, nPenY - height, u2, v1,
- nPenX, nPenY - height, u1, v1, glyph->mCacheTexture);
-}
-
-void Font::drawCachedGlyphBitmap(CachedGlyphInfo* glyph, int x, int y,
- uint8_t* bitmap, uint32_t bitmapW, uint32_t bitmapH, Rect* bounds, const float* pos) {
- int nPenX = x + glyph->mBitmapLeft;
- int nPenY = y + glyph->mBitmapTop;
-
- uint32_t endX = glyph->mStartX + glyph->mBitmapWidth;
- uint32_t endY = glyph->mStartY + glyph->mBitmapHeight;
-
- CacheTexture *cacheTexture = glyph->mCacheTexture;
- uint32_t cacheWidth = cacheTexture->mWidth;
- const uint8_t* cacheBuffer = cacheTexture->mTexture;
-
- uint32_t cacheX = 0, cacheY = 0;
- int32_t bX = 0, bY = 0;
- for (cacheX = glyph->mStartX, bX = nPenX; cacheX < endX; cacheX++, bX++) {
- for (cacheY = glyph->mStartY, bY = nPenY; cacheY < endY; cacheY++, bY++) {
-#if DEBUG_FONT_RENDERER
- if (bX < 0 || bY < 0 || bX >= (int32_t) bitmapW || bY >= (int32_t) bitmapH) {
- ALOGE("Skipping invalid index");
- continue;
- }
-#endif
- uint8_t tempCol = cacheBuffer[cacheY * cacheWidth + cacheX];
- bitmap[bY * bitmapW + bX] = tempCol;
- }
- }
-}
-
-void Font::drawCachedGlyph(CachedGlyphInfo* glyph, float x, float hOffset, float vOffset,
- SkPathMeasure& measure, SkPoint* position, SkVector* tangent) {
- const float halfWidth = glyph->mBitmapWidth * 0.5f;
- const float height = glyph->mBitmapHeight;
-
- vOffset += glyph->mBitmapTop + height;
-
- SkPoint destination[4];
- measure.getPosTan(x + hOffset + glyph->mBitmapLeft + halfWidth, position, tangent);
-
- // Move along the tangent and offset by the normal
- destination[0].set(-tangent->fX * halfWidth - tangent->fY * vOffset,
- -tangent->fY * halfWidth + tangent->fX * vOffset);
- destination[1].set(tangent->fX * halfWidth - tangent->fY * vOffset,
- tangent->fY * halfWidth + tangent->fX * vOffset);
- destination[2].set(destination[1].fX + tangent->fY * height,
- destination[1].fY - tangent->fX * height);
- destination[3].set(destination[0].fX + tangent->fY * height,
- destination[0].fY - tangent->fX * height);
-
- const float u1 = glyph->mBitmapMinU;
- const float u2 = glyph->mBitmapMaxU;
- const float v1 = glyph->mBitmapMinV;
- const float v2 = glyph->mBitmapMaxV;
-
- mState->appendRotatedMeshQuad(
- position->fX + destination[0].fX,
- position->fY + destination[0].fY, u1, v2,
- position->fX + destination[1].fX,
- position->fY + destination[1].fY, u2, v2,
- position->fX + destination[2].fX,
- position->fY + destination[2].fY, u2, v1,
- position->fX + destination[3].fX,
- position->fY + destination[3].fY, u1, v1,
- glyph->mCacheTexture);
-}
-
-CachedGlyphInfo* Font::getCachedGlyph(SkPaint* paint, glyph_t textUnit, bool precaching) {
- CachedGlyphInfo* cachedGlyph = NULL;
- ssize_t index = mCachedGlyphs.indexOfKey(textUnit);
- if (index >= 0) {
- cachedGlyph = mCachedGlyphs.valueAt(index);
- } else {
- cachedGlyph = cacheGlyph(paint, textUnit, precaching);
- }
-
- // Is the glyph still in texture cache?
- if (!cachedGlyph->mIsValid) {
- const SkGlyph& skiaGlyph = GET_METRICS(paint, textUnit);
- updateGlyphCache(paint, skiaGlyph, cachedGlyph, precaching);
- }
-
- return cachedGlyph;
-}
-
-void Font::render(SkPaint* paint, const char* text, uint32_t start, uint32_t len,
- int numGlyphs, int x, int y, uint8_t *bitmap, uint32_t bitmapW, uint32_t bitmapH) {
- if (bitmap != NULL && bitmapW > 0 && bitmapH > 0) {
- render(paint, text, start, len, numGlyphs, x, y, BITMAP, bitmap,
- bitmapW, bitmapH, NULL, NULL);
- } else {
- render(paint, text, start, len, numGlyphs, x, y, FRAMEBUFFER, NULL,
- 0, 0, NULL, NULL);
- }
-}
-
-void Font::render(SkPaint* paint, const char *text, uint32_t start, uint32_t len,
- int numGlyphs, int x, int y, const float* positions) {
- render(paint, text, start, len, numGlyphs, x, y, FRAMEBUFFER, NULL,
- 0, 0, NULL, positions);
-}
-
-void Font::render(SkPaint* paint, const char *text, uint32_t start, uint32_t len,
- int numGlyphs, SkPath* path, float hOffset, float vOffset) {
- if (numGlyphs == 0 || text == NULL || len == 0) {
- return;
- }
-
- text += start;
-
- int glyphsCount = 0;
- SkFixed prevRsbDelta = 0;
-
- float penX = 0.0f;
-
- SkPoint position;
- SkVector tangent;
-
- SkPathMeasure measure(*path, false);
- float pathLength = SkScalarToFloat(measure.getLength());
-
- if (paint->getTextAlign() != SkPaint::kLeft_Align) {
- float textWidth = SkScalarToFloat(paint->measureText(text, len));
- float pathOffset = pathLength;
- if (paint->getTextAlign() == SkPaint::kCenter_Align) {
- textWidth *= 0.5f;
- pathOffset *= 0.5f;
- }
- penX += pathOffset - textWidth;
- }
-
- while (glyphsCount < numGlyphs && penX < pathLength) {
- glyph_t glyph = GET_GLYPH(text);
-
- if (IS_END_OF_STRING(glyph)) {
- break;
- }
-
- CachedGlyphInfo* cachedGlyph = getCachedGlyph(paint, glyph);
- penX += SkFixedToFloat(AUTO_KERN(prevRsbDelta, cachedGlyph->mLsbDelta));
- prevRsbDelta = cachedGlyph->mRsbDelta;
-
- if (cachedGlyph->mIsValid) {
- drawCachedGlyph(cachedGlyph, penX, hOffset, vOffset, measure, &position, &tangent);
- }
-
- penX += SkFixedToFloat(cachedGlyph->mAdvanceX);
-
- glyphsCount++;
- }
-}
-
-void Font::measure(SkPaint* paint, const char* text, uint32_t start, uint32_t len,
- int numGlyphs, Rect *bounds, const float* positions) {
- if (bounds == NULL) {
- ALOGE("No return rectangle provided to measure text");
- return;
- }
- bounds->set(1e6, -1e6, -1e6, 1e6);
- render(paint, text, start, len, numGlyphs, 0, 0, MEASURE, NULL, 0, 0, bounds, positions);
-}
-
-void Font::precache(SkPaint* paint, const char* text, int numGlyphs) {
-
- if (numGlyphs == 0 || text == NULL) {
- return;
- }
- int glyphsCount = 0;
-
- while (glyphsCount < numGlyphs) {
- glyph_t glyph = GET_GLYPH(text);
-
- // Reached the end of the string
- if (IS_END_OF_STRING(glyph)) {
- break;
- }
-
- CachedGlyphInfo* cachedGlyph = getCachedGlyph(paint, glyph, true);
-
- glyphsCount++;
- }
-}
-
-void Font::render(SkPaint* paint, const char* text, uint32_t start, uint32_t len,
- int numGlyphs, int x, int y, RenderMode mode, uint8_t *bitmap,
- uint32_t bitmapW, uint32_t bitmapH, Rect* bounds, const float* positions) {
- if (numGlyphs == 0 || text == NULL || len == 0) {
- return;
- }
-
- static RenderGlyph gRenderGlyph[] = {
- &android::uirenderer::Font::drawCachedGlyph,
- &android::uirenderer::Font::drawCachedGlyphBitmap,
- &android::uirenderer::Font::measureCachedGlyph
- };
- RenderGlyph render = gRenderGlyph[mode];
-
- text += start;
- int glyphsCount = 0;
-
- if (CC_LIKELY(positions == NULL)) {
- SkFixed prevRsbDelta = 0;
-
- float penX = x + 0.5f;
- int penY = y;
-
- while (glyphsCount < numGlyphs) {
- glyph_t glyph = GET_GLYPH(text);
-
- // Reached the end of the string
- if (IS_END_OF_STRING(glyph)) {
- break;
- }
-
- CachedGlyphInfo* cachedGlyph = getCachedGlyph(paint, glyph);
- penX += SkFixedToFloat(AUTO_KERN(prevRsbDelta, cachedGlyph->mLsbDelta));
- prevRsbDelta = cachedGlyph->mRsbDelta;
-
- // If it's still not valid, we couldn't cache it, so we shouldn't draw garbage
- if (cachedGlyph->mIsValid) {
- (*this.*render)(cachedGlyph, (int) floorf(penX), penY,
- bitmap, bitmapW, bitmapH, bounds, positions);
- }
-
- penX += SkFixedToFloat(cachedGlyph->mAdvanceX);
-
- glyphsCount++;
- }
- } else {
- const SkPaint::Align align = paint->getTextAlign();
-
- // This is for renderPosText()
- while (glyphsCount < numGlyphs) {
- glyph_t glyph = GET_GLYPH(text);
-
- // Reached the end of the string
- if (IS_END_OF_STRING(glyph)) {
- break;
- }
-
- CachedGlyphInfo* cachedGlyph = getCachedGlyph(paint, glyph);
-
- // If it's still not valid, we couldn't cache it, so we shouldn't draw garbage
- if (cachedGlyph->mIsValid) {
- int penX = x + positions[(glyphsCount << 1)];
- int penY = y + positions[(glyphsCount << 1) + 1];
-
- switch (align) {
- case SkPaint::kRight_Align:
- penX -= SkFixedToFloat(cachedGlyph->mAdvanceX);
- penY -= SkFixedToFloat(cachedGlyph->mAdvanceY);
- break;
- case SkPaint::kCenter_Align:
- penX -= SkFixedToFloat(cachedGlyph->mAdvanceX >> 1);
- penY -= SkFixedToFloat(cachedGlyph->mAdvanceY >> 1);
- default:
- break;
- }
-
- (*this.*render)(cachedGlyph, penX, penY,
- bitmap, bitmapW, bitmapH, bounds, positions);
- }
-
- glyphsCount++;
- }
- }
-}
-
-void Font::updateGlyphCache(SkPaint* paint, const SkGlyph& skiaGlyph, CachedGlyphInfo* glyph,
- bool precaching) {
- glyph->mAdvanceX = skiaGlyph.fAdvanceX;
- glyph->mAdvanceY = skiaGlyph.fAdvanceY;
- glyph->mBitmapLeft = skiaGlyph.fLeft;
- glyph->mBitmapTop = skiaGlyph.fTop;
- glyph->mLsbDelta = skiaGlyph.fLsbDelta;
- glyph->mRsbDelta = skiaGlyph.fRsbDelta;
-
- uint32_t startX = 0;
- uint32_t startY = 0;
-
- // Get the bitmap for the glyph
- paint->findImage(skiaGlyph);
- mState->cacheBitmap(skiaGlyph, glyph, &startX, &startY, precaching);
-
- if (!glyph->mIsValid) {
- return;
- }
-
- uint32_t endX = startX + skiaGlyph.fWidth;
- uint32_t endY = startY + skiaGlyph.fHeight;
-
- glyph->mStartX = startX;
- glyph->mStartY = startY;
- glyph->mBitmapWidth = skiaGlyph.fWidth;
- glyph->mBitmapHeight = skiaGlyph.fHeight;
-
- uint32_t cacheWidth = glyph->mCacheTexture->mWidth;
- uint32_t cacheHeight = glyph->mCacheTexture->mHeight;
-
- glyph->mBitmapMinU = startX / (float) cacheWidth;
- glyph->mBitmapMinV = startY / (float) cacheHeight;
- glyph->mBitmapMaxU = endX / (float) cacheWidth;
- glyph->mBitmapMaxV = endY / (float) cacheHeight;
-
- mState->mUploadTexture = true;
-}
-
-CachedGlyphInfo* Font::cacheGlyph(SkPaint* paint, glyph_t glyph, bool precaching) {
- CachedGlyphInfo* newGlyph = new CachedGlyphInfo();
- mCachedGlyphs.add(glyph, newGlyph);
-
- const SkGlyph& skiaGlyph = GET_METRICS(paint, glyph);
- newGlyph->mGlyphIndex = skiaGlyph.fID;
- newGlyph->mIsValid = false;
-
- updateGlyphCache(paint, skiaGlyph, newGlyph, precaching);
-
- return newGlyph;
-}
-
-Font* Font::create(FontRenderer* state, uint32_t fontId, float fontSize,
- int flags, uint32_t italicStyle, uint32_t scaleX,
- SkPaint::Style style, uint32_t strokeWidth) {
- Vector<Font*> &activeFonts = state->mActiveFonts;
-
- for (uint32_t i = 0; i < activeFonts.size(); i++) {
- Font* font = activeFonts[i];
- if (font->mFontId == fontId && font->mFontSize == fontSize &&
- font->mFlags == flags && font->mItalicStyle == italicStyle &&
- font->mScaleX == scaleX && font->mStyle == style &&
- (style == SkPaint::kFill_Style || font->mStrokeWidth == strokeWidth)) {
- return font;
- }
- }
-
- Font* newFont = new Font(state, fontId, fontSize, flags, italicStyle,
- scaleX, style, strokeWidth);
- activeFonts.push(newFont);
- return newFont;
-}
-
-///////////////////////////////////////////////////////////////////////////////
// FontRenderer
///////////////////////////////////////////////////////////////////////////////
@@ -618,7 +46,7 @@
mMaxNumberOfQuads = 1024;
mCurrentQuadIndex = 0;
- mTextMeshPtr = NULL;
+ mTextMesh = NULL;
mCurrentCacheTexture = NULL;
mLastCacheTexture = NULL;
@@ -626,30 +54,40 @@
mIndexBufferID = 0;
- mSmallCacheWidth = DEFAULT_TEXT_CACHE_WIDTH;
- mSmallCacheHeight = DEFAULT_TEXT_CACHE_HEIGHT;
+ mSmallCacheWidth = DEFAULT_TEXT_SMALL_CACHE_WIDTH;
+ mSmallCacheHeight = DEFAULT_TEXT_SMALL_CACHE_HEIGHT;
+ mLargeCacheWidth = DEFAULT_TEXT_LARGE_CACHE_WIDTH;
+ mLargeCacheHeight = DEFAULT_TEXT_LARGE_CACHE_HEIGHT;
char property[PROPERTY_VALUE_MAX];
- if (property_get(PROPERTY_TEXT_CACHE_WIDTH, property, NULL) > 0) {
- if (sLogFontRendererCreate) {
- INIT_LOGD(" Setting text cache width to %s pixels", property);
- }
+ if (property_get(PROPERTY_TEXT_SMALL_CACHE_WIDTH, property, NULL) > 0) {
mSmallCacheWidth = atoi(property);
- } else {
- if (sLogFontRendererCreate) {
- INIT_LOGD(" Using default text cache width of %i pixels", mSmallCacheWidth);
- }
}
- if (property_get(PROPERTY_TEXT_CACHE_HEIGHT, property, NULL) > 0) {
- if (sLogFontRendererCreate) {
- INIT_LOGD(" Setting text cache width to %s pixels", property);
- }
+ if (property_get(PROPERTY_TEXT_SMALL_CACHE_HEIGHT, property, NULL) > 0) {
mSmallCacheHeight = atoi(property);
- } else {
- if (sLogFontRendererCreate) {
- INIT_LOGD(" Using default text cache height of %i pixels", mSmallCacheHeight);
- }
+ }
+
+ if (property_get(PROPERTY_TEXT_LARGE_CACHE_WIDTH, property, NULL) > 0) {
+ mLargeCacheWidth = atoi(property);
+ }
+
+ if (property_get(PROPERTY_TEXT_LARGE_CACHE_HEIGHT, property, NULL) > 0) {
+ mLargeCacheHeight = atoi(property);
+ }
+
+ uint32_t maxTextureSize = (uint32_t) Caches::getInstance().maxTextureSize;
+ mSmallCacheWidth = mSmallCacheWidth > maxTextureSize ? maxTextureSize : mSmallCacheWidth;
+ mSmallCacheHeight = mSmallCacheHeight > maxTextureSize ? maxTextureSize : mSmallCacheHeight;
+ mLargeCacheWidth = mLargeCacheWidth > maxTextureSize ? maxTextureSize : mLargeCacheWidth;
+ mLargeCacheHeight = mLargeCacheHeight > maxTextureSize ? maxTextureSize : mLargeCacheHeight;
+
+ if (sLogFontRendererCreate) {
+ INIT_LOGD(" Text cache sizes, in pixels: %i x %i, %i x %i, %i x %i, %i x %i",
+ mSmallCacheWidth, mSmallCacheHeight,
+ mLargeCacheWidth, mLargeCacheHeight >> 1,
+ mLargeCacheWidth, mLargeCacheHeight >> 1,
+ mLargeCacheWidth, mLargeCacheHeight);
}
sLogFontRendererCreate = false;
@@ -666,7 +104,7 @@
Caches::getInstance().unbindIndicesBuffer();
glDeleteBuffers(1, &mIndexBufferID);
- delete[] mTextMeshPtr;
+ delete[] mTextMesh;
}
Vector<Font*> fontsToDereference = mActiveFonts;
@@ -689,68 +127,34 @@
mCacheTextures[i]->init();
}
- #if DEBUG_FONT_RENDERER
+#if DEBUG_FONT_RENDERER
uint16_t totalGlyphs = 0;
for (uint32_t i = 0; i < mCacheTextures.size(); i++) {
- totalGlyphs += mCacheTextures[i]->mNumGlyphs;
+ totalGlyphs += mCacheTextures[i]->getGlyphCount();
// Erase caches, just as a debugging facility
- if (mCacheTextures[i]->mTexture) {
- memset(mCacheTextures[i]->mTexture, 0,
- mCacheTextures[i]->mWidth * mCacheTextures[i]->mHeight);
+ if (mCacheTextures[i]->getTexture()) {
+ memset(mCacheTextures[i]->getTexture(), 0,
+ mCacheTextures[i]->getWidth() * mCacheTextures[i]->getHeight());
}
}
ALOGD("Flushing caches: glyphs cached = %d", totalGlyphs);
#endif
}
-void FontRenderer::deallocateTextureMemory(CacheTexture *cacheTexture) {
- if (cacheTexture && cacheTexture->mTexture) {
- glDeleteTextures(1, &cacheTexture->mTextureId);
- delete[] cacheTexture->mTexture;
- cacheTexture->mTexture = NULL;
- cacheTexture->mTextureId = 0;
- }
-}
-
void FontRenderer::flushLargeCaches() {
// Start from 1; don't deallocate smallest/default texture
for (uint32_t i = 1; i < mCacheTextures.size(); i++) {
CacheTexture* cacheTexture = mCacheTextures[i];
- if (cacheTexture->mTexture != NULL) {
+ if (cacheTexture->getTexture()) {
cacheTexture->init();
for (uint32_t j = 0; j < mActiveFonts.size(); j++) {
mActiveFonts[j]->invalidateTextureCache(cacheTexture);
}
- deallocateTextureMemory(cacheTexture);
+ cacheTexture->releaseTexture();
}
}
}
-void FontRenderer::allocateTextureMemory(CacheTexture* cacheTexture) {
- int width = cacheTexture->mWidth;
- int height = cacheTexture->mHeight;
-
- cacheTexture->mTexture = new uint8_t[width * height];
-
- if (!cacheTexture->mTextureId) {
- glGenTextures(1, &cacheTexture->mTextureId);
- }
-
- Caches::getInstance().activeTexture(0);
- glBindTexture(GL_TEXTURE_2D, cacheTexture->mTextureId);
- glPixelStorei(GL_UNPACK_ALIGNMENT, 1);
- // Initialize texture dimensions
- glTexImage2D(GL_TEXTURE_2D, 0, GL_ALPHA, width, height, 0,
- GL_ALPHA, GL_UNSIGNED_BYTE, 0);
-
- const GLenum filtering = cacheTexture->mLinearFiltering ? GL_LINEAR : GL_NEAREST;
- glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MIN_FILTER, filtering);
- glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MAG_FILTER, filtering);
-
- glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_WRAP_S, GL_CLAMP_TO_EDGE);
- glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_WRAP_T, GL_CLAMP_TO_EDGE);
-}
-
CacheTexture* FontRenderer::cacheBitmapInTexture(const SkGlyph& glyph,
uint32_t* startX, uint32_t* startY) {
for (uint32_t i = 0; i < mCacheTextures.size(); i++) {
@@ -768,7 +172,7 @@
cachedGlyph->mIsValid = false;
// If the glyph is too tall, don't cache it
if (glyph.fHeight + TEXTURE_BORDER_SIZE * 2 >
- mCacheTextures[mCacheTextures.size() - 1]->mHeight) {
+ mCacheTextures[mCacheTextures.size() - 1]->getHeight()) {
ALOGE("Font size too large to fit in cache. width, height = %i, %i",
(int) glyph.fWidth, (int) glyph.fHeight);
return;
@@ -802,14 +206,15 @@
uint32_t endX = startX + glyph.fWidth;
uint32_t endY = startY + glyph.fHeight;
- uint32_t cacheWidth = cacheTexture->mWidth;
+ uint32_t cacheWidth = cacheTexture->getWidth();
- if (!cacheTexture->mTexture) {
+ if (!cacheTexture->getTexture()) {
+ Caches::getInstance().activeTexture(0);
// Large-glyph texture memory is allocated only as needed
- allocateTextureMemory(cacheTexture);
+ cacheTexture->allocateTexture();
}
- uint8_t* cacheBuffer = cacheTexture->mTexture;
+ uint8_t* cacheBuffer = cacheTexture->getTexture();
uint8_t* bitmapBuffer = (uint8_t*) glyph.fImage;
unsigned int stride = glyph.rowBytes();
@@ -849,7 +254,8 @@
CacheTexture* cacheTexture = new CacheTexture(width, height);
if (allocate) {
- allocateTextureMemory(cacheTexture);
+ Caches::getInstance().activeTexture(0);
+ cacheTexture->allocateTexture();
}
return cacheTexture;
@@ -861,21 +267,11 @@
}
mCacheTextures.clear();
- // Next, use other, separate caches for large glyphs.
- uint16_t maxWidth = 0;
- if (Caches::hasInstance()) {
- maxWidth = Caches::getInstance().maxTextureSize;
- }
-
- if (maxWidth > MAX_TEXT_CACHE_WIDTH || maxWidth == 0) {
- maxWidth = MAX_TEXT_CACHE_WIDTH;
- }
-
mUploadTexture = false;
mCacheTextures.push(createCacheTexture(mSmallCacheWidth, mSmallCacheHeight, true));
- mCacheTextures.push(createCacheTexture(maxWidth, 256, false));
- mCacheTextures.push(createCacheTexture(maxWidth, 256, false));
- mCacheTextures.push(createCacheTexture(maxWidth, 512, false));
+ mCacheTextures.push(createCacheTexture(mLargeCacheWidth, mLargeCacheHeight >> 1, false));
+ mCacheTextures.push(createCacheTexture(mLargeCacheWidth, mLargeCacheHeight >> 1, false));
+ mCacheTextures.push(createCacheTexture(mLargeCacheWidth, mLargeCacheHeight, false));
mCurrentCacheTexture = mCacheTextures[0];
}
@@ -909,7 +305,7 @@
uint32_t uvSize = 2;
uint32_t vertsPerQuad = 4;
uint32_t vertexBufferSize = mMaxNumberOfQuads * vertsPerQuad * coordSize * uvSize;
- mTextMeshPtr = new float[vertexBufferSize];
+ mTextMesh = new float[vertexBufferSize];
}
// We don't want to allocate anything unless we actually draw text
@@ -934,16 +330,16 @@
// Iterate over all the cache textures and see which ones need to be updated
for (uint32_t i = 0; i < mCacheTextures.size(); i++) {
CacheTexture* cacheTexture = mCacheTextures[i];
- if (cacheTexture->mDirty && cacheTexture->mTexture != NULL) {
+ if (cacheTexture->isDirty() && cacheTexture->getTexture()) {
uint32_t xOffset = 0;
- uint32_t width = cacheTexture->mWidth;
- uint32_t height = cacheTexture->mHeight;
- void* textureData = cacheTexture->mTexture;
+ uint32_t width = cacheTexture->getWidth();
+ uint32_t height = cacheTexture->getHeight();
+ void* textureData = cacheTexture->getTexture();
- if (cacheTexture->mTextureId != lastTextureId) {
+ if (cacheTexture->getTextureId() != lastTextureId) {
+ lastTextureId = cacheTexture->getTextureId();
caches.activeTexture(0);
- glBindTexture(GL_TEXTURE_2D, cacheTexture->mTextureId);
- lastTextureId = cacheTexture->mTextureId;
+ glBindTexture(GL_TEXTURE_2D, lastTextureId);
}
#if DEBUG_FONT_RENDERER
ALOGD("glTextSubimage for cacheTexture %d: xOff, width height = %d, %d, %d",
@@ -952,18 +348,14 @@
glTexSubImage2D(GL_TEXTURE_2D, 0, xOffset, 0, width, height,
GL_ALPHA, GL_UNSIGNED_BYTE, textureData);
- cacheTexture->mDirty = false;
+ cacheTexture->setDirty(false);
}
}
caches.activeTexture(0);
- glBindTexture(GL_TEXTURE_2D, mCurrentCacheTexture->mTextureId);
- if (mLinearFiltering != mCurrentCacheTexture->mLinearFiltering) {
- const GLenum filtering = mLinearFiltering ? GL_LINEAR : GL_NEAREST;
- glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MIN_FILTER, filtering);
- glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MAG_FILTER, filtering);
- mCurrentCacheTexture->mLinearFiltering = mLinearFiltering;
- }
+ glBindTexture(GL_TEXTURE_2D, mCurrentCacheTexture->getTextureId());
+
+ mCurrentCacheTexture->setLinearFiltering(mLinearFiltering, false);
mLastCacheTexture = mCurrentCacheTexture;
mUploadTexture = false;
@@ -975,7 +367,7 @@
Caches& caches = Caches::getInstance();
caches.bindIndicesBuffer(mIndexBufferID);
if (!mDrawn) {
- float* buffer = mTextMeshPtr;
+ float* buffer = mTextMesh;
int offset = 2;
bool force = caches.unbindMeshBuffer();
@@ -1004,7 +396,7 @@
const uint32_t vertsPerQuad = 4;
const uint32_t floatsPerVert = 4;
- float* currentPos = mTextMeshPtr + mCurrentQuadIndex * vertsPerQuad * floatsPerVert;
+ float* currentPos = mTextMesh + mCurrentQuadIndex * vertsPerQuad * floatsPerVert;
(*currentPos++) = x1;
(*currentPos++) = y1;
@@ -1217,6 +609,19 @@
return mDrawn;
}
+void FontRenderer::removeFont(const Font* font) {
+ for (uint32_t ct = 0; ct < mActiveFonts.size(); ct++) {
+ if (mActiveFonts[ct] == font) {
+ mActiveFonts.removeAt(ct);
+ break;
+ }
+ }
+
+ if (mCurrentFont == font) {
+ mCurrentFont = NULL;
+ }
+}
+
void FontRenderer::computeGaussianWeights(float* weights, int32_t radius) {
// Compute gaussian weights for the blur
// e is the euler's number
@@ -1304,7 +709,6 @@
float currentPixel = 0.0f;
for (int32_t y = 0; y < height; y ++) {
-
uint8_t* output = dest + y * width;
for (int32_t x = 0; x < width; x ++) {
@@ -1338,7 +742,7 @@
}
}
*output = (uint8_t) blurredPixel;
- output ++;
+ output++;
}
}
}
diff --git a/libs/hwui/FontRenderer.h b/libs/hwui/FontRenderer.h
index 8d0d21d..405db09 100644
--- a/libs/hwui/FontRenderer.h
+++ b/libs/hwui/FontRenderer.h
@@ -17,268 +17,22 @@
#ifndef ANDROID_HWUI_FONT_RENDERER_H
#define ANDROID_HWUI_FONT_RENDERER_H
-#include <utils/String8.h>
-#include <utils/String16.h>
#include <utils/Vector.h>
-#include <utils/KeyedVector.h>
-#include <SkScalerContext.h>
#include <SkPaint.h>
-#include <SkPathMeasure.h>
-#include <SkPoint.h>
#include <GLES2/gl2.h>
-#include "Rect.h"
+#include "font/FontUtil.h"
+#include "font/CacheTexture.h"
+#include "font/CachedGlyphInfo.h"
+#include "font/Font.h"
#include "Properties.h"
namespace android {
namespace uirenderer {
///////////////////////////////////////////////////////////////////////////////
-// Defines
-///////////////////////////////////////////////////////////////////////////////
-
-#if RENDER_TEXT_AS_GLYPHS
- typedef uint16_t glyph_t;
- #define TO_GLYPH(g) g
- #define GET_METRICS(paint, glyph) paint->getGlyphMetrics(glyph)
- #define GET_GLYPH(text) nextGlyph((const uint16_t**) &text)
- #define IS_END_OF_STRING(glyph) false
-#else
- typedef SkUnichar glyph_t;
- #define TO_GLYPH(g) ((SkUnichar) g)
- #define GET_METRICS(paint, glyph) paint->getUnicharMetrics(glyph)
- #define GET_GLYPH(text) SkUTF16_NextUnichar((const uint16_t**) &text)
- #define IS_END_OF_STRING(glyph) glyph < 0
-#endif
-
-#define TEXTURE_BORDER_SIZE 1
-
-///////////////////////////////////////////////////////////////////////////////
-// Declarations
-///////////////////////////////////////////////////////////////////////////////
-
-class FontRenderer;
-
-/**
- * CacheBlock is a node in a linked list of current free space areas in a CacheTexture.
- * Using CacheBlocks enables us to pack the cache from top to bottom as well as left to right.
- * When we add a glyph to the cache, we see if it fits within one of the existing columns that
- * have already been started (this is the case if the glyph fits vertically as well as
- * horizontally, and if its width is sufficiently close to the column width to avoid
- * sub-optimal packing of small glyphs into wide columns). If there is no column in which the
- * glyph fits, we check the final node, which is the remaining space in the cache, creating
- * a new column as appropriate.
- *
- * As columns fill up, we remove their CacheBlock from the list to avoid having to check
- * small blocks in the future.
- */
-struct CacheBlock {
- uint16_t mX;
- uint16_t mY;
- uint16_t mWidth;
- uint16_t mHeight;
- CacheBlock* mNext;
- CacheBlock* mPrev;
-
- CacheBlock(uint16_t x, uint16_t y, uint16_t width, uint16_t height, bool empty = false):
- mX(x), mY(y), mWidth(width), mHeight(height), mNext(NULL), mPrev(NULL)
- {
- }
-
- static CacheBlock* insertBlock(CacheBlock* head, CacheBlock *newBlock);
-
- static CacheBlock* removeBlock(CacheBlock* head, CacheBlock *blockToRemove);
-
- void output() {
- CacheBlock *currBlock = this;
- while (currBlock) {
- ALOGD("Block: this, x, y, w, h = %p, %d, %d, %d, %d",
- currBlock, currBlock->mX, currBlock->mY, currBlock->mWidth, currBlock->mHeight);
- currBlock = currBlock->mNext;
- }
- }
-};
-
-class CacheTexture {
-public:
- CacheTexture(uint16_t width, uint16_t height) :
- mTexture(NULL), mTextureId(0), mWidth(width), mHeight(height),
- mLinearFiltering(false), mDirty(false), mNumGlyphs(0) {
- mCacheBlocks = new CacheBlock(TEXTURE_BORDER_SIZE, TEXTURE_BORDER_SIZE,
- mWidth - TEXTURE_BORDER_SIZE, mHeight - TEXTURE_BORDER_SIZE, true);
- }
-
- ~CacheTexture() {
- if (mTexture) {
- delete[] mTexture;
- }
- if (mTextureId) {
- glDeleteTextures(1, &mTextureId);
- }
- reset();
- }
-
- void reset() {
- // Delete existing cache blocks
- while (mCacheBlocks != NULL) {
- CacheBlock* tmpBlock = mCacheBlocks;
- mCacheBlocks = mCacheBlocks->mNext;
- delete tmpBlock;
- }
- mNumGlyphs = 0;
- }
-
- void init() {
- // reset, then create a new remainder space to start again
- reset();
- mCacheBlocks = new CacheBlock(TEXTURE_BORDER_SIZE, TEXTURE_BORDER_SIZE,
- mWidth - TEXTURE_BORDER_SIZE, mHeight - TEXTURE_BORDER_SIZE, true);
- }
-
- bool fitBitmap(const SkGlyph& glyph, uint32_t *retOriginX, uint32_t *retOriginY);
-
- uint8_t* mTexture;
- GLuint mTextureId;
- uint16_t mWidth;
- uint16_t mHeight;
- bool mLinearFiltering;
- bool mDirty;
- uint16_t mNumGlyphs;
- CacheBlock* mCacheBlocks;
-};
-
-struct CachedGlyphInfo {
- // Has the cache been invalidated?
- bool mIsValid;
- // Location of the cached glyph in the bitmap
- // in case we need to resize the texture or
- // render to bitmap
- uint32_t mStartX;
- uint32_t mStartY;
- uint32_t mBitmapWidth;
- uint32_t mBitmapHeight;
- // Also cache texture coords for the quad
- float mBitmapMinU;
- float mBitmapMinV;
- float mBitmapMaxU;
- float mBitmapMaxV;
- // Minimize how much we call freetype
- uint32_t mGlyphIndex;
- uint32_t mAdvanceX;
- uint32_t mAdvanceY;
- // Values below contain a glyph's origin in the bitmap
- int32_t mBitmapLeft;
- int32_t mBitmapTop;
- // Auto-kerning
- SkFixed mLsbDelta;
- SkFixed mRsbDelta;
- CacheTexture* mCacheTexture;
-};
-
-
-///////////////////////////////////////////////////////////////////////////////
-// Font
-///////////////////////////////////////////////////////////////////////////////
-
-/**
- * Represents a font, defined by a Skia font id and a font size. A font is used
- * to generate glyphs and cache them in the FontState.
- */
-class Font {
-public:
- enum Style {
- kFakeBold = 1
- };
-
- ~Font();
-
- /**
- * Renders the specified string of text.
- * If bitmap is specified, it will be used as the render target
- */
- void render(SkPaint* paint, const char *text, uint32_t start, uint32_t len,
- int numGlyphs, int x, int y, uint8_t *bitmap = NULL,
- uint32_t bitmapW = 0, uint32_t bitmapH = 0);
-
- void render(SkPaint* paint, const char *text, uint32_t start, uint32_t len,
- int numGlyphs, int x, int y, const float* positions);
-
- void render(SkPaint* paint, const char *text, uint32_t start, uint32_t len,
- int numGlyphs, SkPath* path, float hOffset, float vOffset);
-
- /**
- * Creates a new font associated with the specified font state.
- */
- static Font* create(FontRenderer* state, uint32_t fontId, float fontSize,
- int flags, uint32_t italicStyle, uint32_t scaleX, SkPaint::Style style,
- uint32_t strokeWidth);
-
-protected:
- friend class FontRenderer;
- typedef void (Font::*RenderGlyph)(CachedGlyphInfo*, int, int, uint8_t*,
- uint32_t, uint32_t, Rect*, const float*);
-
- enum RenderMode {
- FRAMEBUFFER,
- BITMAP,
- MEASURE,
- };
-
- void precache(SkPaint* paint, const char* text, int numGlyphs);
-
- void render(SkPaint* paint, const char *text, uint32_t start, uint32_t len,
- int numGlyphs, int x, int y, RenderMode mode, uint8_t *bitmap,
- uint32_t bitmapW, uint32_t bitmapH, Rect *bounds, const float* positions);
-
- void measure(SkPaint* paint, const char* text, uint32_t start, uint32_t len,
- int numGlyphs, Rect *bounds, const float* positions);
-
- Font(FontRenderer* state, uint32_t fontId, float fontSize, int flags, uint32_t italicStyle,
- uint32_t scaleX, SkPaint::Style style, uint32_t strokeWidth);
-
- // Cache of glyphs
- DefaultKeyedVector<glyph_t, CachedGlyphInfo*> mCachedGlyphs;
-
- void invalidateTextureCache(CacheTexture *cacheTexture = NULL);
-
- CachedGlyphInfo* cacheGlyph(SkPaint* paint, glyph_t glyph, bool precaching);
- void updateGlyphCache(SkPaint* paint, const SkGlyph& skiaGlyph, CachedGlyphInfo* glyph,
- bool precaching);
-
- void measureCachedGlyph(CachedGlyphInfo* glyph, int x, int y,
- uint8_t *bitmap, uint32_t bitmapW, uint32_t bitmapH,
- Rect* bounds, const float* pos);
- void drawCachedGlyph(CachedGlyphInfo* glyph, int x, int y,
- uint8_t *bitmap, uint32_t bitmapW, uint32_t bitmapH,
- Rect* bounds, const float* pos);
- void drawCachedGlyphBitmap(CachedGlyphInfo* glyph, int x, int y,
- uint8_t *bitmap, uint32_t bitmapW, uint32_t bitmapH,
- Rect* bounds, const float* pos);
- void drawCachedGlyph(CachedGlyphInfo* glyph, float x, float hOffset, float vOffset,
- SkPathMeasure& measure, SkPoint* position, SkVector* tangent);
-
- CachedGlyphInfo* getCachedGlyph(SkPaint* paint, glyph_t textUnit, bool precaching = false);
-
- static glyph_t nextGlyph(const uint16_t** srcPtr) {
- const uint16_t* src = *srcPtr;
- glyph_t g = *src++;
- *srcPtr = src;
- return g;
- }
-
- FontRenderer* mState;
- uint32_t mFontId;
- float mFontSize;
- int mFlags;
- uint32_t mItalicStyle;
- uint32_t mScaleX;
- SkPaint::Style mStyle;
- uint32_t mStrokeWidth;
-};
-
-///////////////////////////////////////////////////////////////////////////////
// Renderer
///////////////////////////////////////////////////////////////////////////////
@@ -331,31 +85,24 @@
GLuint getTexture(bool linearFiltering = false) {
checkInit();
- if (linearFiltering != mCurrentCacheTexture->mLinearFiltering) {
- mCurrentCacheTexture->mLinearFiltering = linearFiltering;
- mLinearFiltering = linearFiltering;
- const GLenum filtering = linearFiltering ? GL_LINEAR : GL_NEAREST;
+ mCurrentCacheTexture->setLinearFiltering(linearFiltering);
+ mLinearFiltering = linearFiltering;
- glBindTexture(GL_TEXTURE_2D, mCurrentCacheTexture->mTextureId);
- glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MIN_FILTER, filtering);
- glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MAG_FILTER, filtering);
- }
-
- return mCurrentCacheTexture->mTextureId;
+ return mCurrentCacheTexture->getTextureId();
}
uint32_t getCacheSize() const {
uint32_t size = 0;
for (uint32_t i = 0; i < mCacheTextures.size(); i++) {
CacheTexture* cacheTexture = mCacheTextures[i];
- if (cacheTexture != NULL && cacheTexture->mTexture != NULL) {
- size += cacheTexture->mWidth * cacheTexture->mHeight;
+ if (cacheTexture && cacheTexture->getTexture()) {
+ size += cacheTexture->getWidth() * cacheTexture->getHeight();
}
}
return size;
}
-protected:
+private:
friend class Font;
const uint8_t* mGammaTable;
@@ -389,8 +136,18 @@
float x3, float y3, float u3, float v3,
float x4, float y4, float u4, float v4, CacheTexture* texture);
+ void removeFont(const Font* font);
+
+ void checkTextureUpdate();
+
+ void setTextureDirty() {
+ mUploadTexture = true;
+ }
+
uint32_t mSmallCacheWidth;
uint32_t mSmallCacheHeight;
+ uint32_t mLargeCacheWidth;
+ uint32_t mLargeCacheHeight;
Vector<CacheTexture*> mCacheTextures;
@@ -400,11 +157,10 @@
CacheTexture* mCurrentCacheTexture;
CacheTexture* mLastCacheTexture;
- void checkTextureUpdate();
bool mUploadTexture;
// Pointer to vertex data to speed up frame to frame work
- float *mTextMeshPtr;
+ float* mTextMesh;
uint32_t mCurrentQuadIndex;
uint32_t mMaxNumberOfQuads;
@@ -418,12 +174,13 @@
bool mLinearFiltering;
- void computeGaussianWeights(float* weights, int32_t radius);
- void horizontalBlur(float* weights, int32_t radius, const uint8_t *source, uint8_t *dest,
+ /** We should consider multi-threading this code or using Renderscript **/
+ static void computeGaussianWeights(float* weights, int32_t radius);
+ static void horizontalBlur(float* weights, int32_t radius, const uint8_t *source, uint8_t *dest,
int32_t width, int32_t height);
- void verticalBlur(float* weights, int32_t radius, const uint8_t *source, uint8_t *dest,
+ static void verticalBlur(float* weights, int32_t radius, const uint8_t *source, uint8_t *dest,
int32_t width, int32_t height);
- void blurImage(uint8_t* image, int32_t width, int32_t height, int32_t radius);
+ static void blurImage(uint8_t* image, int32_t width, int32_t height, int32_t radius);
};
}; // namespace uirenderer
diff --git a/libs/hwui/OpenGLRenderer.cpp b/libs/hwui/OpenGLRenderer.cpp
index 9865ec4..a1bb6a2 100644
--- a/libs/hwui/OpenGLRenderer.cpp
+++ b/libs/hwui/OpenGLRenderer.cpp
@@ -139,10 +139,6 @@
// Setup
///////////////////////////////////////////////////////////////////////////////
-uint32_t OpenGLRenderer::getStencilSize() {
- return STENCIL_BUFFER_SIZE;
-}
-
bool OpenGLRenderer::isDeferred() {
return false;
}
@@ -1960,7 +1956,7 @@
// This value is used in the fragment shader to determine how to fill fragments.
// We will need to calculate the actual width proportion on each segment for
// scaled non-hairlines, since the boundary proportion may differ per-axis when scaled.
- float boundaryWidthProportion = 1 / (2 * halfStrokeWidth);
+ float boundaryWidthProportion = .5 - 1 / (2 * halfStrokeWidth);
setupDrawAALine((void*) aaVertices, widthCoords, lengthCoords,
boundaryWidthProportion, widthSlot, lengthSlot);
}
@@ -2024,9 +2020,9 @@
abVector.x *= inverseScaleX;
abVector.y *= inverseScaleY;
float abLength = abVector.length();
- boundaryLengthProportion = abLength / (length + abLength);
+ boundaryLengthProportion = .5 - abLength / (length + abLength);
} else {
- boundaryLengthProportion = .5 / (length + 1);
+ boundaryLengthProportion = .5 - .5 / (length + 1);
}
abVector /= 2;
diff --git a/libs/hwui/OpenGLRenderer.h b/libs/hwui/OpenGLRenderer.h
index b7e57a6..f2b5f0a 100644
--- a/libs/hwui/OpenGLRenderer.h
+++ b/libs/hwui/OpenGLRenderer.h
@@ -208,12 +208,6 @@
SkPaint* filterPaint(SkPaint* paint);
/**
- * Returns the desired size for the stencil buffer. If the returned value
- * is 0, then no stencil buffer is required.
- */
- ANDROID_API static uint32_t getStencilSize();
-
- /**
* Sets the alpha on the current snapshot. This alpha value will be modulated
* with other alpha values when drawing primitives.
*/
diff --git a/libs/hwui/Properties.h b/libs/hwui/Properties.h
index 6b6dc9e..0e3268e 100644
--- a/libs/hwui/Properties.h
+++ b/libs/hwui/Properties.h
@@ -74,8 +74,10 @@
#define PROPERTY_TEXTURE_CACHE_FLUSH_RATE "ro.hwui.texture_cache_flush_rate"
// These properties are defined in pixels
-#define PROPERTY_TEXT_CACHE_WIDTH "ro.hwui.text_cache_width"
-#define PROPERTY_TEXT_CACHE_HEIGHT "ro.hwui.text_cache_height"
+#define PROPERTY_TEXT_SMALL_CACHE_WIDTH "ro.hwui.text_small_cache_width"
+#define PROPERTY_TEXT_SMALL_CACHE_HEIGHT "ro.hwui.text_small_cache_height"
+#define PROPERTY_TEXT_LARGE_CACHE_WIDTH "ro.hwui.text_large_cache_width"
+#define PROPERTY_TEXT_LARGE_CACHE_HEIGHT "ro.hwui.text_large_cache_height"
// Indicates whether gamma correction should be applied in the shaders
// or in lookup tables. Accepted values:
diff --git a/libs/hwui/Snapshot.cpp b/libs/hwui/Snapshot.cpp
index 5d5961a..4484676 100644
--- a/libs/hwui/Snapshot.cpp
+++ b/libs/hwui/Snapshot.cpp
@@ -57,7 +57,7 @@
clipRect = &mClipRectRoot;
#if STENCIL_BUFFER_SIZE
if (s->clipRegion) {
- mClipRegionRoot.merge(*s->clipRegion);
+ mClipRegionRoot.op(*s->clipRegion, SkRegion::kUnion_Op);
clipRegion = &mClipRegionRoot;
}
#endif
@@ -84,8 +84,7 @@
#if STENCIL_BUFFER_SIZE
if (!clipRegion) {
clipRegion = &mClipRegionRoot;
- android::Rect tmp(clipRect->left, clipRect->top, clipRect->right, clipRect->bottom);
- clipRegion->set(tmp);
+ clipRegion->setRect(clipRect->left, clipRect->top, clipRect->right, clipRect->bottom);
}
#endif
}
@@ -93,11 +92,11 @@
void Snapshot::copyClipRectFromRegion() {
#if STENCIL_BUFFER_SIZE
if (!clipRegion->isEmpty()) {
- android::Rect bounds(clipRegion->bounds());
- clipRect->set(bounds.left, bounds.top, bounds.right, bounds.bottom);
+ const SkIRect& bounds = clipRegion->getBounds();
+ clipRect->set(bounds.fLeft, bounds.fTop, bounds.fRight, bounds.fBottom);
if (clipRegion->isRect()) {
- clipRegion->clear();
+ clipRegion->setEmpty();
clipRegion = NULL;
}
} else {
@@ -107,43 +106,11 @@
#endif
}
-bool Snapshot::clipRegionOr(float left, float top, float right, float bottom) {
+bool Snapshot::clipRegionOp(float left, float top, float right, float bottom, SkRegion::Op op) {
#if STENCIL_BUFFER_SIZE
- android::Rect tmp(left, top, right, bottom);
- clipRegion->orSelf(tmp);
- copyClipRectFromRegion();
- return true;
-#else
- return false;
-#endif
-}
-
-bool Snapshot::clipRegionXor(float left, float top, float right, float bottom) {
-#if STENCIL_BUFFER_SIZE
- android::Rect tmp(left, top, right, bottom);
- clipRegion->xorSelf(tmp);
- copyClipRectFromRegion();
- return true;
-#else
- return false;
-#endif
-}
-
-bool Snapshot::clipRegionAnd(float left, float top, float right, float bottom) {
-#if STENCIL_BUFFER_SIZE
- android::Rect tmp(left, top, right, bottom);
- clipRegion->andSelf(tmp);
- copyClipRectFromRegion();
- return true;
-#else
- return false;
-#endif
-}
-
-bool Snapshot::clipRegionNand(float left, float top, float right, float bottom) {
-#if STENCIL_BUFFER_SIZE
- android::Rect tmp(left, top, right, bottom);
- clipRegion->subtractSelf(tmp);
+ SkIRect tmp;
+ tmp.set(left, top, right, bottom);
+ clipRegion->op(tmp, op);
copyClipRectFromRegion();
return true;
#else
@@ -161,14 +128,9 @@
bool clipped = false;
switch (op) {
- case SkRegion::kDifference_Op: {
- ensureClipRegion();
- clipped = clipRegionNand(r.left, r.top, r.right, r.bottom);
- break;
- }
case SkRegion::kIntersect_Op: {
if (CC_UNLIKELY(clipRegion)) {
- clipped = clipRegionOr(r.left, r.top, r.right, r.bottom);
+ clipped = clipRegionOp(r.left, r.top, r.right, r.bottom, SkRegion::kIntersect_Op);
} else {
clipped = clipRect->intersect(r);
if (!clipped) {
@@ -180,26 +142,22 @@
}
case SkRegion::kUnion_Op: {
if (CC_UNLIKELY(clipRegion)) {
- clipped = clipRegionAnd(r.left, r.top, r.right, r.bottom);
+ clipped = clipRegionOp(r.left, r.top, r.right, r.bottom, SkRegion::kUnion_Op);
} else {
clipped = clipRect->unionWith(r);
}
break;
}
- case SkRegion::kXOR_Op: {
- ensureClipRegion();
- clipped = clipRegionXor(r.left, r.top, r.right, r.bottom);
- break;
- }
- case SkRegion::kReverseDifference_Op: {
- // TODO!!!!!!!
- break;
- }
case SkRegion::kReplace_Op: {
setClip(r.left, r.top, r.right, r.bottom);
clipped = true;
break;
}
+ default: {
+ ensureClipRegion();
+ clipped = clipRegionOp(r.left, r.top, r.right, r.bottom, op);
+ break;
+ }
}
if (clipped) {
@@ -213,7 +171,7 @@
clipRect->set(left, top, right, bottom);
#if STENCIL_BUFFER_SIZE
if (clipRegion) {
- clipRegion->clear();
+ clipRegion->setEmpty();
clipRegion = NULL;
}
#endif
diff --git a/libs/hwui/Snapshot.h b/libs/hwui/Snapshot.h
index 30b03fc..a89b740 100644
--- a/libs/hwui/Snapshot.h
+++ b/libs/hwui/Snapshot.h
@@ -198,7 +198,7 @@
*
* This field is used only if STENCIL_BUFFER_SIZE is > 0.
*/
- Region* clipRegion;
+ SkRegion* clipRegion;
/**
* The ancestor layer's dirty region.
@@ -223,17 +223,14 @@
void ensureClipRegion();
void copyClipRectFromRegion();
- bool clipRegionOr(float left, float top, float right, float bottom);
- bool clipRegionXor(float left, float top, float right, float bottom);
- bool clipRegionAnd(float left, float top, float right, float bottom);
- bool clipRegionNand(float left, float top, float right, float bottom);
+ bool clipRegionOp(float left, float top, float right, float bottom, SkRegion::Op op);
mat4 mTransformRoot;
Rect mClipRectRoot;
Rect mLocalClip;
#if STENCIL_BUFFER_SIZE
- Region mClipRegionRoot;
+ SkRegion mClipRegionRoot;
#endif
}; // class Snapshot
diff --git a/libs/hwui/Stencil.cpp b/libs/hwui/Stencil.cpp
new file mode 100644
index 0000000..9d2c86f
--- /dev/null
+++ b/libs/hwui/Stencil.cpp
@@ -0,0 +1,71 @@
+/*
+ * 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.
+ */
+
+#include <GLES2/gl2.h>
+
+#include "Properties.h"
+#include "Stencil.h"
+
+namespace android {
+namespace uirenderer {
+
+Stencil::Stencil(): mState(kDisabled) {
+}
+
+uint32_t Stencil::getStencilSize() {
+ return STENCIL_BUFFER_SIZE;
+}
+
+void Stencil::clear() {
+ glClearStencil(0);
+ glClear(GL_STENCIL_BUFFER_BIT);
+}
+
+void Stencil::enableTest() {
+ if (mState != kTest) {
+ enable();
+ glStencilFunc(GL_LESS, 0x0, 0x1);
+ // We only want to test, let's keep everything
+ glStencilOp(GL_KEEP, GL_KEEP, GL_KEEP);
+ mState = kTest;
+ }
+}
+
+void Stencil::enableWrite() {
+ if (mState != kWrite) {
+ enable();
+ glStencilFunc(GL_ALWAYS, 0x1, 0x1);
+ // The test always passes so the first two values are meaningless
+ glStencilOp(GL_KEEP, GL_KEEP, GL_REPLACE);
+ mState = kWrite;
+ }
+}
+
+void Stencil::enable() {
+ if (!mState == kDisabled) {
+ glEnable(GL_STENCIL_TEST);
+ }
+}
+
+void Stencil::disable() {
+ if (mState != kDisabled) {
+ glDisable(GL_STENCIL_TEST);
+ mState = kDisabled;
+ }
+}
+
+}; // namespace uirenderer
+}; // namespace android
diff --git a/libs/hwui/Stencil.h b/libs/hwui/Stencil.h
new file mode 100644
index 0000000..67ccc78
--- /dev/null
+++ b/libs/hwui/Stencil.h
@@ -0,0 +1,89 @@
+/*
+ * 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.
+ */
+
+#ifndef ANDROID_HWUI_STENCIL_H
+#define ANDROID_HWUI_STENCIL_H
+
+#ifndef LOG_TAG
+ #define LOG_TAG "OpenGLRenderer"
+#endif
+
+#include <cutils/compiler.h>
+
+namespace android {
+namespace uirenderer {
+
+///////////////////////////////////////////////////////////////////////////////
+// Stencil buffer management
+///////////////////////////////////////////////////////////////////////////////
+
+class ANDROID_API Stencil {
+public:
+ Stencil();
+
+ /**
+ * Returns the desired size for the stencil buffer. If the returned value
+ * is 0, then no stencil buffer is required.
+ */
+ ANDROID_API static uint32_t getStencilSize();
+
+ /**
+ * Clears the stencil buffer.
+ */
+ void clear();
+
+ /**
+ * Enables stencil test. When the stencil test is enabled the stencil
+ * buffer is not written into.
+ */
+ void enableTest();
+
+ /**
+ * Enables stencil write. When stencil write is enabled, the stencil
+ * test always succeeds and the value 0x1 is written in the stencil
+ * buffer for each fragment.
+ */
+ void enableWrite();
+
+ /**
+ * Disables stencil test and write.
+ */
+ void disable();
+
+ /**
+ * Indicates whether either test or write is enabled.
+ */
+ bool isEnabled() {
+ return mState != kDisabled;
+ }
+
+private:
+ void enable();
+
+ enum StencilState {
+ kDisabled,
+ kTest,
+ kWrite
+ };
+
+ StencilState mState;
+
+}; // class Stencil
+
+}; // namespace uirenderer
+}; // namespace android
+
+#endif // ANDROID_HWUI_STENCIL_H
diff --git a/libs/hwui/font/CacheTexture.cpp b/libs/hwui/font/CacheTexture.cpp
new file mode 100644
index 0000000..4a3af12
--- /dev/null
+++ b/libs/hwui/font/CacheTexture.cpp
@@ -0,0 +1,192 @@
+/*
+ * 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.
+ */
+
+#include <utils/Log.h>
+
+#include "Debug.h"
+#include "CacheTexture.h"
+
+namespace android {
+namespace uirenderer {
+
+///////////////////////////////////////////////////////////////////////////////
+// CacheBlock
+///////////////////////////////////////////////////////////////////////////////
+
+/**
+ * Insert new block into existing linked list of blocks. Blocks are sorted in increasing-width
+ * order, except for the final block (the remainder space at the right, since we fill from the
+ * left).
+ */
+CacheBlock* CacheBlock::insertBlock(CacheBlock* head, CacheBlock* newBlock) {
+#if DEBUG_FONT_RENDERER
+ ALOGD("insertBlock: this, x, y, w, h = %p, %d, %d, %d, %d",
+ newBlock, newBlock->mX, newBlock->mY,
+ newBlock->mWidth, newBlock->mHeight);
+#endif
+
+ CacheBlock* currBlock = head;
+ CacheBlock* prevBlock = NULL;
+
+ while (currBlock && currBlock->mY != TEXTURE_BORDER_SIZE) {
+ if (newBlock->mWidth < currBlock->mWidth) {
+ newBlock->mNext = currBlock;
+ newBlock->mPrev = prevBlock;
+ currBlock->mPrev = newBlock;
+
+ if (prevBlock) {
+ prevBlock->mNext = newBlock;
+ return head;
+ } else {
+ return newBlock;
+ }
+ }
+
+ prevBlock = currBlock;
+ currBlock = currBlock->mNext;
+ }
+
+ // new block larger than all others - insert at end (but before the remainder space, if there)
+ newBlock->mNext = currBlock;
+ newBlock->mPrev = prevBlock;
+
+ if (currBlock) {
+ currBlock->mPrev = newBlock;
+ }
+
+ if (prevBlock) {
+ prevBlock->mNext = newBlock;
+ return head;
+ } else {
+ return newBlock;
+ }
+}
+
+CacheBlock* CacheBlock::removeBlock(CacheBlock* head, CacheBlock* blockToRemove) {
+#if DEBUG_FONT_RENDERER
+ ALOGD("removeBlock: this, x, y, w, h = %p, %d, %d, %d, %d",
+ blockToRemove, blockToRemove->mX, blockToRemove->mY,
+ blockToRemove->mWidth, blockToRemove->mHeight);
+#endif
+
+ CacheBlock* newHead = head;
+ CacheBlock* nextBlock = blockToRemove->mNext;
+ CacheBlock* prevBlock = blockToRemove->mPrev;
+
+ if (prevBlock) {
+ prevBlock->mNext = nextBlock;
+ } else {
+ newHead = nextBlock;
+ }
+
+ if (nextBlock) {
+ nextBlock->mPrev = prevBlock;
+ }
+
+ delete blockToRemove;
+
+ return newHead;
+}
+
+///////////////////////////////////////////////////////////////////////////////
+// CacheTexture
+///////////////////////////////////////////////////////////////////////////////
+
+bool CacheTexture::fitBitmap(const SkGlyph& glyph, uint32_t* retOriginX, uint32_t* retOriginY) {
+ if (glyph.fHeight + TEXTURE_BORDER_SIZE * 2 > mHeight) {
+ return false;
+ }
+
+ uint16_t glyphW = glyph.fWidth + TEXTURE_BORDER_SIZE;
+ uint16_t glyphH = glyph.fHeight + TEXTURE_BORDER_SIZE;
+
+ // roundedUpW equals glyphW to the next multiple of CACHE_BLOCK_ROUNDING_SIZE.
+ // This columns for glyphs that are close but not necessarily exactly the same size. It trades
+ // off the loss of a few pixels for some glyphs against the ability to store more glyphs
+ // of varying sizes in one block.
+ uint16_t roundedUpW = (glyphW + CACHE_BLOCK_ROUNDING_SIZE - 1) & -CACHE_BLOCK_ROUNDING_SIZE;
+
+ CacheBlock* cacheBlock = mCacheBlocks;
+ while (cacheBlock) {
+ // Store glyph in this block iff: it fits the block's remaining space and:
+ // it's the remainder space (mY == 0) or there's only enough height for this one glyph
+ // or it's within ROUNDING_SIZE of the block width
+ if (roundedUpW <= cacheBlock->mWidth && glyphH <= cacheBlock->mHeight &&
+ (cacheBlock->mY == TEXTURE_BORDER_SIZE ||
+ (cacheBlock->mWidth - roundedUpW < CACHE_BLOCK_ROUNDING_SIZE))) {
+ if (cacheBlock->mHeight - glyphH < glyphH) {
+ // Only enough space for this glyph - don't bother rounding up the width
+ roundedUpW = glyphW;
+ }
+
+ *retOriginX = cacheBlock->mX;
+ *retOriginY = cacheBlock->mY;
+
+ // If this is the remainder space, create a new cache block for this column. Otherwise,
+ // adjust the info about this column.
+ if (cacheBlock->mY == TEXTURE_BORDER_SIZE) {
+ uint16_t oldX = cacheBlock->mX;
+ // Adjust remainder space dimensions
+ cacheBlock->mWidth -= roundedUpW;
+ cacheBlock->mX += roundedUpW;
+
+ if (mHeight - glyphH >= glyphH) {
+ // There's enough height left over to create a new CacheBlock
+ CacheBlock* newBlock = new CacheBlock(oldX, glyphH + TEXTURE_BORDER_SIZE,
+ roundedUpW, mHeight - glyphH - TEXTURE_BORDER_SIZE);
+#if DEBUG_FONT_RENDERER
+ ALOGD("fitBitmap: Created new block: this, x, y, w, h = %p, %d, %d, %d, %d",
+ newBlock, newBlock->mX, newBlock->mY,
+ newBlock->mWidth, newBlock->mHeight);
+#endif
+ mCacheBlocks = CacheBlock::insertBlock(mCacheBlocks, newBlock);
+ }
+ } else {
+ // Insert into current column and adjust column dimensions
+ cacheBlock->mY += glyphH;
+ cacheBlock->mHeight -= glyphH;
+#if DEBUG_FONT_RENDERER
+ ALOGD("fitBitmap: Added to existing block: this, x, y, w, h = %p, %d, %d, %d, %d",
+ cacheBlock, cacheBlock->mX, cacheBlock->mY,
+ cacheBlock->mWidth, cacheBlock->mHeight);
+#endif
+ }
+
+ if (cacheBlock->mHeight < fmin(glyphH, glyphW)) {
+ // If remaining space in this block is too small to be useful, remove it
+ mCacheBlocks = CacheBlock::removeBlock(mCacheBlocks, cacheBlock);
+ }
+
+ mDirty = true;
+ mNumGlyphs++;
+
+#if DEBUG_FONT_RENDERER
+ ALOGD("fitBitmap: current block list:");
+ mCacheBlocks->output();
+#endif
+
+ return true;
+ }
+ cacheBlock = cacheBlock->mNext;
+ }
+#if DEBUG_FONT_RENDERER
+ ALOGD("fitBitmap: returning false for glyph of size %d, %d", glyphW, glyphH);
+#endif
+ return false;
+}
+
+}; // namespace uirenderer
+}; // namespace android
diff --git a/libs/hwui/font/CacheTexture.h b/libs/hwui/font/CacheTexture.h
new file mode 100644
index 0000000..bf1f4a9
--- /dev/null
+++ b/libs/hwui/font/CacheTexture.h
@@ -0,0 +1,204 @@
+/*
+ * 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.
+ */
+
+#ifndef ANDROID_HWUI_CACHE_TEXTURE_H
+#define ANDROID_HWUI_CACHE_TEXTURE_H
+
+#include <GLES2/gl2.h>
+
+#include <SkScalerContext.h>
+
+#include <utils/Log.h>
+
+#include "FontUtil.h"
+
+namespace android {
+namespace uirenderer {
+
+/**
+ * CacheBlock is a node in a linked list of current free space areas in a CacheTexture.
+ * Using CacheBlocks enables us to pack the cache from top to bottom as well as left to right.
+ * When we add a glyph to the cache, we see if it fits within one of the existing columns that
+ * have already been started (this is the case if the glyph fits vertically as well as
+ * horizontally, and if its width is sufficiently close to the column width to avoid
+ * sub-optimal packing of small glyphs into wide columns). If there is no column in which the
+ * glyph fits, we check the final node, which is the remaining space in the cache, creating
+ * a new column as appropriate.
+ *
+ * As columns fill up, we remove their CacheBlock from the list to avoid having to check
+ * small blocks in the future.
+ */
+struct CacheBlock {
+ uint16_t mX;
+ uint16_t mY;
+ uint16_t mWidth;
+ uint16_t mHeight;
+ CacheBlock* mNext;
+ CacheBlock* mPrev;
+
+ CacheBlock(uint16_t x, uint16_t y, uint16_t width, uint16_t height, bool empty = false):
+ mX(x), mY(y), mWidth(width), mHeight(height), mNext(NULL), mPrev(NULL) {
+ }
+
+ static CacheBlock* insertBlock(CacheBlock* head, CacheBlock* newBlock);
+
+ static CacheBlock* removeBlock(CacheBlock* head, CacheBlock* blockToRemove);
+
+ void output() {
+ CacheBlock* currBlock = this;
+ while (currBlock) {
+ ALOGD("Block: this, x, y, w, h = %p, %d, %d, %d, %d",
+ currBlock, currBlock->mX, currBlock->mY, currBlock->mWidth, currBlock->mHeight);
+ currBlock = currBlock->mNext;
+ }
+ }
+};
+
+class CacheTexture {
+public:
+ CacheTexture(uint16_t width, uint16_t height) :
+ mTexture(NULL), mTextureId(0), mWidth(width), mHeight(height),
+ mLinearFiltering(false), mDirty(false), mNumGlyphs(0) {
+ mCacheBlocks = new CacheBlock(TEXTURE_BORDER_SIZE, TEXTURE_BORDER_SIZE,
+ mWidth - TEXTURE_BORDER_SIZE, mHeight - TEXTURE_BORDER_SIZE, true);
+ }
+
+ ~CacheTexture() {
+ if (mTexture) {
+ delete[] mTexture;
+ }
+ if (mTextureId) {
+ glDeleteTextures(1, &mTextureId);
+ }
+ reset();
+ }
+
+ void reset() {
+ // Delete existing cache blocks
+ while (mCacheBlocks != NULL) {
+ CacheBlock* tmpBlock = mCacheBlocks;
+ mCacheBlocks = mCacheBlocks->mNext;
+ delete tmpBlock;
+ }
+ mNumGlyphs = 0;
+ }
+
+ void init() {
+ // reset, then create a new remainder space to start again
+ reset();
+ mCacheBlocks = new CacheBlock(TEXTURE_BORDER_SIZE, TEXTURE_BORDER_SIZE,
+ mWidth - TEXTURE_BORDER_SIZE, mHeight - TEXTURE_BORDER_SIZE, true);
+ }
+
+ void releaseTexture() {
+ if (mTexture) {
+ glDeleteTextures(1, &mTextureId);
+ delete[] mTexture;
+ mTexture = NULL;
+ mTextureId = 0;
+ }
+ }
+
+ /**
+ * This method assumes that the proper texture unit is active.
+ */
+ void allocateTexture() {
+ int width = mWidth;
+ int height = mHeight;
+
+ mTexture = new uint8_t[width * height];
+
+ if (!mTextureId) {
+ glGenTextures(1, &mTextureId);
+ }
+
+ glBindTexture(GL_TEXTURE_2D, mTextureId);
+ glPixelStorei(GL_UNPACK_ALIGNMENT, 1);
+ // Initialize texture dimensions
+ glTexImage2D(GL_TEXTURE_2D, 0, GL_ALPHA, width, height, 0,
+ GL_ALPHA, GL_UNSIGNED_BYTE, 0);
+
+ const GLenum filtering = getLinearFiltering() ? GL_LINEAR : GL_NEAREST;
+ glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MIN_FILTER, filtering);
+ glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MAG_FILTER, filtering);
+
+ glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_WRAP_S, GL_CLAMP_TO_EDGE);
+ glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_WRAP_T, GL_CLAMP_TO_EDGE);
+ }
+
+ bool fitBitmap(const SkGlyph& glyph, uint32_t* retOriginX, uint32_t* retOriginY);
+
+ inline uint16_t getWidth() const {
+ return mWidth;
+ }
+
+ inline uint16_t getHeight() const {
+ return mHeight;
+ }
+
+ inline uint8_t* getTexture() const {
+ return mTexture;
+ }
+
+ inline GLuint getTextureId() const {
+ return mTextureId;
+ }
+
+ inline bool isDirty() const {
+ return mDirty;
+ }
+
+ inline void setDirty(bool dirty) {
+ mDirty = dirty;
+ }
+
+ inline bool getLinearFiltering() const {
+ return mLinearFiltering;
+ }
+
+ /**
+ * This method assumes that the proper texture unit is active.
+ */
+ void setLinearFiltering(bool linearFiltering, bool bind = true) {
+ if (linearFiltering != mLinearFiltering) {
+ mLinearFiltering = linearFiltering;
+
+ const GLenum filtering = linearFiltering ? GL_LINEAR : GL_NEAREST;
+ if (bind) glBindTexture(GL_TEXTURE_2D, getTextureId());
+ glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MIN_FILTER, filtering);
+ glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MAG_FILTER, filtering);
+ }
+ }
+
+ inline uint16_t getGlyphCount() const {
+ return mNumGlyphs;
+ }
+
+private:
+ uint8_t* mTexture;
+ GLuint mTextureId;
+ uint16_t mWidth;
+ uint16_t mHeight;
+ bool mLinearFiltering;
+ bool mDirty;
+ uint16_t mNumGlyphs;
+ CacheBlock* mCacheBlocks;
+};
+
+}; // namespace uirenderer
+}; // namespace android
+
+#endif // ANDROID_HWUI_CACHE_TEXTURE_H
diff --git a/libs/hwui/font/CachedGlyphInfo.h b/libs/hwui/font/CachedGlyphInfo.h
new file mode 100644
index 0000000..6680a00
--- /dev/null
+++ b/libs/hwui/font/CachedGlyphInfo.h
@@ -0,0 +1,58 @@
+/*
+ * 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.
+ */
+
+#ifndef ANDROID_HWUI_CACHED_GLYPH_INFO_H
+#define ANDROID_HWUI_CACHED_GLYPH_INFO_H
+
+#include <SkFixed.h>
+
+#include "CacheTexture.h"
+
+namespace android {
+namespace uirenderer {
+
+struct CachedGlyphInfo {
+ // Has the cache been invalidated?
+ bool mIsValid;
+ // Location of the cached glyph in the bitmap
+ // in case we need to resize the texture or
+ // render to bitmap
+ uint32_t mStartX;
+ uint32_t mStartY;
+ uint32_t mBitmapWidth;
+ uint32_t mBitmapHeight;
+ // Also cache texture coords for the quad
+ float mBitmapMinU;
+ float mBitmapMinV;
+ float mBitmapMaxU;
+ float mBitmapMaxV;
+ // Minimize how much we call freetype
+ uint32_t mGlyphIndex;
+ uint32_t mAdvanceX;
+ uint32_t mAdvanceY;
+ // Values below contain a glyph's origin in the bitmap
+ int32_t mBitmapLeft;
+ int32_t mBitmapTop;
+ // Auto-kerning
+ SkFixed mLsbDelta;
+ SkFixed mRsbDelta;
+ CacheTexture* mCacheTexture;
+};
+
+}; // namespace uirenderer
+}; // namespace android
+
+#endif // ANDROID_HWUI_CACHED_GLYPH_INFO_H
diff --git a/libs/hwui/font/Font.cpp b/libs/hwui/font/Font.cpp
new file mode 100644
index 0000000..6e205b8
--- /dev/null
+++ b/libs/hwui/font/Font.cpp
@@ -0,0 +1,441 @@
+/*
+ * 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.
+ */
+
+#include <cutils/compiler.h>
+
+#include <SkUtils.h>
+
+#include "Debug.h"
+#include "FontUtil.h"
+#include "Font.h"
+#include "FontRenderer.h"
+#include "Properties.h"
+
+namespace android {
+namespace uirenderer {
+
+///////////////////////////////////////////////////////////////////////////////
+// Font
+///////////////////////////////////////////////////////////////////////////////
+
+Font::Font(FontRenderer* state, uint32_t fontId, float fontSize,
+ int flags, uint32_t italicStyle, uint32_t scaleX,
+ SkPaint::Style style, uint32_t strokeWidth) :
+ mState(state), mFontId(fontId), mFontSize(fontSize),
+ mFlags(flags), mItalicStyle(italicStyle), mScaleX(scaleX),
+ mStyle(style), mStrokeWidth(mStrokeWidth) {
+}
+
+
+Font::~Font() {
+ mState->removeFont(this);
+
+ for (uint32_t i = 0; i < mCachedGlyphs.size(); i++) {
+ delete mCachedGlyphs.valueAt(i);
+ }
+}
+
+void Font::invalidateTextureCache(CacheTexture* cacheTexture) {
+ for (uint32_t i = 0; i < mCachedGlyphs.size(); i++) {
+ CachedGlyphInfo* cachedGlyph = mCachedGlyphs.valueAt(i);
+ if (!cacheTexture || cachedGlyph->mCacheTexture == cacheTexture) {
+ cachedGlyph->mIsValid = false;
+ }
+ }
+}
+
+void Font::measureCachedGlyph(CachedGlyphInfo *glyph, int x, int y,
+ uint8_t* bitmap, uint32_t bitmapW, uint32_t bitmapH, Rect* bounds, const float* pos) {
+ int nPenX = x + glyph->mBitmapLeft;
+ int nPenY = y + glyph->mBitmapTop;
+
+ int width = (int) glyph->mBitmapWidth;
+ int height = (int) glyph->mBitmapHeight;
+
+ if (bounds->bottom > nPenY) {
+ bounds->bottom = nPenY;
+ }
+ if (bounds->left > nPenX) {
+ bounds->left = nPenX;
+ }
+ if (bounds->right < nPenX + width) {
+ bounds->right = nPenX + width;
+ }
+ if (bounds->top < nPenY + height) {
+ bounds->top = nPenY + height;
+ }
+}
+
+void Font::drawCachedGlyph(CachedGlyphInfo* glyph, int x, int y,
+ uint8_t* bitmap, uint32_t bitmapW, uint32_t bitmapH, Rect* bounds, const float* pos) {
+ int nPenX = x + glyph->mBitmapLeft;
+ int nPenY = y + glyph->mBitmapTop + glyph->mBitmapHeight;
+
+ float u1 = glyph->mBitmapMinU;
+ float u2 = glyph->mBitmapMaxU;
+ float v1 = glyph->mBitmapMinV;
+ float v2 = glyph->mBitmapMaxV;
+
+ int width = (int) glyph->mBitmapWidth;
+ int height = (int) glyph->mBitmapHeight;
+
+ mState->appendMeshQuad(nPenX, nPenY, u1, v2,
+ nPenX + width, nPenY, u2, v2,
+ nPenX + width, nPenY - height, u2, v1,
+ nPenX, nPenY - height, u1, v1, glyph->mCacheTexture);
+}
+
+void Font::drawCachedGlyphBitmap(CachedGlyphInfo* glyph, int x, int y,
+ uint8_t* bitmap, uint32_t bitmapW, uint32_t bitmapH, Rect* bounds, const float* pos) {
+ int nPenX = x + glyph->mBitmapLeft;
+ int nPenY = y + glyph->mBitmapTop;
+
+ uint32_t endX = glyph->mStartX + glyph->mBitmapWidth;
+ uint32_t endY = glyph->mStartY + glyph->mBitmapHeight;
+
+ CacheTexture* cacheTexture = glyph->mCacheTexture;
+ uint32_t cacheWidth = cacheTexture->getWidth();
+ const uint8_t* cacheBuffer = cacheTexture->getTexture();
+
+ uint32_t cacheX = 0, cacheY = 0;
+ int32_t bX = 0, bY = 0;
+ for (cacheX = glyph->mStartX, bX = nPenX; cacheX < endX; cacheX++, bX++) {
+ for (cacheY = glyph->mStartY, bY = nPenY; cacheY < endY; cacheY++, bY++) {
+#if DEBUG_FONT_RENDERER
+ if (bX < 0 || bY < 0 || bX >= (int32_t) bitmapW || bY >= (int32_t) bitmapH) {
+ ALOGE("Skipping invalid index");
+ continue;
+ }
+#endif
+ uint8_t tempCol = cacheBuffer[cacheY * cacheWidth + cacheX];
+ bitmap[bY * bitmapW + bX] = tempCol;
+ }
+ }
+}
+
+void Font::drawCachedGlyph(CachedGlyphInfo* glyph, float x, float hOffset, float vOffset,
+ SkPathMeasure& measure, SkPoint* position, SkVector* tangent) {
+ const float halfWidth = glyph->mBitmapWidth * 0.5f;
+ const float height = glyph->mBitmapHeight;
+
+ vOffset += glyph->mBitmapTop + height;
+
+ SkPoint destination[4];
+ measure.getPosTan(x + hOffset + glyph->mBitmapLeft + halfWidth, position, tangent);
+
+ // Move along the tangent and offset by the normal
+ destination[0].set(-tangent->fX * halfWidth - tangent->fY * vOffset,
+ -tangent->fY * halfWidth + tangent->fX * vOffset);
+ destination[1].set(tangent->fX * halfWidth - tangent->fY * vOffset,
+ tangent->fY * halfWidth + tangent->fX * vOffset);
+ destination[2].set(destination[1].fX + tangent->fY * height,
+ destination[1].fY - tangent->fX * height);
+ destination[3].set(destination[0].fX + tangent->fY * height,
+ destination[0].fY - tangent->fX * height);
+
+ const float u1 = glyph->mBitmapMinU;
+ const float u2 = glyph->mBitmapMaxU;
+ const float v1 = glyph->mBitmapMinV;
+ const float v2 = glyph->mBitmapMaxV;
+
+ mState->appendRotatedMeshQuad(
+ position->fX + destination[0].fX,
+ position->fY + destination[0].fY, u1, v2,
+ position->fX + destination[1].fX,
+ position->fY + destination[1].fY, u2, v2,
+ position->fX + destination[2].fX,
+ position->fY + destination[2].fY, u2, v1,
+ position->fX + destination[3].fX,
+ position->fY + destination[3].fY, u1, v1,
+ glyph->mCacheTexture);
+}
+
+CachedGlyphInfo* Font::getCachedGlyph(SkPaint* paint, glyph_t textUnit, bool precaching) {
+ CachedGlyphInfo* cachedGlyph = NULL;
+ ssize_t index = mCachedGlyphs.indexOfKey(textUnit);
+ if (index >= 0) {
+ cachedGlyph = mCachedGlyphs.valueAt(index);
+ } else {
+ cachedGlyph = cacheGlyph(paint, textUnit, precaching);
+ }
+
+ // Is the glyph still in texture cache?
+ if (!cachedGlyph->mIsValid) {
+ const SkGlyph& skiaGlyph = GET_METRICS(paint, textUnit);
+ updateGlyphCache(paint, skiaGlyph, cachedGlyph, precaching);
+ }
+
+ return cachedGlyph;
+}
+
+void Font::render(SkPaint* paint, const char* text, uint32_t start, uint32_t len,
+ int numGlyphs, int x, int y, uint8_t *bitmap, uint32_t bitmapW, uint32_t bitmapH) {
+ if (bitmap != NULL && bitmapW > 0 && bitmapH > 0) {
+ render(paint, text, start, len, numGlyphs, x, y, BITMAP, bitmap,
+ bitmapW, bitmapH, NULL, NULL);
+ } else {
+ render(paint, text, start, len, numGlyphs, x, y, FRAMEBUFFER, NULL,
+ 0, 0, NULL, NULL);
+ }
+}
+
+void Font::render(SkPaint* paint, const char *text, uint32_t start, uint32_t len,
+ int numGlyphs, int x, int y, const float* positions) {
+ render(paint, text, start, len, numGlyphs, x, y, FRAMEBUFFER, NULL,
+ 0, 0, NULL, positions);
+}
+
+void Font::render(SkPaint* paint, const char *text, uint32_t start, uint32_t len,
+ int numGlyphs, SkPath* path, float hOffset, float vOffset) {
+ if (numGlyphs == 0 || text == NULL || len == 0) {
+ return;
+ }
+
+ text += start;
+
+ int glyphsCount = 0;
+ SkFixed prevRsbDelta = 0;
+
+ float penX = 0.0f;
+
+ SkPoint position;
+ SkVector tangent;
+
+ SkPathMeasure measure(*path, false);
+ float pathLength = SkScalarToFloat(measure.getLength());
+
+ if (paint->getTextAlign() != SkPaint::kLeft_Align) {
+ float textWidth = SkScalarToFloat(paint->measureText(text, len));
+ float pathOffset = pathLength;
+ if (paint->getTextAlign() == SkPaint::kCenter_Align) {
+ textWidth *= 0.5f;
+ pathOffset *= 0.5f;
+ }
+ penX += pathOffset - textWidth;
+ }
+
+ while (glyphsCount < numGlyphs && penX < pathLength) {
+ glyph_t glyph = GET_GLYPH(text);
+
+ if (IS_END_OF_STRING(glyph)) {
+ break;
+ }
+
+ CachedGlyphInfo* cachedGlyph = getCachedGlyph(paint, glyph);
+ penX += SkFixedToFloat(AUTO_KERN(prevRsbDelta, cachedGlyph->mLsbDelta));
+ prevRsbDelta = cachedGlyph->mRsbDelta;
+
+ if (cachedGlyph->mIsValid) {
+ drawCachedGlyph(cachedGlyph, penX, hOffset, vOffset, measure, &position, &tangent);
+ }
+
+ penX += SkFixedToFloat(cachedGlyph->mAdvanceX);
+
+ glyphsCount++;
+ }
+}
+
+void Font::measure(SkPaint* paint, const char* text, uint32_t start, uint32_t len,
+ int numGlyphs, Rect *bounds, const float* positions) {
+ if (bounds == NULL) {
+ ALOGE("No return rectangle provided to measure text");
+ return;
+ }
+ bounds->set(1e6, -1e6, -1e6, 1e6);
+ render(paint, text, start, len, numGlyphs, 0, 0, MEASURE, NULL, 0, 0, bounds, positions);
+}
+
+void Font::precache(SkPaint* paint, const char* text, int numGlyphs) {
+
+ if (numGlyphs == 0 || text == NULL) {
+ return;
+ }
+ int glyphsCount = 0;
+
+ while (glyphsCount < numGlyphs) {
+ glyph_t glyph = GET_GLYPH(text);
+
+ // Reached the end of the string
+ if (IS_END_OF_STRING(glyph)) {
+ break;
+ }
+
+ CachedGlyphInfo* cachedGlyph = getCachedGlyph(paint, glyph, true);
+
+ glyphsCount++;
+ }
+}
+
+void Font::render(SkPaint* paint, const char* text, uint32_t start, uint32_t len,
+ int numGlyphs, int x, int y, RenderMode mode, uint8_t *bitmap,
+ uint32_t bitmapW, uint32_t bitmapH, Rect* bounds, const float* positions) {
+ if (numGlyphs == 0 || text == NULL || len == 0) {
+ return;
+ }
+
+ static RenderGlyph gRenderGlyph[] = {
+ &android::uirenderer::Font::drawCachedGlyph,
+ &android::uirenderer::Font::drawCachedGlyphBitmap,
+ &android::uirenderer::Font::measureCachedGlyph
+ };
+ RenderGlyph render = gRenderGlyph[mode];
+
+ text += start;
+ int glyphsCount = 0;
+
+ if (CC_LIKELY(positions == NULL)) {
+ SkFixed prevRsbDelta = 0;
+
+ float penX = x + 0.5f;
+ int penY = y;
+
+ while (glyphsCount < numGlyphs) {
+ glyph_t glyph = GET_GLYPH(text);
+
+ // Reached the end of the string
+ if (IS_END_OF_STRING(glyph)) {
+ break;
+ }
+
+ CachedGlyphInfo* cachedGlyph = getCachedGlyph(paint, glyph);
+ penX += SkFixedToFloat(AUTO_KERN(prevRsbDelta, cachedGlyph->mLsbDelta));
+ prevRsbDelta = cachedGlyph->mRsbDelta;
+
+ // If it's still not valid, we couldn't cache it, so we shouldn't draw garbage
+ if (cachedGlyph->mIsValid) {
+ (*this.*render)(cachedGlyph, (int) floorf(penX), penY,
+ bitmap, bitmapW, bitmapH, bounds, positions);
+ }
+
+ penX += SkFixedToFloat(cachedGlyph->mAdvanceX);
+
+ glyphsCount++;
+ }
+ } else {
+ const SkPaint::Align align = paint->getTextAlign();
+
+ // This is for renderPosText()
+ while (glyphsCount < numGlyphs) {
+ glyph_t glyph = GET_GLYPH(text);
+
+ // Reached the end of the string
+ if (IS_END_OF_STRING(glyph)) {
+ break;
+ }
+
+ CachedGlyphInfo* cachedGlyph = getCachedGlyph(paint, glyph);
+
+ // If it's still not valid, we couldn't cache it, so we shouldn't draw garbage
+ if (cachedGlyph->mIsValid) {
+ int penX = x + positions[(glyphsCount << 1)];
+ int penY = y + positions[(glyphsCount << 1) + 1];
+
+ switch (align) {
+ case SkPaint::kRight_Align:
+ penX -= SkFixedToFloat(cachedGlyph->mAdvanceX);
+ penY -= SkFixedToFloat(cachedGlyph->mAdvanceY);
+ break;
+ case SkPaint::kCenter_Align:
+ penX -= SkFixedToFloat(cachedGlyph->mAdvanceX >> 1);
+ penY -= SkFixedToFloat(cachedGlyph->mAdvanceY >> 1);
+ default:
+ break;
+ }
+
+ (*this.*render)(cachedGlyph, penX, penY,
+ bitmap, bitmapW, bitmapH, bounds, positions);
+ }
+
+ glyphsCount++;
+ }
+ }
+}
+
+void Font::updateGlyphCache(SkPaint* paint, const SkGlyph& skiaGlyph, CachedGlyphInfo* glyph,
+ bool precaching) {
+ glyph->mAdvanceX = skiaGlyph.fAdvanceX;
+ glyph->mAdvanceY = skiaGlyph.fAdvanceY;
+ glyph->mBitmapLeft = skiaGlyph.fLeft;
+ glyph->mBitmapTop = skiaGlyph.fTop;
+ glyph->mLsbDelta = skiaGlyph.fLsbDelta;
+ glyph->mRsbDelta = skiaGlyph.fRsbDelta;
+
+ uint32_t startX = 0;
+ uint32_t startY = 0;
+
+ // Get the bitmap for the glyph
+ paint->findImage(skiaGlyph);
+ mState->cacheBitmap(skiaGlyph, glyph, &startX, &startY, precaching);
+
+ if (!glyph->mIsValid) {
+ return;
+ }
+
+ uint32_t endX = startX + skiaGlyph.fWidth;
+ uint32_t endY = startY + skiaGlyph.fHeight;
+
+ glyph->mStartX = startX;
+ glyph->mStartY = startY;
+ glyph->mBitmapWidth = skiaGlyph.fWidth;
+ glyph->mBitmapHeight = skiaGlyph.fHeight;
+
+ uint32_t cacheWidth = glyph->mCacheTexture->getWidth();
+ uint32_t cacheHeight = glyph->mCacheTexture->getHeight();
+
+ glyph->mBitmapMinU = startX / (float) cacheWidth;
+ glyph->mBitmapMinV = startY / (float) cacheHeight;
+ glyph->mBitmapMaxU = endX / (float) cacheWidth;
+ glyph->mBitmapMaxV = endY / (float) cacheHeight;
+
+ mState->setTextureDirty();
+}
+
+CachedGlyphInfo* Font::cacheGlyph(SkPaint* paint, glyph_t glyph, bool precaching) {
+ CachedGlyphInfo* newGlyph = new CachedGlyphInfo();
+ mCachedGlyphs.add(glyph, newGlyph);
+
+ const SkGlyph& skiaGlyph = GET_METRICS(paint, glyph);
+ newGlyph->mGlyphIndex = skiaGlyph.fID;
+ newGlyph->mIsValid = false;
+
+ updateGlyphCache(paint, skiaGlyph, newGlyph, precaching);
+
+ return newGlyph;
+}
+
+Font* Font::create(FontRenderer* state, uint32_t fontId, float fontSize,
+ int flags, uint32_t italicStyle, uint32_t scaleX,
+ SkPaint::Style style, uint32_t strokeWidth) {
+ Vector<Font*> &activeFonts = state->mActiveFonts;
+
+ for (uint32_t i = 0; i < activeFonts.size(); i++) {
+ Font* font = activeFonts[i];
+ if (font->mFontId == fontId && font->mFontSize == fontSize &&
+ font->mFlags == flags && font->mItalicStyle == italicStyle &&
+ font->mScaleX == scaleX && font->mStyle == style &&
+ (style == SkPaint::kFill_Style || font->mStrokeWidth == strokeWidth)) {
+ return font;
+ }
+ }
+
+ Font* newFont = new Font(state, fontId, fontSize, flags, italicStyle,
+ scaleX, style, strokeWidth);
+ activeFonts.push(newFont);
+ return newFont;
+}
+
+}; // namespace uirenderer
+}; // namespace android
diff --git a/libs/hwui/font/Font.h b/libs/hwui/font/Font.h
new file mode 100644
index 0000000..7cab31e
--- /dev/null
+++ b/libs/hwui/font/Font.h
@@ -0,0 +1,130 @@
+/*
+ * 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.
+ */
+
+#ifndef ANDROID_HWUI_FONT_H
+#define ANDROID_HWUI_FONT_H
+
+#include <utils/KeyedVector.h>
+
+#include <SkScalerContext.h>
+#include <SkPaint.h>
+#include <SkPathMeasure.h>
+
+#include "CachedGlyphInfo.h"
+#include "../Rect.h"
+
+namespace android {
+namespace uirenderer {
+
+///////////////////////////////////////////////////////////////////////////////
+// Font
+///////////////////////////////////////////////////////////////////////////////
+
+class FontRenderer;
+
+/**
+ * Represents a font, defined by a Skia font id and a font size. A font is used
+ * to generate glyphs and cache them in the FontState.
+ */
+class Font {
+public:
+ enum Style {
+ kFakeBold = 1
+ };
+
+ ~Font();
+
+ /**
+ * Renders the specified string of text.
+ * If bitmap is specified, it will be used as the render target
+ */
+ void render(SkPaint* paint, const char *text, uint32_t start, uint32_t len,
+ int numGlyphs, int x, int y, uint8_t *bitmap = NULL,
+ uint32_t bitmapW = 0, uint32_t bitmapH = 0);
+
+ void render(SkPaint* paint, const char *text, uint32_t start, uint32_t len,
+ int numGlyphs, int x, int y, const float* positions);
+
+ void render(SkPaint* paint, const char *text, uint32_t start, uint32_t len,
+ int numGlyphs, SkPath* path, float hOffset, float vOffset);
+
+ /**
+ * Creates a new font associated with the specified font state.
+ */
+ static Font* create(FontRenderer* state, uint32_t fontId, float fontSize,
+ int flags, uint32_t italicStyle, uint32_t scaleX, SkPaint::Style style,
+ uint32_t strokeWidth);
+
+private:
+ friend class FontRenderer;
+ typedef void (Font::*RenderGlyph)(CachedGlyphInfo*, int, int, uint8_t*,
+ uint32_t, uint32_t, Rect*, const float*);
+
+ enum RenderMode {
+ FRAMEBUFFER,
+ BITMAP,
+ MEASURE,
+ };
+
+ void precache(SkPaint* paint, const char* text, int numGlyphs);
+
+ void render(SkPaint* paint, const char *text, uint32_t start, uint32_t len,
+ int numGlyphs, int x, int y, RenderMode mode, uint8_t *bitmap,
+ uint32_t bitmapW, uint32_t bitmapH, Rect *bounds, const float* positions);
+
+ void measure(SkPaint* paint, const char* text, uint32_t start, uint32_t len,
+ int numGlyphs, Rect *bounds, const float* positions);
+
+ Font(FontRenderer* state, uint32_t fontId, float fontSize, int flags, uint32_t italicStyle,
+ uint32_t scaleX, SkPaint::Style style, uint32_t strokeWidth);
+
+ // Cache of glyphs
+ DefaultKeyedVector<glyph_t, CachedGlyphInfo*> mCachedGlyphs;
+
+ void invalidateTextureCache(CacheTexture* cacheTexture = NULL);
+
+ CachedGlyphInfo* cacheGlyph(SkPaint* paint, glyph_t glyph, bool precaching);
+ void updateGlyphCache(SkPaint* paint, const SkGlyph& skiaGlyph, CachedGlyphInfo* glyph,
+ bool precaching);
+
+ void measureCachedGlyph(CachedGlyphInfo* glyph, int x, int y,
+ uint8_t *bitmap, uint32_t bitmapW, uint32_t bitmapH,
+ Rect* bounds, const float* pos);
+ void drawCachedGlyph(CachedGlyphInfo* glyph, int x, int y,
+ uint8_t *bitmap, uint32_t bitmapW, uint32_t bitmapH,
+ Rect* bounds, const float* pos);
+ void drawCachedGlyphBitmap(CachedGlyphInfo* glyph, int x, int y,
+ uint8_t *bitmap, uint32_t bitmapW, uint32_t bitmapH,
+ Rect* bounds, const float* pos);
+ void drawCachedGlyph(CachedGlyphInfo* glyph, float x, float hOffset, float vOffset,
+ SkPathMeasure& measure, SkPoint* position, SkVector* tangent);
+
+ CachedGlyphInfo* getCachedGlyph(SkPaint* paint, glyph_t textUnit, bool precaching = false);
+
+ FontRenderer* mState;
+ uint32_t mFontId;
+ float mFontSize;
+ int mFlags;
+ uint32_t mItalicStyle;
+ uint32_t mScaleX;
+ SkPaint::Style mStyle;
+ uint32_t mStrokeWidth;
+};
+
+}; // namespace uirenderer
+}; // namespace android
+
+#endif // ANDROID_HWUI_FONT_H
diff --git a/libs/hwui/font/FontUtil.h b/libs/hwui/font/FontUtil.h
new file mode 100644
index 0000000..12247ba
--- /dev/null
+++ b/libs/hwui/font/FontUtil.h
@@ -0,0 +1,60 @@
+/*
+ * 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.
+ */
+
+#ifndef ANDROID_HWUI_FONT_UTIL_H
+#define ANDROID_HWUI_FONT_UTIL_H
+
+#include <SkUtils.h>
+
+#include "Properties.h"
+
+///////////////////////////////////////////////////////////////////////////////
+// Defines
+///////////////////////////////////////////////////////////////////////////////
+
+#define DEFAULT_TEXT_SMALL_CACHE_WIDTH 1024
+#define DEFAULT_TEXT_SMALL_CACHE_HEIGHT 256
+#define DEFAULT_TEXT_LARGE_CACHE_WIDTH 2048
+#define DEFAULT_TEXT_LARGE_CACHE_HEIGHT 512
+
+#define TEXTURE_BORDER_SIZE 1
+
+#define CACHE_BLOCK_ROUNDING_SIZE 4
+
+#if RENDER_TEXT_AS_GLYPHS
+ typedef uint16_t glyph_t;
+ #define TO_GLYPH(g) g
+ #define GET_METRICS(paint, glyph) paint->getGlyphMetrics(glyph)
+ #define GET_GLYPH(text) nextGlyph((const uint16_t**) &text)
+ #define IS_END_OF_STRING(glyph) false
+
+ static glyph_t nextGlyph(const uint16_t** srcPtr) {
+ const uint16_t* src = *srcPtr;
+ glyph_t g = *src++;
+ *srcPtr = src;
+ return g;
+ }
+#else
+ typedef SkUnichar glyph_t;
+ #define TO_GLYPH(g) ((SkUnichar) g)
+ #define GET_METRICS(paint, glyph) paint->getUnicharMetrics(glyph)
+ #define GET_GLYPH(text) SkUTF16_NextUnichar((const uint16_t**) &text)
+ #define IS_END_OF_STRING(glyph) glyph < 0
+#endif
+
+#define AUTO_KERN(prev, next) (((next) - (prev) + 32) >> 6 << 16)
+
+#endif // ANDROID_HWUI_FONT_UTIL_H
diff --git a/media/java/android/media/MediaScanner.java b/media/java/android/media/MediaScanner.java
index 0d7b45e..88cf4ac 100644
--- a/media/java/android/media/MediaScanner.java
+++ b/media/java/android/media/MediaScanner.java
@@ -1415,7 +1415,8 @@
long lastModifiedSeconds = file.lastModified() / 1000;
if (!MediaFile.isAudioFileType(fileType) && !MediaFile.isVideoFileType(fileType) &&
- !MediaFile.isImageFileType(fileType) && !MediaFile.isPlayListFileType(fileType)) {
+ !MediaFile.isImageFileType(fileType) && !MediaFile.isPlayListFileType(fileType) &&
+ !MediaFile.isDrmFileType(fileType)) {
// no need to use the media scanner, but we need to update last modified and file size
ContentValues values = new ContentValues();
diff --git a/native/android/configuration.cpp b/native/android/configuration.cpp
index 7eb51dd..74cf80e 100644
--- a/native/android/configuration.cpp
+++ b/native/android/configuration.cpp
@@ -123,6 +123,11 @@
return config->smallestScreenWidthDp;
}
+int32_t AConfiguration_getLayoutDirection(AConfiguration* config) {
+ return (config->screenLayout&ResTable_config::MASK_LAYOUTDIR)
+ >> ResTable_config::SHIFT_LAYOUTDIR;
+}
+
// ----------------------------------------------------------------------
void AConfiguration_setMcc(AConfiguration* config, int32_t mcc) {
@@ -210,6 +215,11 @@
config->smallestScreenWidthDp = value;
}
+void AConfiguration_setLayoutDirection(AConfiguration* config, int32_t value) {
+ config->screenLayout = (config->screenLayout&~ResTable_config::MASK_LAYOUTDIR)
+ | ((value<<ResTable_config::SHIFT_LAYOUTDIR)&ResTable_config::MASK_LAYOUTDIR);
+}
+
// ----------------------------------------------------------------------
int32_t AConfiguration_diff(AConfiguration* config1, AConfiguration* config2) {
diff --git a/packages/SettingsProvider/res/values/defaults.xml b/packages/SettingsProvider/res/values/defaults.xml
index 875d2c9..b3d9ea3 100644
--- a/packages/SettingsProvider/res/values/defaults.xml
+++ b/packages/SettingsProvider/res/values/defaults.xml
@@ -46,8 +46,8 @@
<bool name="def_netstats_enabled">true</bool>
<bool name="def_usb_mass_storage_enabled">true</bool>
<bool name="def_wifi_on">false</bool>
- <!-- 0 == default, 1 == never while plugged, 2 == never -->
- <integer name="def_wifi_sleep_policy">0</integer>
+ <!-- 0 == never, 1 == only when plugged in, 2 == always -->
+ <integer name="def_wifi_sleep_policy">2</integer>
<bool name="def_networks_available_notification_on">true</bool>
<bool name="def_backup_enabled">false</bool>
diff --git a/packages/SystemUI/AndroidManifest.xml b/packages/SystemUI/AndroidManifest.xml
index b185471..7176f32 100644
--- a/packages/SystemUI/AndroidManifest.xml
+++ b/packages/SystemUI/AndroidManifest.xml
@@ -167,7 +167,7 @@
android:name=".BeanBag"
android:exported="true"
android:label="BeanBag"
- android:icon="@drawable/redbeandroid"
+ android:icon="@drawable/redbean2"
android:theme="@android:style/Theme.Wallpaper.NoTitleBar.Fullscreen"
android:hardwareAccelerated="true"
android:launchMode="singleInstance"
@@ -184,7 +184,9 @@
<service
android:name=".BeanBagDream"
android:exported="true"
- android:label="Beans in space">
+ android:label="@string/jelly_bean_dream_name"
+ android:enabled="false"
+ >
<intent-filter>
<action android:name="android.intent.action.MAIN" />
<category android:name="android.intent.category.DEFAULT" />
diff --git a/packages/SystemUI/res/drawable/recents_thumbnail_fg.xml b/packages/SystemUI/res/drawable/recents_thumbnail_fg.xml
index d683af9..c209055 100644
--- a/packages/SystemUI/res/drawable/recents_thumbnail_fg.xml
+++ b/packages/SystemUI/res/drawable/recents_thumbnail_fg.xml
@@ -16,5 +16,5 @@
<selector xmlns:android="http://schemas.android.com/apk/res/android">
<item android:drawable="@drawable/recents_thumbnail_bg_press" android:state_selected="true" />
<item android:drawable="@drawable/recents_thumbnail_bg_press" android:state_pressed="true" />
- <item android:drawable="@*android:color/transparent"/>
+ <item android:drawable="@drawable/recents_thumbnail_no_press"/>
</selector>
diff --git a/packages/SystemUI/res/drawable/recents_thumbnail_no_press.xml b/packages/SystemUI/res/drawable/recents_thumbnail_no_press.xml
new file mode 100644
index 0000000..be07b2c
--- /dev/null
+++ b/packages/SystemUI/res/drawable/recents_thumbnail_no_press.xml
@@ -0,0 +1,17 @@
+<?xml version="1.0" encoding="utf-8"?>
+<!-- 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.
+-->
+<color xmlns:android="http://schemas.android.com/apk/res/android"
+ android:color="#00000000" />
diff --git a/packages/SystemUI/res/layout-sw600dp/carrier_label.xml b/packages/SystemUI/res/layout-sw600dp/carrier_label.xml
new file mode 100644
index 0000000..b33caf8
--- /dev/null
+++ b/packages/SystemUI/res/layout-sw600dp/carrier_label.xml
@@ -0,0 +1,23 @@
+<?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.
+*/
+-->
+
+<Space
+ xmlns:android="http://schemas.android.com/apk/res/android"
+ android:visibility="gone"
+ />
\ No newline at end of file
diff --git a/packages/SystemUI/res/layout/carrier_label.xml b/packages/SystemUI/res/layout/carrier_label.xml
new file mode 100644
index 0000000..41a1fff
--- /dev/null
+++ b/packages/SystemUI/res/layout/carrier_label.xml
@@ -0,0 +1,30 @@
+<?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.
+*/
+-->
+
+<TextView
+ xmlns:android="http://schemas.android.com/apk/res/android"
+ android:id="@+id/carrier_label"
+ android:textAppearance="@style/TextAppearance.StatusBar.Expanded.Network"
+ android:layout_height="@dimen/carrier_label_height"
+ android:layout_width="match_parent"
+ android:layout_gravity="bottom"
+ android:layout_marginBottom="@dimen/close_handle_height"
+ android:gravity="center"
+ android:visibility="invisible"
+ />
\ No newline at end of file
diff --git a/packages/SystemUI/res/layout/quick_settings.xml b/packages/SystemUI/res/layout/quick_settings.xml
index c4b881e..8c6258a 100644
--- a/packages/SystemUI/res/layout/quick_settings.xml
+++ b/packages/SystemUI/res/layout/quick_settings.xml
@@ -19,6 +19,7 @@
android:layout_width="match_parent"
android:layout_height="wrap_content"
android:id="@+id/settings_panel"
+ android:background="#80000080"
>
<ImageView
android:layout_width="match_parent"
@@ -26,7 +27,6 @@
android:scaleType="centerInside"
android:src="@drawable/qs_coming_soon"
android:padding="4dp"
- android:background="#80000080"
/>
<LinearLayout android:id="@+id/handle"
android:layout_width="match_parent"
diff --git a/packages/SystemUI/res/layout/status_bar_expanded.xml b/packages/SystemUI/res/layout/status_bar_expanded.xml
index 8eff1f4..f2e83d8 100644
--- a/packages/SystemUI/res/layout/status_bar_expanded.xml
+++ b/packages/SystemUI/res/layout/status_bar_expanded.xml
@@ -29,15 +29,10 @@
android:layout_marginLeft="@dimen/notification_panel_margin_left"
>
- <TextView
- android:id="@+id/carrier_label"
- android:textAppearance="@style/TextAppearance.StatusBar.Expanded.Network"
+ <include
+ layout="@layout/carrier_label"
android:layout_height="@dimen/carrier_label_height"
android:layout_width="match_parent"
- android:layout_gravity="bottom"
- android:layout_marginBottom="@dimen/close_handle_height"
- android:gravity="center"
- android:visibility="invisible"
/>
<LinearLayout
diff --git a/packages/SystemUI/res/layout/super_status_bar.xml b/packages/SystemUI/res/layout/super_status_bar.xml
index ad6b8f4..5e0d1e8 100644
--- a/packages/SystemUI/res/layout/super_status_bar.xml
+++ b/packages/SystemUI/res/layout/super_status_bar.xml
@@ -40,11 +40,11 @@
>
<include layout="@layout/status_bar_expanded"
android:layout_width="match_parent"
- android:layout_height="wrap_content"
+ android:layout_height="match_parent"
/>
<include layout="@layout/quick_settings"
android:layout_width="match_parent"
- android:layout_height="wrap_content"
+ android:layout_height="match_parent"
/>
</com.android.systemui.statusbar.phone.PanelHolder>
diff --git a/packages/SystemUI/res/values-fa/strings.xml b/packages/SystemUI/res/values-fa/strings.xml
index 66d65d9..a55091d 100644
--- a/packages/SystemUI/res/values-fa/strings.xml
+++ b/packages/SystemUI/res/values-fa/strings.xml
@@ -73,8 +73,8 @@
<string name="screenshot_failed_title" msgid="705781116746922771">"تصویر صفحه گرفته نشد."</string>
<string name="screenshot_failed_text" msgid="8134011269572415402">"تصویر صفحه ذخیره نشد. ممکن است دستگاه ذخیره در حال استفاده باشد."</string>
<string name="usb_preference_title" msgid="6551050377388882787">"گزینههای انتقال فایل USB"</string>
- <string name="use_mtp_button_title" msgid="4333504413563023626">"نصب به عنوان دستگاه پخش رسانه (MTP)"</string>
- <string name="use_ptp_button_title" msgid="7517127540301625751">"تصب به عنوان دوربین (PTP)"</string>
+ <string name="use_mtp_button_title" msgid="4333504413563023626">"نصب بهعنوان دستگاه پخش رسانه (MTP)"</string>
+ <string name="use_ptp_button_title" msgid="7517127540301625751">"تصب بهعنوان دوربین (PTP)"</string>
<string name="installer_cd_button_title" msgid="2312667578562201583">"برنامه Android File Transfer را برای Mac نصب کنید"</string>
<string name="accessibility_back" msgid="567011538994429120">"برگشت"</string>
<string name="accessibility_home" msgid="8217216074895377641">"صفحهٔ اصلی"</string>
diff --git a/packages/SystemUI/res/values-iw/strings.xml b/packages/SystemUI/res/values-iw/strings.xml
index f820464..59730af 100644
--- a/packages/SystemUI/res/values-iw/strings.xml
+++ b/packages/SystemUI/res/values-iw/strings.xml
@@ -58,8 +58,8 @@
<string name="label_view" msgid="6304565553218192990">"הצג"</string>
<string name="always_use_device" msgid="1450287437017315906">"השתמש כברירת מחדל עבור מכשיר USB זה"</string>
<string name="always_use_accessory" msgid="1210954576979621596">"השתמש כברירת מחדל עבור אביזר USB זה"</string>
- <string name="usb_debugging_title" msgid="1114766024068112429">"האם לאפשר ניפוי באגים ב-USB?"</string>
- <string name="usb_debugging_message" msgid="719863946976291180">"האם להרשות ניפוי באגים ב-USB ממחשב זה?"\n"טביעת האצבע של מפתח ה-RSA שלך היא"\n"<xliff:g id="FINGERPRINT">%1$s</xliff:g>"</string>
+ <string name="usb_debugging_title" msgid="1114766024068112429">"האם לאפשר ניקוי באגים ב-USB?"</string>
+ <string name="usb_debugging_message" msgid="719863946976291180">"האם להרשות ניקוי באגים ב-USB ממחשב זה?"\n"טביעת האצבע של מפתח ה-RSA שלך היא"\n"<xliff:g id="FINGERPRINT">%1$s</xliff:g>"</string>
<string name="usb_debugging_always" msgid="4253099426793114693">"הרשה תמיד במחשב זה"</string>
<string name="compat_mode_on" msgid="6623839244840638213">"הגדל תצוגה כדי למלא את המסך"</string>
<string name="compat_mode_off" msgid="4434467572461327898">"מתח כדי למלא את המסך"</string>
diff --git a/packages/SystemUI/res/values-sw720dp/config.xml b/packages/SystemUI/res/values-sw720dp/config.xml
index de63d9f..bf01a8d 100644
--- a/packages/SystemUI/res/values-sw720dp/config.xml
+++ b/packages/SystemUI/res/values-sw720dp/config.xml
@@ -29,5 +29,8 @@
<!-- Whether recents thumbnails should stretch in both x and y to fill their
ImageView -->
<bool name="config_recents_thumbnail_image_fits_to_xy">true</bool>
+
+ <!-- Min alpha % that recent items will fade to while being dismissed -->
+ <integer name="config_recent_item_min_alpha">0</integer>
</resources>
diff --git a/packages/SystemUI/res/values/config.xml b/packages/SystemUI/res/values/config.xml
index 13622e6..34e58a3 100644
--- a/packages/SystemUI/res/values/config.xml
+++ b/packages/SystemUI/res/values/config.xml
@@ -78,5 +78,8 @@
<integer name="navigation_bar_deadzone_decay">333</integer>
<bool name="config_dead_zone_flash">false</bool>
+
+ <!-- Min alpha % that recent items will fade to while being dismissed -->
+ <integer name="config_recent_item_min_alpha">3</integer>
</resources>
diff --git a/packages/SystemUI/res/values/strings.xml b/packages/SystemUI/res/values/strings.xml
index 5747f22..609d63b 100644
--- a/packages/SystemUI/res/values/strings.xml
+++ b/packages/SystemUI/res/values/strings.xml
@@ -397,4 +397,7 @@
<!-- Description of the button in the phone-style notification panel that controls auto-rotation, when auto-rotation is off. [CHAR LIMIT=NONE] -->
<string name="accessibility_rotation_lock_on_portrait">Screen is locked in portrait orientation.</string>
+
+ <!-- Name of the Jelly Bean platlogo screensaver -->
+ <string name="jelly_bean_dream_name">BeanFlinger</string>
</resources>
diff --git a/packages/SystemUI/src/com/android/systemui/BeanBag.java b/packages/SystemUI/src/com/android/systemui/BeanBag.java
index 616d72f..f5a90ca 100644
--- a/packages/SystemUI/src/com/android/systemui/BeanBag.java
+++ b/packages/SystemUI/src/com/android/systemui/BeanBag.java
@@ -24,6 +24,7 @@
import android.content.ComponentName;
import android.content.Context;
import android.content.Intent;
+import android.content.pm.PackageManager;
import android.graphics.drawable.AnimationDrawable;
import android.graphics.drawable.BitmapDrawable;
import android.graphics.Bitmap;
@@ -40,6 +41,7 @@
import android.graphics.RectF;
import android.os.Handler;
import android.os.SystemClock;
+import android.provider.Settings;
import android.util.AttributeSet;
import android.util.DisplayMetrics;
import android.util.Pair;
@@ -402,6 +404,11 @@
public void onStart() {
super.onStart();
+ // ACHIEVEMENT UNLOCKED
+ PackageManager pm = getPackageManager();
+ pm.setComponentEnabledSetting(new ComponentName(this, BeanBagDream.class),
+ PackageManager.COMPONENT_ENABLED_STATE_ENABLED, 0);
+
getWindow().addFlags(
WindowManager.LayoutParams.FLAG_ALLOW_LOCK_WHILE_SCREEN_ON
| WindowManager.LayoutParams.FLAG_SHOW_WHEN_LOCKED
diff --git a/packages/SystemUI/src/com/android/systemui/ImageWallpaper.java b/packages/SystemUI/src/com/android/systemui/ImageWallpaper.java
index 2512c02..0671e44 100644
--- a/packages/SystemUI/src/com/android/systemui/ImageWallpaper.java
+++ b/packages/SystemUI/src/com/android/systemui/ImageWallpaper.java
@@ -30,10 +30,8 @@
import android.renderscript.Matrix4f;
import android.service.wallpaper.WallpaperService;
import android.util.Log;
-import android.view.Display;
import android.view.MotionEvent;
import android.view.SurfaceHolder;
-import android.view.WindowManager;
import javax.microedition.khronos.egl.EGL10;
import javax.microedition.khronos.egl.EGLConfig;
diff --git a/packages/SystemUI/src/com/android/systemui/recent/RecentTasksLoader.java b/packages/SystemUI/src/com/android/systemui/recent/RecentTasksLoader.java
index 2a225d9..6d84350 100644
--- a/packages/SystemUI/src/com/android/systemui/recent/RecentTasksLoader.java
+++ b/packages/SystemUI/src/com/android/systemui/recent/RecentTasksLoader.java
@@ -30,7 +30,6 @@
import android.os.AsyncTask;
import android.os.Handler;
import android.os.Process;
-import android.os.UserHandle;
import android.util.Log;
import android.view.MotionEvent;
import android.view.View;
@@ -254,10 +253,9 @@
public boolean onTouch(View v, MotionEvent ev) {
int action = ev.getAction() & MotionEvent.ACTION_MASK;
if (action == MotionEvent.ACTION_DOWN) {
- mHandler.post(mPreloadTasksRunnable);
+ preloadRecentTasksList();
} else if (action == MotionEvent.ACTION_CANCEL) {
- cancelLoadingThumbnailsAndIcons();
- mHandler.removeCallbacks(mPreloadTasksRunnable);
+ cancelPreloadingRecentTasksList();
} else if (action == MotionEvent.ACTION_UP) {
// Remove the preloader if we haven't called it yet
mHandler.removeCallbacks(mPreloadTasksRunnable);
@@ -269,6 +267,15 @@
return false;
}
+ public void preloadRecentTasksList() {
+ mHandler.post(mPreloadTasksRunnable);
+ }
+
+ public void cancelPreloadingRecentTasksList() {
+ cancelLoadingThumbnailsAndIcons();
+ mHandler.removeCallbacks(mPreloadTasksRunnable);
+ }
+
public void cancelLoadingThumbnailsAndIcons(RecentsPanelView caller) {
// Only oblige this request if it comes from the current RecentsPanel
// (eg when you rotate, the old RecentsPanel request should be ignored)
diff --git a/packages/SystemUI/src/com/android/systemui/recent/RecentsActivity.java b/packages/SystemUI/src/com/android/systemui/recent/RecentsActivity.java
index a4c8e64..baacde01 100644
--- a/packages/SystemUI/src/com/android/systemui/recent/RecentsActivity.java
+++ b/packages/SystemUI/src/com/android/systemui/recent/RecentsActivity.java
@@ -136,6 +136,8 @@
mRecentsPanel.setOnTouchListener(new TouchOutsideListener(mRecentsPanel));
mRecentsPanel.setRecentTasksLoader(recentTasksLoader);
recentTasksLoader.setRecentsPanel(mRecentsPanel, mRecentsPanel);
+ mRecentsPanel.setMinSwipeAlpha(
+ getResources().getInteger(R.integer.config_recent_item_min_alpha) / 100f);
handleIntent(getIntent());
mIntentFilter = new IntentFilter();
diff --git a/packages/SystemUI/src/com/android/systemui/recent/RecentsPanelView.java b/packages/SystemUI/src/com/android/systemui/recent/RecentsPanelView.java
index c3ecdb5..730d350 100644
--- a/packages/SystemUI/src/com/android/systemui/recent/RecentsPanelView.java
+++ b/packages/SystemUI/src/com/android/systemui/recent/RecentsPanelView.java
@@ -38,15 +38,11 @@
import android.provider.Settings;
import android.util.AttributeSet;
import android.util.Log;
-import android.view.Display;
-import android.view.KeyEvent;
import android.view.LayoutInflater;
import android.view.MenuItem;
import android.view.MotionEvent;
import android.view.View;
import android.view.ViewGroup;
-import android.view.WindowManager;
-import android.view.WindowManagerGlobal;
import android.view.accessibility.AccessibilityEvent;
import android.view.animation.AnimationUtils;
import android.widget.AdapterView;
@@ -60,7 +56,6 @@
import com.android.systemui.R;
import com.android.systemui.statusbar.BaseStatusBar;
-import com.android.systemui.statusbar.CommandQueue;
import com.android.systemui.statusbar.phone.PhoneStatusBar;
import com.android.systemui.statusbar.tablet.StatusBarPanel;
import com.android.systemui.statusbar.tablet.TabletStatusBar;
@@ -72,7 +67,6 @@
static final String TAG = "RecentsPanelView";
static final boolean DEBUG = TabletStatusBar.DEBUG || PhoneStatusBar.DEBUG || false;
private Context mContext;
- private BaseStatusBar mBar;
private PopupMenu mPopup;
private View mRecentsScrim;
private View mRecentsNoApps;
@@ -164,13 +158,6 @@
public View getView(int position, View convertView, ViewGroup parent) {
if (convertView == null) {
convertView = createView(parent);
- if (convertView.getParent() != null) {
- throw new RuntimeException("Recycled child has parent");
- }
- } else {
- if (convertView.getParent() != null) {
- throw new RuntimeException("Recycled child has parent");
- }
}
ViewHolder holder = (ViewHolder) convertView.getTag();
@@ -366,11 +353,6 @@
return mShowing;
}
- public void setBar(BaseStatusBar bar) {
- mBar = bar;
-
- }
-
public void setStatusBarView(View statusBarView) {
if (mStatusBarTouchProxy != null) {
mStatusBarTouchProxy.setStatusBar(statusBarView);
@@ -600,6 +582,9 @@
usingDrawingCache = true;
}
+ if (bm == null) {
+ throw new RuntimeException("Recents thumbnail is null");
+ }
ActivityOptions opts = ActivityOptions.makeThumbnailScaleUpAnimation(
holder.thumbnailViewImage, bm, 0, 0, null);
diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/BaseStatusBar.java b/packages/SystemUI/src/com/android/systemui/statusbar/BaseStatusBar.java
index 106ce7e..97034bb 100644
--- a/packages/SystemUI/src/com/android/systemui/statusbar/BaseStatusBar.java
+++ b/packages/SystemUI/src/com/android/systemui/statusbar/BaseStatusBar.java
@@ -488,11 +488,18 @@
.getDimensionPixelSize(R.dimen.status_bar_recents_thumbnail_width);
float thumbHeight = res
.getDimensionPixelSize(R.dimen.status_bar_recents_thumbnail_height);
+ if (first == null) {
+ throw new RuntimeException("Recents thumbnail is null");
+ }
if (first.getWidth() != thumbWidth || first.getHeight() != thumbHeight) {
first = Bitmap.createScaledBitmap(first, (int) thumbWidth, (int) thumbHeight,
true);
+ if (first == null) {
+ throw new RuntimeException("Recents thumbnail is null");
+ }
}
+
DisplayMetrics dm = new DisplayMetrics();
mDisplay.getMetrics(dm);
// calculate it here, but consider moving it elsewhere
@@ -521,8 +528,7 @@
+ thumbBgPadding + thumbLeftMargin);
y = (int) (dm.heightPixels
- res.getDimensionPixelSize(R.dimen.status_bar_recents_thumbnail_height) - thumbBgPadding);
- } else { // if (config.orientation ==
- // Configuration.ORIENTATION_LANDSCAPE) {
+ } else { // if (config.orientation == Configuration.ORIENTATION_LANDSCAPE) {
float thumbTopMargin = res
.getDimensionPixelSize(R.dimen.status_bar_recents_thumbnail_top_margin);
float thumbBgPadding = res
@@ -588,25 +594,11 @@
break;
case MSG_PRELOAD_RECENT_APPS:
if (DEBUG) Slog.d(TAG, "preloading recents");
- {
- // TODO:
- // need to implement this
- //final RecentsPanelView recentsPanel = getRecentsPanel();
- //if (recentsPanel != null) {
- //recentsPanel.preloadRecentTasksList();
- //}
- }
+ getRecentTasksLoader().preloadRecentTasksList();
break;
case MSG_CANCEL_PRELOAD_RECENT_APPS:
if (DEBUG) Slog.d(TAG, "cancel preloading recents");
- {
- // TODO:
- // need to implement this
- //final RecentsPanelView recentsPanel = getRecentsPanel();
- //if (recentsPanel != null) {
- //recentsPanel.clearRecentTasksList();
- //}
- }
+ getRecentTasksLoader().cancelPreloadingRecentTasksList();
break;
case MSG_OPEN_SEARCH_PANEL:
if (DEBUG) Slog.d(TAG, "opening search panel");
diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/phone/PanelView.java b/packages/SystemUI/src/com/android/systemui/statusbar/phone/PanelView.java
index 33f467f..2f551e1 100644
--- a/packages/SystemUI/src/com/android/systemui/statusbar/phone/PanelView.java
+++ b/packages/SystemUI/src/com/android/systemui/statusbar/phone/PanelView.java
@@ -5,7 +5,7 @@
import android.content.Context;
import android.content.res.Resources;
import android.util.AttributeSet;
-import android.util.Log;
+import android.util.Slog;
import android.view.MotionEvent;
import android.view.VelocityTracker;
import android.view.View;
@@ -18,7 +18,7 @@
public static final String TAG = PanelView.class.getSimpleName();
public final void LOG(String fmt, Object... args) {
if (!DEBUG) return;
- Log.v(TAG, (mViewName != null ? (mViewName + ": ") : "") + String.format(fmt, args));
+ Slog.v(TAG, (mViewName != null ? (mViewName + ": ") : "") + String.format(fmt, args));
}
public static final boolean BRAKES = false;
@@ -175,6 +175,12 @@
event.offsetLocation(-deltaX, -deltaY);
}
+ // Pass all touches along to the handle, allowing the user to drag the panel closed from its interior
+ @Override
+ public boolean onTouchEvent(MotionEvent event) {
+ return mHandleView.dispatchTouchEvent(event);
+ }
+
@Override
protected void onFinishInflate() {
super.onFinishInflate();
diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/phone/PhoneStatusBar.java b/packages/SystemUI/src/com/android/systemui/statusbar/phone/PhoneStatusBar.java
index 31bc8a0..5646c55 100644
--- a/packages/SystemUI/src/com/android/systemui/statusbar/phone/PhoneStatusBar.java
+++ b/packages/SystemUI/src/com/android/systemui/statusbar/phone/PhoneStatusBar.java
@@ -109,8 +109,6 @@
public static final String ACTION_STATUSBAR_START
= "com.android.internal.policy.statusbar.START";
- private static final boolean SHOW_CARRIER_LABEL = false; // XXX: doesn't work with rubberband panels right now
-
private static final int MSG_OPEN_NOTIFICATION_PANEL = 1000;
private static final int MSG_CLOSE_NOTIFICATION_PANEL = 1001;
// 1020-1030 reserved for BaseStatusBar
@@ -189,6 +187,9 @@
private boolean mCarrierLabelVisible = false;
private int mCarrierLabelHeight;
private TextView mEmergencyCallLabel;
+ private int mNotificationHeaderHeight;
+
+ private boolean mShowCarrierInPanel = false;
// position
int[] mPositionTmp = new int[2];
@@ -310,14 +311,6 @@
mStatusBarView.setPanelHolder(holder);
mNotificationPanel = (PanelView) mStatusBarWindow.findViewById(R.id.notification_panel);
- // don't allow clicks on the panel to pass through to the background where they will cause the panel to close
- View.OnTouchListener clickStopper = new View.OnTouchListener() {
- @Override
- public boolean onTouch(View v, MotionEvent event) {
- return true;
- }
- };
- mNotificationPanel.setOnTouchListener(clickStopper);
mNotificationPanelIsFullScreenWidth =
(mNotificationPanel.getLayoutParams().width == ViewGroup.LayoutParams.MATCH_PARENT);
mNotificationPanel.setSystemUiVisibility(
@@ -326,7 +319,6 @@
// quick settings (WIP)
mSettingsPanel = (PanelView) mStatusBarWindow.findViewById(R.id.settings_panel);
- mSettingsPanel.setOnTouchListener(clickStopper);
if (!ActivityManager.isHighEndGfx()) {
mStatusBarWindow.setBackground(null);
@@ -419,8 +411,10 @@
}});
}
- if (SHOW_CARRIER_LABEL) {
- mCarrierLabel = (TextView)mStatusBarWindow.findViewById(R.id.carrier_label);
+ mCarrierLabel = (TextView)mStatusBarWindow.findViewById(R.id.carrier_label);
+ mShowCarrierInPanel = (mCarrierLabel != null);
+ Slog.v(TAG, "carrierlabel=" + mCarrierLabel + " show=" + mShowCarrierInPanel);
+ if (mShowCarrierInPanel) {
mCarrierLabel.setVisibility(mCarrierLabelVisible ? View.VISIBLE : View.INVISIBLE);
// for mobile devices, we always show mobile connection info here (SPN/PLMN)
@@ -510,19 +504,6 @@
return lp;
}
- /*
- protected void updateRecentsPanel() {
- super.updateRecentsPanel(R.layout.status_bar_recent_panel);
- // Make .03 alpha the minimum so you always see the item a bit-- slightly below
- // .03, the item disappears entirely (as if alpha = 0) and that discontinuity looks
- // a bit jarring
- mRecentsPanel.setMinSwipeAlpha(0.03f);
- if (mNavigationBarView != null) {
- mNavigationBarView.getRecentsButton().setOnTouchListener(mRecentsPanel);
- }
- }
- */
-
@Override
protected void updateSearchPanel() {
super.updateSearchPanel();
@@ -746,6 +727,10 @@
*/
if (notification.notification.fullScreenIntent != null) {
+ // Stop screensaver if the notification has a full-screen intent.
+ // (like an incoming phone call)
+ awakenDreams();
+
// not immersive & a full-screen alert should be shown
Slog.d(TAG, "Notification has fullScreenIntent; sending fullScreenIntent");
try {
@@ -880,7 +865,7 @@
}
protected void updateCarrierLabelVisibility(boolean force) {
- if (!SHOW_CARRIER_LABEL) return;
+ if (!mShowCarrierInPanel) return;
// The idea here is to only show the carrier label when there is enough room to see it,
// i.e. when there aren't enough notifications to fill the panel.
if (DEBUG) {
@@ -891,7 +876,7 @@
final boolean emergencyCallsShownElsewhere = mEmergencyCallLabel != null;
final boolean makeVisible =
!(emergencyCallsShownElsewhere && mNetworkController.isEmergencyOnly())
- && mPile.getHeight() < (mScrollView.getHeight() - mCarrierLabelHeight);
+ && mPile.getHeight() < (mNotificationPanel.getHeight() - mCarrierLabelHeight - mNotificationHeaderHeight);
if (force || mCarrierLabelVisible != makeVisible) {
mCarrierLabelVisible = makeVisible;
@@ -1644,6 +1629,8 @@
lp.gravity = mSettingsPanelGravity;
lp.rightMargin = mNotificationPanelMarginPx;
mSettingsPanel.setLayoutParams(lp);
+
+ updateCarrierLabelVisibility(false);
}
// called by makeStatusbar and also by PhoneStatusBarView
@@ -1918,6 +1905,7 @@
+ res.getDimensionPixelSize(R.dimen.close_handle_underlap);
mCarrierLabelHeight = res.getDimensionPixelSize(R.dimen.carrier_label_height);
+ mNotificationHeaderHeight = res.getDimensionPixelSize(R.dimen.notification_panel_header_height);
if (false) Slog.v(TAG, "updateResources");
}
diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/policy/LocationController.java b/packages/SystemUI/src/com/android/systemui/statusbar/policy/LocationController.java
index a60bba7..bec5d72 100644
--- a/packages/SystemUI/src/com/android/systemui/statusbar/policy/LocationController.java
+++ b/packages/SystemUI/src/com/android/systemui/statusbar/policy/LocationController.java
@@ -26,6 +26,7 @@
import android.content.Intent;
import android.content.IntentFilter;
import android.location.LocationManager;
+import android.os.UserHandle;
import android.provider.Settings;
import android.util.Slog;
import android.view.View;
@@ -84,10 +85,12 @@
}
try {
+ // XXX WHAT TO DO ABOUT MULTI-USER?
if (visible) {
Intent gpsIntent = new Intent(Settings.ACTION_LOCATION_SOURCE_SETTINGS);
gpsIntent.setFlags(Intent.FLAG_ACTIVITY_NEW_TASK);
- PendingIntent pendingIntent = PendingIntent.getActivity(context, 0, gpsIntent, 0);
+ PendingIntent pendingIntent = PendingIntent.getActivityAsUser(context, 0,
+ gpsIntent, 0, null, UserHandle.CURRENT);
Notification n = new Notification.Builder(mContext)
.setSmallIcon(iconId)
@@ -108,11 +111,12 @@
null,
GPS_NOTIFICATION_ID,
n,
- idOut);
+ idOut,
+ UserHandle.USER_CURRENT);
} else {
- mNotificationService.cancelNotification(
- mContext.getPackageName(),
- GPS_NOTIFICATION_ID);
+ mNotificationService.cancelNotificationWithTag(
+ mContext.getPackageName(), null,
+ GPS_NOTIFICATION_ID, UserHandle.USER_CURRENT);
}
} catch (android.os.RemoteException ex) {
// well, it was worth a shot
diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/tablet/TabletStatusBar.java b/packages/SystemUI/src/com/android/systemui/statusbar/tablet/TabletStatusBar.java
index 8ca3a9c..7153ec7 100644
--- a/packages/SystemUI/src/com/android/systemui/statusbar/tablet/TabletStatusBar.java
+++ b/packages/SystemUI/src/com/android/systemui/statusbar/tablet/TabletStatusBar.java
@@ -355,7 +355,7 @@
mWindowManager.addView(mCompatModePanel, lp);
- //mRecentButton.setOnTouchListener(mRecentsPanel); //TODO: plumb this
+ mRecentButton.setOnTouchListener(getRecentTasksLoader());
mPile = (NotificationRowLayout)mNotificationPanel.findViewById(R.id.content);
mPile.removeAllViews();
diff --git a/policy/src/com/android/internal/policy/impl/PhoneWindow.java b/policy/src/com/android/internal/policy/impl/PhoneWindow.java
index 3e96f9bc..e761847 100644
--- a/policy/src/com/android/internal/policy/impl/PhoneWindow.java
+++ b/policy/src/com/android/internal/policy/impl/PhoneWindow.java
@@ -2841,7 +2841,7 @@
mDecor.setDescendantFocusability(ViewGroup.FOCUS_AFTER_DESCENDANTS);
mDecor.setIsRootNamespace(true);
mDecor.setLayoutDirection(
- getContext().getResources().getConfiguration().layoutDirection);
+ getContext().getResources().getConfiguration().getLayoutDirection());
}
if (mContentParent == null) {
mContentParent = generateLayout(mDecor);
diff --git a/policy/src/com/android/internal/policy/impl/PhoneWindowManager.java b/policy/src/com/android/internal/policy/impl/PhoneWindowManager.java
index 0f3546f..b07d8b8 100755
--- a/policy/src/com/android/internal/policy/impl/PhoneWindowManager.java
+++ b/policy/src/com/android/internal/policy/impl/PhoneWindowManager.java
@@ -2809,7 +2809,7 @@
}
/** {@inheritDoc} */
- public void beginAnimationLw(int displayWidth, int displayHeight) {
+ public void beginPostLayoutPolicyLw(int displayWidth, int displayHeight) {
mTopFullscreenOpaqueWindowState = null;
mForceStatusBar = false;
@@ -2819,7 +2819,7 @@
}
/** {@inheritDoc} */
- public void animatingWindowLw(WindowState win,
+ public void applyPostLayoutPolicyLw(WindowState win,
WindowManager.LayoutParams attrs) {
if (DEBUG_LAYOUT) Slog.i(TAG, "Win " + win + ": isVisibleOrBehindKeyguardLw="
+ win.isVisibleOrBehindKeyguardLw());
@@ -2851,7 +2851,7 @@
}
/** {@inheritDoc} */
- public int finishAnimationLw() {
+ public int finishPostLayoutPolicyLw() {
int changes = 0;
boolean topIsFullscreen = false;
@@ -2906,10 +2906,11 @@
mTopIsFullscreen = topIsFullscreen;
- // Hide the key guard if a visible window explicitly specifies that it wants to be displayed
- // when the screen is locked
+ // Hide the key guard if a visible window explicitly specifies that it wants to be
+ // displayed when the screen is locked.
if (mKeyguard != null) {
- if (localLOGV) Log.v(TAG, "finishAnimationLw::mHideKeyguard="+mHideLockScreen);
+ if (localLOGV) Log.v(TAG, "finishPostLayoutPolicyLw: mHideKeyguard="
+ + mHideLockScreen);
if (mDismissKeyguard && !mKeyguardMediator.isSecure()) {
if (mKeyguard.hideLw(true)) {
changes |= FINISH_LAYOUT_REDO_LAYOUT
diff --git a/policy/src/com/android/internal/policy/impl/keyguard/FaceUnlock.java b/policy/src/com/android/internal/policy/impl/keyguard/FaceUnlock.java
index 31d138b..5a0a228 100644
--- a/policy/src/com/android/internal/policy/impl/keyguard/FaceUnlock.java
+++ b/policy/src/com/android/internal/policy/impl/keyguard/FaceUnlock.java
@@ -81,13 +81,16 @@
* Stores some of the structures that Face Unlock will need to access and creates the handler
* will be used to execute messages on the UI thread.
*/
- public FaceUnlock(Context context, KeyguardSecurityCallback keyguardScreenCallback) {
+ public FaceUnlock(Context context) {
mContext = context;
mLockPatternUtils = new LockPatternUtils(context);
- mKeyguardScreenCallback = keyguardScreenCallback;
mHandler = new Handler(this);
}
+ public void setKeyguardCallback(KeyguardSecurityCallback keyguardScreenCallback) {
+ mKeyguardScreenCallback = keyguardScreenCallback;
+ }
+
/**
* Stores and displays the view that Face Unlock is allowed to draw within.
* TODO: since the layout object will eventually be shared by multiple biometric unlock
@@ -114,11 +117,7 @@
if (mHandler.getLooper() != Looper.myLooper()) {
Log.e(TAG, "show() called off of the UI thread");
}
-
removeDisplayMessages();
- if (mFaceUnlockView != null) {
- mFaceUnlockView.setVisibility(View.VISIBLE);
- }
if (timeoutMillis > 0) {
mHandler.sendEmptyMessageDelayed(MSG_HIDE_FACE_UNLOCK_VIEW, timeoutMillis);
}
@@ -270,23 +269,15 @@
*/
void handleShowFaceUnlockView() {
if (DEBUG) Log.d(TAG, "handleShowFaceUnlockView()");
- if (mFaceUnlockView != null) {
- mFaceUnlockView.setVisibility(View.VISIBLE);
- } else {
- Log.e(TAG, "mFaceUnlockView is null in handleShowFaceUnlockView()");
- }
+ // Not required
}
/**
- * Sets the Face Unlock view to invisible, thus exposing the backup lock.
+ * Hide face unlock and show backup
*/
void handleHideFaceUnlockView() {
if (DEBUG) Log.d(TAG, "handleHideFaceUnlockView()");
- if (mFaceUnlockView != null) {
- mFaceUnlockView.setVisibility(View.INVISIBLE);
- } else {
- Log.e(TAG, "mFaceUnlockView is null in handleHideFaceUnlockView()");
- }
+ mKeyguardScreenCallback.showBackupSecurity();
}
/**
@@ -358,11 +349,6 @@
void handleUnlock() {
if (DEBUG) Log.d(TAG, "handleUnlock()");
removeDisplayMessages();
- if (mFaceUnlockView != null) {
- mFaceUnlockView.setVisibility(View.VISIBLE);
- } else {
- Log.e(TAG, "mFaceUnlockView is null in handleUnlock()");
- }
stop();
mKeyguardScreenCallback.reportSuccessfulUnlockAttempt();
mKeyguardScreenCallback.dismiss(true);
@@ -373,11 +359,7 @@
*/
void handleCancel() {
if (DEBUG) Log.d(TAG, "handleCancel()");
- if (mFaceUnlockView != null) {
- mFaceUnlockView.setVisibility(View.INVISIBLE);
- } else {
- Log.e(TAG, "mFaceUnlockView is null in handleCancel()");
- }
+ mKeyguardScreenCallback.dismiss(false);
stop();
mKeyguardScreenCallback.userActivity(BACKUP_LOCK_TIMEOUT);
}
@@ -397,11 +379,7 @@
*/
void handleExposeFallback() {
if (DEBUG) Log.d(TAG, "handleExposeFallback()");
- if (mFaceUnlockView != null) {
- mFaceUnlockView.setVisibility(View.INVISIBLE);
- } else {
- Log.e(TAG, "mFaceUnlockView is null in handleExposeFallback()");
- }
+ // No longer required because face unlock doesn't cover backup unlock.
}
/**
diff --git a/policy/src/com/android/internal/policy/impl/keyguard/KeyguardFaceUnlockView.java b/policy/src/com/android/internal/policy/impl/keyguard/KeyguardFaceUnlockView.java
index 843151b..7dffca8 100644
--- a/policy/src/com/android/internal/policy/impl/keyguard/KeyguardFaceUnlockView.java
+++ b/policy/src/com/android/internal/policy/impl/keyguard/KeyguardFaceUnlockView.java
@@ -16,18 +16,25 @@
package com.android.internal.policy.impl.keyguard;
import android.content.Context;
+import android.telephony.TelephonyManager;
import android.util.AttributeSet;
+import android.util.Log;
+import android.view.View;
import android.widget.LinearLayout;
+import com.android.internal.R;
+import com.android.internal.policy.impl.keyguard.BiometricSensorUnlock;
+import com.android.internal.policy.impl.keyguard.FaceUnlock;
import com.android.internal.widget.LockPatternUtils;
public class KeyguardFaceUnlockView extends LinearLayout implements KeyguardSecurityView {
- // Long enough to stay visible while dialer comes up
- // Short enough to not be visible if the user goes back immediately
- private static final int BIOMETRIC_AREA_EMERGENCY_DIALER_TIMEOUT = 1000;
+ private static final String TAG = "KeyguardFaceUnlockView";
private KeyguardSecurityCallback mKeyguardSecurityCallback;
private LockPatternUtils mLockPatternUtils;
+ private BiometricSensorUnlock mBiometricUnlock;
+ private KeyguardNavigationManager mNavigationManager;
+ private View mFaceUnlockAreaView;
public KeyguardFaceUnlockView(Context context) {
this(context, null);
@@ -38,8 +45,18 @@
}
@Override
+ protected void onFinishInflate() {
+ super.onFinishInflate();
+ mNavigationManager = new KeyguardNavigationManager(this);
+
+ initializeBiometricUnlockView();
+ }
+
+ @Override
public void setKeyguardCallback(KeyguardSecurityCallback callback) {
mKeyguardSecurityCallback = callback;
+ // TODO: formalize this in the interface or factor it out
+ ((FaceUnlock)mBiometricUnlock).setKeyguardCallback(callback);
}
@Override
@@ -54,12 +71,18 @@
@Override
public void onPause() {
-
+ if (mBiometricUnlock != null) {
+ mBiometricUnlock.hide();
+ mBiometricUnlock.stop();
+ }
+ KeyguardUpdateMonitor.getInstance(mContext).removeCallback(mUpdateCallback);
}
@Override
public void onResume() {
-
+ maybeStartBiometricUnlock();
+ mBiometricUnlock.show(0);
+ KeyguardUpdateMonitor.getInstance(mContext).registerCallback(mUpdateCallback);
}
@Override
@@ -72,123 +95,59 @@
return mKeyguardSecurityCallback;
}
- // TODO
- // public void onRefreshBatteryInfo(BatteryStatus status) {
- // // When someone plugs in or unplugs the device, we hide the biometric sensor area and
- // // suppress its startup for the next onScreenTurnedOn(). Since plugging/unplugging
- // // causes the screen to turn on, the biometric unlock would start if it wasn't
- // // suppressed.
- // //
- // // However, if the biometric unlock is already running, we do not want to interrupt it.
- // final boolean pluggedIn = status.isPluggedIn();
- // if (mBiometricUnlock != null && mPluggedIn != pluggedIn
- // && !mBiometricUnlock.isRunning()) {
- // mBiometricUnlock.stop();
- // mBiometricUnlock.hide();
- // mSuppressBiometricUnlock = true;
- // }
- // mPluggedIn = pluggedIn;
- // }
+ @Override
+ protected void onLayout(boolean changed, int l, int t, int r, int b) {
+ super.onLayout(changed, l, t, r, b);
+ mBiometricUnlock.initializeView(mFaceUnlockAreaView);
+ }
- // We need to stop the biometric unlock when a phone call comes in
- // @Override
- // public void onPhoneStateChanged(int phoneState) {
- // if (DEBUG) Log.d(TAG, "phone state: " + phoneState);
- // if (phoneState == TelephonyManager.CALL_STATE_RINGING) {
- // mSuppressBiometricUnlock = true;
- // mBiometricUnlock.stop();
- // mBiometricUnlock.hide();
- // }
- // }
+ private void initializeBiometricUnlockView() {
+ mFaceUnlockAreaView = findViewById(R.id.face_unlock_area_view);
+ if (mFaceUnlockAreaView != null) {
+ mBiometricUnlock = new FaceUnlock(mContext);
+ } else {
+ Log.w(TAG, "Couldn't find biometric unlock view");
+ }
+ }
- // @Override
- // public void onUserSwitched(int userId) {
- // if (mBiometricUnlock != null) {
- // mBiometricUnlock.stop();
- // }
- // mLockPatternUtils.setCurrentUser(userId);
- // updateScreen(getInitialMode(), true);
- // }
+ /**
+ * Starts the biometric unlock if it should be started based on a number of factors including
+ * the mSuppressBiometricUnlock flag. If it should not be started, it hides the biometric
+ * unlock area.
+ */
+ private void maybeStartBiometricUnlock() {
+ if (mBiometricUnlock != null) {
+ KeyguardUpdateMonitor monitor = KeyguardUpdateMonitor.getInstance(mContext);
+ final boolean backupIsTimedOut = (
+ monitor.getFailedUnlockAttempts() >=
+ LockPatternUtils.FAILED_ATTEMPTS_BEFORE_TIMEOUT);
+ if (monitor.getPhoneState() == TelephonyManager.CALL_STATE_IDLE
+ && !monitor.getMaxBiometricUnlockAttemptsReached()
+ && !backupIsTimedOut) {
+ mBiometricUnlock.start();
+ } else {
+ mBiometricUnlock.hide();
+ }
+ }
+ }
- // /**
- // * This returns false if there is any condition that indicates that the biometric unlock should
- // * not be used before the next time the unlock screen is recreated. In other words, if this
- // * returns false there is no need to even construct the biometric unlock.
- // */
- // private boolean useBiometricUnlock() {
- // final ShowingMode unlockMode = getUnlockMode();
- // final boolean backupIsTimedOut = (mUpdateMonitor.getFailedAttempts() >=
- // LockPatternUtils.FAILED_ATTEMPTS_BEFORE_TIMEOUT);
- // return (mLockPatternUtils.usingBiometricWeak() &&
- // mLockPatternUtils.isBiometricWeakInstalled() &&
- // !mUpdateMonitor.getMaxBiometricUnlockAttemptsReached() &&
- // !backupIsTimedOut &&
- // (unlockMode == ShowingMode.Pattern || unlockMode == ShowingMode.Password));
- // }
+ KeyguardUpdateMonitorCallback mUpdateCallback = new KeyguardUpdateMonitorCallback() {
+ // We need to stop the biometric unlock when a phone call comes in
+ @Override
+ public void onPhoneStateChanged(int phoneState) {
+ if (phoneState == TelephonyManager.CALL_STATE_RINGING) {
+ mBiometricUnlock.stop();
+ mBiometricUnlock.hide();
+ }
+ }
- // private void initializeBiometricUnlockView(View view) {
- // boolean restartBiometricUnlock = false;
- //
- // if (mBiometricUnlock != null) {
- // restartBiometricUnlock = mBiometricUnlock.stop();
- // }
- //
- // // Prevents biometric unlock from coming up immediately after a phone call or if there
- // // is a dialog on top of lockscreen. It is only updated if the screen is off because if the
- // // screen is on it's either because of an orientation change, or when it first boots.
- // // In both those cases, we don't want to override the current value of
- // // mSuppressBiometricUnlock and instead want to use the previous value.
- // if (!mScreenOn) {
- // mSuppressBiometricUnlock =
- // mUpdateMonitor.getPhoneState() != TelephonyManager.CALL_STATE_IDLE
- // || mHasDialog;
- // }
- //
- // // If the biometric unlock is not being used, we don't bother constructing it. Then we can
- // // simply check if it is null when deciding whether we should make calls to it.
- // mBiometricUnlock = null;
- // if (useBiometricUnlock()) {
- // // TODO: make faceLockAreaView a more general biometricUnlockView
- // // We will need to add our Face Unlock specific child views programmatically in
- // // initializeView rather than having them in the XML files.
- // View biometricUnlockView = view.findViewById(
- // com.android.internal.R.id.faceLockAreaView);
- // if (biometricUnlockView != null) {
- // mBiometricUnlock = new FaceUnlock(mContext, mUpdateMonitor, mLockPatternUtils,
- // mKeyguardScreenCallback);
- // mBiometricUnlock.initializeView(biometricUnlockView);
- //
- // // If this is being called because the screen turned off, we want to cover the
- // // backup lock so it is covered when the screen turns back on.
- // if (!mScreenOn) mBiometricUnlock.show(0);
- // } else {
- // Log.w(TAG, "Couldn't find biometric unlock view");
- // }
- // }
- //
- // if (mBiometricUnlock != null && restartBiometricUnlock) {
- // maybeStartBiometricUnlock();
- // }
- // }
-
- // /**
- // * Starts the biometric unlock if it should be started based on a number of factors including
- // * the mSuppressBiometricUnlock flag. If it should not be started, it hides the biometric
- // * unlock area.
- // */
- // private void maybeStartBiometricUnlock() {
- // if (mBiometricUnlock != null) {
- // final boolean backupIsTimedOut = (mUpdateMonitor.getFailedAttempts() >=
- // LockPatternUtils.FAILED_ATTEMPTS_BEFORE_TIMEOUT);
- // if (!mSuppressBiometricUnlock
- // && mUpdateMonitor.getPhoneState() == TelephonyManager.CALL_STATE_IDLE
- // && !mUpdateMonitor.getMaxBiometricUnlockAttemptsReached()
- // && !backupIsTimedOut) {
- // mBiometricUnlock.start();
- // } else {
- // mBiometricUnlock.hide();
- // }
- // }
- //}
+ @Override
+ public void onUserSwitched(int userId) {
+ if (mBiometricUnlock != null) {
+ mBiometricUnlock.stop();
+ }
+ mLockPatternUtils.setCurrentUser(userId);
+ }
+ };
}
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 2551c04e..058bf92 100644
--- a/policy/src/com/android/internal/policy/impl/keyguard/KeyguardHostView.java
+++ b/policy/src/com/android/internal/policy/impl/keyguard/KeyguardHostView.java
@@ -108,6 +108,7 @@
public KeyguardHostView(Context context, AttributeSet attrs) {
super(context, attrs);
+ mLockPatternUtils = new LockPatternUtils(context);
mAppWidgetHost = new AppWidgetHost(mContext, APPWIDGET_HOST_ID, mOnClickHandler);
mSecurityModel = new KeyguardSecurityModel(mContext);
@@ -130,10 +131,6 @@
// View Flipper
mViewFlipper = (ViewFlipper) findViewById(R.id.view_flipper);
- mViewFlipper.setInAnimation(AnimationUtils.loadAnimation(mContext,
- R.anim.keyguard_security_animate_in));
- mViewFlipper.setOutAnimation(AnimationUtils.loadAnimation(mContext,
- R.anim.keyguard_security_animate_out));
// Initialize all security views
for (int i = 0; i < mViewIds.length; i++) {
@@ -172,7 +169,7 @@
protected void onAttachedToWindow() {
super.onAttachedToWindow();
mAppWidgetHost.startListening();
- populateWidgets();
+ maybePopulateWidgets();
}
@Override
@@ -362,7 +359,7 @@
* account unlock screen and biometric unlock to show the user's normal unlock.
*/
private void showBackupSecurity() {
- SecurityMode currentMode = mSecurityModel.getSecurityMode();
+ SecurityMode currentMode = mSecurityModel.getAlternateFor(mSecurityModel.getSecurityMode());
SecurityMode backup = mSecurityModel.getBackupFor(currentMode);
showSecurityScreen(getSecurityViewIdForMode(backup));
}
@@ -372,8 +369,7 @@
if (SECURITY_SELECTOR_ID == mCurrentSecurityId) {
SecurityMode securityMode = mSecurityModel.getSecurityMode();
// Allow an alternate, such as biometric unlock
- // TODO: un-comment when face unlock is working again:
- // securityMode = mSecurityModel.getAlternateFor(securityMode);
+ securityMode = mSecurityModel.getAlternateFor(securityMode);
int realSecurityId = getSecurityViewIdForMode(securityMode);
if (SECURITY_SELECTOR_ID == realSecurityId) {
finish = true; // no security required
@@ -381,11 +377,28 @@
showSecurityScreen(realSecurityId); // switch to the "real" security view
}
} else if (authenticated) {
- if (mCurrentSecurityId == SECURITY_PATTERN_ID
- || mCurrentSecurityId == SECURITY_PASSWORD_ID
- || mCurrentSecurityId == SECURITY_ACCOUNT_ID
- || mCurrentSecurityId == SECURITY_BIOMETRIC_ID) {
- finish = true;
+ switch (mCurrentSecurityId) {
+ case SECURITY_PATTERN_ID:
+ case SECURITY_PASSWORD_ID:
+ case SECURITY_ACCOUNT_ID:
+ case SECURITY_BIOMETRIC_ID:
+ finish = true;
+ break;
+
+ case SECURITY_SIM_PIN_ID:
+ case SECURITY_SIM_PUK_ID:
+ // Shortcut for SIM PIN/PUK to go to directly to user's security screen or home
+ SecurityMode securityMode = mSecurityModel.getSecurityMode();
+ if (securityMode != SecurityMode.None) {
+ showSecurityScreen(getSecurityViewIdForMode(securityMode));
+ } else {
+ finish = true;
+ }
+ break;
+
+ default:
+ showSecurityScreen(SECURITY_SELECTOR_ID);
+ break;
}
} else {
// Not authenticated but we were asked to dismiss so go back to selector screen.
@@ -480,10 +493,20 @@
newView.onResume();
mViewMediatorCallback.setNeedsInput(newView.needsInput());
- mCurrentSecurityId = securityViewId;
// Find and show this child.
final int childCount = mViewFlipper.getChildCount();
+
+ // If we're go to/from the selector view, do flip animation, otherwise use fade animation.
+ final boolean doFlip = mCurrentSecurityId == SECURITY_SELECTOR_ID
+ || securityViewId == SECURITY_SELECTOR_ID;
+ final int inAnimation = doFlip ? R.anim.keyguard_security_animate_in
+ : R.anim.keyguard_security_fade_in;
+ final int outAnimation = doFlip ? R.anim.keyguard_security_animate_out
+ : R.anim.keyguard_security_fade_out;
+
+ mViewFlipper.setInAnimation(AnimationUtils.loadAnimation(mContext, inAnimation));
+ mViewFlipper.setOutAnimation(AnimationUtils.loadAnimation(mContext, outAnimation));
for (int i = 0; i < childCount; i++) {
if (securityViewId == mViewFlipper.getChildAt(i).getId()) {
mViewFlipper.setDisplayedChild(i);
@@ -495,6 +518,8 @@
if (securityViewId == SECURITY_SELECTOR_ID) {
setOnDismissRunnable(null);
}
+
+ mCurrentSecurityId = securityViewId;
}
@Override
@@ -581,7 +606,14 @@
addWidget(view);
}
- private void populateWidgets() {
+ private void maybePopulateWidgets() {
+ DevicePolicyManager dpm =
+ (DevicePolicyManager) mContext.getSystemService(Context.DEVICE_POLICY_SERVICE);
+ if (dpm != null && dpm.getKeyguardWidgetsDisabled(null)
+ != DevicePolicyManager.KEYGUARD_DISABLE_WIDGETS_NONE) {
+ Log.v(TAG, "Keyguard widgets disabled because of device policy admin");
+ return;
+ }
SharedPreferences prefs = mContext.getSharedPreferences(
KEYGUARD_WIDGET_PREFS, Context.MODE_PRIVATE);
for (String key : prefs.getAll().keySet()) {
diff --git a/policy/src/com/android/internal/policy/impl/keyguard/KeyguardPasswordView.java b/policy/src/com/android/internal/policy/impl/keyguard/KeyguardPasswordView.java
index 6938561..b2ce73e 100644
--- a/policy/src/com/android/internal/policy/impl/keyguard/KeyguardPasswordView.java
+++ b/policy/src/com/android/internal/policy/impl/keyguard/KeyguardPasswordView.java
@@ -256,12 +256,10 @@
private void verifyPasswordAndUnlock() {
String entry = mPasswordEntry.getText().toString();
- boolean wrongPassword = true;
if (mLockPatternUtils.checkPassword(entry)) {
mCallback.reportSuccessfulUnlockAttempt();
KeyStore.getInstance().password(entry);
mCallback.dismiss(true);
- wrongPassword = false;
} else if (entry.length() > MINIMUM_PASSWORD_LENGTH_BEFORE_REPORT ) {
// to avoid accidental lockout, only count attempts that are long enough to be a
// real password. This may require some tweaking.
@@ -271,9 +269,9 @@
long deadline = mLockPatternUtils.setLockoutAttemptDeadline();
handleAttemptLockout(deadline);
}
+ mNavigationManager.setMessage(
+ mIsAlpha ? R.string.kg_wrong_password : R.string.kg_wrong_pin);
}
- mNavigationManager.setMessage(wrongPassword ?
- (mIsAlpha ? R.string.kg_wrong_password : R.string.kg_wrong_pin) : 0);
mPasswordEntry.setText("");
}
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 a38b247..28f5e8c 100644
--- a/policy/src/com/android/internal/policy/impl/keyguard/KeyguardSelectorView.java
+++ b/policy/src/com/android/internal/policy/impl/keyguard/KeyguardSelectorView.java
@@ -149,7 +149,6 @@
mGlowPadView = (GlowPadView) findViewById(R.id.glow_pad_view);
mGlowPadView.setOnTriggerListener(mOnTriggerListener);
mEmergencyCallButton = (Button) findViewById(R.id.emergency_call_button);
- KeyguardUpdateMonitor.getInstance(getContext()).registerCallback(mInfoCallback);
updateTargets();
}
diff --git a/policy/src/com/android/internal/policy/impl/keyguard/KeyguardSimPinView.java b/policy/src/com/android/internal/policy/impl/keyguard/KeyguardSimPinView.java
index 294ea5c..bc55008 100644
--- a/policy/src/com/android/internal/policy/impl/keyguard/KeyguardSimPinView.java
+++ b/policy/src/com/android/internal/policy/impl/keyguard/KeyguardSimPinView.java
@@ -32,6 +32,7 @@
import android.util.AttributeSet;
import android.view.KeyEvent;
+import android.view.MotionEvent;
import android.view.View;
import android.view.WindowManager;
import android.view.inputmethod.EditorInfo;
@@ -56,6 +57,8 @@
private LockPatternUtils mLockPatternUtils;
private KeyguardNavigationManager mNavigationManager;
+ private volatile boolean mSimCheckInProgress;
+
public KeyguardSimPinView(Context context) {
this(context, null);
}
@@ -91,8 +94,6 @@
}
});
}
-
- setFocusableInTouchMode(true);
reset();
}
@@ -131,7 +132,7 @@
mPin = pin;
}
- abstract void onSimLockChangedResponse(boolean success);
+ abstract void onSimCheckResponse(boolean success);
@Override
public void run() {
@@ -140,13 +141,13 @@
.checkService("phone")).supplyPin(mPin);
post(new Runnable() {
public void run() {
- onSimLockChangedResponse(result);
+ onSimCheckResponse(result);
}
});
} catch (RemoteException e) {
post(new Runnable() {
public void run() {
- onSimLockChangedResponse(false);
+ onSimCheckResponse(false);
}
});
}
@@ -156,8 +157,10 @@
public boolean onEditorAction(TextView v, int actionId, KeyEvent event) {
// Check if this was the result of hitting the enter key
mCallback.userActivity(DIGIT_PRESS_WAKE_MILLIS);
- if (actionId == EditorInfo.IME_NULL || actionId == EditorInfo.IME_ACTION_DONE
- || actionId == EditorInfo.IME_ACTION_NEXT) {
+ if (event.getAction() == MotionEvent.ACTION_DOWN && (
+ actionId == EditorInfo.IME_NULL
+ || actionId == EditorInfo.IME_ACTION_DONE
+ || actionId == EditorInfo.IME_ACTION_NEXT)) {
checkPin();
return true;
}
@@ -190,27 +193,31 @@
getSimUnlockProgressDialog().show();
- new CheckSimPin(mPinEntry.getText().toString()) {
- void onSimLockChangedResponse(final boolean success) {
- post(new Runnable() {
- public void run() {
- if (mSimUnlockProgressDialog != null) {
- mSimUnlockProgressDialog.hide();
+ if (!mSimCheckInProgress) {
+ mSimCheckInProgress = true; // there should be only one
+ new CheckSimPin(mPinEntry.getText().toString()) {
+ void onSimCheckResponse(final boolean success) {
+ post(new Runnable() {
+ public void run() {
+ if (mSimUnlockProgressDialog != null) {
+ mSimUnlockProgressDialog.hide();
+ }
+ if (success) {
+ // before closing the keyguard, report back that the sim is unlocked
+ // so it knows right away.
+ KeyguardUpdateMonitor.getInstance(getContext()).reportSimUnlocked();
+ mCallback.dismiss(true);
+ } else {
+ mNavigationManager.setMessage(R.string.kg_password_wrong_pin_code);
+ mPinEntry.setText("");
+ }
+ mCallback.userActivity(0);
+ mSimCheckInProgress = false;
}
- if (success) {
- // before closing the keyguard, report back that the sim is unlocked
- // so it knows right away.
- KeyguardUpdateMonitor.getInstance(getContext()).reportSimUnlocked();
- mCallback.dismiss(false); //
- } else {
- mNavigationManager.setMessage(R.string.kg_password_wrong_pin_code);
- mPinEntry.setText("");
- }
- mCallback.userActivity(0);
- }
- });
- }
- }.start();
+ });
+ }
+ }.start();
+ }
}
public void setLockPatternUtils(LockPatternUtils utils) {
diff --git a/policy/src/com/android/internal/policy/impl/keyguard/KeyguardSimPukView.java b/policy/src/com/android/internal/policy/impl/keyguard/KeyguardSimPukView.java
index 801dfc3..e04bff9 100644
--- a/policy/src/com/android/internal/policy/impl/keyguard/KeyguardSimPukView.java
+++ b/policy/src/com/android/internal/policy/impl/keyguard/KeyguardSimPukView.java
@@ -27,6 +27,7 @@
import android.util.AttributeSet;
import android.util.Log;
import android.view.KeyEvent;
+import android.view.MotionEvent;
import android.view.View;
import android.view.WindowManager;
import android.view.inputmethod.EditorInfo;
@@ -63,6 +64,8 @@
private LockPatternUtils mLockPatternUtils;
+ private volatile boolean mCheckInProgress;
+
public KeyguardSimPukView(Context context) {
this(context, null);
}
@@ -233,48 +236,54 @@
getSimUnlockProgressDialog().show();
- new CheckSimPuk(mPukText.getText().toString(),
- mPinText.getText().toString()) {
- void onSimLockChangedResponse(final boolean success) {
- mPinText.post(new Runnable() {
- public void run() {
- if (mSimUnlockProgressDialog != null) {
- mSimUnlockProgressDialog.hide();
+ if (!mCheckInProgress) {
+ mCheckInProgress = true;
+ new CheckSimPuk(mPukText.getText().toString(),
+ mPinText.getText().toString()) {
+ void onSimLockChangedResponse(final boolean success) {
+ mPinText.post(new Runnable() {
+ public void run() {
+ if (mSimUnlockProgressDialog != null) {
+ mSimUnlockProgressDialog.hide();
+ }
+ if (success) {
+ mCallback.dismiss(true);
+ } else {
+ mNavigationManager.setMessage(R.string.kg_invalid_puk);
+ mPukText.setText("");
+ mPinText.setText("");
+ }
+ mCheckInProgress = false;
}
- if (success) {
- mCallback.dismiss(true);
- } else {
- mNavigationManager.setMessage(R.string.kg_invalid_puk);
- mPukText.setText("");
- mPinText.setText("");
- }
- }
- });
- }
- }.start();
+ });
+ }
+ }.start();
+ }
}
@Override
public boolean onEditorAction(TextView view, int actionId, KeyEvent event) {
// Check if this was the result of hitting the enter key
mCallback.userActivity(DIGIT_PRESS_WAKE_MILLIS);
- if (actionId == EditorInfo.IME_NULL
+ if (event.getAction() == MotionEvent.ACTION_DOWN) {
+ if (actionId == EditorInfo.IME_NULL
|| actionId == EditorInfo.IME_ACTION_DONE
|| actionId == EditorInfo.IME_ACTION_NEXT) {
- if (view == mPukText && mPukText.getText().length() < 8) {
- mNavigationManager.setMessage(R.string.kg_invalid_sim_puk_hint);
- mPukText.setText("");
- mPukText.requestFocus();
- return true;
- } else if (view == mPinText) {
- if (mPinText.getText().length() < 4 || mPinText.getText().length() > 8) {
- mNavigationManager.setMessage(R.string.kg_invalid_sim_pin_hint);
- mPinText.setText("");
- mPinText.requestFocus();
- } else {
- checkPuk();
+ if (view == mPukText && mPukText.getText().length() < 8) {
+ mNavigationManager.setMessage(R.string.kg_invalid_sim_puk_hint);
+ mPukText.setText("");
+ mPukText.requestFocus();
+ return true;
+ } else if (view == mPinText) {
+ if (mPinText.getText().length() < 4 || mPinText.getText().length() > 8) {
+ mNavigationManager.setMessage(R.string.kg_invalid_sim_pin_hint);
+ mPinText.setText("");
+ mPinText.requestFocus();
+ } else {
+ checkPuk();
+ }
+ return true;
}
- return true;
}
}
return false;
diff --git a/policy/src/com/android/internal/policy/impl/keyguard/KeyguardStatusView.java b/policy/src/com/android/internal/policy/impl/keyguard/KeyguardStatusView.java
index d6ce967..481e9ad 100644
--- a/policy/src/com/android/internal/policy/impl/keyguard/KeyguardStatusView.java
+++ b/policy/src/com/android/internal/policy/impl/keyguard/KeyguardStatusView.java
@@ -21,6 +21,8 @@
import android.widget.GridLayout;
public class KeyguardStatusView extends GridLayout {
+ private KeyguardStatusViewManager mStatusViewManager;
+
public KeyguardStatusView(Context context) {
this(context, null, 0);
}
@@ -38,7 +40,7 @@
super.onFinishInflate();
// StatusView manages all of the widgets in this view.
- new KeyguardStatusViewManager(this);
+ mStatusViewManager = new KeyguardStatusViewManager(this);
}
}
diff --git a/policy/src/com/android/internal/policy/impl/keyguard/KeyguardStatusViewManager.java b/policy/src/com/android/internal/policy/impl/keyguard/KeyguardStatusViewManager.java
index 06ed88a..20fad0b 100644
--- a/policy/src/com/android/internal/policy/impl/keyguard/KeyguardStatusViewManager.java
+++ b/policy/src/com/android/internal/policy/impl/keyguard/KeyguardStatusViewManager.java
@@ -119,7 +119,7 @@
mDateView = (TextView) findViewById(R.id.date);
mStatus1View = (TextView) findViewById(R.id.status1);
mAlarmStatusView = (TextView) findViewById(R.id.alarm_status);
- mOwnerInfoView = (TextView) findViewById(R.id.propertyOf);
+ mOwnerInfoView = (TextView) findViewById(R.id.owner_info);
mDigitalClock = (DigitalClock) findViewById(R.id.time);
// Registering this callback immediately updates the battery state, among other things.
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 39df5a2..281ed19 100644
--- a/policy/src/com/android/internal/policy/impl/keyguard/KeyguardUpdateMonitor.java
+++ b/policy/src/com/android/internal/policy/impl/keyguard/KeyguardUpdateMonitor.java
@@ -100,7 +100,7 @@
private boolean mClockVisible;
- private ArrayList<WeakReference<KeyguardUpdateMonitorCallback>>
+ private final ArrayList<WeakReference<KeyguardUpdateMonitorCallback>>
mCallbacks = Lists.newArrayList();
private ContentObserver mContentObserver;
@@ -586,39 +586,46 @@
/**
* Remove the given observer's callback.
*
- * @param observer The observer to remove
+ * @param callback The callback to remove
*/
- public void removeCallback(Object observer) {
- mCallbacks.remove(observer);
+ public void removeCallback(KeyguardUpdateMonitorCallback callback) {
+ if (DEBUG) Log.v(TAG, "*** unregister callback for " + callback);
+ for (int i = mCallbacks.size() - 1; i >= 0; i--) {
+ if (mCallbacks.get(i).get() == callback) {
+ mCallbacks.remove(i);
+ }
+ }
}
/**
* Register to receive notifications about general keyguard information
* (see {@link InfoCallback}.
- * @param callback The callback.
+ * @param callback The callback to register
*/
public void registerCallback(KeyguardUpdateMonitorCallback callback) {
- if (!mCallbacks.contains(callback)) {
- mCallbacks.add(new WeakReference<KeyguardUpdateMonitorCallback>(callback));
- // Notify listener of the current state
- callback.onRefreshBatteryInfo(mBatteryStatus);
- callback.onTimeChanged();
- callback.onRingerModeChanged(mRingMode);
- callback.onPhoneStateChanged(mPhoneState);
- callback.onRefreshCarrierInfo(mTelephonyPlmn, mTelephonySpn);
- callback.onClockVisibilityChanged();
- callback.onSimStateChanged(mSimState);
- } else {
- if (DEBUG) Log.e(TAG, "Object tried to add another callback",
- new Exception("Called by"));
- }
-
- // Clean up any unused references
- for (int i = mCallbacks.size() - 1; i >= 0; i--) {
- if (mCallbacks.get(i).get() == null) {
- mCallbacks.remove(i);
+ if (DEBUG) Log.v(TAG, "*** register callback for " + callback);
+ // Prevent adding duplicate callbacks
+ for (int i = 0; i < mCallbacks.size(); i++) {
+ if (mCallbacks.get(i).get() == callback) {
+ if (DEBUG) Log.e(TAG, "Object tried to add another callback",
+ new Exception("Called by"));
+ return;
}
}
+ mCallbacks.add(new WeakReference<KeyguardUpdateMonitorCallback>(callback));
+ removeCallback(null); // remove unused references
+ sendUpdates(callback);
+ }
+
+ private void sendUpdates(KeyguardUpdateMonitorCallback callback) {
+ // Notify listener of the current state
+ callback.onRefreshBatteryInfo(mBatteryStatus);
+ callback.onTimeChanged();
+ callback.onRingerModeChanged(mRingMode);
+ callback.onPhoneStateChanged(mPhoneState);
+ callback.onRefreshCarrierInfo(mTelephonyPlmn, mTelephonySpn);
+ callback.onClockVisibilityChanged();
+ callback.onSimStateChanged(mSimState);
}
public void reportClockVisible(boolean visible) {
diff --git a/policy/src/com/android/internal/policy/impl/keyguard_obsolete/KeyguardStatusViewManager.java b/policy/src/com/android/internal/policy/impl/keyguard_obsolete/KeyguardStatusViewManager.java
index 409f87b..b6ffde0 100644
--- a/policy/src/com/android/internal/policy/impl/keyguard_obsolete/KeyguardStatusViewManager.java
+++ b/policy/src/com/android/internal/policy/impl/keyguard_obsolete/KeyguardStatusViewManager.java
@@ -179,7 +179,7 @@
mDateView = (TextView) findViewById(R.id.date);
mStatus1View = (TextView) findViewById(R.id.status1);
mAlarmStatusView = (TextView) findViewById(R.id.alarm_status);
- mOwnerInfoView = (TextView) findViewById(R.id.propertyOf);
+ mOwnerInfoView = (TextView) findViewById(R.id.owner_info);
mTransportView = (TransportControlView) findViewById(R.id.transport);
mEmergencyCallButton = (Button) findViewById(R.id.emergencyCallButton);
mEmergencyCallButtonEnabledInScreen = emergencyButtonEnabledInScreen;
diff --git a/policy/src/com/android/internal/policy/impl/keyguard_obsolete/LockPatternKeyguardView.java b/policy/src/com/android/internal/policy/impl/keyguard_obsolete/LockPatternKeyguardView.java
index 0ce87b1..4dc83b6 100644
--- a/policy/src/com/android/internal/policy/impl/keyguard_obsolete/LockPatternKeyguardView.java
+++ b/policy/src/com/android/internal/policy/impl/keyguard_obsolete/LockPatternKeyguardView.java
@@ -1013,7 +1013,7 @@
// TODO: make faceLockAreaView a more general biometricUnlockView
// We will need to add our Face Unlock specific child views programmatically in
// initializeView rather than having them in the XML files.
- View biometricUnlockView = view.findViewById(R.id.faceLockAreaView);
+ View biometricUnlockView = view.findViewById(R.id.face_unlock_area_view);
if (biometricUnlockView != null) {
mBiometricUnlock = new FaceUnlock(mContext, mUpdateMonitor, mLockPatternUtils,
mKeyguardScreenCallback);
diff --git a/services/java/com/android/server/AppWidgetService.java b/services/java/com/android/server/AppWidgetService.java
index 8e341df..343e70d 100644
--- a/services/java/com/android/server/AppWidgetService.java
+++ b/services/java/com/android/server/AppWidgetService.java
@@ -27,10 +27,10 @@
import android.content.IntentFilter;
import android.content.ServiceConnection;
import android.content.pm.PackageManager;
-import android.os.Binder;
import android.os.Bundle;
import android.os.IBinder;
import android.os.RemoteException;
+import android.os.UserHandle;
import android.util.Pair;
import android.util.Slog;
import android.util.SparseArray;
@@ -150,13 +150,13 @@
// Register for the boot completed broadcast, so we can send the
// ENABLE broacasts. If we try to send them now, they time out,
// because the system isn't ready to handle them yet.
- mContext.registerReceiver(mBroadcastReceiver,
+ mContext.registerReceiverAsUser(mBroadcastReceiver, UserHandle.ALL,
new IntentFilter(Intent.ACTION_BOOT_COMPLETED), null, null);
// Register for configuration changes so we can update the names
// of the widgets when the locale changes.
- mContext.registerReceiver(mBroadcastReceiver, new IntentFilter(
- Intent.ACTION_CONFIGURATION_CHANGED), null, null);
+ mContext.registerReceiverAsUser(mBroadcastReceiver, UserHandle.ALL,
+ new IntentFilter(Intent.ACTION_CONFIGURATION_CHANGED), null, null);
// Register for broadcasts about package install, etc., so we can
// update the provider list.
@@ -165,12 +165,14 @@
filter.addAction(Intent.ACTION_PACKAGE_CHANGED);
filter.addAction(Intent.ACTION_PACKAGE_REMOVED);
filter.addDataScheme("package");
- mContext.registerReceiver(mBroadcastReceiver, filter);
+ mContext.registerReceiverAsUser(mBroadcastReceiver, UserHandle.ALL,
+ filter, 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(mBroadcastReceiver, sdFilter);
+ mContext.registerReceiverAsUser(mBroadcastReceiver, UserHandle.ALL,
+ sdFilter, null, null);
IntentFilter userFilter = new IntentFilter();
userFilter.addAction(Intent.ACTION_USER_REMOVED);
@@ -180,60 +182,75 @@
onUserRemoved(intent.getIntExtra(Intent.EXTRA_USER_HANDLE, -1));
}
}, userFilter);
+
+ IntentFilter userStopFilter = new IntentFilter();
+ userStopFilter.addAction(Intent.ACTION_USER_STOPPED);
+ mContext.registerReceiverAsUser(new BroadcastReceiver() {
+ @Override
+ public void onReceive(Context context, Intent intent) {
+ onUserStopped(getSendingUserId());
+ }
+ }, UserHandle.ALL, userFilter, null, null);
}
@Override
public int allocateAppWidgetId(String packageName, int hostId) throws RemoteException {
- return getImplForUser().allocateAppWidgetId(packageName, hostId);
+ return getImplForUser(UserHandle.getCallingUserId()).allocateAppWidgetId(
+ packageName, hostId);
}
@Override
public void deleteAppWidgetId(int appWidgetId) throws RemoteException {
- getImplForUser().deleteAppWidgetId(appWidgetId);
+ getImplForUser(UserHandle.getCallingUserId()).deleteAppWidgetId(appWidgetId);
}
@Override
public void deleteHost(int hostId) throws RemoteException {
- getImplForUser().deleteHost(hostId);
+ getImplForUser(UserHandle.getCallingUserId()).deleteHost(hostId);
}
@Override
public void deleteAllHosts() throws RemoteException {
- getImplForUser().deleteAllHosts();
+ getImplForUser(UserHandle.getCallingUserId()).deleteAllHosts();
}
@Override
public void bindAppWidgetId(int appWidgetId, ComponentName provider) throws RemoteException {
- getImplForUser().bindAppWidgetId(appWidgetId, provider);
+ getImplForUser(UserHandle.getCallingUserId()).bindAppWidgetId(appWidgetId, provider);
}
@Override
public boolean bindAppWidgetIdIfAllowed(
String packageName, int appWidgetId, ComponentName provider) throws RemoteException {
- return getImplForUser().bindAppWidgetIdIfAllowed(packageName, appWidgetId, provider);
+ return getImplForUser(UserHandle.getCallingUserId()).bindAppWidgetIdIfAllowed(
+ packageName, appWidgetId, provider);
}
@Override
public boolean hasBindAppWidgetPermission(String packageName) throws RemoteException {
- return getImplForUser().hasBindAppWidgetPermission(packageName);
+ return getImplForUser(UserHandle.getCallingUserId()).hasBindAppWidgetPermission(
+ packageName);
}
@Override
public void setBindAppWidgetPermission(String packageName, boolean permission)
throws RemoteException {
- getImplForUser().setBindAppWidgetPermission(packageName, permission);
+ getImplForUser(UserHandle.getCallingUserId()).setBindAppWidgetPermission(
+ packageName, permission);
}
@Override
public void bindRemoteViewsService(int appWidgetId, Intent intent, IBinder connection)
throws RemoteException {
- getImplForUser().bindRemoteViewsService(appWidgetId, intent, connection);
+ getImplForUser(UserHandle.getCallingUserId()).bindRemoteViewsService(
+ appWidgetId, intent, connection);
}
@Override
public int[] startListening(IAppWidgetHost host, String packageName, int hostId,
List<RemoteViews> updatedViews) throws RemoteException {
- return getImplForUser().startListening(host, packageName, hostId, updatedViews);
+ return getImplForUser(UserHandle.getCallingUserId()).startListening(host,
+ packageName, hostId, updatedViews);
}
public void onUserRemoved(int userId) {
@@ -247,8 +264,10 @@
}
}
- private AppWidgetServiceImpl getImplForUser() {
- final int userId = Binder.getOrigCallingUser();
+ public void onUserStopped(int userId) {
+ }
+
+ private AppWidgetServiceImpl getImplForUser(int userId) {
AppWidgetServiceImpl service = mAppWidgetServices.get(userId);
if (service == null) {
Slog.e(TAG, "Unable to find AppWidgetServiceImpl for the current user");
@@ -265,27 +284,27 @@
@Override
public int[] getAppWidgetIds(ComponentName provider) throws RemoteException {
- return getImplForUser().getAppWidgetIds(provider);
+ return getImplForUser(UserHandle.getCallingUserId()).getAppWidgetIds(provider);
}
@Override
public AppWidgetProviderInfo getAppWidgetInfo(int appWidgetId) throws RemoteException {
- return getImplForUser().getAppWidgetInfo(appWidgetId);
+ return getImplForUser(UserHandle.getCallingUserId()).getAppWidgetInfo(appWidgetId);
}
@Override
public RemoteViews getAppWidgetViews(int appWidgetId) throws RemoteException {
- return getImplForUser().getAppWidgetViews(appWidgetId);
+ return getImplForUser(UserHandle.getCallingUserId()).getAppWidgetViews(appWidgetId);
}
@Override
public void updateAppWidgetOptions(int appWidgetId, Bundle options) {
- getImplForUser().updateAppWidgetOptions(appWidgetId, options);
+ getImplForUser(UserHandle.getCallingUserId()).updateAppWidgetOptions(appWidgetId, options);
}
@Override
public Bundle getAppWidgetOptions(int appWidgetId) {
- return getImplForUser().getAppWidgetOptions(appWidgetId);
+ return getImplForUser(UserHandle.getCallingUserId()).getAppWidgetOptions(appWidgetId);
}
static int[] getAppWidgetIds(Provider p) {
@@ -299,40 +318,43 @@
@Override
public List<AppWidgetProviderInfo> getInstalledProviders() throws RemoteException {
- return getImplForUser().getInstalledProviders();
+ return getImplForUser(UserHandle.getCallingUserId()).getInstalledProviders();
}
@Override
public void notifyAppWidgetViewDataChanged(int[] appWidgetIds, int viewId)
throws RemoteException {
- getImplForUser().notifyAppWidgetViewDataChanged(appWidgetIds, viewId);
+ getImplForUser(UserHandle.getCallingUserId()).notifyAppWidgetViewDataChanged(
+ appWidgetIds, viewId);
}
@Override
public void partiallyUpdateAppWidgetIds(int[] appWidgetIds, RemoteViews views)
throws RemoteException {
- getImplForUser().partiallyUpdateAppWidgetIds(appWidgetIds, views);
+ getImplForUser(UserHandle.getCallingUserId()).partiallyUpdateAppWidgetIds(
+ appWidgetIds, views);
}
@Override
public void stopListening(int hostId) throws RemoteException {
- getImplForUser().stopListening(hostId);
+ getImplForUser(UserHandle.getCallingUserId()).stopListening(hostId);
}
@Override
public void unbindRemoteViewsService(int appWidgetId, Intent intent) throws RemoteException {
- getImplForUser().unbindRemoteViewsService(appWidgetId, intent);
+ getImplForUser(UserHandle.getCallingUserId()).unbindRemoteViewsService(
+ appWidgetId, intent);
}
@Override
public void updateAppWidgetIds(int[] appWidgetIds, RemoteViews views) throws RemoteException {
- getImplForUser().updateAppWidgetIds(appWidgetIds, views);
+ getImplForUser(UserHandle.getCallingUserId()).updateAppWidgetIds(appWidgetIds, views);
}
@Override
public void updateAppWidgetProvider(ComponentName provider, RemoteViews views)
throws RemoteException {
- getImplForUser().updateAppWidgetProvider(provider, views);
+ getImplForUser(UserHandle.getCallingUserId()).updateAppWidgetProvider(provider, views);
}
@Override
@@ -349,16 +371,29 @@
String action = intent.getAction();
// Slog.d(TAG, "received " + action);
if (Intent.ACTION_BOOT_COMPLETED.equals(action)) {
- getImplForUser().sendInitialBroadcasts();
+ int userId = intent.getIntExtra(Intent.EXTRA_USER_HANDLE, -1);
+ if (userId >= 0) {
+ getImplForUser(userId).sendInitialBroadcasts();
+ } else {
+ Slog.w(TAG, "Not user handle supplied in " + intent);
+ }
} else if (Intent.ACTION_CONFIGURATION_CHANGED.equals(action)) {
for (int i = 0; i < mAppWidgetServices.size(); i++) {
AppWidgetServiceImpl service = mAppWidgetServices.valueAt(i);
service.onConfigurationChanged();
}
} else {
- for (int i = 0; i < mAppWidgetServices.size(); i++) {
- AppWidgetServiceImpl service = mAppWidgetServices.valueAt(i);
- service.onBroadcastReceived(intent);
+ int sendingUser = getSendingUserId();
+ if (sendingUser == UserHandle.USER_ALL) {
+ for (int i = 0; i < mAppWidgetServices.size(); i++) {
+ AppWidgetServiceImpl service = mAppWidgetServices.valueAt(i);
+ service.onBroadcastReceived(intent);
+ }
+ } else {
+ AppWidgetServiceImpl service = mAppWidgetServices.get(sendingUser);
+ if (service != null) {
+ service.onBroadcastReceived(intent);
+ }
}
}
}
diff --git a/services/java/com/android/server/AppWidgetServiceImpl.java b/services/java/com/android/server/AppWidgetServiceImpl.java
index 79dabee..57ab921 100644
--- a/services/java/com/android/server/AppWidgetServiceImpl.java
+++ b/services/java/com/android/server/AppWidgetServiceImpl.java
@@ -40,6 +40,7 @@
import android.net.Uri;
import android.os.Binder;
import android.os.Bundle;
+import android.os.Environment;
import android.os.IBinder;
import android.os.RemoteException;
import android.os.SystemClock;
@@ -1634,11 +1635,11 @@
}
static File getSettingsFile(int userId) {
- return new File("/data/system/users/" + userId + "/" + SETTINGS_FILENAME);
+ return new File(Environment.getUserSystemDirectory(userId), SETTINGS_FILENAME);
}
AtomicFile savedStateFile() {
- File dir = new File("/data/system/users/" + mUserId);
+ File dir = Environment.getUserSystemDirectory(mUserId);
File settingsFile = getSettingsFile(mUserId);
if (!settingsFile.exists() && mUserId == 0) {
if (!dir.exists()) {
diff --git a/services/java/com/android/server/BackupManagerService.java b/services/java/com/android/server/BackupManagerService.java
index 8be0ba8..955ea23 100644
--- a/services/java/com/android/server/BackupManagerService.java
+++ b/services/java/com/android/server/BackupManagerService.java
@@ -1663,8 +1663,7 @@
synchronized(mClearDataLock) {
mClearingData = true;
try {
- mActivityManager.clearApplicationUserData(packageName, observer,
- Binder.getOrigCallingUser());
+ mActivityManager.clearApplicationUserData(packageName, observer, 0);
} catch (RemoteException e) {
// can't happen because the activity manager is in this process
}
diff --git a/services/java/com/android/server/BatteryService.java b/services/java/com/android/server/BatteryService.java
index f0989e7..fe8529b 100644
--- a/services/java/com/android/server/BatteryService.java
+++ b/services/java/com/android/server/BatteryService.java
@@ -116,6 +116,7 @@
private int mLowBatteryWarningLevel;
private int mLowBatteryCloseWarningLevel;
+ private int mShutdownBatteryTemperature;
private int mPlugType;
private int mLastPlugType = -1; // Extra state so we can detect first run
@@ -138,6 +139,8 @@
com.android.internal.R.integer.config_lowBatteryWarningLevel);
mLowBatteryCloseWarningLevel = mContext.getResources().getInteger(
com.android.internal.R.integer.config_lowBatteryCloseWarningLevel);
+ mShutdownBatteryTemperature = mContext.getResources().getInteger(
+ com.android.internal.R.integer.config_shutdownBatteryTemperature);
mPowerSupplyObserver.startObserving("SUBSYSTEM=power_supply");
@@ -228,9 +231,11 @@
}
private final void shutdownIfOverTemp() {
- // shut down gracefully if temperature is too high (> 68.0C)
- // wait until the system has booted before attempting to display the shutdown dialog.
- if (mBatteryTemperature > 680 && ActivityManagerNative.isSystemReady()) {
+ // shut down gracefully if temperature is too high (> 68.0C by default)
+ // wait until the system has booted before attempting to display the
+ // shutdown dialog.
+ if (mBatteryTemperature > mShutdownBatteryTemperature
+ && ActivityManagerNative.isSystemReady()) {
Intent intent = new Intent(Intent.ACTION_REQUEST_SHUTDOWN);
intent.putExtra(Intent.EXTRA_KEY_CONFIRM, false);
intent.setFlags(Intent.FLAG_ACTIVITY_NEW_TASK);
@@ -259,7 +264,7 @@
} else {
mPlugType = BATTERY_PLUGGED_NONE;
}
-
+
// Let the battery stats keep track of the current level.
try {
mBatteryStats.setBatteryState(mBatteryStatus, mBatteryHealth,
@@ -268,7 +273,7 @@
} catch (RemoteException e) {
// Should never happen.
}
-
+
shutdownIfNoPower();
shutdownIfOverTemp();
diff --git a/services/java/com/android/server/ConnectivityService.java b/services/java/com/android/server/ConnectivityService.java
index 04991bb..cbbfda1 100644
--- a/services/java/com/android/server/ConnectivityService.java
+++ b/services/java/com/android/server/ConnectivityService.java
@@ -1852,8 +1852,13 @@
Intent intent = new Intent(ConnectivityManager.ACTION_DATA_ACTIVITY_CHANGE);
intent.putExtra(ConnectivityManager.EXTRA_DEVICE_TYPE, deviceType);
intent.putExtra(ConnectivityManager.EXTRA_IS_ACTIVE, active);
- mContext.sendOrderedBroadcastAsUser(intent, UserHandle.ALL,
- RECEIVE_DATA_ACTIVITY_CHANGE, null, null, 0, null, null);
+ final long ident = Binder.clearCallingIdentity();
+ try {
+ mContext.sendOrderedBroadcastAsUser(intent, UserHandle.ALL,
+ RECEIVE_DATA_ACTIVITY_CHANGE, null, null, 0, null, null);
+ } finally {
+ Binder.restoreCallingIdentity(ident);
+ }
}
/**
@@ -1927,7 +1932,12 @@
log("sendStickyBroadcast: action=" + intent.getAction());
}
- mContext.sendStickyBroadcastAsUser(intent, UserHandle.ALL);
+ final long ident = Binder.clearCallingIdentity();
+ try {
+ mContext.sendStickyBroadcastAsUser(intent, UserHandle.ALL);
+ } finally {
+ Binder.restoreCallingIdentity(ident);
+ }
}
}
@@ -2467,7 +2477,12 @@
* Connectivity events can happen before boot has completed ...
*/
intent.addFlags(Intent.FLAG_RECEIVER_REGISTERED_ONLY_BEFORE_BOOT);
- mContext.sendBroadcastAsUser(intent, UserHandle.ALL);
+ final long ident = Binder.clearCallingIdentity();
+ try {
+ mContext.sendBroadcastAsUser(intent, UserHandle.ALL);
+ } finally {
+ Binder.restoreCallingIdentity(ident);
+ }
}
// Caller must grab mDnsLock.
@@ -3112,7 +3127,12 @@
intent.addFlags(Intent.FLAG_RECEIVER_REPLACE_PENDING |
Intent.FLAG_RECEIVER_REGISTERED_ONLY_BEFORE_BOOT);
intent.putExtra(Proxy.EXTRA_PROXY_INFO, proxy);
- mContext.sendStickyBroadcastAsUser(intent, UserHandle.ALL);
+ final long ident = Binder.clearCallingIdentity();
+ try {
+ mContext.sendStickyBroadcastAsUser(intent, UserHandle.ALL);
+ } finally {
+ Binder.restoreCallingIdentity(ident);
+ }
}
private static class SettingsObserver extends ContentObserver {
diff --git a/services/java/com/android/server/DevicePolicyManagerService.java b/services/java/com/android/server/DevicePolicyManagerService.java
index 77b062c..61517b1 100644
--- a/services/java/com/android/server/DevicePolicyManagerService.java
+++ b/services/java/com/android/server/DevicePolicyManagerService.java
@@ -177,6 +177,9 @@
static final long DEF_PASSWORD_EXPIRATION_DATE = 0;
long passwordExpirationDate = DEF_PASSWORD_EXPIRATION_DATE;
+ static final int DEF_KEYGUARD_WIDGET_DISABLED = 0; // none
+ int disableKeyguardWidgets = DEF_KEYGUARD_WIDGET_DISABLED;
+
boolean encryptionRequested = false;
boolean disableCamera = false;
@@ -286,6 +289,11 @@
out.attribute(null, "value", Boolean.toString(disableCamera));
out.endTag(null, "disable-camera");
}
+ if (disableKeyguardWidgets != DEF_KEYGUARD_WIDGET_DISABLED) {
+ out.startTag(null, "disable-keyguard-widgets");
+ out.attribute(null, "value", Integer.toString(disableKeyguardWidgets));
+ out.endTag(null, "disable-keyguard-widgets");
+ }
}
void readFromXml(XmlPullParser parser)
@@ -2093,6 +2101,46 @@
}
}
+ /**
+ * Selectively disable keyguard widgets.
+ */
+ public void setKeyguardWidgetsDisabled(ComponentName who, int which) {
+ synchronized (this) {
+ if (who == null) {
+ throw new NullPointerException("ComponentName is null");
+ }
+ ActiveAdmin ap = getActiveAdminForCallerLocked(who,
+ DeviceAdminInfo.USES_POLICY_DISABLE_KEYGUARD_WIDGETS);
+ if ((ap.disableKeyguardWidgets & which) != which) {
+ ap.disableKeyguardWidgets |= which;
+ saveSettingsLocked();
+ }
+ syncDeviceCapabilitiesLocked();
+ }
+ }
+
+ /**
+ * Gets the disabled state for widgets in keyguard for the given admin,
+ * or the aggregate of all active admins if who is null.
+ */
+ public int getKeyguardWidgetsDisabled(ComponentName who) {
+ synchronized (this) {
+ if (who != null) {
+ ActiveAdmin admin = getActiveAdminUncheckedLocked(who);
+ return (admin != null) ? admin.disableKeyguardWidgets : 0;
+ }
+
+ // Determine whether or not keyguard widgets are disabled for any active admins.
+ final int N = mAdminList.size();
+ int which = 0;
+ for (int i = 0; i < N; i++) {
+ ActiveAdmin admin = mAdminList.get(i);
+ which |= admin.disableKeyguardWidgets;
+ }
+ return which;
+ }
+ }
+
@Override
protected void dump(FileDescriptor fd, PrintWriter pw, String[] args) {
if (mContext.checkCallingOrSelfPermission(android.Manifest.permission.DUMP)
diff --git a/services/java/com/android/server/InputMethodManagerService.java b/services/java/com/android/server/InputMethodManagerService.java
index 747cf0b..c685473 100644
--- a/services/java/com/android/server/InputMethodManagerService.java
+++ b/services/java/com/android/server/InputMethodManagerService.java
@@ -3579,7 +3579,7 @@
private static final String ATTR_IS_AUXILIARY = "isAuxiliary";
private final AtomicFile mAdditionalInputMethodSubtypeFile;
private final HashMap<String, InputMethodInfo> mMethodMap;
- private final HashMap<String, List<InputMethodSubtype>> mSubtypesMap =
+ private final HashMap<String, List<InputMethodSubtype>> mAdditionalSubtypesMap =
new HashMap<String, List<InputMethodSubtype>>();
public InputMethodFileManager(HashMap<String, InputMethodInfo> methodMap) {
if (methodMap == null) {
@@ -3595,18 +3595,19 @@
mAdditionalInputMethodSubtypeFile = new AtomicFile(subtypeFile);
if (!subtypeFile.exists()) {
// If "subtypes.xml" doesn't exist, create a blank file.
- writeAdditionalInputMethodSubtypes(mSubtypesMap, mAdditionalInputMethodSubtypeFile,
- methodMap);
+ writeAdditionalInputMethodSubtypes(
+ mAdditionalSubtypesMap, mAdditionalInputMethodSubtypeFile, methodMap);
} else {
- readAdditionalInputMethodSubtypes(mSubtypesMap, mAdditionalInputMethodSubtypeFile);
+ readAdditionalInputMethodSubtypes(
+ mAdditionalSubtypesMap, mAdditionalInputMethodSubtypeFile);
}
}
private void deleteAllInputMethodSubtypes(String imiId) {
synchronized (mMethodMap) {
- mSubtypesMap.remove(imiId);
- writeAdditionalInputMethodSubtypes(mSubtypesMap, mAdditionalInputMethodSubtypeFile,
- mMethodMap);
+ mAdditionalSubtypesMap.remove(imiId);
+ writeAdditionalInputMethodSubtypes(
+ mAdditionalSubtypesMap, mAdditionalInputMethodSubtypeFile, mMethodMap);
}
}
@@ -3619,17 +3620,20 @@
final InputMethodSubtype subtype = additionalSubtypes[i];
if (!subtypes.contains(subtype)) {
subtypes.add(subtype);
+ } else {
+ Slog.w(TAG, "Duplicated subtype definition found: "
+ + subtype.getLocale() + ", " + subtype.getMode());
}
}
- mSubtypesMap.put(imi.getId(), subtypes);
- writeAdditionalInputMethodSubtypes(mSubtypesMap, mAdditionalInputMethodSubtypeFile,
- mMethodMap);
+ mAdditionalSubtypesMap.put(imi.getId(), subtypes);
+ writeAdditionalInputMethodSubtypes(
+ mAdditionalSubtypesMap, mAdditionalInputMethodSubtypeFile, mMethodMap);
}
}
public HashMap<String, List<InputMethodSubtype>> getAllAdditionalInputMethodSubtypes() {
synchronized (mMethodMap) {
- return mSubtypesMap;
+ return mAdditionalSubtypesMap;
}
}
diff --git a/services/java/com/android/server/LocationManagerService.java b/services/java/com/android/server/LocationManagerService.java
index 5993f32..37dae35 100644
--- a/services/java/com/android/server/LocationManagerService.java
+++ b/services/java/com/android/server/LocationManagerService.java
@@ -26,6 +26,7 @@
import android.content.pm.PackageManager;
import android.content.pm.PackageManager.NameNotFoundException;
import android.content.res.Resources;
+import android.database.ContentObserver;
import android.database.Cursor;
import android.location.Address;
import android.location.Criteria;
@@ -87,7 +88,7 @@
* The service class that manages LocationProviders and issues location
* updates and alerts.
*/
-public class LocationManagerService extends ILocationManager.Stub implements Observer, Runnable {
+public class LocationManagerService extends ILocationManager.Stub implements Runnable {
private static final String TAG = "LocationManagerService";
public static final boolean D = false;
@@ -207,24 +208,30 @@
mWakeLock = powerManager.newWakeLock(PowerManager.PARTIAL_WAKE_LOCK, WAKELOCK_KEY);
mPackageManager = mContext.getPackageManager();
+ mBlacklist = new LocationBlacklist(mContext, mLocationHandler);
+ mBlacklist.init();
+ mLocationFudger = new LocationFudger();
+
synchronized (mLock) {
loadProvidersLocked();
}
- mBlacklist = new LocationBlacklist(mContext, mLocationHandler);
- mBlacklist.init();
+
mGeofenceManager = new GeofenceManager(mContext, mBlacklist);
- mLocationFudger = new LocationFudger();
// listen for settings changes
- ContentResolver resolver = mContext.getContentResolver();
- Cursor settingsCursor = resolver.query(Settings.Secure.CONTENT_URI, null,
- "(" + NameValueTable.NAME + "=?)",
- new String[]{Settings.Secure.LOCATION_PROVIDERS_ALLOWED}, null);
- ContentQueryMap query = new ContentQueryMap(settingsCursor, NameValueTable.NAME, true,
- mLocationHandler);
- settingsCursor.close();
- query.addObserver(this);
+ mContext.getContentResolver().registerContentObserver(
+ Settings.Secure.getUriFor(Settings.Secure.LOCATION_PROVIDERS_ALLOWED), true,
+ new ContentObserver(mLocationHandler) {
+ @Override
+ public void onChange(boolean selfChange) {
+ synchronized (mLock) {
+ updateProvidersLocked();
+ }
+ }
+ });
mPackageMonitor.register(mContext, Looper.myLooper(), true);
+
+ updateProvidersLocked();
}
private void loadProvidersLocked() {
@@ -299,8 +306,6 @@
if (mGeocodeProvider == null) {
Slog.e(TAG, "no geocoder provider found");
}
-
- updateProvidersLocked();
}
/**
@@ -544,14 +549,6 @@
}
}
- /** Settings Observer callback */
- @Override
- public void update(Observable o, Object arg) {
- synchronized (mLock) {
- updateProvidersLocked();
- }
- }
-
private void addProviderLocked(LocationProviderInterface provider) {
mProviders.add(provider);
mProvidersByName.put(provider.getName(), provider);
diff --git a/services/java/com/android/server/NotificationManagerService.java b/services/java/com/android/server/NotificationManagerService.java
index e45cee3..3caba1f 100755
--- a/services/java/com/android/server/NotificationManagerService.java
+++ b/services/java/com/android/server/NotificationManagerService.java
@@ -20,6 +20,7 @@
import static org.xmlpull.v1.XmlPullParser.END_TAG;
import static org.xmlpull.v1.XmlPullParser.START_TAG;
+import android.app.ActivityManager;
import android.app.ActivityManagerNative;
import android.app.AppGlobals;
import android.app.IActivityManager;
@@ -150,8 +151,6 @@
private AtomicFile mPolicyFile;
private HashSet<String> mBlockedPackages = new HashSet<String>();
- private IDreamManager mSandman;
-
private static final int DB_VERSION = 1;
private static final String TAG_BODY = "notification-policy";
@@ -318,17 +317,20 @@
final int id;
final int uid;
final int initialPid;
+ final int userId;
final Notification notification;
final int score;
IBinder statusBarKey;
- NotificationRecord(String pkg, String tag, int id, int uid, int initialPid, int score, Notification notification)
+ NotificationRecord(String pkg, String tag, int id, int uid, int initialPid,
+ int userId, int score, Notification notification)
{
this.pkg = pkg;
this.tag = tag;
this.id = id;
this.uid = uid;
this.initialPid = initialPid;
+ this.userId = userId;
this.score = score;
this.notification = notification;
}
@@ -343,7 +345,7 @@
pw.println(prefix + " deleteIntent=" + notification.deleteIntent);
pw.println(prefix + " tickerText=" + notification.tickerText);
pw.println(prefix + " contentView=" + notification.contentView);
- pw.println(prefix + " uid=" + uid);
+ pw.println(prefix + " uid=" + uid + " userId=" + userId);
pw.println(prefix + " defaults=0x" + Integer.toHexString(notification.defaults));
pw.println(prefix + " flags=0x" + Integer.toHexString(notification.flags));
pw.println(prefix + " sound=" + notification.sound);
@@ -430,18 +432,25 @@
}
public void onClearAll() {
- cancelAll();
+ // XXX to be totally correct, the caller should tell us which user
+ // this is for.
+ cancelAll(ActivityManager.getCurrentUser());
}
public void onNotificationClick(String pkg, String tag, int id) {
+ // XXX to be totally correct, the caller should tell us which user
+ // this is for.
cancelNotification(pkg, tag, id, Notification.FLAG_AUTO_CANCEL,
- Notification.FLAG_FOREGROUND_SERVICE, false);
+ Notification.FLAG_FOREGROUND_SERVICE, false,
+ ActivityManager.getCurrentUser());
}
public void onNotificationClear(String pkg, String tag, int id) {
+ // XXX to be totally correct, the caller should tell us which user
+ // this is for.
cancelNotification(pkg, tag, id, 0,
Notification.FLAG_ONGOING_EVENT | Notification.FLAG_FOREGROUND_SERVICE,
- true);
+ true, ActivityManager.getCurrentUser());
}
public void onPanelRevealed() {
@@ -480,7 +489,9 @@
int uid, int initialPid, String message) {
Slog.d(TAG, "onNotification error pkg=" + pkg + " tag=" + tag + " id=" + id
+ "; will crashApplication(uid=" + uid + ", pid=" + initialPid + ")");
- cancelNotification(pkg, tag, id, 0, 0, false);
+ // XXX to be totally correct, the caller should tell us which user
+ // this is for.
+ cancelNotification(pkg, tag, id, 0, 0, false, UserHandle.getUserId(uid));
long ident = Binder.clearCallingIdentity();
try {
ActivityManagerNative.getDefault().crashApplication(uid, initialPid, pkg,
@@ -532,7 +543,8 @@
}
if (pkgList != null && (pkgList.length > 0)) {
for (String pkgName : pkgList) {
- cancelAllNotificationsInt(pkgName, 0, 0, !queryRestart);
+ cancelAllNotificationsInt(pkgName, 0, 0, !queryRestart,
+ UserHandle.USER_ALL);
}
}
} else if (action.equals(Intent.ACTION_SCREEN_ON)) {
@@ -548,7 +560,7 @@
} else if (action.equals(Intent.ACTION_USER_STOPPED)) {
int userHandle = intent.getIntExtra(Intent.EXTRA_USER_HANDLE, -1);
if (userHandle >= 0) {
- cancelAllNotificationsUser(userHandle);
+ cancelAllNotificationsInt(null, 0, 0, true, userHandle);
}
} else if (action.equals(Intent.ACTION_USER_PRESENT)) {
// turn off LED when user passes through lock screen
@@ -644,8 +656,6 @@
void systemReady() {
mAudioService = IAudioService.Stub.asInterface(
ServiceManager.getService(Context.AUDIO_SERVICE));
- mSandman = IDreamManager.Stub.asInterface(
- ServiceManager.getService("dreams"));
// no beeping until we're basically done booting
mSystemReady = true;
@@ -856,17 +866,11 @@
// Notifications
// ============================================================================
- @Deprecated
- public void enqueueNotification(String pkg, int id, Notification notification, int[] idOut)
- {
- enqueueNotificationWithTag(pkg, null /* tag */, id, notification, idOut);
- }
-
public void enqueueNotificationWithTag(String pkg, String tag, int id, Notification notification,
- int[] idOut)
+ int[] idOut, int userId)
{
enqueueNotificationInternal(pkg, Binder.getCallingUid(), Binder.getCallingPid(),
- tag, id, notification, idOut);
+ tag, id, notification, idOut, userId);
}
private final static int clamp(int x, int low, int high) {
@@ -877,7 +881,7 @@
// Not exposed via Binder; for system use only (otherwise malicious apps could spoof the
// uid/pid of another application)
public void enqueueNotificationInternal(String pkg, int callingUid, int callingPid,
- String tag, int id, Notification notification, int[] idOut)
+ String tag, int id, Notification notification, int[] idOut, int userId)
{
if (DBG) {
Slog.v(TAG, "enqueueNotificationInternal: pkg=" + pkg + " id=" + id + " notification=" + notification);
@@ -885,6 +889,9 @@
checkCallerIsSystemOrSameApp(pkg);
final boolean isSystemNotification = ("android".equals(pkg));
+ userId = ActivityManager.handleIncomingUser(callingPid,
+ callingUid, userId, false, true, "enqueueNotification", pkg);
+
// Limit the number of notifications that any given package except the android
// package can enqueue. Prevents DOS attacks and deals with leaks.
if (!isSystemNotification) {
@@ -959,12 +966,12 @@
synchronized (mNotificationList) {
NotificationRecord r = new NotificationRecord(pkg, tag, id,
- callingUid, callingPid,
+ callingUid, callingPid, userId,
score,
notification);
NotificationRecord old = null;
- int index = indexOfNotificationLocked(pkg, tag, id);
+ int index = indexOfNotificationLocked(pkg, tag, id, userId);
if (index < 0) {
mNotificationList.add(r);
} else {
@@ -984,16 +991,6 @@
| Notification.FLAG_NO_CLEAR;
}
- // Stop screensaver if the notification has a full-screen intent.
- // (like an incoming phone call)
- if (notification.fullScreenIntent != null && mSandman != null) {
- try {
- mSandman.awaken();
- } catch (RemoteException e) {
- // noop
- }
- }
-
if (notification.icon != 0) {
StatusBarNotification n = new StatusBarNotification(pkg, id, tag,
r.uid, r.initialPid, score, notification);
@@ -1036,6 +1033,7 @@
if (((mDisabledNotifications & StatusBarManager.DISABLE_NOTIFICATION_ALERTS) == 0)
&& (!(old != null
&& (notification.flags & Notification.FLAG_ONLY_ALERT_ONCE) != 0 ))
+ && (r.userId == UserHandle.USER_ALL || r.userId == userId)
&& mSystemReady) {
final AudioManager audioManager = (AudioManager) mContext
@@ -1195,12 +1193,12 @@
* and none of the {@code mustNotHaveFlags}.
*/
private void cancelNotification(String pkg, String tag, int id, int mustHaveFlags,
- int mustNotHaveFlags, boolean sendDelete) {
+ int mustNotHaveFlags, boolean sendDelete, int userId) {
EventLog.writeEvent(EventLogTags.NOTIFICATION_CANCEL, pkg, id, tag,
mustHaveFlags, mustNotHaveFlags);
synchronized (mNotificationList) {
- int index = indexOfNotificationLocked(pkg, tag, id);
+ int index = indexOfNotificationLocked(pkg, tag, id, userId);
if (index >= 0) {
NotificationRecord r = mNotificationList.get(index);
@@ -1224,7 +1222,7 @@
* {@code mustHaveFlags}.
*/
boolean cancelAllNotificationsInt(String pkg, int mustHaveFlags,
- int mustNotHaveFlags, boolean doit) {
+ int mustNotHaveFlags, boolean doit, int userId) {
EventLog.writeEvent(EventLogTags.NOTIFICATION_CANCEL_ALL, pkg, mustHaveFlags,
mustNotHaveFlags);
@@ -1233,6 +1231,9 @@
boolean canceledSomething = false;
for (int i = N-1; i >= 0; --i) {
NotificationRecord r = mNotificationList.get(i);
+ if (userId != UserHandle.USER_ALL && r.userId != userId) {
+ continue;
+ }
if ((r.notification.flags & mustHaveFlags) != mustHaveFlags) {
continue;
}
@@ -1256,48 +1257,25 @@
}
}
- /**
- * Cancels all notifications from a given user.
- */
- boolean cancelAllNotificationsUser(int userHandle) {
- synchronized (mNotificationList) {
- final int N = mNotificationList.size();
- boolean canceledSomething = false;
- for (int i = N-1; i >= 0; --i) {
- NotificationRecord r = mNotificationList.get(i);
- if (UserHandle.getUserId(r.uid) != userHandle) {
- continue;
- }
- canceledSomething = true;
- mNotificationList.remove(i);
- cancelNotificationLocked(r, false);
- }
- if (canceledSomething) {
- updateLightsLocked();
- }
- return canceledSomething;
- }
- }
-
- @Deprecated
- public void cancelNotification(String pkg, int id) {
- cancelNotificationWithTag(pkg, null /* tag */, id);
- }
-
- public void cancelNotificationWithTag(String pkg, String tag, int id) {
+ public void cancelNotificationWithTag(String pkg, String tag, int id, int userId) {
checkCallerIsSystemOrSameApp(pkg);
+ userId = ActivityManager.handleIncomingUser(Binder.getCallingPid(),
+ Binder.getCallingUid(), userId, false, true, "cancelNotificationWithTag", pkg);
// Don't allow client applications to cancel foreground service notis.
cancelNotification(pkg, tag, id, 0,
Binder.getCallingUid() == Process.SYSTEM_UID
- ? 0 : Notification.FLAG_FOREGROUND_SERVICE, false);
+ ? 0 : Notification.FLAG_FOREGROUND_SERVICE, false, userId);
}
- public void cancelAllNotifications(String pkg) {
+ public void cancelAllNotifications(String pkg, int userId) {
checkCallerIsSystemOrSameApp(pkg);
+ userId = ActivityManager.handleIncomingUser(Binder.getCallingPid(),
+ Binder.getCallingUid(), userId, true, true, "cancelAllNotifications", pkg);
+
// Calling from user space, don't allow the canceling of actively
// running foreground services.
- cancelAllNotificationsInt(pkg, 0, Notification.FLAG_FOREGROUND_SERVICE, true);
+ cancelAllNotificationsInt(pkg, 0, Notification.FLAG_FOREGROUND_SERVICE, true, userId);
}
void checkCallerIsSystem() {
@@ -1325,12 +1303,16 @@
}
}
- void cancelAll() {
+ void cancelAll(int userId) {
synchronized (mNotificationList) {
final int N = mNotificationList.size();
for (int i=N-1; i>=0; i--) {
NotificationRecord r = mNotificationList.get(i);
+ if (r.userId != userId) {
+ continue;
+ }
+
if ((r.notification.flags & (Notification.FLAG_ONGOING_EVENT
| Notification.FLAG_NO_CLEAR)) == 0) {
mNotificationList.remove(i);
@@ -1375,12 +1357,15 @@
}
// lock on mNotificationList
- private int indexOfNotificationLocked(String pkg, String tag, int id)
+ private int indexOfNotificationLocked(String pkg, String tag, int id, int userId)
{
ArrayList<NotificationRecord> list = mNotificationList;
final int len = list.size();
for (int i=0; i<len; i++) {
NotificationRecord r = list.get(i);
+ if (r.userId != userId || r.id != id) {
+ continue;
+ }
if (tag == null) {
if (r.tag != null) {
continue;
@@ -1390,7 +1375,7 @@
continue;
}
}
- if (r.id == id && r.pkg.equals(pkg)) {
+ if (r.pkg.equals(pkg)) {
return i;
}
}
diff --git a/services/java/com/android/server/StatusBarManagerService.java b/services/java/com/android/server/StatusBarManagerService.java
index 78c0c12..9f53fad 100644
--- a/services/java/com/android/server/StatusBarManagerService.java
+++ b/services/java/com/android/server/StatusBarManagerService.java
@@ -27,7 +27,6 @@
import android.os.IBinder;
import android.os.RemoteException;
import android.util.Slog;
-import android.view.View;
import com.android.internal.statusbar.IStatusBar;
import com.android.internal.statusbar.IStatusBarService;
diff --git a/services/java/com/android/server/SystemBackupAgent.java b/services/java/com/android/server/SystemBackupAgent.java
index a7a583c..8cf273d 100644
--- a/services/java/com/android/server/SystemBackupAgent.java
+++ b/services/java/com/android/server/SystemBackupAgent.java
@@ -24,8 +24,10 @@
import android.app.backup.FullBackupDataOutput;
import android.app.backup.WallpaperBackupHelper;
import android.content.Context;
+import android.os.Environment;
import android.os.ParcelFileDescriptor;
import android.os.ServiceManager;
+import android.os.UserHandle;
import android.util.Slog;
@@ -45,11 +47,13 @@
private static final String WALLPAPER_INFO_FILENAME = "wallpaper_info.xml";
// TODO: Will need to change if backing up non-primary user's wallpaper
- private static final String WALLPAPER_IMAGE_DIR = "/data/system/users/0";
+ private static final String WALLPAPER_IMAGE_DIR =
+ Environment.getUserSystemDirectory(UserHandle.USER_OWNER).getAbsolutePath();
private static final String WALLPAPER_IMAGE = WallpaperBackupHelper.WALLPAPER_IMAGE;
// TODO: Will need to change if backing up non-primary user's wallpaper
- private static final String WALLPAPER_INFO_DIR = "/data/system/users/0";
+ private static final String WALLPAPER_INFO_DIR =
+ Environment.getUserSystemDirectory(UserHandle.USER_OWNER).getAbsolutePath();
private static final String WALLPAPER_INFO = WallpaperBackupHelper.WALLPAPER_INFO;
// Use old keys to keep legacy data compatibility and avoid writing two wallpapers
private static final String WALLPAPER_IMAGE_KEY = WallpaperBackupHelper.WALLPAPER_IMAGE_KEY;
diff --git a/services/java/com/android/server/SystemServer.java b/services/java/com/android/server/SystemServer.java
index 48b1215..1aad9b3 100644
--- a/services/java/com/android/server/SystemServer.java
+++ b/services/java/com/android/server/SystemServer.java
@@ -207,7 +207,7 @@
context = ActivityManagerService.main(factoryTest);
Slog.i(TAG, "Display Manager");
- display = new DisplayManagerService(context, uiHandler);
+ display = new DisplayManagerService(context, wmHandler, uiHandler);
ServiceManager.addService(Context.DISPLAY_SERVICE, display, true);
Slog.i(TAG, "Telephony Registry");
@@ -303,6 +303,7 @@
ServiceManager.addService(Context.INPUT_SERVICE, inputManager);
ActivityManagerService.self().setWindowManager(wm);
+ display.setWindowManager(wm);
// Skip Bluetooth if we have an emulator kernel
// TODO: Use a more reliable check to see if this product should
diff --git a/services/java/com/android/server/UiModeManagerService.java b/services/java/com/android/server/UiModeManagerService.java
index 4900e00..f618263 100644
--- a/services/java/com/android/server/UiModeManagerService.java
+++ b/services/java/com/android/server/UiModeManagerService.java
@@ -165,7 +165,7 @@
try {
ActivityManagerNative.getDefault().startActivityWithConfig(
null, homeIntent, null, null, null, 0, 0,
- newConfig, null);
+ newConfig, null, UserHandle.USER_CURRENT);
mHoldingConfiguration = false;
} catch (RemoteException e) {
Slog.w(TAG, e.getCause());
diff --git a/services/java/com/android/server/WallpaperManagerService.java b/services/java/com/android/server/WallpaperManagerService.java
index 643e937..a807f4c 100644
--- a/services/java/com/android/server/WallpaperManagerService.java
+++ b/services/java/com/android/server/WallpaperManagerService.java
@@ -92,8 +92,6 @@
* restarting it vs. just reverting to the static wallpaper.
*/
static final long MIN_WALLPAPER_CRASH_TIME = 10000;
-
- static final File WALLPAPER_BASE_DIR = new File("/data/system/users");
static final String WALLPAPER = "wallpaper";
static final String WALLPAPER_INFO = "wallpaper_info.xml";
@@ -395,12 +393,12 @@
mIPackageManager = AppGlobals.getPackageManager();
mMonitor = new MyPackageMonitor();
mMonitor.register(context, null, true);
- WALLPAPER_BASE_DIR.mkdirs();
- loadSettingsLocked(0);
+ getWallpaperDir(UserHandle.USER_OWNER).mkdirs();
+ loadSettingsLocked(UserHandle.USER_OWNER);
}
private static File getWallpaperDir(int userId) {
- return new File(WALLPAPER_BASE_DIR + "/" + userId);
+ return Environment.getUserSystemDirectory(userId);
}
@Override
@@ -414,7 +412,7 @@
public void systemReady() {
if (DEBUG) Slog.v(TAG, "systemReady");
- WallpaperData wallpaper = mWallpaperMap.get(0);
+ WallpaperData wallpaper = mWallpaperMap.get(UserHandle.USER_OWNER);
switchWallpaper(wallpaper);
wallpaper.wallpaperObserver = new WallpaperObserver(wallpaper);
wallpaper.wallpaperObserver.startWatching();
@@ -776,11 +774,11 @@
intent.setComponent(componentName);
intent.putExtra(Intent.EXTRA_CLIENT_LABEL,
com.android.internal.R.string.wallpaper_binding_label);
- intent.putExtra(Intent.EXTRA_CLIENT_INTENT, PendingIntent.getActivity(
+ intent.putExtra(Intent.EXTRA_CLIENT_INTENT, PendingIntent.getActivityAsUser(
mContext, 0,
Intent.createChooser(new Intent(Intent.ACTION_SET_WALLPAPER),
mContext.getText(com.android.internal.R.string.chooser_wallpaper)),
- 0));
+ 0, null, new UserHandle(serviceUserId)));
if (!mContext.bindService(intent, newConn, Context.BIND_AUTO_CREATE, serviceUserId)) {
String msg = "Unable to bind service: "
+ componentName;
@@ -880,7 +878,7 @@
}
private static JournaledFile makeJournaledFile(int userId) {
- final String base = getWallpaperDir(userId) + "/" + WALLPAPER_INFO;
+ final String base = new File(getWallpaperDir(userId), WALLPAPER_INFO).getAbsolutePath();
return new JournaledFile(new File(base), new File(base + ".tmp"));
}
diff --git a/services/java/com/android/server/Watchdog.java b/services/java/com/android/server/Watchdog.java
index 2c5038e..9edfad6 100644
--- a/services/java/com/android/server/Watchdog.java
+++ b/services/java/com/android/server/Watchdog.java
@@ -26,7 +26,6 @@
import android.content.Context;
import android.content.Intent;
import android.content.IntentFilter;
-import android.os.Build;
import android.os.Debug;
import android.os.Handler;
import android.os.Message;
@@ -40,8 +39,6 @@
import android.util.Slog;
import java.io.File;
-import java.io.FileWriter;
-import java.io.IOException;
import java.util.ArrayList;
import java.util.Calendar;
@@ -432,10 +429,11 @@
}
// If we got here, that means that the system is most likely hung.
+ // First collect stack traces from all threads of the system process.
+ // Then kill this process so that the system will restart.
final String name = (mCurrentMonitor != null) ?
mCurrentMonitor.getClass().getName() : "null";
- Slog.w(TAG, "WATCHDOG PROBLEM IN SYSTEM SERVER: " + name);
EventLog.writeEvent(EventLogTags.WATCHDOG, name);
ArrayList<Integer> pids = new ArrayList<Integer>();
@@ -470,15 +468,11 @@
dropboxThread.join(2000); // wait up to 2 seconds for it to return.
} catch (InterruptedException ignored) {}
- // Only kill/crash the process if the debugger is not attached.
+ // Only kill the process if the debugger is not attached.
if (!Debug.isDebuggerConnected()) {
Slog.w(TAG, "*** WATCHDOG KILLING SYSTEM PROCESS: " + name);
- if (!Build.TYPE.equals("user")) {
- forceCrashDump();
- } else {
- Process.killProcess(Process.myPid());
- System.exit(10);
- }
+ Process.killProcess(Process.myPid());
+ System.exit(10);
} else {
Slog.w(TAG, "Debugger connected: Watchdog is *not* killing the system process");
}
@@ -487,50 +481,6 @@
}
}
- private void forceCrashDump() {
- /* Sync file system to flash the data which is written just before the
- * crash.
- */
- java.lang.Process p = null;
- try {
- p = Runtime.getRuntime().exec("sync");
- if (p != null) {
- // It is not necessary to check the exit code, here.
- // 'sync' command always succeeds, and this function returns 0.
- p.waitFor();
- } else {
- Slog.e(TAG, "Failed to execute 'sync' command. (no process handle)");
- }
- } catch (Exception e) {
- // This code is an emergency path to crash MUT. The system already
- // caused fatal error, and then calls this function to create a
- // crash dump. This function must run the code below to force a
- // crash, even if the sync command failed.
- // Therefore, ignore all exceptions, here.
- Slog.e(TAG, "Failed to execute 'sync' command prior to forcing crash: " + e);
- } finally {
- if (p != null) {
- p.destroy();
- }
- }
-
- FileWriter out = null;
- try {
- out = new FileWriter("/proc/sysrq-trigger");
- out.write("c");
- } catch (IOException e) {
- Slog.e(TAG, "Failed to write to sysrq-trigger while triggering crash: " + e);
- } finally {
- if (out != null) {
- try {
- out.close();
- } catch (IOException e) {
- Slog.e(TAG, "Failed to close sysrq-trigger while triggering crash: " + e);
- }
- }
- }
- }
-
private File dumpKernelStackTraces() {
String tracesPath = SystemProperties.get("dalvik.vm.stack-trace-file", null);
if (tracesPath == null || tracesPath.length() == 0) {
diff --git a/services/java/com/android/server/am/ActiveServices.java b/services/java/com/android/server/am/ActiveServices.java
index e836e77..6a3010b 100644
--- a/services/java/com/android/server/am/ActiveServices.java
+++ b/services/java/com/android/server/am/ActiveServices.java
@@ -323,7 +323,7 @@
int startId) {
if (DEBUG_SERVICE) Slog.v(TAG, "stopServiceToken: " + className
+ " " + token + " startId=" + startId);
- ServiceRecord r = findServiceLocked(className, token);
+ ServiceRecord r = findServiceLocked(className, token, UserHandle.getCallingUserId());
if (r != null) {
if (startId >= 0) {
// Asked to only stop if done with all work. Note that
@@ -366,9 +366,10 @@
public void setServiceForegroundLocked(ComponentName className, IBinder token,
int id, Notification notification, boolean removeNotification) {
+ final int userId = UserHandle.getCallingUserId();
final long origId = Binder.clearCallingIdentity();
try {
- ServiceRecord r = findServiceLocked(className, token);
+ ServiceRecord r = findServiceLocked(className, token, userId);
if (r != null) {
if (id != 0) {
if (notification == null) {
@@ -427,9 +428,6 @@
if (DEBUG_SERVICE) Slog.v(TAG, "bindService: " + service
+ " type=" + resolvedType + " conn=" + connection.asBinder()
+ " flags=0x" + Integer.toHexString(flags));
- if (DEBUG_MU)
- Slog.i(TAG_MU, "bindService uid=" + Binder.getCallingUid() + " origUid="
- + Binder.getOrigCallingUid());
final ProcessRecord callerApp = mAm.getRecordForAppLocked(caller);
if (callerApp == null) {
throw new SecurityException(
@@ -677,8 +675,8 @@
}
private final ServiceRecord findServiceLocked(ComponentName name,
- IBinder token) {
- ServiceRecord r = mServiceMap.getServiceByName(name, Binder.getOrigCallingUser());
+ IBinder token, int userId) {
+ ServiceRecord r = mServiceMap.getServiceByName(name, userId);
return r == token ? r : null;
}
@@ -1424,9 +1422,6 @@
r.callStart = false;
}
}
- if (DEBUG_MU)
- Slog.v(TAG_MU, "before serviceDontExecutingLocked, uid="
- + Binder.getOrigCallingUid());
final long origId = Binder.clearCallingIdentity();
serviceDoneExecutingLocked(r, inStopping);
Binder.restoreCallingIdentity(origId);
diff --git a/services/java/com/android/server/am/ActivityManagerService.java b/services/java/com/android/server/am/ActivityManagerService.java
index 6c87252..614b93a 100644
--- a/services/java/com/android/server/am/ActivityManagerService.java
+++ b/services/java/com/android/server/am/ActivityManagerService.java
@@ -116,6 +116,7 @@
import android.provider.Settings;
import android.text.format.Time;
import android.util.EventLog;
+import android.util.LocaleUtil;
import android.util.Log;
import android.util.Pair;
import android.util.PrintWriterPrinter;
@@ -153,7 +154,6 @@
import java.util.List;
import java.util.Locale;
import java.util.Map;
-import java.util.Map.Entry;
import java.util.Set;
import java.util.concurrent.atomic.AtomicBoolean;
import java.util.concurrent.atomic.AtomicLong;
@@ -495,7 +495,8 @@
@Override
protected BroadcastFilter newResult(BroadcastFilter filter, int match, int userId) {
- if (userId == UserHandle.USER_ALL || userId == filter.owningUserId) {
+ if (userId == UserHandle.USER_ALL || filter.owningUserId == UserHandle.USER_ALL
+ || userId == filter.owningUserId) {
return super.newResult(filter, match, userId);
}
return null;
@@ -1129,13 +1130,15 @@
notification.vibrate = null;
notification.setLatestEventInfo(context, text,
mContext.getText(R.string.heavy_weight_notification_detail),
- PendingIntent.getActivity(mContext, 0, root.intent,
- PendingIntent.FLAG_CANCEL_CURRENT));
+ PendingIntent.getActivityAsUser(mContext, 0, root.intent,
+ PendingIntent.FLAG_CANCEL_CURRENT, null,
+ new UserHandle(root.userId)));
try {
int[] outId = new int[1];
- inm.enqueueNotification("android", R.string.heavy_weight_notification,
- notification, outId);
+ inm.enqueueNotificationWithTag("android", null,
+ R.string.heavy_weight_notification,
+ notification, outId, root.userId);
} catch (RuntimeException e) {
Slog.w(ActivityManagerService.TAG,
"Error showing notification for heavy-weight app", e);
@@ -1151,8 +1154,8 @@
return;
}
try {
- inm.cancelNotification("android",
- R.string.heavy_weight_notification);
+ inm.cancelNotificationWithTag("android", null,
+ R.string.heavy_weight_notification, msg.arg1);
} catch (RuntimeException e) {
Slog.w(ActivityManagerService.TAG,
"Error canceling notification for service", e);
@@ -1528,7 +1531,8 @@
ConfigurationInfo.GL_ES_VERSION_UNDEFINED);
mConfiguration.setToDefaults();
- mConfiguration.locale = Locale.getDefault();
+ mConfiguration.setLocale(Locale.getDefault());
+
mConfigurationSeq = mConfiguration.seq = 1;
mProcessStats.init();
@@ -2371,14 +2375,6 @@
// Requesting home, set the identity to the current user
// HACK!
userId = mCurrentUserId;
- } else {
- // TODO: Fix this in a better way - calls coming from SystemUI should probably carry
- // the current user's userId
- if (Binder.getCallingUid() < Process.FIRST_APPLICATION_UID) {
- userId = 0;
- } else {
- userId = Binder.getOrigCallingUser();
- }
}
}
return mMainStack.startActivityMayWait(caller, -1, intent, resolvedType,
@@ -2392,21 +2388,22 @@
ParcelFileDescriptor profileFd, Bundle options) {
enforceNotIsolatedCaller("startActivityAndWait");
WaitResult res = new WaitResult();
- int userId = Binder.getOrigCallingUser();
mMainStack.startActivityMayWait(caller, -1, intent, resolvedType,
resultTo, resultWho, requestCode, startFlags, profileFile, profileFd,
- res, null, options, userId);
+ res, null, options, UserHandle.getCallingUserId());
return res;
}
public final int startActivityWithConfig(IApplicationThread caller,
Intent intent, String resolvedType, IBinder resultTo,
String resultWho, int requestCode, int startFlags, Configuration config,
- Bundle options) {
+ Bundle options, int userId) {
enforceNotIsolatedCaller("startActivityWithConfig");
+ userId = handleIncomingUserLocked(Binder.getCallingPid(), Binder.getCallingUid(), userId,
+ false, true, "startActivityWithConfig", null);
int ret = mMainStack.startActivityMayWait(caller, -1, intent, resolvedType,
resultTo, resultWho, requestCode, startFlags,
- null, null, null, config, options, Binder.getOrigCallingUser());
+ null, null, null, config, options, userId);
return ret;
}
@@ -2537,21 +2534,16 @@
}
}
- public final int startActivityInPackage(int uid,
+ final int startActivityInPackage(int uid,
Intent intent, String resolvedType, IBinder resultTo,
- String resultWho, int requestCode, int startFlags, Bundle options) {
-
- // This is so super not safe, that only the system (or okay root)
- // can do it.
- final int callingUid = Binder.getCallingUid();
- if (callingUid != 0 && callingUid != Process.myUid()) {
- throw new SecurityException(
- "startActivityInPackage only available to the system");
- }
+ String resultWho, int requestCode, int startFlags, Bundle options, int userId) {
+
+ userId = handleIncomingUserLocked(Binder.getCallingPid(), Binder.getCallingUid(), userId,
+ false, true, "startActivityInPackage", null);
int ret = mMainStack.startActivityMayWait(null, uid, intent, resolvedType,
resultTo, resultWho, requestCode, startFlags,
- null, null, null, null, options, UserHandle.getUserId(uid));
+ null, null, null, null, options, userId);
return ret;
}
@@ -2559,23 +2551,18 @@
Intent[] intents, String[] resolvedTypes, IBinder resultTo, Bundle options) {
enforceNotIsolatedCaller("startActivities");
int ret = mMainStack.startActivities(caller, -1, intents, resolvedTypes, resultTo,
- options, Binder.getOrigCallingUser());
+ options, UserHandle.getCallingUserId());
return ret;
}
- public final int startActivitiesInPackage(int uid,
+ final int startActivitiesInPackage(int uid,
Intent[] intents, String[] resolvedTypes, IBinder resultTo,
- Bundle options) {
+ Bundle options, int userId) {
- // This is so super not safe, that only the system (or okay root)
- // can do it.
- final int callingUid = Binder.getCallingUid();
- if (callingUid != 0 && callingUid != Process.myUid()) {
- throw new SecurityException(
- "startActivityInPackage only available to the system");
- }
+ userId = handleIncomingUserLocked(Binder.getCallingPid(), Binder.getCallingUid(), userId,
+ false, true, "startActivityInPackage", null);
int ret = mMainStack.startActivities(null, uid, intents, resolvedTypes, resultTo,
- options, UserHandle.getUserId(uid));
+ options, userId);
return ret;
}
@@ -2709,8 +2696,9 @@
}
}
+ mHandler.sendMessage(mHandler.obtainMessage(CANCEL_HEAVY_NOTIFICATION_MSG,
+ mHeavyWeightProcess.userId, 0));
mHeavyWeightProcess = null;
- mHandler.sendEmptyMessage(CANCEL_HEAVY_NOTIFICATION_MSG);
}
}
@@ -3584,15 +3572,32 @@
public void closeSystemDialogs(String reason) {
enforceNotIsolatedCaller("closeSystemDialogs");
+ final int pid = Binder.getCallingPid();
final int uid = Binder.getCallingUid();
final long origId = Binder.clearCallingIdentity();
- synchronized (this) {
- closeSystemDialogsLocked(uid, reason);
+ try {
+ synchronized (this) {
+ // Only allow this from foreground processes, so that background
+ // applications can't abuse it to prevent system UI from being shown.
+ if (uid >= Process.FIRST_APPLICATION_UID) {
+ ProcessRecord proc;
+ synchronized (mPidsSelfLocked) {
+ proc = mPidsSelfLocked.get(pid);
+ }
+ if (proc.curRawAdj > ProcessList.PERCEPTIBLE_APP_ADJ) {
+ Slog.w(TAG, "Ignoring closeSystemDialogs " + reason
+ + " from background process " + proc);
+ return;
+ }
+ }
+ closeSystemDialogsLocked(reason);
+ }
+ } finally {
+ Binder.restoreCallingIdentity(origId);
}
- Binder.restoreCallingIdentity(origId);
}
- void closeSystemDialogsLocked(int callingUid, String reason) {
+ void closeSystemDialogsLocked(String reason) {
Intent intent = new Intent(Intent.ACTION_CLOSE_SYSTEM_DIALOGS);
intent.addFlags(Intent.FLAG_RECEIVER_REGISTERED_ONLY);
if (reason != null) {
@@ -3608,14 +3613,9 @@
}
}
- final long origId = Binder.clearCallingIdentity();
- try {
- broadcastIntentLocked(null, null, intent, null,
- null, 0, null, null, null, false, false, -1,
- callingUid, UserHandle.USER_ALL);
- } finally {
- Binder.restoreCallingIdentity(origId);
- }
+ broadcastIntentLocked(null, null, intent, null,
+ null, 0, null, null, null, false, false, -1,
+ Process.SYSTEM_UID, UserHandle.USER_ALL);
}
public Debug.MemoryInfo[] getProcessMemoryInfo(int[] pids)
@@ -3884,8 +3884,9 @@
mProcessNames.remove(name, uid);
mIsolatedProcesses.remove(app.uid);
if (mHeavyWeightProcess == app) {
+ mHandler.sendMessage(mHandler.obtainMessage(CANCEL_HEAVY_NOTIFICATION_MSG,
+ mHeavyWeightProcess.userId, 0));
mHeavyWeightProcess = null;
- mHandler.sendEmptyMessage(CANCEL_HEAVY_NOTIFICATION_MSG);
}
boolean needRestart = false;
if (app.pid > 0 && app.pid != MY_PID) {
@@ -3931,8 +3932,9 @@
mProcessNames.remove(app.processName, app.uid);
mIsolatedProcesses.remove(app.uid);
if (mHeavyWeightProcess == app) {
+ mHandler.sendMessage(mHandler.obtainMessage(CANCEL_HEAVY_NOTIFICATION_MSG,
+ mHeavyWeightProcess.userId, 0));
mHeavyWeightProcess = null;
- mHandler.sendEmptyMessage(CANCEL_HEAVY_NOTIFICATION_MSG);
}
// Take care of any launching providers waiting for this process.
checkAppInLaunchingProvidersLocked(app, true);
@@ -4288,12 +4290,13 @@
UserStartedState uss = mStartedUsers.valueAt(i);
if (uss.mState == UserStartedState.STATE_BOOTING) {
uss.mState = UserStartedState.STATE_RUNNING;
- broadcastIntentLocked(null, null,
- new Intent(Intent.ACTION_BOOT_COMPLETED, null),
+ final int userId = mStartedUsers.keyAt(i);
+ Intent intent = new Intent(Intent.ACTION_BOOT_COMPLETED, null);
+ intent.putExtra(Intent.EXTRA_USER_HANDLE, userId);
+ broadcastIntentLocked(null, null, intent,
null, null, 0, null, null,
android.Manifest.permission.RECEIVE_BOOT_COMPLETED,
- false, false, MY_PID, Process.SYSTEM_UID,
- mStartedUsers.keyAt(i));
+ false, false, MY_PID, Process.SYSTEM_UID, userId);
}
}
}
@@ -4405,7 +4408,7 @@
public IIntentSender getIntentSender(int type,
String packageName, IBinder token, String resultWho,
int requestCode, Intent[] intents, String[] resolvedTypes,
- int flags, Bundle options) {
+ int flags, Bundle options, int userId) {
enforceNotIsolatedCaller("getIntentSender");
// Refuse possible leaked file descriptors
if (intents != null) {
@@ -4439,6 +4442,8 @@
synchronized(this) {
int callingUid = Binder.getCallingUid();
+ userId = handleIncomingUserLocked(Binder.getCallingPid(), callingUid, userId,
+ false, true, "getIntentSender", null);
try {
if (callingUid != 0 && callingUid != Process.SYSTEM_UID) {
int uid = AppGlobals.getPackageManager()
@@ -4453,11 +4458,8 @@
throw new SecurityException(msg);
}
}
-
- if (DEBUG_MU)
- Slog.i(TAG_MU, "Getting intent sender for origCallingUid="
- + Binder.getOrigCallingUid());
- return getIntentSenderLocked(type, packageName, Binder.getOrigCallingUid(),
+
+ return getIntentSenderLocked(type, packageName, callingUid, userId,
token, resultWho, requestCode, intents, resolvedTypes, flags, options);
} catch (RemoteException e) {
@@ -4466,8 +4468,8 @@
}
}
- IIntentSender getIntentSenderLocked(int type,
- String packageName, int callingUid, IBinder token, String resultWho,
+ IIntentSender getIntentSenderLocked(int type, String packageName,
+ int callingUid, int userId, IBinder token, String resultWho,
int requestCode, Intent[] intents, String[] resolvedTypes, int flags,
Bundle options) {
if (DEBUG_MU)
@@ -4491,8 +4493,7 @@
PendingIntentRecord.Key key = new PendingIntentRecord.Key(
type, packageName, activity, resultWho,
- requestCode, intents, resolvedTypes, flags, options,
- UserHandle.getUserId(callingUid));
+ requestCode, intents, resolvedTypes, flags, options, userId);
WeakReference<PendingIntentRecord> ref;
ref = mIntentSenderRecords.get(key);
PendingIntentRecord rec = ref != null ? ref.get() : null;
@@ -6233,7 +6234,7 @@
}
private final ContentProviderHolder getContentProviderImpl(IApplicationThread caller,
- String name, IBinder token, boolean stable) {
+ String name, IBinder token, boolean stable, int userId) {
ContentProviderRecord cpr;
ContentProviderConnection conn = null;
ProviderInfo cpi = null;
@@ -6248,10 +6249,13 @@
+ " (pid=" + Binder.getCallingPid()
+ ") when getting content provider " + name);
}
+ if (r.userId != userId) {
+ throw new SecurityException("Calling requested user " + userId
+ + " but app is user " + r.userId);
+ }
}
// First check if this content provider has been published...
- int userId = UserHandle.getUserId(r != null ? r.uid : Binder.getCallingUid());
cpr = mProviderMap.getProviderByName(name, userId);
boolean providerRunning = cpr != null;
if (providerRunning) {
@@ -6506,17 +6510,19 @@
throw new SecurityException(msg);
}
- return getContentProviderImpl(caller, name, null, stable);
+ return getContentProviderImpl(caller, name, null, stable,
+ UserHandle.getCallingUserId());
}
public ContentProviderHolder getContentProviderExternal(String name, IBinder token) {
enforceCallingPermission(android.Manifest.permission.ACCESS_CONTENT_PROVIDERS_EXTERNALLY,
"Do not have permission in call getContentProviderExternal()");
- return getContentProviderExternalUnchecked(name, token);
+ return getContentProviderExternalUnchecked(name, token, UserHandle.getCallingUserId());
}
- private ContentProviderHolder getContentProviderExternalUnchecked(String name,IBinder token) {
- return getContentProviderImpl(null, name, token, true);
+ private ContentProviderHolder getContentProviderExternalUnchecked(String name,
+ IBinder token, int userId) {
+ return getContentProviderImpl(null, name, token, true, userId);
}
/**
@@ -6547,13 +6553,12 @@
public void removeContentProviderExternal(String name, IBinder token) {
enforceCallingPermission(android.Manifest.permission.ACCESS_CONTENT_PROVIDERS_EXTERNALLY,
"Do not have permission in call removeContentProviderExternal()");
- removeContentProviderExternalUnchecked(name, token);
+ removeContentProviderExternalUnchecked(name, token, UserHandle.getCallingUserId());
}
- private void removeContentProviderExternalUnchecked(String name, IBinder token) {
+ private void removeContentProviderExternalUnchecked(String name, IBinder token, int userId) {
synchronized (this) {
- ContentProviderRecord cpr = mProviderMap.getProviderByName(name,
- Binder.getOrigCallingUser());
+ ContentProviderRecord cpr = mProviderMap.getProviderByName(name, userId);
if(cpr == null) {
//remove from mProvidersByClass
if(localLOGV) Slog.v(TAG, name+" content provider not found in providers list");
@@ -6562,8 +6567,7 @@
//update content provider record entry info
ComponentName comp = new ComponentName(cpr.info.packageName, cpr.info.name);
- ContentProviderRecord localCpr = mProviderMap.getProviderByClass(comp,
- Binder.getOrigCallingUser());
+ ContentProviderRecord localCpr = mProviderMap.getProviderByClass(comp, userId);
if (localCpr.hasExternalProcessHandles()) {
if (localCpr.removeExternalProcessHandleLocked(token)) {
updateOomAdjLocked();
@@ -6777,11 +6781,12 @@
public String getProviderMimeType(Uri uri) {
enforceNotIsolatedCaller("getProviderMimeType");
final String name = uri.getAuthority();
+ final int userId = UserHandle.getCallingUserId();
final long ident = Binder.clearCallingIdentity();
ContentProviderHolder holder = null;
try {
- holder = getContentProviderExternalUnchecked(name, null);
+ holder = getContentProviderExternalUnchecked(name, null, userId);
if (holder != null) {
return holder.provider.getType(uri);
}
@@ -6790,7 +6795,7 @@
return null;
} finally {
if (holder != null) {
- removeContentProviderExternalUnchecked(name, null);
+ removeContentProviderExternalUnchecked(name, null, userId);
}
Binder.restoreCallingIdentity(ident);
}
@@ -6894,8 +6899,9 @@
public ParcelFileDescriptor openContentUri(Uri uri) throws RemoteException {
enforceNotIsolatedCaller("openContentUri");
+ final int userId = UserHandle.getCallingUserId();
String name = uri.getAuthority();
- ContentProviderHolder cph = getContentProviderExternalUnchecked(name, null);
+ ContentProviderHolder cph = getContentProviderExternalUnchecked(name, null, userId);
ParcelFileDescriptor pfd = null;
if (cph != null) {
// We record the binder invoker's uid in thread-local storage before
@@ -6917,7 +6923,7 @@
}
// We've got the fd now, so we're done with the provider.
- removeContentProviderExternalUnchecked(name, null);
+ removeContentProviderExternalUnchecked(name, null, userId);
} else {
Slog.d(TAG, "Failed to get provider for authority '" + name + "'");
}
@@ -7557,7 +7563,7 @@
finisher = new IIntentReceiver.Stub() {
public void performReceive(Intent intent, int resultCode,
String data, Bundle extras, boolean ordered,
- boolean sticky) {
+ boolean sticky, int sendingUser) {
// The raw IIntentReceiver interface is called
// with the AM lock held, so redispatch to
// execute our code without the lock.
@@ -10562,8 +10568,9 @@
mProcessNames.remove(app.processName, app.uid);
mIsolatedProcesses.remove(app.uid);
if (mHeavyWeightProcess == app) {
+ mHandler.sendMessage(mHandler.obtainMessage(CANCEL_HEAVY_NOTIFICATION_MSG,
+ mHeavyWeightProcess.userId, 0));
mHeavyWeightProcess = null;
- mHandler.sendEmptyMessage(CANCEL_HEAVY_NOTIFICATION_MSG);
}
} else if (!app.removed) {
// This app is persistent, so we need to keep its record around.
@@ -10614,7 +10621,9 @@
restart = true;
} else {
removeDyingProviderLocked(app, cpr, true);
+ // cpr should have been removed from mLaunchingProviders
NL = mLaunchingProviders.size();
+ i--;
}
}
}
@@ -10663,13 +10672,13 @@
}
ComponentName startServiceInPackage(int uid,
- Intent service, String resolvedType) {
+ Intent service, String resolvedType, int userId) {
synchronized(this) {
if (DEBUG_SERVICE)
Slog.v(TAG, "startServiceInPackage: " + service + " type=" + resolvedType);
final long origId = Binder.clearCallingIdentity();
ComponentName res = mServices.startServiceLocked(null, service,
- resolvedType, -1, uid, UserHandle.getUserId(uid));
+ resolvedType, -1, uid, userId);
Binder.restoreCallingIdentity(origId);
return res;
}
@@ -10716,6 +10725,14 @@
}
}
+ public int handleIncomingUser(int callingPid, int callingUid, int userId, boolean allowAll,
+ boolean requireFull, String name, String callerPackage) {
+ synchronized(this) {
+ return handleIncomingUserLocked(callingPid, callingUid, userId, allowAll,
+ requireFull, name, callerPackage);
+ }
+ }
+
int handleIncomingUserLocked(int callingPid, int callingUid, int userId, boolean allowAll,
boolean requireFull, String name, String callerPackage) {
final int callingUserId = UserHandle.getUserId(callingUid);
@@ -10733,13 +10750,24 @@
// owner user instead of failing.
userId = callingUserId;
} else {
- String msg = "Permission Denial: " + name + " from " + callerPackage
- + " asks to run as user " + userId
- + " but is calling from user " + UserHandle.getUserId(callingUid)
- + "; this requires "
- + (requireFull
- ? android.Manifest.permission.INTERACT_ACROSS_USERS_FULL
- : android.Manifest.permission.INTERACT_ACROSS_USERS);
+ StringBuilder builder = new StringBuilder(128);
+ builder.append("Permission Denial: ");
+ builder.append(name);
+ if (callerPackage != null) {
+ builder.append(" from ");
+ builder.append(callerPackage);
+ }
+ builder.append(" asks to run as user ");
+ builder.append(userId);
+ builder.append(" but is calling from user ");
+ builder.append(UserHandle.getUserId(callingUid));
+ builder.append("; this requires ");
+ builder.append(android.Manifest.permission.INTERACT_ACROSS_USERS_FULL);
+ if (!requireFull) {
+ builder.append("or");
+ builder.append(android.Manifest.permission.INTERACT_ACROSS_USERS);
+ }
+ String msg = builder.toString();
Slog.w(TAG, msg);
throw new SecurityException(msg);
}
@@ -11026,9 +11054,10 @@
}
public Intent registerReceiver(IApplicationThread caller, String callerPackage,
- IIntentReceiver receiver, IntentFilter filter, String permission) {
+ IIntentReceiver receiver, IntentFilter filter, String permission, int userId) {
enforceNotIsolatedCaller("registerReceiver");
int callingUid;
+ int callingPid;
synchronized(this) {
ProcessRecord callerApp = null;
if (caller != null) {
@@ -11045,11 +11074,16 @@
+ " is not running in process " + callerApp);
}
callingUid = callerApp.info.uid;
+ callingPid = callerApp.pid;
} else {
callerPackage = null;
callingUid = Binder.getCallingUid();
+ callingPid = Binder.getCallingPid();
}
+ userId = this.handleIncomingUserLocked(callingPid, callingUid, userId,
+ true, true, "registerReceiver", callerPackage);
+
List allSticky = null;
// Look for any matching sticky broadcasts...
@@ -11083,9 +11117,8 @@
ReceiverList rl
= (ReceiverList)mRegisteredReceivers.get(receiver.asBinder());
if (rl == null) {
- rl = new ReceiverList(this, callerApp,
- Binder.getCallingPid(),
- Binder.getCallingUid(), receiver);
+ rl = new ReceiverList(this, callerApp, callingPid, callingUid,
+ userId, receiver);
if (rl.app != null) {
rl.app.receivers.add(rl);
} else {
@@ -11097,9 +11130,21 @@
rl.linkedToDeath = true;
}
mRegisteredReceivers.put(receiver.asBinder(), rl);
+ } else if (rl.uid != callingUid) {
+ throw new IllegalArgumentException(
+ "Receiver requested to register for uid " + callingUid
+ + " was previously registered for uid " + rl.uid);
+ } else if (rl.pid != callingPid) {
+ throw new IllegalArgumentException(
+ "Receiver requested to register for pid " + callingPid
+ + " was previously registered for pid " + rl.pid);
+ } else if (rl.userId != userId) {
+ throw new IllegalArgumentException(
+ "Receiver requested to register for user " + userId
+ + " was previously registered for user " + rl.userId);
}
BroadcastFilter bf = new BroadcastFilter(filter, rl, callerPackage,
- permission, callingUid);
+ permission, callingUid, userId);
rl.add(bf);
if (!bf.debugCheck()) {
Slog.w(TAG, "==> For Dynamic broadast");
@@ -13760,11 +13805,13 @@
if (uss.mState == UserStartedState.STATE_BOOTING
&& mStartedUsers.get(uss.mHandle.getIdentifier()) == uss) {
uss.mState = UserStartedState.STATE_RUNNING;
- broadcastIntentLocked(null, null,
- new Intent(Intent.ACTION_BOOT_COMPLETED, null),
+ final int userId = uss.mHandle.getIdentifier();
+ Intent intent = new Intent(Intent.ACTION_BOOT_COMPLETED, null);
+ intent.putExtra(Intent.EXTRA_USER_HANDLE, userId);
+ broadcastIntentLocked(null, null, intent,
null, null, 0, null, null,
android.Manifest.permission.RECEIVE_BOOT_COMPLETED,
- false, false, MY_PID, Process.SYSTEM_UID, uss.mHandle.getIdentifier());
+ false, false, MY_PID, Process.SYSTEM_UID, userId);
}
}
}
@@ -13820,7 +13867,7 @@
final IIntentReceiver resultReceiver = new IIntentReceiver.Stub() {
@Override
public void performReceive(Intent intent, int resultCode, String data,
- Bundle extras, boolean ordered, boolean sticky) {
+ Bundle extras, boolean ordered, boolean sticky, int sendingUser) {
finishUserStop(uss);
}
};
diff --git a/services/java/com/android/server/am/ActivityStack.java b/services/java/com/android/server/am/ActivityStack.java
index ef4e9be..399ea59 100755
--- a/services/java/com/android/server/am/ActivityStack.java
+++ b/services/java/com/android/server/am/ActivityStack.java
@@ -3077,7 +3077,7 @@
IIntentSender target = mService.getIntentSenderLocked(
ActivityManager.INTENT_SENDER_ACTIVITY, "android",
- realCallingUid, null, null, 0, new Intent[] { intent },
+ realCallingUid, userId, null, null, 0, new Intent[] { intent },
new String[] { resolvedType }, PendingIntent.FLAG_CANCEL_CURRENT
| PendingIntent.FLAG_ONE_SHOT, null);
diff --git a/services/java/com/android/server/am/BroadcastFilter.java b/services/java/com/android/server/am/BroadcastFilter.java
index b0ccd8f..07440b5 100644
--- a/services/java/com/android/server/am/BroadcastFilter.java
+++ b/services/java/com/android/server/am/BroadcastFilter.java
@@ -17,7 +17,6 @@
package com.android.server.am;
import android.content.IntentFilter;
-import android.os.UserHandle;
import android.util.PrintWriterPrinter;
import android.util.Printer;
@@ -32,13 +31,13 @@
final int owningUserId;
BroadcastFilter(IntentFilter _filter, ReceiverList _receiverList,
- String _packageName, String _requiredPermission, int _owningUid) {
+ String _packageName, String _requiredPermission, int _owningUid, int _userId) {
super(_filter);
receiverList = _receiverList;
packageName = _packageName;
requiredPermission = _requiredPermission;
owningUid = _owningUid;
- owningUserId = UserHandle.getUserId(owningUid);
+ owningUserId = _userId;
}
public void dump(PrintWriter pw, String prefix) {
diff --git a/services/java/com/android/server/am/BroadcastQueue.java b/services/java/com/android/server/am/BroadcastQueue.java
index 34dec3a..1b6ff7a 100644
--- a/services/java/com/android/server/am/BroadcastQueue.java
+++ b/services/java/com/android/server/am/BroadcastQueue.java
@@ -222,7 +222,7 @@
mService.ensurePackageDexOpt(r.intent.getComponent().getPackageName());
app.thread.scheduleReceiver(new Intent(r.intent), r.curReceiver,
mService.compatibilityInfoForPackageLocked(r.curReceiver.applicationInfo),
- r.resultCode, r.resultData, r.resultExtras, r.ordered);
+ r.resultCode, r.resultData, r.resultExtras, r.ordered, r.userId);
if (DEBUG_BROADCAST) Slog.v(TAG,
"Process cur broadcast " + r + " DELIVERED for app " + app);
started = true;
@@ -357,15 +357,16 @@
private static void performReceiveLocked(ProcessRecord app, IIntentReceiver receiver,
Intent intent, int resultCode, String data, Bundle extras,
- boolean ordered, boolean sticky) throws RemoteException {
+ boolean ordered, boolean sticky, int sendingUser) throws RemoteException {
// Send the intent to the receiver asynchronously using one-way binder calls.
if (app != null && app.thread != null) {
// If we have an app thread, do the call through that so it is
// correctly ordered with other one-way calls.
app.thread.scheduleRegisteredReceiver(receiver, intent, resultCode,
- data, extras, ordered, sticky);
+ data, extras, ordered, sticky, sendingUser);
} else {
- receiver.performReceive(intent, resultCode, data, extras, ordered, sticky);
+ receiver.performReceive(intent, resultCode, data, extras, ordered,
+ sticky, sendingUser);
}
}
@@ -428,8 +429,8 @@
+ " (seq=" + seq + "): " + r);
}
performReceiveLocked(filter.receiverList.app, filter.receiverList.receiver,
- new Intent(r.intent), r.resultCode,
- r.resultData, r.resultExtras, r.ordered, r.initialSticky);
+ new Intent(r.intent), r.resultCode, r.resultData,
+ r.resultExtras, r.ordered, r.initialSticky, r.userId);
if (ordered) {
r.state = BroadcastRecord.CALL_DONE_RECEIVE;
}
@@ -579,7 +580,7 @@
}
performReceiveLocked(r.callerApp, r.resultTo,
new Intent(r.intent), r.resultCode,
- r.resultData, r.resultExtras, false, false);
+ r.resultData, r.resultExtras, false, false, r.userId);
// Set this to null so that the reference
// (local and remote) isnt kept in the mBroadcastHistory.
r.resultTo = null;
diff --git a/services/java/com/android/server/am/PendingIntentRecord.java b/services/java/com/android/server/am/PendingIntentRecord.java
index 8e70376..0f72409 100644
--- a/services/java/com/android/server/am/PendingIntentRecord.java
+++ b/services/java/com/android/server/am/PendingIntentRecord.java
@@ -243,11 +243,10 @@
allIntents[allIntents.length-1] = finalIntent;
allResolvedTypes[allResolvedTypes.length-1] = resolvedType;
owner.startActivitiesInPackage(uid, allIntents,
- allResolvedTypes, resultTo, options);
+ allResolvedTypes, resultTo, options, key.userId);
} else {
- owner.startActivityInPackage(uid,
- finalIntent, resolvedType,
- resultTo, resultWho, requestCode, 0, options);
+ owner.startActivityInPackage(uid, finalIntent, resolvedType,
+ resultTo, resultWho, requestCode, 0, options, key.userId);
}
} catch (RuntimeException e) {
Slog.w(ActivityManagerService.TAG,
@@ -265,8 +264,7 @@
owner.broadcastIntentInPackage(key.packageName, uid,
finalIntent, resolvedType,
finishedReceiver, code, null, null,
- requiredPermission, (finishedReceiver != null), false, UserHandle
- .getUserId(uid));
+ requiredPermission, (finishedReceiver != null), false, key.userId);
sendFinish = false;
} catch (RuntimeException e) {
Slog.w(ActivityManagerService.TAG,
@@ -276,7 +274,7 @@
case ActivityManager.INTENT_SENDER_SERVICE:
try {
owner.startServiceInPackage(uid,
- finalIntent, resolvedType);
+ finalIntent, resolvedType, key.userId);
} catch (RuntimeException e) {
Slog.w(ActivityManagerService.TAG,
"Unable to send startService intent", e);
@@ -287,7 +285,7 @@
if (sendFinish) {
try {
finishedReceiver.performReceive(new Intent(finalIntent), 0,
- null, null, false, false);
+ null, null, false, false, key.userId);
} catch (RemoteException e) {
}
}
diff --git a/services/java/com/android/server/am/ProviderMap.java b/services/java/com/android/server/am/ProviderMap.java
index 15fbb98..7a4fef6 100644
--- a/services/java/com/android/server/am/ProviderMap.java
+++ b/services/java/com/android/server/am/ProviderMap.java
@@ -116,48 +116,46 @@
}
}
- void removeProviderByName(String name, int optionalUserId) {
+ void removeProviderByName(String name, int userId) {
if (mSingletonByName.containsKey(name)) {
if (DBG)
Slog.i(TAG, "Removing from globalByName name=" + name);
mSingletonByName.remove(name);
} else {
- // TODO: Verify this works, i.e., the caller happens to be from the correct user
+ if (userId < 0) throw new IllegalArgumentException("Bad user " + userId);
if (DBG)
Slog.i(TAG,
- "Removing from providersByName name=" + name + " user="
- + (optionalUserId == -1 ? Binder.getOrigCallingUser() : optionalUserId));
- HashMap<String, ContentProviderRecord> map = getProvidersByName(optionalUserId);
+ "Removing from providersByName name=" + name + " user=" + userId);
+ HashMap<String, ContentProviderRecord> map = getProvidersByName(userId);
// map returned by getProvidersByName wouldn't be null
map.remove(name);
if (map.size() == 0) {
- mProvidersByNamePerUser.remove(optionalUserId);
+ mProvidersByNamePerUser.remove(userId);
}
}
}
- void removeProviderByClass(ComponentName name, int optionalUserId) {
+ void removeProviderByClass(ComponentName name, int userId) {
if (mSingletonByClass.containsKey(name)) {
if (DBG)
Slog.i(TAG, "Removing from globalByClass name=" + name);
mSingletonByClass.remove(name);
} else {
+ if (userId < 0) throw new IllegalArgumentException("Bad user " + userId);
if (DBG)
Slog.i(TAG,
- "Removing from providersByClass name=" + name + " user="
- + (optionalUserId == -1 ? Binder.getOrigCallingUser() : optionalUserId));
- HashMap<ComponentName, ContentProviderRecord> map = getProvidersByClass(optionalUserId);
+ "Removing from providersByClass name=" + name + " user=" + userId);
+ HashMap<ComponentName, ContentProviderRecord> map = getProvidersByClass(userId);
// map returned by getProvidersByClass wouldn't be null
map.remove(name);
if (map.size() == 0) {
- mProvidersByClassPerUser.remove(optionalUserId);
+ mProvidersByClassPerUser.remove(userId);
}
}
}
- private HashMap<String, ContentProviderRecord> getProvidersByName(int optionalUserId) {
- final int userId = optionalUserId >= 0
- ? optionalUserId : Binder.getOrigCallingUser();
+ private HashMap<String, ContentProviderRecord> getProvidersByName(int userId) {
+ if (userId < 0) throw new IllegalArgumentException("Bad user " + userId);
final HashMap<String, ContentProviderRecord> map = mProvidersByNamePerUser.get(userId);
if (map == null) {
HashMap<String, ContentProviderRecord> newMap = new HashMap<String, ContentProviderRecord>();
@@ -168,12 +166,13 @@
}
}
- HashMap<ComponentName, ContentProviderRecord> getProvidersByClass(int optionalUserId) {
- final int userId = optionalUserId >= 0
- ? optionalUserId : Binder.getOrigCallingUser();
- final HashMap<ComponentName, ContentProviderRecord> map = mProvidersByClassPerUser.get(userId);
+ HashMap<ComponentName, ContentProviderRecord> getProvidersByClass(int userId) {
+ if (userId < 0) throw new IllegalArgumentException("Bad user " + userId);
+ final HashMap<ComponentName, ContentProviderRecord> map
+ = mProvidersByClassPerUser.get(userId);
if (map == null) {
- HashMap<ComponentName, ContentProviderRecord> newMap = new HashMap<ComponentName, ContentProviderRecord>();
+ HashMap<ComponentName, ContentProviderRecord> newMap
+ = new HashMap<ComponentName, ContentProviderRecord>();
mProvidersByClassPerUser.put(userId, newMap);
return newMap;
} else {
diff --git a/services/java/com/android/server/am/ReceiverList.java b/services/java/com/android/server/am/ReceiverList.java
index 32c24c6..9b6701e 100644
--- a/services/java/com/android/server/am/ReceiverList.java
+++ b/services/java/com/android/server/am/ReceiverList.java
@@ -39,18 +39,20 @@
public final ProcessRecord app;
public final int pid;
public final int uid;
+ public final int userId;
BroadcastRecord curBroadcast = null;
boolean linkedToDeath = false;
String stringName;
ReceiverList(ActivityManagerService _owner, ProcessRecord _app,
- int _pid, int _uid, IIntentReceiver _receiver) {
+ int _pid, int _uid, int _userId, IIntentReceiver _receiver) {
owner = _owner;
receiver = _receiver;
app = _app;
pid = _pid;
uid = _uid;
+ userId = _userId;
}
// Want object identity, not the array identity we are inheriting.
@@ -67,8 +69,9 @@
}
void dumpLocal(PrintWriter pw, String prefix) {
- pw.print(prefix); pw.print("app="); pw.print(app);
- pw.print(" pid="); pw.print(pid); pw.print(" uid="); pw.println(uid);
+ pw.print(prefix); pw.print("app="); pw.print(app.toShortString());
+ pw.print(" pid="); pw.print(pid); pw.print(" uid="); pw.print(uid);
+ pw.print(" user="); pw.println(userId);
if (curBroadcast != null || linkedToDeath) {
pw.print(prefix); pw.print("curBroadcast="); pw.print(curBroadcast);
pw.print(" linkedToDeath="); pw.println(linkedToDeath);
@@ -103,6 +106,8 @@
sb.append((app != null ? app.processName : "(unknown name)"));
sb.append('/');
sb.append(uid);
+ sb.append("/u");
+ sb.append(userId);
sb.append((receiver.asBinder() instanceof Binder) ? " local:" : " remote:");
sb.append(Integer.toHexString(System.identityHashCode(receiver.asBinder())));
sb.append('}');
diff --git a/services/java/com/android/server/am/ServiceRecord.java b/services/java/com/android/server/am/ServiceRecord.java
index 5d60b9c..7055fdc 100644
--- a/services/java/com/android/server/am/ServiceRecord.java
+++ b/services/java/com/android/server/am/ServiceRecord.java
@@ -370,7 +370,7 @@
try {
int[] outId = new int[1];
nm.enqueueNotificationInternal(localPackageName, appUid, appPid,
- null, localForegroundId, localForegroundNoti, outId);
+ null, localForegroundId, localForegroundNoti, outId, userId);
} catch (RuntimeException e) {
Slog.w(ActivityManagerService.TAG,
"Error showing notification for service", e);
@@ -399,7 +399,8 @@
return;
}
try {
- inm.cancelNotification(localPackageName, localForegroundId);
+ inm.cancelNotificationWithTag(localPackageName, null,
+ localForegroundId, userId);
} catch (RuntimeException e) {
Slog.w(ActivityManagerService.TAG,
"Error canceling notification for service", e);
diff --git a/services/java/com/android/server/display/DisplayAdapter.java b/services/java/com/android/server/display/DisplayAdapter.java
index d19fe01..abc1d32 100644
--- a/services/java/com/android/server/display/DisplayAdapter.java
+++ b/services/java/com/android/server/display/DisplayAdapter.java
@@ -28,38 +28,53 @@
* For now, all display adapters are registered in the system server but
* in principle it could be done from other processes.
* </p><p>
- * Display devices are not thread-safe and must only be accessed
- * on the display manager service's handler thread.
+ * Display adapters are guarded by the {@link DisplayManagerService.SyncRoot} lock.
* </p>
*/
-public class DisplayAdapter {
+abstract class DisplayAdapter {
+ private final DisplayManagerService.SyncRoot mSyncRoot;
private final Context mContext;
- private final String mName;
private final Handler mHandler;
- private Listener mListener;
+ private final Listener mListener;
+ private final String mName;
public static final int DISPLAY_DEVICE_EVENT_ADDED = 1;
public static final int DISPLAY_DEVICE_EVENT_CHANGED = 2;
public static final int DISPLAY_DEVICE_EVENT_REMOVED = 3;
- public DisplayAdapter(Context context, String name) {
+ public DisplayAdapter(DisplayManagerService.SyncRoot syncRoot,
+ Context context, Handler handler, Listener listener, String name) {
+ mSyncRoot = syncRoot;
mContext = context;
+ mHandler = handler;
+ mListener = listener;
mName = name;
- mHandler = new Handler();
}
+ /**
+ * Gets the object that the display adapter should synchronize on when handling
+ * calls that come in from outside of the display manager service.
+ */
+ public final DisplayManagerService.SyncRoot getSyncRoot() {
+ return mSyncRoot;
+ }
+
+ /**
+ * Gets the display adapter's context.
+ */
public final Context getContext() {
return mContext;
}
+ /**
+ * Gets a handler that the display adapter may use to post asynchronous messages.
+ */
public final Handler getHandler() {
return mHandler;
}
/**
* Gets the display adapter name for debugging purposes.
- *
- * @return The display adapter name.
*/
public final String getName() {
return mName;
@@ -68,35 +83,24 @@
/**
* Registers the display adapter with the display manager.
*
- * @param listener The listener for callbacks. The listener will
- * be invoked on the display manager service's handler thread.
+ * The display adapter should register any built-in display devices as soon as possible.
+ * The boot process will wait for the default display to be registered.
+ * Other display devices can be registered dynamically later.
*/
- public final void register(Listener listener) {
- mListener = listener;
- onRegister();
+ public void registerLocked() {
}
/**
* Dumps the local state of the display adapter.
*/
- public void dump(PrintWriter pw) {
- }
-
- /**
- * Called when the display adapter is registered.
- *
- * The display adapter should register any built-in display devices as soon as possible.
- * The boot process will wait for the default display to be registered.
- *
- * Other display devices can be registered dynamically later.
- */
- protected void onRegister() {
+ public void dumpLocked(PrintWriter pw) {
}
/**
* Sends a display device event to the display adapter listener asynchronously.
*/
- protected void sendDisplayDeviceEvent(final DisplayDevice device, final int event) {
+ protected final void sendDisplayDeviceEventLocked(
+ final DisplayDevice device, final int event) {
mHandler.post(new Runnable() {
@Override
public void run() {
@@ -105,7 +109,20 @@
});
}
+ /**
+ * Sends a request to perform traversals.
+ */
+ protected final void sendTraversalRequestLocked() {
+ mHandler.post(new Runnable() {
+ @Override
+ public void run() {
+ mListener.onTraversalRequested();
+ }
+ });
+ }
+
public interface Listener {
public void onDisplayDeviceEvent(DisplayDevice device, int event);
+ public void onTraversalRequested();
}
}
diff --git a/services/java/com/android/server/display/DisplayDevice.java b/services/java/com/android/server/display/DisplayDevice.java
index c83ce96..bdc87f9 100644
--- a/services/java/com/android/server/display/DisplayDevice.java
+++ b/services/java/com/android/server/display/DisplayDevice.java
@@ -16,20 +16,35 @@
package com.android.server.display;
+import android.graphics.Rect;
+import android.graphics.SurfaceTexture;
import android.os.IBinder;
+import android.view.Surface;
+
+import java.io.PrintWriter;
/**
* Represents a physical display device such as the built-in display
* an external monitor, or a WiFi display.
* <p>
- * Display devices are not thread-safe and must only be accessed
- * on the display manager service's handler thread.
+ * Display devices are guarded by the {@link DisplayManagerService.SyncRoot} lock.
* </p>
*/
-public abstract class DisplayDevice {
+abstract class DisplayDevice {
private final DisplayAdapter mDisplayAdapter;
private final IBinder mDisplayToken;
+ // The display device does not manage these properties itself, they are set by
+ // the display manager service. The display device shouldn't really be looking at these.
+ private int mCurrentLayerStack = -1;
+ private int mCurrentOrientation = -1;
+ private Rect mCurrentLayerStackRect;
+ private Rect mCurrentDisplayRect;
+
+ // The display device does own its surface texture, but it should only set it
+ // within a transaction from performTraversalInTransactionLocked.
+ private SurfaceTexture mCurrentSurfaceTexture;
+
public DisplayDevice(DisplayAdapter displayAdapter, IBinder displayToken) {
mDisplayAdapter = displayAdapter;
mDisplayToken = displayToken;
@@ -40,7 +55,7 @@
*
* @return The display adapter.
*/
- public final DisplayAdapter getAdapter() {
+ public final DisplayAdapter getAdapterLocked() {
return mDisplayAdapter;
}
@@ -50,22 +65,101 @@
* @return The display token, or null if the display is not being managed
* by Surface Flinger.
*/
- public final IBinder getDisplayToken() {
+ public final IBinder getDisplayTokenLocked() {
return mDisplayToken;
}
/**
+ * Gets the name of the display device.
+ *
+ * @return The display device name.
+ */
+ public final String getNameLocked() {
+ return getDisplayDeviceInfoLocked().name;
+ }
+
+ /**
* Gets information about the display device.
*
- * @param outInfo The object to populate with the information.
+ * The information returned should not change between calls unless the display
+ * adapter sent a {@link DisplayAdapter#DISPLAY_DEVICE_EVENT_CHANGED} event and
+ * {@link #applyPendingDisplayDeviceInfoChangesLocked()} has been called to apply
+ * the pending changes.
+ *
+ * @return The display device info, which should be treated as immutable by the caller.
+ * The display device should allocate a new display device info object whenever
+ * the data changes.
*/
- public abstract void getInfo(DisplayDeviceInfo outInfo);
+ public abstract DisplayDeviceInfo getDisplayDeviceInfoLocked();
- // For debugging purposes.
- @Override
- public String toString() {
- DisplayDeviceInfo info = new DisplayDeviceInfo();
- getInfo(info);
- return info.toString() + ", owner=\"" + mDisplayAdapter.getName() + "\"";
+ /**
+ * Applies any pending changes to the observable state of the display device
+ * if the display adapter sent a {@link DisplayAdapter#DISPLAY_DEVICE_EVENT_CHANGED} event.
+ */
+ public void applyPendingDisplayDeviceInfoChangesLocked() {
+ }
+
+ /**
+ * Gives the display device a chance to update its properties while in a transaction.
+ */
+ public void performTraversalInTransactionLocked() {
+ }
+
+ /**
+ * Sets the display layer stack while in a transaction.
+ */
+ public final void setLayerStackInTransactionLocked(int layerStack) {
+ if (mCurrentLayerStack == layerStack) {
+ return;
+ }
+ mCurrentLayerStack = layerStack;
+ Surface.setDisplayLayerStack(mDisplayToken, layerStack);
+ }
+
+ /**
+ * Sets the display projection while in a transaction.
+ *
+ * @param orientation defines the display's orientation
+ * @param layerStackRect defines which area of the window manager coordinate
+ * space will be used
+ * @param displayRect defines where on the display will layerStackRect be
+ * mapped to. displayRect is specified post-orientation, that is
+ * it uses the orientation seen by the end-user
+ */
+ public final void setProjectionInTransactionLocked(int orientation, Rect layerStackRect, Rect displayRect) {
+ mCurrentOrientation = orientation;
+ if (mCurrentLayerStackRect == null) {
+ mCurrentLayerStackRect = new Rect();
+ }
+ mCurrentLayerStackRect.set(layerStackRect);
+ if (mCurrentDisplayRect == null) {
+ mCurrentDisplayRect = new Rect();
+ }
+ mCurrentDisplayRect.set(displayRect);
+ Surface.setDisplayProjection(mDisplayToken, orientation, layerStackRect, displayRect);
+ }
+
+ /**
+ * Sets the surface texture while in a transaction.
+ */
+ public final void setSurfaceTextureInTransactionLocked(SurfaceTexture surfaceTexture) {
+ if (mCurrentSurfaceTexture == surfaceTexture) {
+ return;
+ }
+ mCurrentSurfaceTexture = surfaceTexture;
+ Surface.setDisplaySurface(mDisplayToken, surfaceTexture);
+ }
+
+ /**
+ * Dumps the local state of the display device.
+ * Does not need to dump the display device info because that is already dumped elsewhere.
+ */
+ public void dumpLocked(PrintWriter pw) {
+ pw.println("mAdapter=" + mDisplayAdapter.getName());
+ pw.println("mCurrentLayerStack=" + mCurrentLayerStack);
+ pw.println("mCurrentOrientation=" + mCurrentOrientation);
+ pw.println("mCurrentViewport=" + mCurrentLayerStackRect);
+ pw.println("mCurrentFrame=" + mCurrentDisplayRect);
+ pw.println("mCurrentSurfaceTexture=" + mCurrentSurfaceTexture);
}
}
diff --git a/services/java/com/android/server/display/DisplayDeviceInfo.java b/services/java/com/android/server/display/DisplayDeviceInfo.java
index c7b8c24..6f82119 100644
--- a/services/java/com/android/server/display/DisplayDeviceInfo.java
+++ b/services/java/com/android/server/display/DisplayDeviceInfo.java
@@ -16,14 +16,31 @@
package com.android.server.display;
+import libcore.util.Objects;
+
/**
* Describes the characteristics of a physical display device.
*/
-public final class DisplayDeviceInfo {
+final class DisplayDeviceInfo {
+ /**
+ * Flag: Indicates that this display device should be considered the default display
+ * device of the system.
+ */
public static final int FLAG_DEFAULT_DISPLAY = 1 << 0;
+
+ /**
+ * Flag: Indicates that this display device can show secure surfaces.
+ */
public static final int FLAG_SECURE = 1 << 1;
/**
+ * Flag: Indicates that this display device can rotate to show contents in a
+ * different orientation. Otherwise the rotation is assumed to be fixed in the
+ * natural orientation and the display manager should transform the content to fit.
+ */
+ public static final int FLAG_SUPPORTS_ROTATION = 1 << 2;
+
+ /**
* Gets the name of the display device, which may be derived from
* EDID or other sources. The name may be displayed to the user.
*/
@@ -48,6 +65,28 @@
public int flags;
+ @Override
+ public boolean equals(Object o) {
+ return o instanceof DisplayDeviceInfo && equals((DisplayDeviceInfo)o);
+ }
+
+ public boolean equals(DisplayDeviceInfo other) {
+ return other != null
+ && Objects.equal(name, other.name)
+ && width == other.width
+ && height == other.height
+ && refreshRate == other.refreshRate
+ && densityDpi == other.densityDpi
+ && xDpi == other.xDpi
+ && yDpi == other.yDpi
+ && flags == other.flags;
+ }
+
+ @Override
+ public int hashCode() {
+ return 0; // don't care
+ }
+
public void copyFrom(DisplayDeviceInfo other) {
name = other.name;
width = other.width;
@@ -62,9 +101,9 @@
// For debugging purposes
@Override
public String toString() {
- return "\"" + name + "\": " + width + " x " + height + ", " + refreshRate + " fps, "
+ return "DisplayDeviceInfo{\"" + name + "\": " + width + " x " + height + ", " + refreshRate + " fps, "
+ "density " + densityDpi + ", " + xDpi + " x " + yDpi + " dpi"
- + flagsToString(flags);
+ + flagsToString(flags) + "}";
}
private static String flagsToString(int flags) {
diff --git a/services/java/com/android/server/display/DisplayManagerService.java b/services/java/com/android/server/display/DisplayManagerService.java
index cf835ef..706007a 100644
--- a/services/java/com/android/server/display/DisplayManagerService.java
+++ b/services/java/com/android/server/display/DisplayManagerService.java
@@ -36,11 +36,9 @@
import android.util.SparseArray;
import android.view.Display;
import android.view.DisplayInfo;
-import android.view.Surface;
import java.io.FileDescriptor;
import java.io.PrintWriter;
-import java.io.StringWriter;
import java.util.ArrayList;
/**
@@ -66,12 +64,24 @@
* two classes: display adapters handle individual display devices whereas
* the display manager service handles the global state. Second, it eliminates
* the potential for deadlocks resulting from asynchronous display device discovery.
+ * </p>
+ *
+ * <h3>Synchronization</h3>
+ * <p>
+ * Because the display manager may be accessed by multiple threads, the synchronization
+ * story gets a little complicated. In particular, the window manager may call into
+ * the display manager while holding a surface transaction with the expectation that
+ * it can apply changes immediately. Unfortunately, that means we can't just do
+ * everything asynchronously (*grump*).
* </p><p>
- * To keep things simple, display adapters and display devices are single-threaded
- * and are only accessed on the display manager's handler thread. Of course, the
- * display manager must be accessible by multiple thread (especially for
- * incoming binder calls) so all of the display manager's state is synchronized
- * and guarded by a lock.
+ * To make this work, all of the objects that belong to the display manager must
+ * use the same lock. We call this lock the synchronization root and it has a unique
+ * type {@link DisplayManagerService.SyncRoot}. Methods that require this lock are
+ * named with the "Locked" suffix.
+ * </p><p>
+ * Where things get tricky is that the display manager is not allowed to make
+ * any potentially reentrant calls, especially into the window manager. We generally
+ * avoid this by making all potentially reentrant out-calls asynchronous.
* </p>
*/
public final class DisplayManagerService extends IDisplayManager.Stub {
@@ -84,15 +94,29 @@
private static final int MSG_REGISTER_DEFAULT_DISPLAY_ADAPTER = 1;
private static final int MSG_REGISTER_ADDITIONAL_DISPLAY_ADAPTERS = 2;
private static final int MSG_DELIVER_DISPLAY_EVENT = 3;
-
- private final Object mLock = new Object();
+ private static final int MSG_REQUEST_TRAVERSAL = 4;
private final Context mContext;
private final boolean mHeadless;
-
private final DisplayManagerHandler mHandler;
- private final DisplayAdapterListener mDisplayAdapterListener = new DisplayAdapterListener();
- private final SparseArray<CallbackRecord> mCallbacks =
+ private final Handler mUiHandler;
+ private final DisplayAdapterListener mDisplayAdapterListener;
+ private WindowManagerFuncs mWindowManagerFuncs;
+
+ // The synchronization root for the display manager.
+ // This lock guards most of the display manager's state.
+ private final SyncRoot mSyncRoot = new SyncRoot();
+
+ // True if in safe mode.
+ // This option may disable certain display adapters.
+ public boolean mSafeMode;
+
+ // True if we are in a special boot mode where only core applications and
+ // services should be started. This option may disable certain display adapters.
+ public boolean mOnlyCore;
+
+ // All callback records indexed by calling process id.
+ public final SparseArray<CallbackRecord> mCallbacks =
new SparseArray<CallbackRecord>();
// List of all currently registered display adapters.
@@ -101,26 +125,33 @@
// List of all currently connected display devices.
private final ArrayList<DisplayDevice> mDisplayDevices = new ArrayList<DisplayDevice>();
- // List of all logical displays, indexed by logical display id.
- private final SparseArray<LogicalDisplay> mLogicalDisplays = new SparseArray<LogicalDisplay>();
+ // List of all removed display devices.
+ private final ArrayList<DisplayDevice> mRemovedDisplayDevices = new ArrayList<DisplayDevice>();
+
+ // List of all logical displays indexed by logical display id.
+ private final SparseArray<LogicalDisplay> mLogicalDisplays =
+ new SparseArray<LogicalDisplay>();
private int mNextNonDefaultDisplayId = Display.DEFAULT_DISPLAY + 1;
- // True if in safe mode.
- // This option may disable certain display adapters.
- private boolean mSafeMode;
-
- // True if we are in a special boot mode where only core applications and
- // services should be started. This option may disable certain display adapters.
- private boolean mOnlyCore;
+ // Set to true when there are pending display changes that have yet to be applied
+ // to the surface flinger state.
+ private boolean mPendingTraversal;
// Temporary callback list, used when sending display events to applications.
- private ArrayList<CallbackRecord> mTempCallbacks = new ArrayList<CallbackRecord>();
+ // May be used outside of the lock but only on the handler thread.
+ private final ArrayList<CallbackRecord> mTempCallbacks = new ArrayList<CallbackRecord>();
- public DisplayManagerService(Context context, Handler uiHandler) {
+ // Temporary display info, used for comparing display configurations.
+ private final DisplayInfo mTempDisplayInfo = new DisplayInfo();
+
+ public DisplayManagerService(Context context, Handler mainHandler, Handler uiHandler) {
mContext = context;
mHeadless = SystemProperties.get(SYSTEM_HEADLESS).equals("1");
- mHandler = new DisplayManagerHandler(uiHandler.getLooper());
+ mHandler = new DisplayManagerHandler(mainHandler.getLooper());
+ mUiHandler = uiHandler;
+ mDisplayAdapterListener = new DisplayAdapterListener();
+
mHandler.sendEmptyMessage(MSG_REGISTER_DEFAULT_DISPLAY_ADAPTER);
}
@@ -128,7 +159,7 @@
* Pauses the boot process to wait for the first display to be initialized.
*/
public boolean waitForDefaultDisplay() {
- synchronized (mLock) {
+ synchronized (mSyncRoot) {
long timeout = SystemClock.uptimeMillis() + WAIT_FOR_DEFAULT_DISPLAY_TIMEOUT;
while (mLogicalDisplays.get(Display.DEFAULT_DISPLAY) == null) {
long delay = timeout - SystemClock.uptimeMillis();
@@ -139,7 +170,7 @@
Slog.d(TAG, "waitForDefaultDisplay: waiting, timeout=" + delay);
}
try {
- mLock.wait(delay);
+ mSyncRoot.wait(delay);
} catch (InterruptedException ex) {
}
}
@@ -148,62 +179,28 @@
}
/**
+ * Called during initialization to associated the display manager with the
+ * window manager.
+ */
+ public void setWindowManager(WindowManagerFuncs windowManagerFuncs) {
+ synchronized (mSyncRoot) {
+ mWindowManagerFuncs = windowManagerFuncs;
+ scheduleTraversalLocked();
+ }
+ }
+
+ /**
* Called when the system is ready to go.
*/
public void systemReady(boolean safeMode, boolean onlyCore) {
- synchronized (mLock) {
+ synchronized (mSyncRoot) {
mSafeMode = safeMode;
mOnlyCore = onlyCore;
}
+
mHandler.sendEmptyMessage(MSG_REGISTER_ADDITIONAL_DISPLAY_ADAPTERS);
}
- // Runs on handler.
- private void registerDefaultDisplayAdapter() {
- // Register default display adapter.
- if (mHeadless) {
- registerDisplayAdapter(new HeadlessDisplayAdapter(mContext));
- } else {
- registerDisplayAdapter(new LocalDisplayAdapter(mContext));
- }
- }
-
- // Runs on handler.
- private void registerAdditionalDisplayAdapters() {
- if (shouldRegisterNonEssentialDisplayAdapters()) {
- registerDisplayAdapter(new OverlayDisplayAdapter(mContext));
- }
- }
-
- private boolean shouldRegisterNonEssentialDisplayAdapters() {
- // In safe mode, we disable non-essential display adapters to give the user
- // an opportunity to fix broken settings or other problems that might affect
- // system stability.
- // In only-core mode, we disable non-essential display adapters to minimize
- // the number of dependencies that are started while in this mode and to
- // prevent problems that might occur due to the device being encrypted.
- synchronized (mLock) {
- return !mSafeMode && !mOnlyCore;
- }
- }
-
- // Runs on handler.
- private void registerDisplayAdapter(DisplayAdapter adapter) {
- synchronized (mLock) {
- mDisplayAdapters.add(adapter);
- }
-
- adapter.register(mDisplayAdapterListener);
- }
-
- // FIXME: this isn't the right API for the long term
- public void getDefaultExternalDisplayDeviceInfo(DisplayDeviceInfo info) {
- // hardcoded assuming 720p touch screen plugged into HDMI and USB
- // need to redesign this
- info.width = 1280;
- info.height = 720;
- }
-
/**
* Returns true if the device is headless.
*
@@ -214,55 +211,42 @@
}
/**
- * Sets the new logical display orientation.
+ * Overrides the display information of a particular logical display.
+ * This is used by the window manager to control the size and characteristics
+ * of the default display. It is expected to apply the requested change
+ * to the display information synchronously so that applications will immediately
+ * observe the new state.
*
* @param displayId The logical display id.
- * @param orientation One of the Surface.ROTATION_* constants.
+ * @param info The new data to be stored.
*/
- public void setDisplayOrientation(int displayId, int orientation) {
- synchronized (mLock) {
- // TODO: update mirror transforms
+ public void setDisplayInfoOverrideFromWindowManager(
+ int displayId, DisplayInfo info) {
+ synchronized (mSyncRoot) {
LogicalDisplay display = mLogicalDisplays.get(displayId);
- if (display != null && display.mPrimaryDisplayDevice != null) {
- IBinder displayToken = display.mPrimaryDisplayDevice.getDisplayToken();
- if (displayToken != null) {
- Surface.openTransaction();
- try {
- Surface.setDisplayOrientation(displayToken, orientation);
- } finally {
- Surface.closeTransaction();
- }
+ if (display != null) {
+ mTempDisplayInfo.copyFrom(display.getDisplayInfoLocked());
+ display.setDisplayInfoOverrideFromWindowManagerLocked(info);
+ if (!mTempDisplayInfo.equals(display.getDisplayInfoLocked())) {
+ sendDisplayEventLocked(displayId, DisplayManagerGlobal.EVENT_DISPLAY_CHANGED);
+ scheduleTraversalLocked();
}
-
- display.mBaseDisplayInfo.rotation = orientation;
- sendDisplayEventLocked(displayId, DisplayManagerGlobal.EVENT_DISPLAY_CHANGED);
}
}
}
/**
- * Overrides the display information of a particular logical display.
- * This is used by the window manager to control the size and characteristics
- * of the default display.
- *
- * @param displayId The logical display id.
- * @param info The new data to be stored.
+ * Called by the window manager to perform traversals while holding a
+ * surface flinger transaction.
*/
- public void setDisplayInfoOverrideFromWindowManager(int displayId, DisplayInfo info) {
- synchronized (mLock) {
- LogicalDisplay display = mLogicalDisplays.get(displayId);
- if (display != null) {
- if (info != null) {
- if (display.mOverrideDisplayInfo == null) {
- display.mOverrideDisplayInfo = new DisplayInfo();
- }
- display.mOverrideDisplayInfo.copyFrom(info);
- } else {
- display.mOverrideDisplayInfo = null;
- }
-
- sendDisplayEventLocked(displayId, DisplayManagerGlobal.EVENT_DISPLAY_CHANGED);
+ public void performTraversalInTransactionFromWindowManager() {
+ synchronized (mSyncRoot) {
+ if (!mPendingTraversal) {
+ return;
}
+ mPendingTraversal = false;
+
+ performTraversalInTransactionLocked();
}
}
@@ -271,27 +255,28 @@
*
* @param displayId The logical display id.
* @param The logical display info, or null if the display does not exist.
+ * This object must be treated as immutable.
*/
@Override // Binder call
public DisplayInfo getDisplayInfo(int displayId) {
- synchronized (mLock) {
+ synchronized (mSyncRoot) {
LogicalDisplay display = mLogicalDisplays.get(displayId);
if (display != null) {
- if (display.mOverrideDisplayInfo != null) {
- return new DisplayInfo(display.mOverrideDisplayInfo);
- }
- return new DisplayInfo(display.mBaseDisplayInfo);
+ return display.getDisplayInfoLocked();
}
return null;
}
}
+ /**
+ * Returns the list of all display ids.
+ */
@Override // Binder call
public int[] getDisplayIds() {
- synchronized (mLock) {
+ synchronized (mSyncRoot) {
final int count = mLogicalDisplays.size();
int[] displayIds = new int[count];
- for (int i = 0; i > count; i++) {
+ for (int i = 0; i < count; i++) {
displayIds[i] = mLogicalDisplays.keyAt(i);
}
return displayIds;
@@ -304,7 +289,7 @@
throw new IllegalArgumentException("listener must not be null");
}
- synchronized (mLock) {
+ synchronized (mSyncRoot) {
int callingPid = Binder.getCallingPid();
if (mCallbacks.get(callingPid) != null) {
throw new SecurityException("The calling process has already "
@@ -325,98 +310,234 @@
}
private void onCallbackDied(int pid) {
- synchronized (mLock) {
+ synchronized (mSyncRoot) {
mCallbacks.remove(pid);
}
}
- // Runs on handler.
+ private void registerDefaultDisplayAdapter() {
+ // Register default display adapter.
+ synchronized (mSyncRoot) {
+ if (mHeadless) {
+ registerDisplayAdapterLocked(new HeadlessDisplayAdapter(
+ mSyncRoot, mContext, mHandler, mDisplayAdapterListener));
+ } else {
+ registerDisplayAdapterLocked(new LocalDisplayAdapter(
+ mSyncRoot, mContext, mHandler, mDisplayAdapterListener));
+ }
+ }
+ }
+
+ private void registerAdditionalDisplayAdapters() {
+ synchronized (mSyncRoot) {
+ if (shouldRegisterNonEssentialDisplayAdaptersLocked()) {
+ registerDisplayAdapterLocked(new OverlayDisplayAdapter(
+ mSyncRoot, mContext, mHandler, mDisplayAdapterListener, mUiHandler));
+ }
+ }
+ }
+
+ private boolean shouldRegisterNonEssentialDisplayAdaptersLocked() {
+ // In safe mode, we disable non-essential display adapters to give the user
+ // an opportunity to fix broken settings or other problems that might affect
+ // system stability.
+ // In only-core mode, we disable non-essential display adapters to minimize
+ // the number of dependencies that are started while in this mode and to
+ // prevent problems that might occur due to the device being encrypted.
+ return !mSafeMode && !mOnlyCore;
+ }
+
+ private void registerDisplayAdapterLocked(DisplayAdapter adapter) {
+ mDisplayAdapters.add(adapter);
+ adapter.registerLocked();
+ }
+
private void handleDisplayDeviceAdded(DisplayDevice device) {
- synchronized (mLock) {
+ synchronized (mSyncRoot) {
if (mDisplayDevices.contains(device)) {
- Slog.w(TAG, "Attempted to add already added display device: " + device);
+ Slog.w(TAG, "Attempted to add already added display device: "
+ + device.getDisplayDeviceInfoLocked());
return;
}
mDisplayDevices.add(device);
-
- LogicalDisplay display = new LogicalDisplay(device);
- display.updateFromPrimaryDisplayDevice();
-
- boolean isDefault = (display.mPrimaryDisplayDeviceInfo.flags
- & DisplayDeviceInfo.FLAG_DEFAULT_DISPLAY) != 0;
- if (isDefault && mLogicalDisplays.get(Display.DEFAULT_DISPLAY) != null) {
- Slog.w(TAG, "Attempted to add a second default device: " + device);
- isDefault = false;
- }
-
- int displayId = isDefault ? Display.DEFAULT_DISPLAY : mNextNonDefaultDisplayId++;
- mLogicalDisplays.put(displayId, display);
-
- sendDisplayEventLocked(displayId, DisplayManagerGlobal.EVENT_DISPLAY_ADDED);
-
- // Wake up waitForDefaultDisplay.
- if (isDefault) {
- mLock.notifyAll();
- }
+ addLogicalDisplayLocked(device);
+ scheduleTraversalLocked();
}
}
- // Runs on handler.
private void handleDisplayDeviceChanged(DisplayDevice device) {
- synchronized (mLock) {
+ synchronized (mSyncRoot) {
if (!mDisplayDevices.contains(device)) {
- Slog.w(TAG, "Attempted to change non-existent display device: " + device);
+ Slog.w(TAG, "Attempted to change non-existent display device: "
+ + device.getDisplayDeviceInfoLocked());
return;
}
- for (int i = mLogicalDisplays.size(); i-- > 0; ) {
- LogicalDisplay display = mLogicalDisplays.valueAt(i);
- if (display.mPrimaryDisplayDevice == device) {
- final int displayId = mLogicalDisplays.keyAt(i);
- display.updateFromPrimaryDisplayDevice();
- sendDisplayEventLocked(displayId, DisplayManagerGlobal.EVENT_DISPLAY_CHANGED);
- }
+ device.applyPendingDisplayDeviceInfoChangesLocked();
+ if (updateLogicalDisplaysLocked()) {
+ scheduleTraversalLocked();
}
}
}
- // Runs on handler.
private void handleDisplayDeviceRemoved(DisplayDevice device) {
- synchronized (mLock) {
+ synchronized (mSyncRoot) {
if (!mDisplayDevices.remove(device)) {
- Slog.w(TAG, "Attempted to remove non-existent display device: " + device);
+ Slog.w(TAG, "Attempted to remove non-existent display device: "
+ + device.getDisplayDeviceInfoLocked());
return;
}
- for (int i = mLogicalDisplays.size(); i-- > 0; ) {
- LogicalDisplay display = mLogicalDisplays.valueAt(i);
- if (display.mPrimaryDisplayDevice == device) {
- final int displayId = mLogicalDisplays.keyAt(i);
- mLogicalDisplays.removeAt(i);
- sendDisplayEventLocked(displayId, DisplayManagerGlobal.EVENT_DISPLAY_REMOVED);
- }
- }
+ mRemovedDisplayDevices.add(device);
+ updateLogicalDisplaysLocked();
+ scheduleTraversalLocked();
}
}
- // Posts a message to send a display event at the next opportunity.
+ // Adds a new logical display based on the given display device.
+ // Sends notifications if needed.
+ private void addLogicalDisplayLocked(DisplayDevice device) {
+ DisplayDeviceInfo deviceInfo = device.getDisplayDeviceInfoLocked();
+ boolean isDefault = (deviceInfo.flags
+ & DisplayDeviceInfo.FLAG_DEFAULT_DISPLAY) != 0;
+ if (isDefault && mLogicalDisplays.get(Display.DEFAULT_DISPLAY) != null) {
+ Slog.w(TAG, "Ignoring attempt to add a second default display: " + deviceInfo);
+ isDefault = false;
+ }
+
+ final int displayId = assignDisplayIdLocked(isDefault);
+ final int layerStack = assignLayerStackLocked(displayId);
+
+ LogicalDisplay display = new LogicalDisplay(layerStack, device);
+ display.updateLocked(mDisplayDevices);
+ if (!display.isValidLocked()) {
+ // This should never happen currently.
+ Slog.w(TAG, "Ignoring display device because the logical display "
+ + "created from it was not considered valid: " + deviceInfo);
+ return;
+ }
+
+ mLogicalDisplays.put(displayId, display);
+
+ // Wake up waitForDefaultDisplay.
+ if (isDefault) {
+ mSyncRoot.notifyAll();
+ }
+
+ sendDisplayEventLocked(displayId, DisplayManagerGlobal.EVENT_DISPLAY_ADDED);
+ }
+
+ private int assignDisplayIdLocked(boolean isDefault) {
+ return isDefault ? Display.DEFAULT_DISPLAY : mNextNonDefaultDisplayId++;
+ }
+
+ private int assignLayerStackLocked(int displayId) {
+ // Currently layer stacks and display ids are the same.
+ // This need not be the case.
+ return displayId;
+ }
+
+ // Updates all existing logical displays given the current set of display devices.
+ // Removes invalid logical displays.
+ // Sends notifications if needed.
+ private boolean updateLogicalDisplaysLocked() {
+ boolean changed = false;
+ for (int i = mLogicalDisplays.size(); i-- > 0; ) {
+ final int displayId = mLogicalDisplays.keyAt(i);
+ LogicalDisplay display = mLogicalDisplays.valueAt(i);
+
+ mTempDisplayInfo.copyFrom(display.getDisplayInfoLocked());
+ display.updateLocked(mDisplayDevices);
+ if (!display.isValidLocked()) {
+ mLogicalDisplays.removeAt(i);
+ sendDisplayEventLocked(displayId, DisplayManagerGlobal.EVENT_DISPLAY_REMOVED);
+ changed = true;
+ } else if (!mTempDisplayInfo.equals(display.getDisplayInfoLocked())) {
+ sendDisplayEventLocked(displayId, DisplayManagerGlobal.EVENT_DISPLAY_CHANGED);
+ changed = true;
+ }
+ }
+ return changed;
+ }
+
+ private void performTraversalInTransactionLocked() {
+ // Perform one last traversal for each removed display device.
+ final int removedCount = mRemovedDisplayDevices.size();
+ for (int i = 0; i < removedCount; i++) {
+ DisplayDevice device = mRemovedDisplayDevices.get(i);
+ device.performTraversalInTransactionLocked();
+ }
+ mRemovedDisplayDevices.clear();
+
+ // Configure each display device.
+ final int count = mDisplayDevices.size();
+ for (int i = 0; i < count; i++) {
+ DisplayDevice device = mDisplayDevices.get(i);
+ configureDisplayInTransactionLocked(device);
+ device.performTraversalInTransactionLocked();
+ }
+ }
+
+ private void configureDisplayInTransactionLocked(DisplayDevice device) {
+ // TODO: add a proper per-display mirroring control
+ boolean isMirroring = SystemProperties.getBoolean("debug.display.mirror", true);
+
+ // Find the logical display that the display device is showing.
+ LogicalDisplay display = null;
+ if (!isMirroring) {
+ display = findLogicalDisplayForDeviceLocked(device);
+ }
+ if (display == null) {
+ display = mLogicalDisplays.get(Display.DEFAULT_DISPLAY);
+ }
+
+ // Apply the logical display configuration to the display device.
+ if (display == null) {
+ // TODO: no logical display for the device, blank it
+ Slog.d(TAG, "Missing logical display to use for physical display device: "
+ + device.getDisplayDeviceInfoLocked());
+ } else {
+ display.configureDisplayInTransactionLocked(device);
+ }
+ }
+
+ private LogicalDisplay findLogicalDisplayForDeviceLocked(DisplayDevice device) {
+ final int count = mLogicalDisplays.size();
+ for (int i = 0; i < count; i++) {
+ LogicalDisplay display = mLogicalDisplays.valueAt(i);
+ if (display.getPrimaryDisplayDeviceLocked() == device) {
+ return display;
+ }
+ }
+ return null;
+ }
+
private void sendDisplayEventLocked(int displayId, int event) {
Message msg = mHandler.obtainMessage(MSG_DELIVER_DISPLAY_EVENT, displayId, event);
mHandler.sendMessage(msg);
}
- // Runs on handler.
- // This method actually sends display event notifications.
- // Note that it must be very careful not to be holding the lock while sending
- // is in progress.
+ // Requests that performTraversalsInTransactionFromWindowManager be called at a
+ // later time to apply changes to surfaces and displays.
+ private void scheduleTraversalLocked() {
+ if (!mPendingTraversal && mWindowManagerFuncs != null) {
+ mPendingTraversal = true;
+ mHandler.sendEmptyMessage(MSG_REQUEST_TRAVERSAL);
+ }
+ }
+
+ // Runs on Handler thread.
+ // Delivers display event notifications to callbacks.
private void deliverDisplayEvent(int displayId, int event) {
if (DEBUG) {
- Slog.d(TAG, "Delivering display event: displayId=" + displayId + ", event=" + event);
+ Slog.d(TAG, "Delivering display event: displayId="
+ + displayId + ", event=" + event);
}
+ // Grab the lock and copy the callbacks.
final int count;
- synchronized (mLock) {
+ synchronized (mSyncRoot) {
count = mCallbacks.size();
mTempCallbacks.clear();
for (int i = 0; i < count; i++) {
@@ -424,6 +545,7 @@
}
}
+ // After releasing the lock, send the notifications out.
for (int i = 0; i < count; i++) {
mTempCallbacks.get(i).notifyDisplayEventAsync(displayId, event);
}
@@ -443,30 +565,22 @@
pw.println("DISPLAY MANAGER (dumpsys display)");
pw.println(" mHeadless=" + mHeadless);
- mHandler.runWithScissors(new Runnable() {
- @Override
- public void run() {
- dumpLocal(pw);
- }
- });
- }
-
- // Runs on handler.
- private void dumpLocal(PrintWriter pw) {
- synchronized (mLock) {
+ synchronized (mSyncRoot) {
IndentingPrintWriter ipw = new IndentingPrintWriter(pw, " ");
+ ipw.increaseIndent();
pw.println();
pw.println("Display Adapters: size=" + mDisplayAdapters.size());
for (DisplayAdapter adapter : mDisplayAdapters) {
pw.println(" " + adapter.getName());
- adapter.dump(ipw);
+ adapter.dumpLocked(ipw);
}
pw.println();
pw.println("Display Devices: size=" + mDisplayDevices.size());
for (DisplayDevice device : mDisplayDevices) {
- pw.println(" " + device);
+ pw.println(" " + device.getDisplayDeviceInfoLocked());
+ device.dumpLocked(ipw);
}
final int logicalDisplayCount = mLogicalDisplays.size();
@@ -476,14 +590,31 @@
int displayId = mLogicalDisplays.keyAt(i);
LogicalDisplay display = mLogicalDisplays.valueAt(i);
pw.println(" Display " + displayId + ":");
- pw.println(" mPrimaryDisplayDevice=" + display.mPrimaryDisplayDevice);
- pw.println(" mBaseDisplayInfo=" + display.mBaseDisplayInfo);
- pw.println(" mOverrideDisplayInfo="
- + display.mOverrideDisplayInfo);
+ display.dumpLocked(ipw);
}
}
}
+ /**
+ * This is the object that everything in the display manager locks on.
+ * We make it an inner class within the {@link DisplayManagerService} to so that it is
+ * clear that the object belongs to the display manager service and that it is
+ * a unique object with a special purpose.
+ */
+ public static final class SyncRoot {
+ }
+
+ /**
+ * Private interface to the window manager.
+ */
+ public interface WindowManagerFuncs {
+ /**
+ * Request that the window manager call {@link #performTraversalInTransaction}
+ * within a surface transaction at a later time.
+ */
+ void requestTraversal();
+ }
+
private final class DisplayManagerHandler extends Handler {
public DisplayManagerHandler(Looper looper) {
super(looper, null, true /*async*/);
@@ -503,6 +634,10 @@
case MSG_DELIVER_DISPLAY_EVENT:
deliverDisplayEvent(msg.arg1, msg.arg2);
break;
+
+ case MSG_REQUEST_TRAVERSAL:
+ mWindowManagerFuncs.requestTraversal();
+ break;
}
}
}
@@ -524,6 +659,13 @@
break;
}
}
+
+ @Override
+ public void onTraversalRequested() {
+ synchronized (mSyncRoot) {
+ scheduleTraversalLocked();
+ }
+ }
}
private final class CallbackRecord implements DeathRecipient {
@@ -553,46 +695,4 @@
}
}
}
-
- /**
- * Each logical display is primarily associated with one display device.
- * The primary display device is nominally responsible for the basic properties
- * of the logical display such as its size, refresh rate, and dpi.
- *
- * A logical display may be mirrored onto other display devices besides its
- * primary display device, but it always remains bound to its primary.
- * Note that the contents of a logical display may not always be visible, even
- * on its primary display device, such as in the case where the logical display's
- * primary display device is currently mirroring content from a different logical display.
- */
- private final static class LogicalDisplay {
- public final DisplayInfo mBaseDisplayInfo = new DisplayInfo();
- public DisplayInfo mOverrideDisplayInfo; // set by the window manager
-
- public final DisplayDevice mPrimaryDisplayDevice;
- public final DisplayDeviceInfo mPrimaryDisplayDeviceInfo = new DisplayDeviceInfo();
-
- public LogicalDisplay(DisplayDevice primaryDisplayDevice) {
- mPrimaryDisplayDevice = primaryDisplayDevice;
- }
-
- public void updateFromPrimaryDisplayDevice() {
- // Bootstrap the logical display using its associated primary physical display.
- mPrimaryDisplayDevice.getInfo(mPrimaryDisplayDeviceInfo);
-
- mBaseDisplayInfo.appWidth = mPrimaryDisplayDeviceInfo.width;
- mBaseDisplayInfo.appHeight = mPrimaryDisplayDeviceInfo.height;
- mBaseDisplayInfo.logicalWidth = mPrimaryDisplayDeviceInfo.width;
- mBaseDisplayInfo.logicalHeight = mPrimaryDisplayDeviceInfo.height;
- mBaseDisplayInfo.rotation = Surface.ROTATION_0;
- mBaseDisplayInfo.refreshRate = mPrimaryDisplayDeviceInfo.refreshRate;
- mBaseDisplayInfo.logicalDensityDpi = mPrimaryDisplayDeviceInfo.densityDpi;
- mBaseDisplayInfo.physicalXDpi = mPrimaryDisplayDeviceInfo.xDpi;
- mBaseDisplayInfo.physicalYDpi = mPrimaryDisplayDeviceInfo.yDpi;
- mBaseDisplayInfo.smallestNominalAppWidth = mPrimaryDisplayDeviceInfo.width;
- mBaseDisplayInfo.smallestNominalAppHeight = mPrimaryDisplayDeviceInfo.height;
- mBaseDisplayInfo.largestNominalAppWidth = mPrimaryDisplayDeviceInfo.width;
- mBaseDisplayInfo.largestNominalAppHeight = mPrimaryDisplayDeviceInfo.height;
- }
- }
}
diff --git a/services/java/com/android/server/display/HeadlessDisplayAdapter.java b/services/java/com/android/server/display/HeadlessDisplayAdapter.java
index f5c78b9..3aaacf1 100644
--- a/services/java/com/android/server/display/HeadlessDisplayAdapter.java
+++ b/services/java/com/android/server/display/HeadlessDisplayAdapter.java
@@ -17,44 +17,52 @@
package com.android.server.display;
import android.content.Context;
+import android.os.Handler;
import android.util.DisplayMetrics;
/**
* Provides a fake default display for headless systems.
* <p>
- * Display adapters are not thread-safe and must only be accessed
- * on the display manager service's handler thread.
+ * Display adapters are guarded by the {@link DisplayManagerService.SyncRoot} lock.
* </p>
*/
-public final class HeadlessDisplayAdapter extends DisplayAdapter {
+final class HeadlessDisplayAdapter extends DisplayAdapter {
private static final String TAG = "HeadlessDisplayAdapter";
- public HeadlessDisplayAdapter(Context context) {
- super(context, TAG);
+ public HeadlessDisplayAdapter(DisplayManagerService.SyncRoot syncRoot,
+ Context context, Handler handler, Listener listener) {
+ super(syncRoot, context, handler, listener, TAG);
}
@Override
- protected void onRegister() {
- sendDisplayDeviceEvent(new HeadlessDisplayDevice(), DISPLAY_DEVICE_EVENT_ADDED);
+ public void registerLocked() {
+ super.registerLocked();
+ sendDisplayDeviceEventLocked(new HeadlessDisplayDevice(), DISPLAY_DEVICE_EVENT_ADDED);
}
private final class HeadlessDisplayDevice extends DisplayDevice {
+ private DisplayDeviceInfo mInfo;
+
public HeadlessDisplayDevice() {
super(HeadlessDisplayAdapter.this, null);
}
@Override
- public void getInfo(DisplayDeviceInfo outInfo) {
- outInfo.name = getContext().getResources().getString(
- com.android.internal.R.string.display_manager_built_in_display_name);
- outInfo.width = 640;
- outInfo.height = 480;
- outInfo.refreshRate = 60;
- outInfo.densityDpi = DisplayMetrics.DENSITY_DEFAULT;
- outInfo.xDpi = 160;
- outInfo.yDpi = 160;
- outInfo.flags = DisplayDeviceInfo.FLAG_DEFAULT_DISPLAY
- | DisplayDeviceInfo.FLAG_SECURE;
+ public DisplayDeviceInfo getDisplayDeviceInfoLocked() {
+ if (mInfo == null) {
+ mInfo = new DisplayDeviceInfo();
+ mInfo.name = getContext().getResources().getString(
+ com.android.internal.R.string.display_manager_built_in_display_name);
+ mInfo.width = 640;
+ mInfo.height = 480;
+ mInfo.refreshRate = 60;
+ mInfo.densityDpi = DisplayMetrics.DENSITY_DEFAULT;
+ mInfo.xDpi = 160;
+ mInfo.yDpi = 160;
+ mInfo.flags = DisplayDeviceInfo.FLAG_DEFAULT_DISPLAY
+ | DisplayDeviceInfo.FLAG_SECURE;
+ }
+ return mInfo;
}
}
}
diff --git a/services/java/com/android/server/display/LocalDisplayAdapter.java b/services/java/com/android/server/display/LocalDisplayAdapter.java
index 73544fc..4a8829a 100644
--- a/services/java/com/android/server/display/LocalDisplayAdapter.java
+++ b/services/java/com/android/server/display/LocalDisplayAdapter.java
@@ -17,58 +17,132 @@
package com.android.server.display;
import android.content.Context;
+import android.os.Handler;
import android.os.IBinder;
+import android.util.SparseArray;
import android.view.Surface;
import android.view.Surface.PhysicalDisplayInfo;
+import java.io.PrintWriter;
+
/**
* A display adapter for the local displays managed by Surface Flinger.
* <p>
- * Display adapters are not thread-safe and must only be accessed
- * on the display manager service's handler thread.
+ * Display adapters are guarded by the {@link DisplayManagerService.SyncRoot} lock.
* </p>
*/
-public final class LocalDisplayAdapter extends DisplayAdapter {
+final class LocalDisplayAdapter extends DisplayAdapter {
private static final String TAG = "LocalDisplayAdapter";
- public LocalDisplayAdapter(Context context) {
- super(context, TAG);
+ private static final int[] BUILT_IN_DISPLAY_IDS_TO_SCAN = new int[] {
+ Surface.BUILT_IN_DISPLAY_ID_MAIN,
+ Surface.BUILT_IN_DISPLAY_ID_HDMI,
+ };
+
+ private final SparseArray<LocalDisplayDevice> mDevices =
+ new SparseArray<LocalDisplayDevice>();
+
+ private final PhysicalDisplayInfo mTempPhys = new PhysicalDisplayInfo();
+
+ public LocalDisplayAdapter(DisplayManagerService.SyncRoot syncRoot,
+ Context context, Handler handler, Listener listener) {
+ super(syncRoot, context, handler, listener, TAG);
}
@Override
- protected void onRegister() {
+ public void registerLocked() {
// TODO: listen for notifications from Surface Flinger about
// built-in displays being added or removed and rescan as needed.
- IBinder displayToken = Surface.getBuiltInDisplay(Surface.BUILT_IN_DISPLAY_ID_MAIN);
- sendDisplayDeviceEvent(new LocalDisplayDevice(displayToken, true),
- DISPLAY_DEVICE_EVENT_ADDED);
+ super.registerLocked();
+ scanDisplaysLocked();
+ }
+
+ private void scanDisplaysLocked() {
+ for (int builtInDisplayId : BUILT_IN_DISPLAY_IDS_TO_SCAN) {
+ IBinder displayToken = Surface.getBuiltInDisplay(builtInDisplayId);
+ if (displayToken != null && Surface.getDisplayInfo(displayToken, mTempPhys)) {
+ LocalDisplayDevice device = mDevices.get(builtInDisplayId);
+ if (device == null) {
+ // Display was added.
+ device = new LocalDisplayDevice(displayToken, builtInDisplayId, mTempPhys);
+ mDevices.put(builtInDisplayId, device);
+ sendDisplayDeviceEventLocked(device, DISPLAY_DEVICE_EVENT_ADDED);
+ } else if (device.updatePhysicalDisplayInfoLocked(mTempPhys)) {
+ // Display properties changed.
+ sendDisplayDeviceEventLocked(device, DISPLAY_DEVICE_EVENT_CHANGED);
+ }
+ } else {
+ LocalDisplayDevice device = mDevices.get(builtInDisplayId);
+ if (device != null) {
+ // Display was removed.
+ mDevices.remove(builtInDisplayId);
+ sendDisplayDeviceEventLocked(device, DISPLAY_DEVICE_EVENT_REMOVED);
+ }
+ }
+ }
}
private final class LocalDisplayDevice extends DisplayDevice {
- private final boolean mIsDefault;
+ private final int mBuiltInDisplayId;
+ private final PhysicalDisplayInfo mPhys;
- public LocalDisplayDevice(IBinder displayToken, boolean isDefault) {
+ private DisplayDeviceInfo mInfo;
+ private boolean mHavePendingChanges;
+
+ public LocalDisplayDevice(IBinder displayToken, int builtInDisplayId,
+ PhysicalDisplayInfo phys) {
super(LocalDisplayAdapter.this, displayToken);
- mIsDefault = isDefault;
+ mBuiltInDisplayId = builtInDisplayId;
+ mPhys = new PhysicalDisplayInfo(phys);
+ }
+
+ public boolean updatePhysicalDisplayInfoLocked(PhysicalDisplayInfo phys) {
+ if (!mPhys.equals(phys)) {
+ mPhys.copyFrom(phys);
+ mHavePendingChanges = true;
+ return true;
+ }
+ return false;
}
@Override
- public void getInfo(DisplayDeviceInfo outInfo) {
- PhysicalDisplayInfo phys = new PhysicalDisplayInfo();
- Surface.getDisplayInfo(getDisplayToken(), phys);
-
- outInfo.name = getContext().getResources().getString(
- com.android.internal.R.string.display_manager_built_in_display_name);
- outInfo.width = phys.width;
- outInfo.height = phys.height;
- outInfo.refreshRate = phys.refreshRate;
- outInfo.densityDpi = (int)(phys.density * 160 + 0.5f);
- outInfo.xDpi = phys.xDpi;
- outInfo.yDpi = phys.yDpi;
- if (mIsDefault) {
- outInfo.flags = DisplayDeviceInfo.FLAG_DEFAULT_DISPLAY
- | DisplayDeviceInfo.FLAG_SECURE;
+ public void applyPendingDisplayDeviceInfoChangesLocked() {
+ if (mHavePendingChanges) {
+ mInfo = null;
+ mHavePendingChanges = false;
}
}
+
+ @Override
+ public DisplayDeviceInfo getDisplayDeviceInfoLocked() {
+ if (mInfo == null) {
+ mInfo = new DisplayDeviceInfo();
+ mInfo.width = mPhys.width;
+ mInfo.height = mPhys.height;
+ mInfo.refreshRate = mPhys.refreshRate;
+ mInfo.densityDpi = (int)(mPhys.density * 160 + 0.5f);
+ mInfo.xDpi = mPhys.xDpi;
+ mInfo.yDpi = mPhys.yDpi;
+ if (mBuiltInDisplayId == Surface.BUILT_IN_DISPLAY_ID_MAIN) {
+ mInfo.name = getContext().getResources().getString(
+ com.android.internal.R.string.display_manager_built_in_display_name);
+ mInfo.flags = DisplayDeviceInfo.FLAG_DEFAULT_DISPLAY
+ | DisplayDeviceInfo.FLAG_SECURE
+ | DisplayDeviceInfo.FLAG_SUPPORTS_ROTATION;
+ } else {
+ mInfo.name = getContext().getResources().getString(
+ com.android.internal.R.string.display_manager_hdmi_display_name);
+ mInfo.flags = DisplayDeviceInfo.FLAG_SECURE;
+ }
+ }
+ return mInfo;
+ }
+
+ @Override
+ public void dumpLocked(PrintWriter pw) {
+ super.dumpLocked(pw);
+ pw.println("mBuiltInDisplayId=" + mBuiltInDisplayId);
+ pw.println("mPhys=" + mPhys);
+ }
}
}
diff --git a/services/java/com/android/server/display/LogicalDisplay.java b/services/java/com/android/server/display/LogicalDisplay.java
new file mode 100644
index 0000000..c864189
--- /dev/null
+++ b/services/java/com/android/server/display/LogicalDisplay.java
@@ -0,0 +1,266 @@
+/*
+ * 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.display;
+
+import android.graphics.Rect;
+import android.view.DisplayInfo;
+import android.view.Surface;
+
+import java.io.PrintWriter;
+import java.util.List;
+
+import libcore.util.Objects;
+
+/**
+ * Describes how a logical display is configured.
+ * <p>
+ * At this time, we only support logical displays that are coupled to a particular
+ * primary display device from which the logical display derives its basic properties
+ * such as its size, density and refresh rate.
+ * </p><p>
+ * A logical display may be mirrored onto multiple display devices in addition to its
+ * primary display device. Note that the contents of a logical display may not
+ * always be visible, even on its primary display device, such as in the case where
+ * the primary display device is currently mirroring content from a different
+ * logical display.
+ * </p><p>
+ * This object is designed to encapsulate as much of the policy of logical
+ * displays as possible. The idea is to make it easy to implement new kinds of
+ * logical displays mostly by making local changes to this class.
+ * </p><p>
+ * Note: The display manager architecture does not actually require logical displays
+ * to be associated with any individual display device. Logical displays and
+ * display devices are orthogonal concepts. Some mapping will exist between
+ * logical displays and display devices but it can be many-to-many and
+ * and some might have no relation at all.
+ * </p><p>
+ * Logical displays are guarded by the {@link DisplayManagerService.SyncRoot} lock.
+ * </p>
+ */
+final class LogicalDisplay {
+ private final DisplayInfo mBaseDisplayInfo = new DisplayInfo();
+
+ private final int mLayerStack;
+ private DisplayInfo mOverrideDisplayInfo; // set by the window manager
+ private DisplayInfo mInfo;
+
+ // The display device that this logical display is based on and which
+ // determines the base metrics that it uses.
+ private DisplayDevice mPrimaryDisplayDevice;
+ private DisplayDeviceInfo mPrimaryDisplayDeviceInfo;
+
+ // Temporary rectangle used when needed.
+ private final Rect mTempLayerStackRect = new Rect();
+ private final Rect mTempDisplayRect = new Rect();
+
+ public LogicalDisplay(int layerStack, DisplayDevice primaryDisplayDevice) {
+ mLayerStack = layerStack;
+ mPrimaryDisplayDevice = primaryDisplayDevice;
+ }
+
+ /**
+ * Gets the primary display device associated with this logical display.
+ *
+ * @return The primary display device.
+ */
+ public DisplayDevice getPrimaryDisplayDeviceLocked() {
+ return mPrimaryDisplayDevice;
+ }
+
+ /**
+ * Gets information about the logical display.
+ *
+ * @return The device info, which should be treated as immutable by the caller.
+ * The logical display should allocate a new display info object whenever
+ * the data changes.
+ */
+ public DisplayInfo getDisplayInfoLocked() {
+ if (mInfo == null) {
+ mInfo = new DisplayInfo();
+ if (mOverrideDisplayInfo != null) {
+ mInfo.copyFrom(mOverrideDisplayInfo);
+ mInfo.layerStack = mBaseDisplayInfo.layerStack;
+ mInfo.name = mBaseDisplayInfo.name;
+ } else {
+ mInfo.copyFrom(mBaseDisplayInfo);
+ }
+ }
+ return mInfo;
+ }
+
+ /**
+ * Sets overridden logical display information from the window manager.
+ * This method can be used to adjust application insets, rotation, and other
+ * properties that the window manager takes care of.
+ *
+ * @param info The logical display information, may be null.
+ */
+ public void setDisplayInfoOverrideFromWindowManagerLocked(DisplayInfo info) {
+ if (info != null) {
+ if (mOverrideDisplayInfo == null) {
+ mOverrideDisplayInfo = new DisplayInfo(info);
+ mInfo = null;
+ } else if (!mOverrideDisplayInfo.equals(info)) {
+ mOverrideDisplayInfo.copyFrom(info);
+ mInfo = null;
+ }
+ } else if (mOverrideDisplayInfo != null) {
+ mOverrideDisplayInfo = null;
+ mInfo = null;
+ }
+ }
+
+ /**
+ * Returns true if the logical display is in a valid state.
+ * This method should be checked after calling {@link #update} to handle the
+ * case where a logical display should be removed because all of its associated
+ * display devices are gone or if it is otherwise no longer needed.
+ *
+ * @return True if the logical display is still valid.
+ */
+ public boolean isValidLocked() {
+ return mPrimaryDisplayDevice != null;
+ }
+
+ /**
+ * Updates the state of the logical display based on the available display devices.
+ * The logical display might become invalid if it is attached to a display device
+ * that no longer exists.
+ *
+ * @param devices The list of all connected display devices.
+ */
+ public void updateLocked(List<DisplayDevice> devices) {
+ // Nothing to update if already invalid.
+ if (mPrimaryDisplayDevice == null) {
+ return;
+ }
+
+ // Check whether logical display has become invalid.
+ if (!devices.contains(mPrimaryDisplayDevice)) {
+ mPrimaryDisplayDevice = null;
+ return;
+ }
+
+ // Bootstrap the logical display using its associated primary physical display.
+ // We might use more elaborate configurations later. It's possible that the
+ // configuration of several physical displays might be used to determine the
+ // logical display that they are sharing. (eg. Adjust size for pixel-perfect
+ // mirroring over HDMI.)
+ DisplayDeviceInfo deviceInfo = mPrimaryDisplayDevice.getDisplayDeviceInfoLocked();
+ if (!Objects.equal(mPrimaryDisplayDeviceInfo, deviceInfo)) {
+ mBaseDisplayInfo.layerStack = mLayerStack;
+ mBaseDisplayInfo.name = deviceInfo.name;
+ mBaseDisplayInfo.appWidth = deviceInfo.width;
+ mBaseDisplayInfo.appHeight = deviceInfo.height;
+ mBaseDisplayInfo.logicalWidth = deviceInfo.width;
+ mBaseDisplayInfo.logicalHeight = deviceInfo.height;
+ mBaseDisplayInfo.rotation = Surface.ROTATION_0;
+ mBaseDisplayInfo.refreshRate = deviceInfo.refreshRate;
+ mBaseDisplayInfo.logicalDensityDpi = deviceInfo.densityDpi;
+ mBaseDisplayInfo.physicalXDpi = deviceInfo.xDpi;
+ mBaseDisplayInfo.physicalYDpi = deviceInfo.yDpi;
+ mBaseDisplayInfo.smallestNominalAppWidth = deviceInfo.width;
+ mBaseDisplayInfo.smallestNominalAppHeight = deviceInfo.height;
+ mBaseDisplayInfo.largestNominalAppWidth = deviceInfo.width;
+ mBaseDisplayInfo.largestNominalAppHeight = deviceInfo.height;
+
+ mPrimaryDisplayDeviceInfo = deviceInfo;
+ mInfo = null;
+ }
+ }
+
+ /**
+ * Applies the layer stack and transformation to the given display device
+ * so that it shows the contents of this logical display.
+ *
+ * We know that the given display device is only ever showing the contents of
+ * a single logical display, so this method is expected to blow away all of its
+ * transformation properties to make it happen regardless of what the
+ * display device was previously showing.
+ *
+ * The caller must have an open Surface transaction.
+ *
+ * The display device may not be the primary display device, in the case
+ * where the display is being mirrored.
+ *
+ * @param device The display device to modify.
+ */
+ public void configureDisplayInTransactionLocked(DisplayDevice device) {
+ final DisplayInfo displayInfo = getDisplayInfoLocked();
+ final DisplayDeviceInfo displayDeviceInfo = device.getDisplayDeviceInfoLocked();
+
+ // Set the layer stack.
+ device.setLayerStackInTransactionLocked(mLayerStack);
+
+ // Set the viewport.
+ // This is the area of the logical display that we intend to show on the
+ // display device. For now, it is always the full size of the logical display.
+ mTempLayerStackRect.set(0, 0, displayInfo.logicalWidth, displayInfo.logicalHeight);
+
+ // Set the orientation.
+ // The orientation specifies how the physical coordinate system of the display
+ // is rotated when the contents of the logical display are rendered.
+ int orientation = Surface.ROTATION_0;
+ if (device == mPrimaryDisplayDevice
+ && (displayDeviceInfo.flags & DisplayDeviceInfo.FLAG_SUPPORTS_ROTATION) != 0) {
+ orientation = displayInfo.rotation;
+ }
+
+ // Set the frame.
+ // The frame specifies the rotated physical coordinates into which the viewport
+ // is mapped. We need to take care to preserve the aspect ratio of the viewport.
+ // Currently we maximize the area to fill the display, but we could try to be
+ // more clever and match resolutions.
+ boolean rotated = (orientation == Surface.ROTATION_90
+ || orientation == Surface.ROTATION_270);
+ int physWidth = rotated ? displayDeviceInfo.height : displayDeviceInfo.width;
+ int physHeight = rotated ? displayDeviceInfo.width : displayDeviceInfo.height;
+
+ // Determine whether the width or height is more constrained to be scaled.
+ // physWidth / displayInfo.logicalWidth => letter box
+ // or physHeight / displayInfo.logicalHeight => pillar box
+ //
+ // We avoid a division (and possible floating point imprecision) here by
+ // multiplying the fractions by the product of their denominators before
+ // comparing them.
+ int displayRectWidth, displayRectHeight;
+ if (physWidth * displayInfo.logicalHeight
+ < physHeight * displayInfo.logicalWidth) {
+ // Letter box.
+ displayRectWidth = physWidth;
+ displayRectHeight = displayInfo.logicalHeight * physWidth / displayInfo.logicalWidth;
+ } else {
+ // Pillar box.
+ displayRectWidth = displayInfo.logicalWidth * physHeight / displayInfo.logicalHeight;
+ displayRectHeight = physHeight;
+ }
+ int displayRectTop = (physHeight - displayRectHeight) / 2;
+ int displayRectLeft = (physWidth - displayRectWidth) / 2;
+ mTempDisplayRect.set(displayRectLeft, displayRectTop,
+ displayRectLeft + displayRectWidth, displayRectTop + displayRectHeight);
+
+ device.setProjectionInTransactionLocked(orientation, mTempLayerStackRect, mTempDisplayRect);
+ }
+
+ public void dumpLocked(PrintWriter pw) {
+ pw.println("mLayerStack=" + mLayerStack);
+ pw.println("mPrimaryDisplayDevice=" + (mPrimaryDisplayDevice != null ?
+ mPrimaryDisplayDevice.getNameLocked() : "null"));
+ pw.println("mBaseDisplayInfo=" + mBaseDisplayInfo);
+ pw.println("mOverrideDisplayInfo=" + mOverrideDisplayInfo);
+ }
+}
\ No newline at end of file
diff --git a/services/java/com/android/server/display/OverlayDisplayAdapter.java b/services/java/com/android/server/display/OverlayDisplayAdapter.java
index b52ccf2..ea7e88de 100644
--- a/services/java/com/android/server/display/OverlayDisplayAdapter.java
+++ b/services/java/com/android/server/display/OverlayDisplayAdapter.java
@@ -19,25 +19,16 @@
import android.content.Context;
import android.database.ContentObserver;
import android.graphics.SurfaceTexture;
-import android.hardware.display.DisplayManager;
+import android.os.Handler;
import android.os.IBinder;
import android.provider.Settings;
import android.util.DisplayMetrics;
import android.util.Slog;
-import android.view.Display;
-import android.view.DisplayInfo;
import android.view.Gravity;
-import android.view.LayoutInflater;
-import android.view.MotionEvent;
-import android.view.ScaleGestureDetector;
import android.view.Surface;
-import android.view.TextureView;
-import android.view.TextureView.SurfaceTextureListener;
-import android.view.View;
-import android.view.WindowManager;
-import android.widget.TextView;
import java.io.PrintWriter;
+import java.io.StringWriter;
import java.util.ArrayList;
import java.util.regex.Matcher;
import java.util.regex.Pattern;
@@ -47,13 +38,16 @@
* for development purposes. Use Development Settings to enable one or more
* overlay displays.
* <p>
- * Display adapters are not thread-safe and must only be accessed
- * on the display manager service's handler thread.
+ * This object has two different handlers (which may be the same) which must not
+ * get confused. The main handler is used to posting messages to the display manager
+ * service as usual. The UI handler is only used by the {@link OverlayDisplayWindow}.
+ * </p><p>
+ * Display adapters are guarded by the {@link DisplayManagerService.SyncRoot} lock.
* </p>
*/
-public final class OverlayDisplayAdapter extends DisplayAdapter {
- private static final String TAG = "OverlayDisplayAdapter";
- private static final boolean DEBUG = false;
+final class OverlayDisplayAdapter extends DisplayAdapter {
+ static final String TAG = "OverlayDisplayAdapter";
+ static final boolean DEBUG = false;
private static final int MIN_WIDTH = 100;
private static final int MIN_HEIGHT = 100;
@@ -63,36 +57,44 @@
private static final Pattern SETTING_PATTERN =
Pattern.compile("(\\d+)x(\\d+)/(\\d+)");
- private final ArrayList<Overlay> mOverlays = new ArrayList<Overlay>();
+ private final Handler mUiHandler;
+ private final ArrayList<OverlayDisplayHandle> mOverlays =
+ new ArrayList<OverlayDisplayHandle>();
private String mCurrentOverlaySetting = "";
- public OverlayDisplayAdapter(Context context) {
- super(context, TAG);
+ public OverlayDisplayAdapter(DisplayManagerService.SyncRoot syncRoot,
+ Context context, Handler handler, Listener listener, Handler uiHandler) {
+ super(syncRoot, context, handler, listener, TAG);
+ mUiHandler = uiHandler;
}
@Override
- public void dump(PrintWriter pw) {
+ public void dumpLocked(PrintWriter pw) {
+ super.dumpLocked(pw);
pw.println("mCurrentOverlaySetting=" + mCurrentOverlaySetting);
pw.println("mOverlays: size=" + mOverlays.size());
- for (Overlay overlay : mOverlays) {
- overlay.dump(pw);
+ for (OverlayDisplayHandle overlay : mOverlays) {
+ overlay.dumpLocked(pw);
}
}
@Override
- protected void onRegister() {
+ public void registerLocked() {
+ super.registerLocked();
getContext().getContentResolver().registerContentObserver(
Settings.System.getUriFor(Settings.Secure.OVERLAY_DISPLAY_DEVICES), true,
new ContentObserver(getHandler()) {
@Override
public void onChange(boolean selfChange) {
- updateOverlayDisplayDevices();
+ synchronized (getSyncRoot()) {
+ updateOverlayDisplayDevicesLocked();
+ }
}
});
- updateOverlayDisplayDevices();
+ updateOverlayDisplayDevicesLocked();
}
- private void updateOverlayDisplayDevices() {
+ private void updateOverlayDisplayDevicesLocked() {
String value = Settings.System.getString(getContext().getContentResolver(),
Settings.Secure.OVERLAY_DISPLAY_DEVICES);
if (value == null) {
@@ -106,19 +108,20 @@
if (!mOverlays.isEmpty()) {
Slog.i(TAG, "Dismissing all overlay display devices.");
- for (Overlay overlay : mOverlays) {
- overlay.dismiss();
+ for (OverlayDisplayHandle overlay : mOverlays) {
+ overlay.dismissLocked();
}
mOverlays.clear();
}
- int number = 1;
+ int count = 0;
for (String part : value.split(";")) {
- if (number > 4) {
- Slog.w(TAG, "Too many overlay display devices.");
- }
Matcher matcher = SETTING_PATTERN.matcher(part);
if (matcher.matches()) {
+ if (count >= 4) {
+ Slog.w(TAG, "Too many overlay display devices specified: " + value);
+ break;
+ }
try {
int width = Integer.parseInt(matcher.group(1), 10);
int height = Integer.parseInt(matcher.group(2), 10);
@@ -127,10 +130,18 @@
&& height >= MIN_HEIGHT && height <= MAX_HEIGHT
&& densityDpi >= DisplayMetrics.DENSITY_LOW
&& densityDpi <= DisplayMetrics.DENSITY_XXHIGH) {
+ int number = ++count;
+ String name = getContext().getResources().getString(
+ com.android.internal.R.string.display_manager_overlay_display_name,
+ number);
+ int gravity = chooseOverlayGravity(number);
+
Slog.i(TAG, "Showing overlay display device #" + number
- + ": width=" + width + ", height=" + height
+ + ": name=" + name + ", width=" + width + ", height=" + height
+ ", densityDpi=" + densityDpi);
- mOverlays.add(new Overlay(number++, width, height, densityDpi));
+
+ mOverlays.add(new OverlayDisplayHandle(name,
+ width, height, densityDpi, gravity));
continue;
}
} catch (NumberFormatException ex) {
@@ -138,401 +149,195 @@
} else if (part.isEmpty()) {
continue;
}
- Slog.w(TAG, "Malformed overlay display devices setting: \"" + value + "\"");
- }
-
- for (Overlay overlay : mOverlays) {
- overlay.show();
+ Slog.w(TAG, "Malformed overlay display devices setting: " + value);
}
}
- // Manages an overlay window.
- private final class Overlay {
- private final float INITIAL_SCALE = 0.5f;
- private final float MIN_SCALE = 0.3f;
- private final float MAX_SCALE = 1.0f;
- private final float WINDOW_ALPHA = 0.8f;
-
- // When true, disables support for moving and resizing the overlay.
- // The window is made non-touchable, which makes it possible to
- // directly interact with the content underneath.
- private final boolean DISABLE_MOVE_AND_RESIZE = false;
-
- private final DisplayManager mDisplayManager;
- private final WindowManager mWindowManager;
-
- private final int mNumber;
- private final int mWidth;
- private final int mHeight;
- private final int mDensityDpi;
-
- private final String mName;
- private final String mTitle;
-
- private final Display mDefaultDisplay;
- private final DisplayInfo mDefaultDisplayInfo = new DisplayInfo();
- private final IBinder mDisplayToken;
- private final OverlayDisplayDevice mDisplayDevice;
-
- private View mWindowContent;
- private WindowManager.LayoutParams mWindowParams;
- private TextureView mTextureView;
- private TextView mTitleTextView;
- private ScaleGestureDetector mScaleGestureDetector;
-
- private boolean mWindowVisible;
- private int mWindowX;
- private int mWindowY;
- private float mWindowScale;
-
- private int mLiveTranslationX;
- private int mLiveTranslationY;
- private float mLiveScale = 1.0f;
-
- private int mDragPointerId;
- private float mDragTouchX;
- private float mDragTouchY;
-
- public Overlay(int number, int width, int height, int densityDpi) {
- Context context = getContext();
- mDisplayManager = (DisplayManager)context.getSystemService(
- Context.DISPLAY_SERVICE);
- mWindowManager = (WindowManager)context.getSystemService(
- Context.WINDOW_SERVICE);
-
- mNumber = number;
- mWidth = width;
- mHeight = height;
- mDensityDpi = densityDpi;
-
- mName = context.getResources().getString(
- com.android.internal.R.string.display_manager_overlay_display_name, number);
- mTitle = context.getResources().getString(
- com.android.internal.R.string.display_manager_overlay_display_title,
- mNumber, mWidth, mHeight, mDensityDpi);
-
- mDefaultDisplay = mWindowManager.getDefaultDisplay();
- updateDefaultDisplayInfo();
-
- mDisplayToken = Surface.createDisplay(mName);
- mDisplayDevice = new OverlayDisplayDevice(mDisplayToken, mName,
- mDefaultDisplayInfo.refreshRate, mDensityDpi);
-
- createWindow();
+ private static int chooseOverlayGravity(int overlayNumber) {
+ switch (overlayNumber) {
+ case 1:
+ return Gravity.TOP | Gravity.LEFT;
+ case 2:
+ return Gravity.BOTTOM | Gravity.RIGHT;
+ case 3:
+ return Gravity.TOP | Gravity.RIGHT;
+ case 4:
+ default:
+ return Gravity.BOTTOM | Gravity.LEFT;
}
-
- public void show() {
- if (!mWindowVisible) {
- mDisplayManager.registerDisplayListener(mDisplayListener, null);
- if (!updateDefaultDisplayInfo()) {
- mDisplayManager.unregisterDisplayListener(mDisplayListener);
- return;
- }
-
- clearLiveState();
- updateWindowParams();
- mWindowManager.addView(mWindowContent, mWindowParams);
- mWindowVisible = true;
- }
- }
-
- public void dismiss() {
- if (mWindowVisible) {
- mDisplayManager.unregisterDisplayListener(mDisplayListener);
- mWindowManager.removeView(mWindowContent);
- mWindowVisible = false;
- }
- }
-
- public void relayout() {
- if (mWindowVisible) {
- updateWindowParams();
- mWindowManager.updateViewLayout(mWindowContent, mWindowParams);
- }
- }
-
- public void dump(PrintWriter pw) {
- pw.println(" #" + mNumber + ": "
- + mWidth + "x" + mHeight + ", " + mDensityDpi + " dpi");
- pw.println(" mName=" + mName);
- pw.println(" mWindowVisible=" + mWindowVisible);
- pw.println(" mWindowX=" + mWindowX);
- pw.println(" mWindowY=" + mWindowY);
- pw.println(" mWindowScale=" + mWindowScale);
- pw.println(" mWindowParams=" + mWindowParams);
- if (mTextureView != null) {
- pw.println(" mTextureView.getScaleX()=" + mTextureView.getScaleX());
- pw.println(" mTextureView.getScaleY()=" + mTextureView.getScaleY());
- }
- pw.println(" mLiveTranslationX=" + mLiveTranslationX);
- pw.println(" mLiveTranslationY=" + mLiveTranslationY);
- pw.println(" mLiveScale=" + mLiveScale);
- }
-
- private boolean updateDefaultDisplayInfo() {
- if (!mDefaultDisplay.getDisplayInfo(mDefaultDisplayInfo)) {
- Slog.w(TAG, "Cannot show overlay display because there is no "
- + "default display upon which to show it.");
- return false;
- }
- return true;
- }
-
- private void createWindow() {
- Context context = getContext();
- LayoutInflater inflater = LayoutInflater.from(context);
-
- mWindowContent = inflater.inflate(
- com.android.internal.R.layout.overlay_display_window, null);
- mWindowContent.setOnTouchListener(mOnTouchListener);
-
- mTextureView = (TextureView)mWindowContent.findViewById(
- com.android.internal.R.id.overlay_display_window_texture);
- mTextureView.setPivotX(0);
- mTextureView.setPivotY(0);
- mTextureView.getLayoutParams().width = mWidth;
- mTextureView.getLayoutParams().height = mHeight;
- mTextureView.setSurfaceTextureListener(mSurfaceTextureListener);
-
- mTitleTextView = (TextView)mWindowContent.findViewById(
- com.android.internal.R.id.overlay_display_window_title);
- mTitleTextView.setText(mTitle);
-
- mWindowParams = new WindowManager.LayoutParams(
- WindowManager.LayoutParams.TYPE_DISPLAY_OVERLAY);
- mWindowParams.flags |= WindowManager.LayoutParams.FLAG_LAYOUT_IN_SCREEN
- | WindowManager.LayoutParams.FLAG_LAYOUT_NO_LIMITS
- | WindowManager.LayoutParams.FLAG_NOT_FOCUSABLE
- | WindowManager.LayoutParams.FLAG_NOT_TOUCH_MODAL
- | WindowManager.LayoutParams.FLAG_HARDWARE_ACCELERATED;
- if (DISABLE_MOVE_AND_RESIZE) {
- mWindowParams.flags |= WindowManager.LayoutParams.FLAG_NOT_TOUCHABLE;
- }
- mWindowParams.privateFlags |=
- WindowManager.LayoutParams.PRIVATE_FLAG_FORCE_HARDWARE_ACCELERATED;
- mWindowParams.alpha = WINDOW_ALPHA;
- mWindowParams.gravity = Gravity.TOP | Gravity.LEFT;
- mWindowParams.setTitle(mTitle);
-
- mScaleGestureDetector = new ScaleGestureDetector(context, mOnScaleGestureListener);
-
- // By default, arrange the displays in the four corners.
- mWindowVisible = false;
- mWindowScale = INITIAL_SCALE;
- if (mNumber == 2 || mNumber == 3) {
- mWindowX = mDefaultDisplayInfo.logicalWidth;
- } else {
- mWindowX = 0;
- }
- if (mNumber == 2 || mNumber == 4) {
- mWindowY = mDefaultDisplayInfo.logicalHeight;
- } else {
- mWindowY = 0;
- }
- }
-
- private void updateWindowParams() {
- float scale = mWindowScale * mLiveScale;
- scale = Math.min(scale, (float)mDefaultDisplayInfo.logicalWidth / mWidth);
- scale = Math.min(scale, (float)mDefaultDisplayInfo.logicalHeight / mHeight);
- scale = Math.max(MIN_SCALE, Math.min(MAX_SCALE, scale));
-
- float offsetScale = (scale / mWindowScale - 1.0f) * 0.5f;
- int width = (int)(mWidth * scale);
- int height = (int)(mHeight * scale);
- int x = mWindowX + mLiveTranslationX - (int)(width * offsetScale);
- int y = mWindowY + mLiveTranslationY - (int)(height * offsetScale);
- x = Math.max(0, Math.min(x, mDefaultDisplayInfo.logicalWidth - width));
- y = Math.max(0, Math.min(y, mDefaultDisplayInfo.logicalHeight - height));
-
- if (DEBUG) {
- Slog.d(TAG, "updateWindowParams: scale=" + scale
- + ", offsetScale=" + offsetScale
- + ", x=" + x + ", y=" + y
- + ", width=" + width + ", height=" + height);
- }
-
- mTextureView.setScaleX(scale);
- mTextureView.setScaleY(scale);
-
- mWindowParams.x = x;
- mWindowParams.y = y;
- mWindowParams.width = width;
- mWindowParams.height = height;
- }
-
- private void saveWindowParams() {
- mWindowX = mWindowParams.x;
- mWindowY = mWindowParams.y;
- mWindowScale = mTextureView.getScaleX();
- clearLiveState();
- }
-
- private void clearLiveState() {
- mLiveTranslationX = 0;
- mLiveTranslationY = 0;
- mLiveScale = 1.0f;
- }
-
- private final DisplayManager.DisplayListener mDisplayListener =
- new DisplayManager.DisplayListener() {
- @Override
- public void onDisplayAdded(int displayId) {
- }
-
- @Override
- public void onDisplayChanged(int displayId) {
- if (displayId == mDefaultDisplay.getDisplayId()) {
- if (updateDefaultDisplayInfo()) {
- relayout();
- } else {
- dismiss();
- }
- }
- }
-
- @Override
- public void onDisplayRemoved(int displayId) {
- if (displayId == mDefaultDisplay.getDisplayId()) {
- dismiss();
- }
- }
- };
-
- private final SurfaceTextureListener mSurfaceTextureListener =
- new SurfaceTextureListener() {
- @Override
- public void onSurfaceTextureAvailable(SurfaceTexture surface, int width, int height) {
- Surface.openTransaction();
- try {
- Surface.setDisplaySurface(mDisplayToken, surface);
- } finally {
- Surface.closeTransaction();
- }
-
- mDisplayDevice.setSize(width, height);
- sendDisplayDeviceEvent(mDisplayDevice, DISPLAY_DEVICE_EVENT_ADDED);
- }
-
- @Override
- public boolean onSurfaceTextureDestroyed(SurfaceTexture surface) {
- sendDisplayDeviceEvent(mDisplayDevice, DISPLAY_DEVICE_EVENT_REMOVED);
-
- Surface.openTransaction();
- try {
- Surface.setDisplaySurface(mDisplayToken, null);
- } finally {
- Surface.closeTransaction();
- }
- return true;
- }
-
- @Override
- public void onSurfaceTextureSizeChanged(SurfaceTexture surface, int width, int height) {
- mDisplayDevice.setSize(width, height);
- sendDisplayDeviceEvent(mDisplayDevice, DISPLAY_DEVICE_EVENT_CHANGED);
- }
-
- @Override
- public void onSurfaceTextureUpdated(SurfaceTexture surface) {
- }
- };
-
- private final View.OnTouchListener mOnTouchListener = new View.OnTouchListener() {
- @Override
- public boolean onTouch(View view, MotionEvent event) {
- // Work in screen coordinates.
- final float oldX = event.getX();
- final float oldY = event.getY();
- event.setLocation(event.getRawX(), event.getRawY());
-
- mScaleGestureDetector.onTouchEvent(event);
-
- switch (event.getActionMasked()) {
- case MotionEvent.ACTION_DOWN:
- resetDrag(event);
- break;
-
- case MotionEvent.ACTION_MOVE:
- if (event.getPointerCount() == 1) {
- int index = event.findPointerIndex(mDragPointerId);
- if (index < 0) {
- resetDrag(event);
- } else {
- mLiveTranslationX = (int)(event.getX(index) - mDragTouchX);
- mLiveTranslationY = (int)(event.getY(index) - mDragTouchY);
- relayout();
- }
- }
- break;
-
- case MotionEvent.ACTION_UP:
- case MotionEvent.ACTION_CANCEL:
- saveWindowParams();
- break;
- }
-
- // Revert to window coordinates.
- event.setLocation(oldX, oldY);
- return true;
- }
-
- private void resetDrag(MotionEvent event) {
- saveWindowParams();
- mDragPointerId = event.getPointerId(0);
- mDragTouchX = event.getX();
- mDragTouchY = event.getY();
- }
- };
-
- private final ScaleGestureDetector.OnScaleGestureListener mOnScaleGestureListener =
- new ScaleGestureDetector.SimpleOnScaleGestureListener() {
- @Override
- public boolean onScaleBegin(ScaleGestureDetector detector) {
- saveWindowParams();
- mDragPointerId = -1; // cause drag to be reset
- return true;
- }
-
- @Override
- public boolean onScale(ScaleGestureDetector detector) {
- mLiveScale = detector.getScaleFactor();
- relayout();
- return false;
- }
- };
}
private final class OverlayDisplayDevice extends DisplayDevice {
private final String mName;
+ private final int mWidth;
+ private final int mHeight;
private final float mRefreshRate;
private final int mDensityDpi;
- private int mWidth;
- private int mHeight;
+
+ private SurfaceTexture mSurfaceTexture;
+ private boolean mSurfaceTextureChanged;
+
+ private DisplayDeviceInfo mInfo;
public OverlayDisplayDevice(IBinder displayToken, String name,
- float refreshRate, int densityDpi) {
+ int width, int height, float refreshRate, int densityDpi) {
super(OverlayDisplayAdapter.this, displayToken);
mName = name;
+ mWidth = width;
+ mHeight = height;
mRefreshRate = refreshRate;
mDensityDpi = densityDpi;
}
- public void setSize(int width, int height) {
- mWidth = width;
- mHeight = height;
+ public void setSurfaceTextureLocked(SurfaceTexture surfaceTexture) {
+ if (surfaceTexture != mSurfaceTexture) {
+ mSurfaceTexture = surfaceTexture;
+ mSurfaceTextureChanged = true;
+ sendTraversalRequestLocked();
+ }
}
@Override
- public void getInfo(DisplayDeviceInfo outInfo) {
- outInfo.name = mName;
- outInfo.width = mWidth;
- outInfo.height = mHeight;
- outInfo.refreshRate = mRefreshRate;
- outInfo.densityDpi = mDensityDpi;
- outInfo.xDpi = mDensityDpi;
- outInfo.yDpi = mDensityDpi;
- outInfo.flags = DisplayDeviceInfo.FLAG_SECURE;
+ public void performTraversalInTransactionLocked() {
+ if (mSurfaceTextureChanged) {
+ setSurfaceTextureInTransactionLocked(mSurfaceTexture);
+ mSurfaceTextureChanged = false;
+ }
}
+
+ @Override
+ public DisplayDeviceInfo getDisplayDeviceInfoLocked() {
+ if (mInfo == null) {
+ mInfo = new DisplayDeviceInfo();
+ mInfo.name = mName;
+ mInfo.width = mWidth;
+ mInfo.height = mHeight;
+ mInfo.refreshRate = mRefreshRate;
+ mInfo.densityDpi = mDensityDpi;
+ mInfo.xDpi = mDensityDpi;
+ mInfo.yDpi = mDensityDpi;
+ mInfo.flags = DisplayDeviceInfo.FLAG_SECURE;
+ }
+ return mInfo;
+ }
+ }
+
+ /**
+ * Functions as a handle for overlay display devices which are created and
+ * destroyed asynchronously.
+ *
+ * Guarded by the {@link DisplayManagerService.SyncRoot} lock.
+ */
+ private final class OverlayDisplayHandle implements OverlayDisplayWindow.Listener {
+ private final String mName;
+ private final int mWidth;
+ private final int mHeight;
+ private final int mDensityDpi;
+ private final int mGravity;
+
+ private OverlayDisplayWindow mWindow;
+ private OverlayDisplayDevice mDevice;
+
+ public OverlayDisplayHandle(String name,
+ int width, int height, int densityDpi, int gravity) {
+ mName = name;
+ mWidth = width;
+ mHeight = height;
+ mDensityDpi = densityDpi;
+ mGravity = gravity;
+
+ mUiHandler.post(mShowRunnable);
+ }
+
+ public void dismissLocked() {
+ mUiHandler.removeCallbacks(mShowRunnable);
+ mUiHandler.post(mDismissRunnable);
+ }
+
+ // Called on the UI thread.
+ @Override
+ public void onWindowCreated(SurfaceTexture surfaceTexture, float refreshRate) {
+ synchronized (getSyncRoot()) {
+ IBinder displayToken = Surface.createDisplay(mName);
+ mDevice = new OverlayDisplayDevice(displayToken, mName,
+ mWidth, mHeight, refreshRate, mDensityDpi);
+ mDevice.setSurfaceTextureLocked(surfaceTexture);
+
+ sendDisplayDeviceEventLocked(mDevice, DISPLAY_DEVICE_EVENT_ADDED);
+ }
+ }
+
+ // Called on the UI thread.
+ @Override
+ public void onWindowDestroyed() {
+ synchronized (getSyncRoot()) {
+ if (mDevice != null) {
+ mDevice.setSurfaceTextureLocked(null);
+
+ sendDisplayDeviceEventLocked(mDevice, DISPLAY_DEVICE_EVENT_REMOVED);
+ }
+ }
+ }
+
+ public void dumpLocked(PrintWriter pw) {
+ pw.println(" " + mName + ": ");
+ pw.println(" mWidth=" + mWidth);
+ pw.println(" mHeight=" + mHeight);
+ pw.println(" mDensityDpi=" + mDensityDpi);
+ pw.println(" mGravity=" + mGravity);
+
+ // Try to dump the window state.
+ // This call may hang if the UI thread is waiting to acquire our lock so
+ // we use a short timeout to recover just in case.
+ if (mWindow != null) {
+ final StringWriter sw = new StringWriter();
+ final OverlayDisplayWindow window = mWindow;
+ if (mUiHandler.runWithScissors(new Runnable() {
+ @Override
+ public void run() {
+ PrintWriter lpw = new PrintWriter(sw);
+ window.dump(lpw);
+ lpw.close();
+ }
+ }, 200)) {
+ for (String line : sw.toString().split("\n")) {
+ pw.println(line);
+ }
+ } else {
+ pw.println(" ... timed out while attempting to dump window state");
+ }
+ }
+ }
+
+ // Runs on the UI thread.
+ private final Runnable mShowRunnable = new Runnable() {
+ @Override
+ public void run() {
+ OverlayDisplayWindow window = new OverlayDisplayWindow(getContext(),
+ mName, mWidth, mHeight, mDensityDpi, mGravity,
+ OverlayDisplayHandle.this);
+ window.show();
+
+ synchronized (getSyncRoot()) {
+ mWindow = window;
+ }
+ }
+ };
+
+ // Runs on the UI thread.
+ private final Runnable mDismissRunnable = new Runnable() {
+ @Override
+ public void run() {
+ OverlayDisplayWindow window;
+ synchronized (getSyncRoot()) {
+ window = mWindow;
+ mWindow = null;
+ }
+
+ if (window != null) {
+ window.dismiss();
+ }
+ }
+ };
}
}
diff --git a/services/java/com/android/server/display/OverlayDisplayWindow.java b/services/java/com/android/server/display/OverlayDisplayWindow.java
new file mode 100644
index 0000000..6adfa0f
--- /dev/null
+++ b/services/java/com/android/server/display/OverlayDisplayWindow.java
@@ -0,0 +1,361 @@
+/*
+ * 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.display;
+
+import android.content.Context;
+import android.graphics.SurfaceTexture;
+import android.hardware.display.DisplayManager;
+import android.util.Slog;
+import android.view.Display;
+import android.view.DisplayInfo;
+import android.view.GestureDetector;
+import android.view.Gravity;
+import android.view.LayoutInflater;
+import android.view.MotionEvent;
+import android.view.ScaleGestureDetector;
+import android.view.TextureView;
+import android.view.View;
+import android.view.WindowManager;
+import android.view.TextureView.SurfaceTextureListener;
+import android.widget.TextView;
+
+import java.io.PrintWriter;
+
+/**
+ * Manages an overlay window on behalf of {@link OverlayDisplayAdapter}.
+ * <p>
+ * This object must only be accessed on the UI thread.
+ * No locks are held by this object and locks must not be held while making called into it.
+ * </p>
+ */
+final class OverlayDisplayWindow {
+ private static final String TAG = "OverlayDisplayWindow";
+ private static final boolean DEBUG = false;
+
+ private final float INITIAL_SCALE = 0.5f;
+ private final float MIN_SCALE = 0.3f;
+ private final float MAX_SCALE = 1.0f;
+ private final float WINDOW_ALPHA = 0.8f;
+
+ // When true, disables support for moving and resizing the overlay.
+ // The window is made non-touchable, which makes it possible to
+ // directly interact with the content underneath.
+ private final boolean DISABLE_MOVE_AND_RESIZE = false;
+
+ private final Context mContext;
+ private final String mName;
+ private final int mWidth;
+ private final int mHeight;
+ private final int mDensityDpi;
+ private final int mGravity;
+ private final Listener mListener;
+ private final String mTitle;
+
+ private final DisplayManager mDisplayManager;
+ private final WindowManager mWindowManager;
+
+
+ private final Display mDefaultDisplay;
+ private final DisplayInfo mDefaultDisplayInfo = new DisplayInfo();
+
+ private View mWindowContent;
+ private WindowManager.LayoutParams mWindowParams;
+ private TextureView mTextureView;
+ private TextView mTitleTextView;
+
+ private GestureDetector mGestureDetector;
+ private ScaleGestureDetector mScaleGestureDetector;
+
+ private boolean mWindowVisible;
+ private int mWindowX;
+ private int mWindowY;
+ private float mWindowScale;
+
+ private float mLiveTranslationX;
+ private float mLiveTranslationY;
+ private float mLiveScale = 1.0f;
+
+ public OverlayDisplayWindow(Context context, String name,
+ int width, int height, int densityDpi, int gravity, Listener listener) {
+ mContext = context;
+ mName = name;
+ mWidth = width;
+ mHeight = height;
+ mDensityDpi = densityDpi;
+ mGravity = gravity;
+ mListener = listener;
+ mTitle = context.getResources().getString(
+ com.android.internal.R.string.display_manager_overlay_display_title,
+ mName, mWidth, mHeight, mDensityDpi);
+
+ mDisplayManager = (DisplayManager)context.getSystemService(
+ Context.DISPLAY_SERVICE);
+ mWindowManager = (WindowManager)context.getSystemService(
+ Context.WINDOW_SERVICE);
+
+ mDefaultDisplay = mWindowManager.getDefaultDisplay();
+ updateDefaultDisplayInfo();
+
+ createWindow();
+ }
+
+ public void show() {
+ if (!mWindowVisible) {
+ mDisplayManager.registerDisplayListener(mDisplayListener, null);
+ if (!updateDefaultDisplayInfo()) {
+ mDisplayManager.unregisterDisplayListener(mDisplayListener);
+ return;
+ }
+
+ clearLiveState();
+ updateWindowParams();
+ mWindowManager.addView(mWindowContent, mWindowParams);
+ mWindowVisible = true;
+ }
+ }
+
+ public void dismiss() {
+ if (mWindowVisible) {
+ mDisplayManager.unregisterDisplayListener(mDisplayListener);
+ mWindowManager.removeView(mWindowContent);
+ mWindowVisible = false;
+ }
+ }
+
+ public void relayout() {
+ if (mWindowVisible) {
+ updateWindowParams();
+ mWindowManager.updateViewLayout(mWindowContent, mWindowParams);
+ }
+ }
+
+ public void dump(PrintWriter pw) {
+ pw.println(" mWindowVisible=" + mWindowVisible);
+ pw.println(" mWindowX=" + mWindowX);
+ pw.println(" mWindowY=" + mWindowY);
+ pw.println(" mWindowScale=" + mWindowScale);
+ pw.println(" mWindowParams=" + mWindowParams);
+ if (mTextureView != null) {
+ pw.println(" mTextureView.getScaleX()=" + mTextureView.getScaleX());
+ pw.println(" mTextureView.getScaleY()=" + mTextureView.getScaleY());
+ }
+ pw.println(" mLiveTranslationX=" + mLiveTranslationX);
+ pw.println(" mLiveTranslationY=" + mLiveTranslationY);
+ pw.println(" mLiveScale=" + mLiveScale);
+ }
+
+ private boolean updateDefaultDisplayInfo() {
+ if (!mDefaultDisplay.getDisplayInfo(mDefaultDisplayInfo)) {
+ Slog.w(TAG, "Cannot show overlay display because there is no "
+ + "default display upon which to show it.");
+ return false;
+ }
+ return true;
+ }
+
+ private void createWindow() {
+ LayoutInflater inflater = LayoutInflater.from(mContext);
+
+ mWindowContent = inflater.inflate(
+ com.android.internal.R.layout.overlay_display_window, null);
+ mWindowContent.setOnTouchListener(mOnTouchListener);
+
+ mTextureView = (TextureView)mWindowContent.findViewById(
+ com.android.internal.R.id.overlay_display_window_texture);
+ mTextureView.setPivotX(0);
+ mTextureView.setPivotY(0);
+ mTextureView.getLayoutParams().width = mWidth;
+ mTextureView.getLayoutParams().height = mHeight;
+ mTextureView.setOpaque(false);
+ mTextureView.setSurfaceTextureListener(mSurfaceTextureListener);
+
+ mTitleTextView = (TextView)mWindowContent.findViewById(
+ com.android.internal.R.id.overlay_display_window_title);
+ mTitleTextView.setText(mTitle);
+
+ mWindowParams = new WindowManager.LayoutParams(
+ WindowManager.LayoutParams.TYPE_DISPLAY_OVERLAY);
+ mWindowParams.flags |= WindowManager.LayoutParams.FLAG_LAYOUT_IN_SCREEN
+ | WindowManager.LayoutParams.FLAG_LAYOUT_NO_LIMITS
+ | WindowManager.LayoutParams.FLAG_NOT_FOCUSABLE
+ | WindowManager.LayoutParams.FLAG_NOT_TOUCH_MODAL
+ | WindowManager.LayoutParams.FLAG_HARDWARE_ACCELERATED;
+ if (DISABLE_MOVE_AND_RESIZE) {
+ mWindowParams.flags |= WindowManager.LayoutParams.FLAG_NOT_TOUCHABLE;
+ }
+ mWindowParams.privateFlags |=
+ WindowManager.LayoutParams.PRIVATE_FLAG_FORCE_HARDWARE_ACCELERATED;
+ mWindowParams.alpha = WINDOW_ALPHA;
+ mWindowParams.gravity = Gravity.TOP | Gravity.LEFT;
+ mWindowParams.setTitle(mTitle);
+
+ mGestureDetector = new GestureDetector(mContext, mOnGestureListener);
+ mScaleGestureDetector = new ScaleGestureDetector(mContext, mOnScaleGestureListener);
+
+ // Set the initial position and scale.
+ // The position and scale will be clamped when the display is first shown.
+ mWindowX = (mGravity & Gravity.LEFT) == Gravity.LEFT ?
+ 0 : mDefaultDisplayInfo.logicalWidth;
+ mWindowY = (mGravity & Gravity.TOP) == Gravity.TOP ?
+ 0 : mDefaultDisplayInfo.logicalHeight;
+ mWindowScale = INITIAL_SCALE;
+ }
+
+ private void updateWindowParams() {
+ float scale = mWindowScale * mLiveScale;
+ scale = Math.min(scale, (float)mDefaultDisplayInfo.logicalWidth / mWidth);
+ scale = Math.min(scale, (float)mDefaultDisplayInfo.logicalHeight / mHeight);
+ scale = Math.max(MIN_SCALE, Math.min(MAX_SCALE, scale));
+
+ float offsetScale = (scale / mWindowScale - 1.0f) * 0.5f;
+ int width = (int)(mWidth * scale);
+ int height = (int)(mHeight * scale);
+ int x = (int)(mWindowX + mLiveTranslationX - width * offsetScale);
+ int y = (int)(mWindowY + mLiveTranslationY - height * offsetScale);
+ x = Math.max(0, Math.min(x, mDefaultDisplayInfo.logicalWidth - width));
+ y = Math.max(0, Math.min(y, mDefaultDisplayInfo.logicalHeight - height));
+
+ if (DEBUG) {
+ Slog.d(TAG, "updateWindowParams: scale=" + scale
+ + ", offsetScale=" + offsetScale
+ + ", x=" + x + ", y=" + y
+ + ", width=" + width + ", height=" + height);
+ }
+
+ mTextureView.setScaleX(scale);
+ mTextureView.setScaleY(scale);
+
+ mWindowParams.x = x;
+ mWindowParams.y = y;
+ mWindowParams.width = width;
+ mWindowParams.height = height;
+ }
+
+ private void saveWindowParams() {
+ mWindowX = mWindowParams.x;
+ mWindowY = mWindowParams.y;
+ mWindowScale = mTextureView.getScaleX();
+ clearLiveState();
+ }
+
+ private void clearLiveState() {
+ mLiveTranslationX = 0f;
+ mLiveTranslationY = 0f;
+ mLiveScale = 1.0f;
+ }
+
+ private final DisplayManager.DisplayListener mDisplayListener =
+ new DisplayManager.DisplayListener() {
+ @Override
+ public void onDisplayAdded(int displayId) {
+ }
+
+ @Override
+ public void onDisplayChanged(int displayId) {
+ if (displayId == mDefaultDisplay.getDisplayId()) {
+ if (updateDefaultDisplayInfo()) {
+ relayout();
+ } else {
+ dismiss();
+ }
+ }
+ }
+
+ @Override
+ public void onDisplayRemoved(int displayId) {
+ if (displayId == mDefaultDisplay.getDisplayId()) {
+ dismiss();
+ }
+ }
+ };
+
+ private final SurfaceTextureListener mSurfaceTextureListener =
+ new SurfaceTextureListener() {
+ @Override
+ public void onSurfaceTextureAvailable(SurfaceTexture surface, int width, int height) {
+ mListener.onWindowCreated(surface, mDefaultDisplayInfo.refreshRate);
+ }
+
+ @Override
+ public boolean onSurfaceTextureDestroyed(SurfaceTexture surface) {
+ mListener.onWindowDestroyed();
+ return true;
+ }
+
+ @Override
+ public void onSurfaceTextureSizeChanged(SurfaceTexture surface, int width, int height) {
+ }
+
+ @Override
+ public void onSurfaceTextureUpdated(SurfaceTexture surface) {
+ }
+ };
+
+ private final View.OnTouchListener mOnTouchListener = new View.OnTouchListener() {
+ @Override
+ public boolean onTouch(View view, MotionEvent event) {
+ // Work in screen coordinates.
+ final float oldX = event.getX();
+ final float oldY = event.getY();
+ event.setLocation(event.getRawX(), event.getRawY());
+
+ mGestureDetector.onTouchEvent(event);
+ mScaleGestureDetector.onTouchEvent(event);
+
+ switch (event.getActionMasked()) {
+ case MotionEvent.ACTION_UP:
+ case MotionEvent.ACTION_CANCEL:
+ saveWindowParams();
+ break;
+ }
+
+ // Revert to window coordinates.
+ event.setLocation(oldX, oldY);
+ return true;
+ }
+ };
+
+ private final GestureDetector.OnGestureListener mOnGestureListener =
+ new GestureDetector.SimpleOnGestureListener() {
+ @Override
+ public boolean onScroll(MotionEvent e1, MotionEvent e2,
+ float distanceX, float distanceY) {
+ mLiveTranslationX -= distanceX;
+ mLiveTranslationY -= distanceY;
+ relayout();
+ return true;
+ }
+ };
+
+ private final ScaleGestureDetector.OnScaleGestureListener mOnScaleGestureListener =
+ new ScaleGestureDetector.SimpleOnScaleGestureListener() {
+ @Override
+ public boolean onScale(ScaleGestureDetector detector) {
+ mLiveScale *= detector.getScaleFactor();
+ relayout();
+ return true;
+ }
+ };
+
+ /**
+ * Watches for significant changes in the overlay display window lifecycle.
+ */
+ public interface Listener {
+ public void onWindowCreated(SurfaceTexture surfaceTexture, float refreshRate);
+ public void onWindowDestroyed();
+ }
+}
\ No newline at end of file
diff --git a/services/java/com/android/server/input/InputManagerService.java b/services/java/com/android/server/input/InputManagerService.java
index 29c68eb..fd4f5fc 100644
--- a/services/java/com/android/server/input/InputManagerService.java
+++ b/services/java/com/android/server/input/InputManagerService.java
@@ -282,29 +282,28 @@
nativeReloadDeviceAliases(mPtr);
}
- public void setDisplaySize(int displayId, int width, int height,
- int externalWidth, int externalHeight) {
- if (width <= 0 || height <= 0 || externalWidth <= 0 || externalHeight <= 0) {
+ public void setDisplaySize(int displayId, int width, int height) {
+ if (width <= 0 || height <= 0) {
throw new IllegalArgumentException("Invalid display id or dimensions.");
}
if (DEBUG) {
- Slog.d(TAG, "Setting display #" + displayId + " size to " + width + "x" + height
- + " external size " + externalWidth + "x" + externalHeight);
+ Slog.d(TAG, "Setting display #" + displayId + " size to " + width + "x" + height);
}
- nativeSetDisplaySize(mPtr, displayId, width, height, externalWidth, externalHeight);
+ // FIXME: external size is deprecated
+ nativeSetDisplaySize(mPtr, displayId, width, height, 1280, 720);
}
-
- public void setDisplayOrientation(int displayId, int rotation, int externalRotation) {
+
+ public void setDisplayOrientation(int displayId, int rotation) {
if (rotation < Surface.ROTATION_0 || rotation > Surface.ROTATION_270) {
throw new IllegalArgumentException("Invalid rotation.");
}
if (DEBUG) {
- Slog.d(TAG, "Setting display #" + displayId + " orientation to rotation " + rotation
- + " external rotation " + externalRotation);
+ Slog.d(TAG, "Setting display #" + displayId + " orientation to rotation " + rotation);
}
- nativeSetDisplayOrientation(mPtr, displayId, rotation, externalRotation);
+ // FIXME: external rotation is deprecated
+ nativeSetDisplayOrientation(mPtr, displayId, rotation, Surface.ROTATION_0);
}
/**
diff --git a/services/java/com/android/server/net/NetworkPolicyManagerService.java b/services/java/com/android/server/net/NetworkPolicyManagerService.java
index c6e66cf..46bddc4 100644
--- a/services/java/com/android/server/net/NetworkPolicyManagerService.java
+++ b/services/java/com/android/server/net/NetworkPolicyManagerService.java
@@ -788,11 +788,13 @@
}
// TODO: move to NotificationManager once we can mock it
+ // XXX what to do about multi-user?
try {
final String packageName = mContext.getPackageName();
final int[] idReceived = new int[1];
mNotifManager.enqueueNotificationWithTag(
- packageName, tag, 0x0, builder.getNotification(), idReceived);
+ packageName, tag, 0x0, builder.getNotification(), idReceived,
+ UserHandle.USER_OWNER);
mActiveNotifs.add(tag);
} catch (RemoteException e) {
// ignored; service lives in system_server
@@ -822,11 +824,12 @@
PendingIntent.getBroadcast(mContext, 0, intent, PendingIntent.FLAG_UPDATE_CURRENT));
// TODO: move to NotificationManager once we can mock it
+ // XXX what to do about multi-user?
try {
final String packageName = mContext.getPackageName();
final int[] idReceived = new int[1];
mNotifManager.enqueueNotificationWithTag(packageName, tag,
- 0x0, builder.getNotification(), idReceived);
+ 0x0, builder.getNotification(), idReceived, UserHandle.USER_OWNER);
mActiveNotifs.add(tag);
} catch (RemoteException e) {
// ignored; service lives in system_server
@@ -835,10 +838,11 @@
private void cancelNotification(String tag) {
// TODO: move to NotificationManager once we can mock it
+ // XXX what to do about multi-user?
try {
final String packageName = mContext.getPackageName();
mNotifManager.cancelNotificationWithTag(
- packageName, tag, 0x0);
+ packageName, tag, 0x0, UserHandle.USER_OWNER);
} catch (RemoteException e) {
// ignored; service lives in system_server
}
diff --git a/services/java/com/android/server/pm/PackageManagerService.java b/services/java/com/android/server/pm/PackageManagerService.java
index 183f8ec..55da11f 100644
--- a/services/java/com/android/server/pm/PackageManagerService.java
+++ b/services/java/com/android/server/pm/PackageManagerService.java
@@ -208,8 +208,7 @@
/**
* Whether verification is enabled by default.
*/
- // STOPSHIP: change this to true
- private static final boolean DEFAULT_VERIFY_ENABLE = false;
+ private static final boolean DEFAULT_VERIFY_ENABLE = true;
/**
* The default maximum time to wait for the verification agent to return in
@@ -9635,7 +9634,8 @@
if (pkgList.size() > 0) {
sendResourcesChangedBroadcast(false, pkgList, uidArr, new IIntentReceiver.Stub() {
public void performReceive(Intent intent, int resultCode, String data,
- Bundle extras, boolean ordered, boolean sticky) throws RemoteException {
+ Bundle extras, boolean ordered, boolean sticky,
+ int sendingUser) throws RemoteException {
Message msg = mHandler.obtainMessage(UPDATED_MEDIA_STATUS,
reportStatus ? 1 : 0, 1, keys);
mHandler.sendMessage(msg);
diff --git a/services/java/com/android/server/pm/Settings.java b/services/java/com/android/server/pm/Settings.java
index 68b594a..5f10d44 100644
--- a/services/java/com/android/server/pm/Settings.java
+++ b/services/java/com/android/server/pm/Settings.java
@@ -745,13 +745,12 @@
}
private File getUserPackagesStateFile(int userId) {
- return new File(mSystemDir,
- "users/" + userId + "/package-restrictions.xml");
+ return new File(Environment.getUserSystemDirectory(userId), "package-restrictions.xml");
}
private File getUserPackagesStateBackupFile(int userId) {
- return new File(mSystemDir,
- "users/" + userId + "/package-restrictions-backup.xml");
+ return new File(Environment.getUserSystemDirectory(userId),
+ "package-restrictions-backup.xml");
}
void writeAllUsersPackageRestrictionsLPr() {
diff --git a/services/java/com/android/server/pm/UserManagerService.java b/services/java/com/android/server/pm/UserManagerService.java
index 492158d..a13c16e 100644
--- a/services/java/com/android/server/pm/UserManagerService.java
+++ b/services/java/com/android/server/pm/UserManagerService.java
@@ -594,6 +594,7 @@
// Update the user list
writeUserListLocked();
updateUserIdsLocked();
+ removeDirectoryRecursive(Environment.getUserSystemDirectory(userHandle));
}
}
@@ -603,6 +604,17 @@
mContext.sendBroadcast(addedIntent, android.Manifest.permission.MANAGE_USERS);
}
+ private void removeDirectoryRecursive(File parent) {
+ if (parent.isDirectory()) {
+ String[] files = parent.list();
+ for (String filename : files) {
+ File child = new File(parent, filename);
+ removeDirectoryRecursive(child);
+ }
+ }
+ parent.delete();
+ }
+
@Override
public int getUserSerialNumber(int userHandle) {
synchronized (mPackagesLock) {
diff --git a/services/java/com/android/server/power/DisplayPowerController.java b/services/java/com/android/server/power/DisplayPowerController.java
index 6b6d899..4f5561a 100644
--- a/services/java/com/android/server/power/DisplayPowerController.java
+++ b/services/java/com/android/server/power/DisplayPowerController.java
@@ -1008,7 +1008,7 @@
public void run() {
dumpLocal(pw);
}
- });
+ }, 1000);
}
private void dumpLocal(PrintWriter pw) {
diff --git a/services/java/com/android/server/wm/AppWindowAnimator.java b/services/java/com/android/server/wm/AppWindowAnimator.java
index c25f010..c6b7e0d 100644
--- a/services/java/com/android/server/wm/AppWindowAnimator.java
+++ b/services/java/com/android/server/wm/AppWindowAnimator.java
@@ -234,10 +234,8 @@
return false;
}
- mAnimator.mPendingLayoutChanges |= WindowManagerPolicy.FINISH_LAYOUT_REDO_ANIM;
- if (WindowManagerService.DEBUG_LAYOUT_REPEATS) {
- mService.debugLayoutRepeats("AppWindowToken", mAnimator.mPendingLayoutChanges);
- }
+ mAnimator.setAppLayoutChanges(this, WindowManagerPolicy.FINISH_LAYOUT_REDO_ANIM,
+ "AppWindowToken");
clearAnimation();
animating = false;
diff --git a/services/java/com/android/server/wm/DimAnimator.java b/services/java/com/android/server/wm/DimAnimator.java
index afcf339c..87a2880 100644
--- a/services/java/com/android/server/wm/DimAnimator.java
+++ b/services/java/com/android/server/wm/DimAnimator.java
@@ -212,5 +212,12 @@
mDimHeight = dimHeight;
mDimTarget = dimTarget;
}
+
+ Parameters(Parameters o) {
+ mDimWinAnimator = o.mDimWinAnimator;
+ mDimWidth = o.mDimWidth;
+ mDimHeight = o.mDimHeight;
+ mDimTarget = o.mDimTarget;
+ }
}
}
diff --git a/services/java/com/android/server/wm/DisplayContent.java b/services/java/com/android/server/wm/DisplayContent.java
index 6e5bbcb..f4964cf 100644
--- a/services/java/com/android/server/wm/DisplayContent.java
+++ b/services/java/com/android/server/wm/DisplayContent.java
@@ -62,6 +62,10 @@
final DisplayInfo mDisplayInfo = new DisplayInfo();
final Display mDisplay;
+ // Accessed directly by all users.
+ boolean layoutNeeded;
+ int pendingLayoutChanges;
+
DisplayContent(Display display) {
mDisplay = display;
mDisplayId = display.getDisplayId();
@@ -81,6 +85,7 @@
}
DisplayInfo getDisplayInfo() {
+ mDisplay.getDisplayInfo(mDisplayInfo);
return mDisplayInfo;
}
@@ -106,6 +111,7 @@
pw.print("x"); pw.print(mDisplayInfo.smallestNominalAppHeight);
pw.print("-"); pw.print(mDisplayInfo.largestNominalAppWidth);
pw.print("x"); pw.println(mDisplayInfo.largestNominalAppHeight);
+ pw.print("layoutNeeded="); pw.println(layoutNeeded);
pw.println();
}
}
diff --git a/services/java/com/android/server/wm/DragState.java b/services/java/com/android/server/wm/DragState.java
index 3fcf680..dc52fcf 100644
--- a/services/java/com/android/server/wm/DragState.java
+++ b/services/java/com/android/server/wm/DragState.java
@@ -23,13 +23,14 @@
import android.content.ClipData;
import android.content.ClipDescription;
+import android.graphics.Point;
import android.graphics.Region;
import android.os.IBinder;
import android.os.Message;
import android.os.Process;
import android.os.RemoteException;
import android.util.Slog;
-import android.view.DisplayInfo;
+import android.view.Display;
import android.view.DragEvent;
import android.view.InputChannel;
import android.view.Surface;
@@ -59,7 +60,7 @@
WindowState mTargetWindow;
ArrayList<WindowState> mNotifiedWindows;
boolean mDragInProgress;
- DisplayContent mDisplayContent;
+ Display mDisplay;
private final Region mTmpRegion = new Region();
@@ -87,10 +88,10 @@
}
/**
- * @param displayContent The display parameters associated with the window being dragged.
+ * @param display The Display that the window being dragged is on.
*/
- void register(DisplayContent displayContent) {
- mDisplayContent = displayContent;
+ void register(Display display) {
+ mDisplay = display;
if (WindowManagerService.DEBUG_DRAG) Slog.d(WindowManagerService.TAG, "registering drag input channel");
if (mClientChannel != null) {
Slog.e(WindowManagerService.TAG, "Duplicate register of drag input channel");
@@ -108,7 +109,7 @@
WindowManagerService.DEFAULT_INPUT_DISPATCHING_TIMEOUT_NANOS;
mDragWindowHandle = new InputWindowHandle(mDragApplicationHandle, null,
- mDisplayContent.getDisplayId());
+ mDisplay.getDisplayId());
mDragWindowHandle.name = "drag";
mDragWindowHandle.inputChannel = mServerChannel;
mDragWindowHandle.layer = getDragLayerLw();
@@ -132,9 +133,10 @@
// The drag window covers the entire display
mDragWindowHandle.frameLeft = 0;
mDragWindowHandle.frameTop = 0;
- DisplayInfo displayInfo = mDisplayContent.getDisplayInfo();
- mDragWindowHandle.frameRight = displayInfo.logicalWidth;
- mDragWindowHandle.frameBottom = displayInfo.logicalHeight;
+ Point p = new Point();
+ mDisplay.getRealSize(p);
+ mDragWindowHandle.frameRight = p.x;
+ mDragWindowHandle.frameBottom = p.y;
// Pause rotations before a drag.
if (WindowManagerService.DEBUG_ORIENTATION) {
@@ -187,7 +189,7 @@
Slog.d(WindowManagerService.TAG, "broadcasting DRAG_STARTED at (" + touchX + ", " + touchY + ")");
}
- final WindowList windows = mDisplayContent.getWindowList();
+ final WindowList windows = mService.getWindowList(mDisplay);
final int N = windows.size();
for (int i = 0; i < N; i++) {
sendDragStartedLw(windows.get(i), touchX, touchY, mDataDescription);
@@ -390,7 +392,7 @@
final int x = (int) xf;
final int y = (int) yf;
- final WindowList windows = mDisplayContent.getWindowList();
+ final WindowList windows = mService.getWindowList(mDisplay);
final int N = windows.size();
for (int i = N - 1; i >= 0; i--) {
WindowState child = windows.get(i);
diff --git a/services/java/com/android/server/wm/ScreenRotationAnimation.java b/services/java/com/android/server/wm/ScreenRotationAnimation.java
index acf3249..7c7d4b1 100644
--- a/services/java/com/android/server/wm/ScreenRotationAnimation.java
+++ b/services/java/com/android/server/wm/ScreenRotationAnimation.java
@@ -240,7 +240,7 @@
WindowManagerService.SHOW_SURFACE_ALLOC) Slog.i(WindowManagerService.TAG,
" FREEZE " + mSurface + ": CREATE");
- setRotation(originalRotation);
+ setRotationInTransaction(originalRotation);
} finally {
if (!inTransaction) {
Surface.closeTransaction();
@@ -260,7 +260,7 @@
return delta;
}
- void setSnapshotTransform(Matrix matrix, float alpha) {
+ private void setSnapshotTransformInTransaction(Matrix matrix, float alpha) {
if (mSurface != null) {
matrix.getValues(mTmpFloats);
mSurface.setPosition(mTmpFloats[Matrix.MTRANS_X],
@@ -303,7 +303,7 @@
}
// Must be called while in a transaction.
- private void setRotation(int rotation) {
+ private void setRotationInTransaction(int rotation) {
mCurRotation = rotation;
// Compute the transformation matrix that must be applied
@@ -313,13 +313,13 @@
createRotationMatrix(delta, mWidth, mHeight, mSnapshotInitialMatrix);
if (DEBUG_STATE) Slog.v(TAG, "**** ROTATION: " + delta);
- setSnapshotTransform(mSnapshotInitialMatrix, 1.0f);
+ setSnapshotTransformInTransaction(mSnapshotInitialMatrix, 1.0f);
}
// Must be called while in a transaction.
- public boolean setRotation(int rotation, SurfaceSession session,
+ public boolean setRotationInTransaction(int rotation, SurfaceSession session,
long maxAnimationDuration, float animationScale, int finalWidth, int finalHeight) {
- setRotation(rotation);
+ setRotationInTransaction(rotation);
if (TWO_PHASE_ANIMATION) {
return startAnimation(session, maxAnimationDuration, animationScale,
finalWidth, finalHeight, false);
@@ -515,16 +515,15 @@
WindowManagerService.TAG,
">>> OPEN TRANSACTION ScreenRotationAnimation.startAnimation");
Surface.openTransaction();
-
- // Compute the transformation matrix that must be applied
- // the the black frame to make it stay in the initial position
- // before the new screen rotation. This is different than the
- // snapshot transformation because the snapshot is always based
- // of the native orientation of the screen, not the orientation
- // we were last in.
- createRotationMatrix(delta, mOriginalWidth, mOriginalHeight, mFrameInitialMatrix);
-
try {
+ // Compute the transformation matrix that must be applied
+ // the the black frame to make it stay in the initial position
+ // before the new screen rotation. This is different than the
+ // snapshot transformation because the snapshot is always based
+ // of the native orientation of the screen, not the orientation
+ // we were last in.
+ createRotationMatrix(delta, mOriginalWidth, mOriginalHeight, mFrameInitialMatrix);
+
Rect outer = new Rect(-mOriginalWidth*1, -mOriginalHeight*1,
mOriginalWidth*2, mOriginalHeight*2);
Rect inner = new Rect(0, 0, mOriginalWidth, mOriginalHeight);
@@ -844,7 +843,7 @@
return more;
}
- void updateSurfaces() {
+ void updateSurfacesInTransaction() {
if (!mStarted) {
return;
}
@@ -884,7 +883,7 @@
}
}
- setSnapshotTransform(mSnapshotFinalMatrix, mExitTransformation.getAlpha());
+ setSnapshotTransformInTransaction(mSnapshotFinalMatrix, mExitTransformation.getAlpha());
}
public boolean stepAnimationLocked(long now) {
diff --git a/services/java/com/android/server/wm/Session.java b/services/java/com/android/server/wm/Session.java
index 1ffbecc..4038b70 100644
--- a/services/java/com/android/server/wm/Session.java
+++ b/services/java/com/android/server/wm/Session.java
@@ -280,7 +280,7 @@
// !!! FIXME: put all this heavy stuff onto the mH looper, as well as
// the actual drag event dispatch stuff in the dragstate
- mService.mDragState.register(callingWin.mDisplayContent);
+ mService.mDragState.register(callingWin.mDisplayContent.getDisplay());
mService.mInputMonitor.updateInputWindowsLw(true /*force*/);
if (!mService.mInputManager.transferTouchFocus(callingWin.mInputChannel,
mService.mDragState.mServerChannel)) {
diff --git a/services/java/com/android/server/wm/WindowAnimator.java b/services/java/com/android/server/wm/WindowAnimator.java
index 580f00d..f0b514c 100644
--- a/services/java/com/android/server/wm/WindowAnimator.java
+++ b/services/java/com/android/server/wm/WindowAnimator.java
@@ -16,12 +16,12 @@
import android.os.SystemClock;
import android.util.Log;
import android.util.Slog;
+import android.util.SparseIntArray;
import android.view.Display;
import android.view.Surface;
import android.view.WindowManagerPolicy;
import android.view.animation.Animation;
-import com.android.internal.policy.impl.PhoneWindowManager;
import com.android.server.wm.WindowManagerService.AppWindowAnimParams;
import com.android.server.wm.WindowManagerService.LayoutToAnimatorParams;
@@ -47,7 +47,8 @@
int mAdjResult;
- int mPendingLayoutChanges;
+ // Layout changes for individual Displays. Indexed by displayId.
+ SparseIntArray mPendingLayoutChanges = new SparseIntArray();
/** Overall window dimensions */
int mDw, mDh;
@@ -97,7 +98,7 @@
static class AnimatorToLayoutParams {
boolean mUpdateQueued;
int mBulkUpdateParams;
- int mPendingLayoutChanges;
+ SparseIntArray mPendingLayoutChanges;
WindowState mWindowDetachedWallpaper;
}
/** Do not modify unless holding mService.mWindowMap or this and mAnimToLayout in that order */
@@ -137,7 +138,7 @@
final AnimatorToLayoutParams animToLayout = mAnimToLayout;
synchronized (animToLayout) {
animToLayout.mBulkUpdateParams = mBulkUpdateParams;
- animToLayout.mPendingLayoutChanges = mPendingLayoutChanges;
+ animToLayout.mPendingLayoutChanges = mPendingLayoutChanges.clone();
animToLayout.mWindowDetachedWallpaper = mWindowDetachedWallpaper;
if (!animToLayout.mUpdateQueued) {
@@ -175,7 +176,7 @@
// Set the new DimAnimator params.
DimAnimator.Parameters dimParams = layoutToAnim.mDimParams;
if (dimParams == null) {
- mDimParams = dimParams;
+ mDimParams = null;
} else {
final WindowStateAnimator newWinAnimator = dimParams.mDimWinAnimator;
@@ -186,7 +187,7 @@
if (newWinAnimator.mSurfaceShown &&
(existingDimWinAnimator == null || !existingDimWinAnimator.mSurfaceShown
|| existingDimWinAnimator.mAnimLayer < newWinAnimator.mAnimLayer)) {
- mDimParams = dimParams;
+ mDimParams = new DimAnimator.Parameters(dimParams);
}
}
@@ -214,7 +215,8 @@
if (!winAnimator.mLastHidden) {
winAnimator.hide();
mService.dispatchWallpaperVisibility(wallpaper, false);
- mPendingLayoutChanges |= WindowManagerPolicy.FINISH_LAYOUT_REDO_WALLPAPER;
+ setPendingLayoutChanges(Display.DEFAULT_DISPLAY,
+ WindowManagerPolicy.FINISH_LAYOUT_REDO_WALLPAPER);
}
}
token.hidden = true;
@@ -233,11 +235,8 @@
mAnimating = true;
} else if (wasAnimating) {
// stopped animating, do one more pass through the layout
- mPendingLayoutChanges |= WindowManagerPolicy.FINISH_LAYOUT_REDO_WALLPAPER;
- if (WindowManagerService.DEBUG_LAYOUT_REPEATS) {
- mService.debugLayoutRepeats("appToken " + appAnimator.mAppToken + " done",
- mPendingLayoutChanges);
- }
+ setAppLayoutChanges(appAnimator, WindowManagerPolicy.FINISH_LAYOUT_REDO_WALLPAPER,
+ "appToken " + appAnimator.mAppToken + " done");
if (WindowManagerService.DEBUG_ANIM) Slog.v(TAG,
"updateWindowsApps...: done animating " + appAnimator.mAppToken);
}
@@ -252,11 +251,8 @@
mAnimating = true;
} else if (wasAnimating) {
// stopped animating, do one more pass through the layout
- mPendingLayoutChanges |= WindowManagerPolicy.FINISH_LAYOUT_REDO_WALLPAPER;
- if (WindowManagerService.DEBUG_LAYOUT_REPEATS) {
- mService.debugLayoutRepeats("exiting appToken " + appAnimator.mAppToken
- + " done", mPendingLayoutChanges);
- }
+ setAppLayoutChanges(appAnimator, WindowManagerPolicy.FINISH_LAYOUT_REDO_WALLPAPER,
+ "exiting appToken " + appAnimator.mAppToken + " done");
if (WindowManagerService.DEBUG_ANIM) Slog.v(TAG,
"updateWindowsApps...: done animating exiting " + appAnimator.mAppToken);
}
@@ -302,10 +298,11 @@
if (wasAnimating && !winAnimator.mAnimating && mWallpaperTarget == win) {
mBulkUpdateParams |= SET_WALLPAPER_MAY_CHANGE;
- mPendingLayoutChanges |= WindowManagerPolicy.FINISH_LAYOUT_REDO_WALLPAPER;
+ setPendingLayoutChanges(Display.DEFAULT_DISPLAY,
+ WindowManagerPolicy.FINISH_LAYOUT_REDO_WALLPAPER);
if (WindowManagerService.DEBUG_LAYOUT_REPEATS) {
mService.debugLayoutRepeats("updateWindowsAndWallpaperLocked 2",
- mPendingLayoutChanges);
+ mPendingLayoutChanges.get(Display.DEFAULT_DISPLAY));
}
}
@@ -315,10 +312,12 @@
WindowManagerService.DEBUG_VISIBILITY) Slog.v(TAG,
"Animation started that could impact force hide: " + win);
mBulkUpdateParams |= SET_FORCE_HIDING_CHANGED;
- mPendingLayoutChanges |= WindowManagerPolicy.FINISH_LAYOUT_REDO_WALLPAPER;
+ final int displayId = win.mDisplayContent.getDisplayId();
+ setPendingLayoutChanges(displayId,
+ WindowManagerPolicy.FINISH_LAYOUT_REDO_WALLPAPER);
if (WindowManagerService.DEBUG_LAYOUT_REPEATS) {
mService.debugLayoutRepeats("updateWindowsAndWallpaperLocked 3",
- mPendingLayoutChanges);
+ mPendingLayoutChanges.get(displayId));
}
mService.mFocusMayChange = true;
}
@@ -377,10 +376,11 @@
}
if (changed && (flags & FLAG_SHOW_WALLPAPER) != 0) {
mBulkUpdateParams |= SET_WALLPAPER_MAY_CHANGE;
- mPendingLayoutChanges |= WindowManagerPolicy.FINISH_LAYOUT_REDO_WALLPAPER;
+ setPendingLayoutChanges(Display.DEFAULT_DISPLAY,
+ WindowManagerPolicy.FINISH_LAYOUT_REDO_WALLPAPER);
if (WindowManagerService.DEBUG_LAYOUT_REPEATS) {
mService.debugLayoutRepeats("updateWindowsAndWallpaperLocked 4",
- mPendingLayoutChanges);
+ mPendingLayoutChanges.get(Display.DEFAULT_DISPLAY));
}
}
}
@@ -390,10 +390,12 @@
if (winAnimator.mDrawState == WindowStateAnimator.READY_TO_SHOW) {
if (atoken == null || atoken.allDrawn) {
if (winAnimator.performShowLocked()) {
- mPendingLayoutChanges |= WindowManagerPolicy.FINISH_LAYOUT_REDO_ANIM;
+ final int displayId = win.mDisplayContent.getDisplayId();
+ mPendingLayoutChanges.put(displayId,
+ WindowManagerPolicy.FINISH_LAYOUT_REDO_ANIM);
if (WindowManagerService.DEBUG_LAYOUT_REPEATS) {
mService.debugLayoutRepeats("updateWindowsAndWallpaperLocked 5",
- mPendingLayoutChanges);
+ mPendingLayoutChanges.get(displayId));
}
}
}
@@ -536,14 +538,14 @@
+ wtoken + " numInteresting=" + wtoken.numInterestingWindows
+ " numDrawn=" + wtoken.numDrawnWindows);
// This will set mOrientationChangeComplete and cause a pass through layout.
- mPendingLayoutChanges |= WindowManagerPolicy.FINISH_LAYOUT_REDO_WALLPAPER;
+ setAppLayoutChanges(appAnimator,
+ WindowManagerPolicy.FINISH_LAYOUT_REDO_WALLPAPER,
+ "testTokenMayBeDrawnLocked: freezingScreen");
} else {
- mPendingLayoutChanges |= PhoneWindowManager.FINISH_LAYOUT_REDO_ANIM;
- if (WindowManagerService.DEBUG_LAYOUT_REPEATS) {
- mService.debugLayoutRepeats("testTokenMayBeDrawnLocked",
- mPendingLayoutChanges);
- }
-
+ setAppLayoutChanges(appAnimator,
+ WindowManagerPolicy.FINISH_LAYOUT_REDO_ANIM,
+ "testTokenMayBeDrawnLocked");
+
// We can now show all of the drawn windows!
if (!mService.mOpeningApps.contains(wtoken)) {
mAnimating |= appAnimator.showAllWindowsLocked();
@@ -557,12 +559,6 @@
private void performAnimationsLocked(final WinAnimatorList winAnimatorList) {
updateWindowsLocked(winAnimatorList);
updateWallpaperLocked(winAnimatorList);
-
- if ((mPendingLayoutChanges & WindowManagerPolicy.FINISH_LAYOUT_REDO_WALLPAPER) != 0) {
- mPendingActions |= WALLPAPER_ACTION_PENDING;
- }
-
- testTokenMayBeDrawnLocked();
}
// TODO(cmautner): Change the following comment when no longer locked on mWindowMap */
@@ -571,13 +567,8 @@
if (!mInitialized) {
return;
}
- for (int i = mWinAnimatorLists.size() - 1; i >= 0; i--) {
- animateLocked(mWinAnimatorLists.get(i));
- }
- }
- private void animateLocked(final WinAnimatorList winAnimatorList) {
- mPendingLayoutChanges = 0;
+ mPendingLayoutChanges.clear();
mCurrentTime = SystemClock.uptimeMillis();
mBulkUpdateParams = SET_ORIENTATION_CHANGE_COMPLETE;
boolean wasAnimating = mAnimating;
@@ -586,23 +577,29 @@
Slog.i(TAG, "!!! animate: entry time=" + mCurrentTime);
}
- // Update animations of all applications, including those
- // associated with exiting/removed apps
+ if (WindowManagerService.SHOW_TRANSACTIONS) Slog.i(
+ TAG, ">>> OPEN TRANSACTION animateLocked");
Surface.openTransaction();
-
try {
updateWindowsAppsAndRotationAnimationsLocked();
- performAnimationsLocked(winAnimatorList);
- // THIRD LOOP: Update the surfaces of all windows.
+ for (int i = mWinAnimatorLists.size() - 1; i >= 0; i--) {
+ final WinAnimatorList winAnimatorList = mWinAnimatorLists.get(i);
- if (mScreenRotationAnimation != null) {
- mScreenRotationAnimation.updateSurfaces();
+ // Update animations of all applications, including those
+ // associated with exiting/removed apps
+ performAnimationsLocked(winAnimatorList);
+
+ final int N = winAnimatorList.size();
+ for (int j = 0; j < N; j++) {
+ winAnimatorList.get(j).prepareSurfaceLocked(true);
+ }
}
- final int N = winAnimatorList.size();
- for (int i = 0; i < N; i++) {
- winAnimatorList.get(i).prepareSurfaceLocked(true);
+ testTokenMayBeDrawnLocked();
+
+ if (mScreenRotationAnimation != null) {
+ mScreenRotationAnimation.updateSurfacesInTransaction();
}
if (mDimParams != null) {
@@ -629,9 +626,18 @@
Log.wtf(TAG, "Unhandled exception in Window Manager", e);
} finally {
Surface.closeTransaction();
+ if (WindowManagerService.SHOW_TRANSACTIONS) Slog.i(
+ TAG, "<<< CLOSE TRANSACTION animateLocked");
}
- if (mBulkUpdateParams != 0 || mPendingLayoutChanges != 0) {
+ for (int i = mPendingLayoutChanges.size() - 1; i >= 0; i--) {
+ if ((mPendingLayoutChanges.valueAt(i)
+ & WindowManagerPolicy.FINISH_LAYOUT_REDO_WALLPAPER) != 0) {
+ mPendingActions |= WALLPAPER_ACTION_PENDING;
+ }
+ }
+
+ if (mBulkUpdateParams != 0 || mPendingLayoutChanges.size() > 0) {
updateAnimToLayoutLocked();
}
@@ -645,7 +651,8 @@
if (WindowManagerService.DEBUG_WINDOW_TRACE) {
Slog.i(TAG, "!!! animate: exit mAnimating=" + mAnimating
+ " mBulkUpdateParams=" + Integer.toHexString(mBulkUpdateParams)
- + " mPendingLayoutChanges=" + Integer.toHexString(mPendingLayoutChanges));
+ + " mPendingLayoutChanges(DEFAULT_DISPLAY)="
+ + Integer.toHexString(mPendingLayoutChanges.get(Display.DEFAULT_DISPLAY)));
}
}
@@ -705,7 +712,30 @@
}
}
- synchronized void clearPendingActions() {
- mPendingActions = 0;
+ void clearPendingActions() {
+ synchronized (this) {
+ mPendingActions = 0;
+ }
+ }
+
+ void setPendingLayoutChanges(final int displayId, final int changes) {
+ mPendingLayoutChanges.put(displayId, mPendingLayoutChanges.get(displayId) | changes);
+ }
+
+ void setAppLayoutChanges(final AppWindowAnimator appAnimator, final int changes, String s) {
+ // Used to track which displays layout changes have been done.
+ SparseIntArray displays = new SparseIntArray();
+ for (int i = appAnimator.mAllAppWinAnimators.size() - 1; i >= 0; i--) {
+ WindowStateAnimator winAnimator = appAnimator.mAllAppWinAnimators.get(i);
+ final int displayId = winAnimator.mWin.mDisplayContent.getDisplayId();
+ if (displays.indexOfKey(displayId) < 0) {
+ setPendingLayoutChanges(displayId, changes);
+ if (WindowManagerService.DEBUG_LAYOUT_REPEATS) {
+ mService.debugLayoutRepeats(s, mPendingLayoutChanges.get(displayId));
+ }
+ // Keep from processing this display again.
+ displays.put(displayId, changes);
+ }
+ }
}
}
diff --git a/services/java/com/android/server/wm/WindowManagerService.java b/services/java/com/android/server/wm/WindowManagerService.java
index 0d9db90..c82fa55 100755
--- a/services/java/com/android/server/wm/WindowManagerService.java
+++ b/services/java/com/android/server/wm/WindowManagerService.java
@@ -45,7 +45,6 @@
import com.android.server.EventLogTags;
import com.android.server.Watchdog;
import com.android.server.am.BatteryStatsService;
-import com.android.server.display.DisplayDeviceInfo;
import com.android.server.display.DisplayManagerService;
import com.android.server.input.InputManagerService;
import com.android.server.power.PowerManagerService;
@@ -100,7 +99,6 @@
import android.util.FloatMath;
import android.util.Log;
import android.util.SparseArray;
-//import android.util.LogPrinter;
import android.util.Pair;
import android.util.Slog;
import android.util.SparseIntArray;
@@ -163,7 +161,8 @@
/** {@hide} */
public class WindowManagerService extends IWindowManager.Stub
- implements Watchdog.Monitor, WindowManagerPolicy.WindowManagerFuncs {
+ implements Watchdog.Monitor, WindowManagerPolicy.WindowManagerFuncs,
+ DisplayManagerService.WindowManagerFuncs {
static final String TAG = "WindowManager";
static final boolean DEBUG = false;
static final boolean DEBUG_ADD_REMOVE = false;
@@ -454,8 +453,6 @@
int mSystemDecorLayer = 0;
final Rect mScreenRect = new Rect();
- int mPendingLayoutChanges = 0;
- boolean mLayoutNeeded = true;
boolean mTraversalScheduled = false;
boolean mDisplayFrozen = false;
boolean mWaitingForConfig = false;
@@ -751,7 +748,7 @@
holder[0] = new WindowManagerService(context, pm, dm,
uiHandler, haveInputMethods, showBootMsgs, onlyCore);
}
- });
+ }, 0);
return holder[0];
}
@@ -766,7 +763,7 @@
* TYPE_LAYER_MULTIPLIER
+ TYPE_LAYER_OFFSET;
}
- });
+ }, 0);
}
private WindowManagerService(Context context, PowerManagerService pm,
@@ -825,8 +822,11 @@
Watchdog.getInstance().addMonitor(this);
Surface.openTransaction();
- createWatermark();
- Surface.closeTransaction();
+ try {
+ createWatermarkInTransaction();
+ } finally {
+ Surface.closeTransaction();
+ }
}
public InputManagerService getInputManagerService() {
@@ -1766,7 +1766,7 @@
token.hidden = !visible;
// Need to do a layout to ensure the wallpaper now has the
// correct size.
- mLayoutNeeded = true;
+ getDefaultDisplayContent().layoutNeeded = true;
}
int curWallpaperIndex = token.windows.size();
@@ -2009,7 +2009,7 @@
token.hidden = !visible;
// Need to do a layout to ensure the wallpaper now has the
// correct size.
- mLayoutNeeded = true;
+ getDefaultDisplayContent().layoutNeeded = true;
}
int curWallpaperIndex = token.windows.size();
@@ -2308,7 +2308,7 @@
//Slog.i(TAG, "*** Running exit animation...");
win.mExiting = true;
win.mRemoveOnExit = true;
- mLayoutNeeded = true;
+ win.mDisplayContent.layoutNeeded = true;
updateFocusedWindowLocked(UPDATE_FOCUS_WILL_PLACE_SURFACES,
false /*updateInputWindows*/);
performLayoutAndPlaceSurfacesLocked();
@@ -2425,7 +2425,7 @@
if (!mInLayout) {
assignLayersLocked(windows);
- mLayoutNeeded = true;
+ win.mDisplayContent.layoutNeeded = true;
performLayoutAndPlaceSurfacesLocked();
if (win.mAppToken != null) {
win.mAppToken.updateReportedVisibilityLocked();
@@ -2491,7 +2491,7 @@
w.mGivenVisibleInsets.scale(w.mGlobalScale);
w.mGivenTouchableRegion.scale(w.mGlobalScale);
}
- mLayoutNeeded = true;
+ w.mDisplayContent.layoutNeeded = true;
performLayoutAndPlaceSurfacesLocked();
}
}
@@ -2586,7 +2586,7 @@
window.mGivenTouchableRegion.op((int)dispRect.left, (int)dispRect.top,
(int)dispRect.right, (int)dispRect.bottom, Region.Op.DIFFERENCE);
window.mTouchableInsets = ViewTreeObserver.InternalInsetsInfo.TOUCHABLE_INSETS_REGION;
- mLayoutNeeded = true;
+ window.mDisplayContent.layoutNeeded = true;
performLayoutAndPlaceSurfacesLocked();
}
@@ -2854,7 +2854,7 @@
}
}
- mLayoutNeeded = true;
+ win.mDisplayContent.layoutNeeded = true;
win.mGivenInsetsPending = (flags&WindowManagerGlobal.RELAYOUT_INSETS_PENDING) != 0;
if (assignLayers) {
assignLayersLocked(win.getWindowList());
@@ -2944,7 +2944,7 @@
if ((win.mAttrs.flags&FLAG_SHOW_WALLPAPER) != 0) {
adjustWallpaperWindowsLocked();
}
- mLayoutNeeded = true;
+ win.mDisplayContent.layoutNeeded = true;
performLayoutAndPlaceSurfacesLocked();
}
}
@@ -3458,11 +3458,11 @@
win.mWinAnimator.applyAnimationLocked(WindowManagerPolicy.TRANSIT_EXIT,
false);
changed = true;
+ win.mDisplayContent.layoutNeeded = true;
}
}
if (changed) {
- mLayoutNeeded = true;
performLayoutAndPlaceSurfacesLocked();
updateFocusedWindowLocked(UPDATE_FOCUS_NORMAL,
false /*updateInputWindows*/);
@@ -3718,7 +3718,7 @@
if (computeScreenConfigurationLocked(mTempConfiguration)) {
if (currentConfig.diff(mTempConfiguration) != 0) {
mWaitingForConfig = true;
- mLayoutNeeded = true;
+ getDefaultDisplayContent().layoutNeeded = true;
startFreezingDisplayLocked(false);
config = new Configuration(mTempConfiguration);
}
@@ -3804,6 +3804,7 @@
}
}
+ @Override
public int getAppOrientation(IApplicationToken token) {
synchronized(mWindowMap) {
AppWindowToken wtoken = findAppWindowToken(token.asBinder());
@@ -3815,6 +3816,7 @@
}
}
+ @Override
public void setFocusedApp(IBinder token, boolean moveFocusNow) {
if (!checkCallingPermission(android.Manifest.permission.MANAGE_APP_TOKENS,
"setFocusedApp()")) {
@@ -3852,6 +3854,7 @@
}
}
+ @Override
public void prepareAppTransition(int transit, boolean alwaysKeepCurrent) {
if (!checkCallingPermission(android.Manifest.permission.MANAGE_APP_TOKENS,
"prepareAppTransition()")) {
@@ -4081,7 +4084,7 @@
updateFocusedWindowLocked(UPDATE_FOCUS_WILL_PLACE_SURFACES,
true /*updateInputWindows*/);
- mLayoutNeeded = true;
+ getDefaultDisplayContent().layoutNeeded = true;
performLayoutAndPlaceSurfacesLocked();
Binder.restoreCallingIdentity(origId);
return;
@@ -4246,6 +4249,7 @@
WindowManagerPolicy.TRANSIT_ENTER, true);
}
changed = true;
+ win.mDisplayContent.layoutNeeded = true;
}
} else if (win.isVisibleNow()) {
if (!runningAppAnimation) {
@@ -4253,6 +4257,7 @@
WindowManagerPolicy.TRANSIT_EXIT, false);
}
changed = true;
+ win.mDisplayContent.layoutNeeded = true;
}
}
@@ -4274,7 +4279,6 @@
+ wtoken.hiddenRequested);
if (changed) {
- mLayoutNeeded = true;
mInputMonitor.setUpdateInputWindowsNeededLw();
if (performLayout) {
updateFocusedWindowLocked(UPDATE_FOCUS_WILL_PLACE_SURFACES,
@@ -4402,6 +4406,7 @@
mInnerFields.mOrientationChangeComplete = false;
}
unfrozeWindows = true;
+ w.mDisplayContent.layoutNeeded = true;
}
}
if (force || unfrozeWindows) {
@@ -4411,7 +4416,6 @@
}
if (unfreezeSurfaceNow) {
if (unfrozeWindows) {
- mLayoutNeeded = true;
performLayoutAndPlaceSurfacesLocked();
}
stopFreezingDisplayLocked();
@@ -4756,13 +4760,15 @@
final DisplayContent displayContent = iterator.next();
final WindowList windows = displayContent.getWindowList();
final int pos = findWindowOffsetLocked(windows, index);
- reAddAppWindowsLocked(displayContent, pos, wtoken);
+ final int newPos = reAddAppWindowsLocked(displayContent, pos, wtoken);
+ if (pos != newPos) {
+ displayContent.layoutNeeded = true;
+ }
}
if (DEBUG_REORDER) Slog.v(TAG, "Final window list:");
if (DEBUG_REORDER) dumpWindowsLocked();
updateFocusedWindowLocked(UPDATE_FOCUS_WILL_PLACE_SURFACES,
false /*updateInputWindows*/);
- mLayoutNeeded = true;
mInputMonitor.setUpdateInputWindowsNeededLw();
performLayoutAndPlaceSurfacesLocked();
mInputMonitor.updateInputWindowsLw(false /*force*/);
@@ -4802,7 +4808,10 @@
final DisplayContent displayContent = iterator.next();
final WindowList windows = displayContent.getWindowList();
final int pos = findWindowOffsetLocked(windows, tokenPos);
- reAddAppWindowsLocked(displayContent, pos, wtoken);
+ final int newPos = reAddAppWindowsLocked(displayContent, pos, wtoken);
+ if (pos != newPos) {
+ displayContent.layoutNeeded = true;
+ }
if (updateFocusAndLayout && !updateFocusedWindowLocked(UPDATE_FOCUS_WILL_PLACE_SURFACES,
false /*updateInputWindows*/)) {
@@ -4815,7 +4824,6 @@
// Note that the above updateFocusedWindowLocked conditional used to sit here.
- mLayoutNeeded = true;
if (!mInLayout) {
performLayoutAndPlaceSurfacesLocked();
}
@@ -4844,7 +4852,11 @@
for (i=0; i<N; i++) {
WindowToken token = mTokenMap.get(tokens.get(i));
if (token != null) {
- pos = reAddAppWindowsLocked(displayContent, pos, token);
+ final int newPos = reAddAppWindowsLocked(displayContent, pos, token);
+ if (newPos != pos) {
+ displayContent.layoutNeeded = true;
+ }
+ pos = newPos;
}
}
if (!updateFocusedWindowLocked(UPDATE_FOCUS_WILL_PLACE_SURFACES,
@@ -4857,7 +4869,6 @@
// Note that the above updateFocusedWindowLocked used to sit here.
- mLayoutNeeded = true;
performLayoutAndPlaceSurfacesLocked();
mInputMonitor.updateInputWindowsLw(false /*force*/);
@@ -5651,7 +5662,7 @@
synchronized(mWindowMap) {
changed = updateRotationUncheckedLocked(false);
if (!changed || forceRelayout) {
- mLayoutNeeded = true;
+ getDefaultDisplayContent().layoutNeeded = true;
performLayoutAndPlaceSurfacesLocked();
}
}
@@ -5729,43 +5740,46 @@
mH.removeMessages(H.WINDOW_FREEZE_TIMEOUT);
mH.sendMessageDelayed(mH.obtainMessage(H.WINDOW_FREEZE_TIMEOUT), 2000);
mWaitingForConfig = true;
- mLayoutNeeded = true;
+ getDefaultDisplayContent().layoutNeeded = true;
startFreezingDisplayLocked(inTransaction);
- mInputManager.setDisplayOrientation(0, rotation, Surface.ROTATION_0);
+ mInputManager.setDisplayOrientation(0, rotation);
// We need to update our screen size information to match the new
// rotation. Note that this is redundant with the later call to
// sendNewConfiguration() that must be called after this function
// returns... however we need to do the screen size part of that
- // before then so we have the correct size to use when initializiation
+ // before then so we have the correct size to use when initializing
// the rotation animation for the new rotation.
computeScreenConfigurationLocked(null);
- if (!inTransaction) {
- if (SHOW_TRANSACTIONS) Slog.i(TAG,
- ">>> OPEN TRANSACTION setRotationUnchecked");
- Surface.openTransaction();
- }
final DisplayContent displayContent = getDefaultDisplayContent();
final DisplayInfo displayInfo = displayContent.getDisplayInfo();
+ if (!inTransaction) {
+ if (SHOW_TRANSACTIONS) {
+ Slog.i(TAG, ">>> OPEN TRANSACTION setRotationUnchecked");
+ }
+ Surface.openTransaction();
+ }
try {
// NOTE: We disable the rotation in the emulator because
// it doesn't support hardware OpenGL emulation yet.
if (CUSTOM_SCREEN_ROTATION && mAnimator.mScreenRotationAnimation != null
&& mAnimator.mScreenRotationAnimation.hasScreenshot()) {
- if (mAnimator.mScreenRotationAnimation.setRotation(rotation, mFxSession,
+ if (mAnimator.mScreenRotationAnimation.setRotationInTransaction(
+ rotation, mFxSession,
MAX_ANIMATION_DURATION, mTransitionAnimationScale,
displayInfo.logicalWidth, displayInfo.logicalHeight)) {
updateLayoutToAnimationLocked();
}
}
- mDisplayManagerService.setDisplayOrientation(
- displayContent.getDisplayId(), rotation);
+
+ mDisplayManagerService.performTraversalInTransactionFromWindowManager();
} finally {
if (!inTransaction) {
Surface.closeTransaction();
- if (SHOW_LIGHT_TRANSACTIONS) Slog.i(TAG,
- "<<< CLOSE TRANSACTION setRotationUnchecked");
+ if (SHOW_LIGHT_TRANSACTIONS) {
+ Slog.i(TAG, "<<< CLOSE TRANSACTION setRotationUnchecked");
+ }
}
}
@@ -6384,7 +6398,8 @@
sl = reduceConfigLayout(sl, Surface.ROTATION_180, density, unrotDw, unrotDh);
sl = reduceConfigLayout(sl, Surface.ROTATION_270, density, unrotDh, unrotDw);
outConfig.smallestScreenWidthDp = (int)(displayInfo.smallestNominalAppWidth / density);
- outConfig.screenLayout = sl;
+ outConfig.screenLayout =
+ sl|(outConfig.screenLayout&Configuration.SCREENLAYOUT_LAYOUTDIR_MASK);
}
private int reduceCompatConfigWidthSize(int curSize, int rotation, DisplayMetrics dm,
@@ -6801,15 +6816,11 @@
mAnimator.setDisplayDimensions(displayInfo.logicalWidth, displayInfo.logicalHeight,
displayInfo.appWidth, displayInfo.appHeight);
- DisplayDeviceInfo info = new DisplayDeviceInfo();
- mDisplayManagerService.getDefaultExternalDisplayDeviceInfo(info);
-
final DisplayContent displayContent = getDefaultDisplayContent();
mInputManager.setDisplaySize(displayContent.getDisplayId(),
- displayContent.mInitialDisplayWidth, displayContent.mInitialDisplayHeight,
- info.width, info.height);
+ displayContent.mInitialDisplayWidth, displayContent.mInitialDisplayHeight);
mInputManager.setDisplayOrientation(displayContent.getDisplayId(),
- mDefaultDisplay.getRotation(), Surface.ROTATION_0);
+ mDefaultDisplay.getRotation());
mPolicy.setInitialDisplaySize(mDefaultDisplay, displayContent.mInitialDisplayWidth,
displayContent.mInitialDisplayHeight, displayContent.mInitialDisplayDensity);
}
@@ -7574,7 +7585,7 @@
mPolicy.setInitialDisplaySize(mDefaultDisplay, displayContent.mBaseDisplayWidth,
displayContent.mBaseDisplayHeight, displayContent.mBaseDisplayDensity);
- mLayoutNeeded = true;
+ displayContent.layoutNeeded = true;
boolean configChanged = updateOrientationFromAppTokensLocked(false);
mTempConfiguration.setToDefaults();
@@ -7829,31 +7840,11 @@
}
try {
- DisplayContentsIterator iterator = new DisplayContentsIterator();
- while (iterator.hasNext()) {
- final DisplayContent displayContent = iterator.next();
- performLayoutAndPlaceSurfacesLockedInner(displayContent, recoveringMemory);
-
- final int N = mPendingRemove.size();
- if (N > 0) {
- if (mPendingRemoveTmp.length < N) {
- mPendingRemoveTmp = new WindowState[N+10];
- }
- mPendingRemove.toArray(mPendingRemoveTmp);
- mPendingRemove.clear();
- for (int i=0; i<N; i++) {
- WindowState w = mPendingRemoveTmp[i];
- removeWindowInnerLocked(w.mSession, w);
- }
-
- assignLayersLocked(displayContent.getWindowList());
- mLayoutNeeded = true;
- }
- }
+ performLayoutAndPlaceSurfacesLockedInner(recoveringMemory);
mInLayout = false;
- if (mLayoutNeeded) {
+ if (needsLayout()) {
if (++mLayoutRepeatCount < 6) {
requestTraversalLocked();
} else {
@@ -7878,11 +7869,11 @@
private final void performLayoutLockedInner(final DisplayContent displayContent,
boolean initial, boolean updateInputWindows) {
- if (!mLayoutNeeded) {
+ if (!displayContent.layoutNeeded) {
return;
}
+ displayContent.layoutNeeded = false;
WindowList windows = displayContent.getWindowList();
- mLayoutNeeded = false;
DisplayInfo displayInfo = displayContent.getDisplayInfo();
final int dw = displayInfo.logicalWidth;
@@ -7899,7 +7890,7 @@
if (DEBUG_LAYOUT) {
Slog.v(TAG, "-------------------------------------");
Slog.v(TAG, "performLayout: needed="
- + mLayoutNeeded + " dw=" + dw + " dh=" + dh);
+ + displayContent.layoutNeeded + " dw=" + dw + " dh=" + dh);
}
WindowStateAnimator universeBackground = null;
@@ -8044,8 +8035,7 @@
/**
* Extracted from {@link #performLayoutAndPlaceSurfacesLockedInner} to reduce size of method.
- * @param windows TODO(cmautner):
- *
+ * @param windows List of windows on default display.
* @return bitmap indicating if another pass through layout must be made.
*/
public int handleAppTransitionReadyLocked(WindowList windows) {
@@ -8299,9 +8289,9 @@
// This has changed the visibility of windows, so perform
// a new layout to get them all up-to-date.
- changes |= PhoneWindowManager.FINISH_LAYOUT_REDO_LAYOUT
+ changes |= WindowManagerPolicy.FINISH_LAYOUT_REDO_LAYOUT
| WindowManagerPolicy.FINISH_LAYOUT_REDO_CONFIG;
- mLayoutNeeded = true;
+ getDefaultDisplayContent().layoutNeeded = true;
// TODO(multidisplay): IMEs are only supported on the default display.
if (windows == getDefaultWindowList() && !moveInputMethodWindowsIfNeededLocked(true)) {
@@ -8523,8 +8513,7 @@
}
// "Something has changed! Let's make it correct now."
- private final void performLayoutAndPlaceSurfacesLockedInner(
- final DisplayContent displayContent, boolean recoveringMemory) {
+ private final void performLayoutAndPlaceSurfacesLockedInner(boolean recoveringMemory) {
if (DEBUG_WINDOW_TRACE) {
Slog.v(TAG, "performLayoutAndPlaceSurfacesLockedInner: entry. Called by "
+ Debug.getCallers(3));
@@ -8534,13 +8523,7 @@
return;
}
- final WindowList windows = displayContent.getWindowList();
final long currentTime = SystemClock.uptimeMillis();
- final DisplayInfo displayInfo = displayContent.getDisplayInfo();
- final int dw = displayInfo.logicalWidth;
- final int dh = displayInfo.logicalHeight;
- final int innerDw = displayInfo.appWidth;
- final int innerDh = displayInfo.appHeight;
int i;
@@ -8565,198 +8548,233 @@
mInnerFields.mButtonBrightness = -1;
mTransactionSequence++;
+ final DisplayContent defaultDisplay = getDefaultDisplayContent();
+ final DisplayInfo defaultInfo = defaultDisplay.getDisplayInfo();
+ final int defaultDw = defaultInfo.logicalWidth;
+ final int defaultDh = defaultInfo.logicalHeight;
+ final int defaultInnerDw = defaultInfo.appWidth;
+ final int defaultInnerDh = defaultInfo.appHeight;
+
if (SHOW_LIGHT_TRANSACTIONS) Slog.i(TAG,
">>> OPEN TRANSACTION performLayoutAndPlaceSurfaces");
-
Surface.openTransaction();
-
- if (mWatermark != null) {
- mWatermark.positionSurface(dw, dh);
- }
- if (mStrictModeFlash != null) {
- mStrictModeFlash.positionSurface(dw, dh);
- }
-
try {
- int repeats = 0;
- do {
- repeats++;
- if (repeats > 6) {
- Slog.w(TAG, "Animation repeat aborted after too many iterations");
- mLayoutNeeded = false;
- break;
- }
+ if (mWatermark != null) {
+ mWatermark.positionSurface(defaultDw, defaultDh);
+ }
+ if (mStrictModeFlash != null) {
+ mStrictModeFlash.positionSurface(defaultDw, defaultDh);
+ }
- if (DEBUG_LAYOUT_REPEATS) debugLayoutRepeats("On entry to LockedInner",
- mPendingLayoutChanges);
-
- if ((mPendingLayoutChanges & WindowManagerPolicy.FINISH_LAYOUT_REDO_WALLPAPER) != 0) {
- if ((adjustWallpaperWindowsLocked() & ADJUST_WALLPAPER_LAYERS_CHANGED) != 0) {
- assignLayersLocked(windows);
- mLayoutNeeded = true;
- }
- }
-
- if ((mPendingLayoutChanges & WindowManagerPolicy.FINISH_LAYOUT_REDO_CONFIG) != 0) {
- if (DEBUG_LAYOUT) Slog.v(TAG, "Computing new config from layout");
- if (updateOrientationFromAppTokensLocked(true)) {
- mLayoutNeeded = true;
- mH.sendEmptyMessage(H.SEND_NEW_CONFIGURATION);
- }
- }
-
- if ((mPendingLayoutChanges & WindowManagerPolicy.FINISH_LAYOUT_REDO_LAYOUT) != 0) {
- mLayoutNeeded = true;
- }
-
- // FIRST LOOP: Perform a layout, if needed.
- if (repeats < 4) {
- performLayoutLockedInner(displayContent, repeats == 1, false /*updateInputWindows*/);
- } else {
- Slog.w(TAG, "Layout repeat skipped after too many iterations");
- }
-
- // FIRST AND ONE HALF LOOP: Make WindowManagerPolicy think
- // it is animating.
- mPendingLayoutChanges = 0;
- if (DEBUG_LAYOUT_REPEATS) debugLayoutRepeats("loop number " + mLayoutRepeatCount,
- mPendingLayoutChanges);
- mPolicy.beginAnimationLw(dw, dh);
- for (i = windows.size() - 1; i >= 0; i--) {
- WindowState w = windows.get(i);
- if (w.mHasSurface) {
- mPolicy.animatingWindowLw(w, w.mAttrs);
- }
- }
- mPendingLayoutChanges |= mPolicy.finishAnimationLw();
- if (DEBUG_LAYOUT_REPEATS) debugLayoutRepeats("after finishAnimationLw",
- mPendingLayoutChanges);
- } while (mPendingLayoutChanges != 0);
-
- final boolean someoneLosingFocus = !mLosingFocus.isEmpty();
-
- mInnerFields.mObscured = false;
- mInnerFields.mDimming = false;
- mInnerFields.mSyswin = false;
+ // Give the display manager a chance to adjust properties
+ // like display rotation if it needs to.
+ mDisplayManagerService.performTraversalInTransactionFromWindowManager();
boolean focusDisplayed = false;
boolean updateAllDrawn = false;
- final int N = windows.size();
- for (i=N-1; i>=0; i--) {
- WindowState w = windows.get(i);
- final boolean obscuredChanged = w.mObscured != mInnerFields.mObscured;
+ DisplayContentsIterator iterator = new DisplayContentsIterator();
+ while (iterator.hasNext()) {
+ final DisplayContent displayContent = iterator.next();
+ WindowList windows = displayContent.getWindowList();
+ DisplayInfo displayInfo = displayContent.getDisplayInfo();
+ final int displayId = displayContent.getDisplayId();
+ final int dw = displayInfo.logicalWidth;
+ final int dh = displayInfo.logicalHeight;
+ final int innerDw = displayInfo.appWidth;
+ final int innerDh = displayInfo.appHeight;
+ final boolean isDefaultDisplay = (displayId == Display.DEFAULT_DISPLAY);
- // Update effect.
- w.mObscured = mInnerFields.mObscured;
- if (!mInnerFields.mObscured) {
- handleNotObscuredLocked(w, currentTime, innerDw, innerDh);
- }
-
- if (obscuredChanged && (mWallpaperTarget == w) && w.isVisibleLw()) {
- // This is the wallpaper target and its obscured state
- // changed... make sure the current wallaper's visibility
- // has been updated accordingly.
- updateWallpaperVisibilityLocked();
- }
-
- final WindowStateAnimator winAnimator = w.mWinAnimator;
-
- // If the window has moved due to its containing
- // content frame changing, then we'd like to animate
- // it.
- if (w.mHasSurface && w.shouldAnimateMove()) {
- // Frame has moved, containing content frame
- // has also moved, and we're not currently animating...
- // let's do something.
- Animation a = AnimationUtils.loadAnimation(mContext,
- com.android.internal.R.anim.window_move_from_decor);
- winAnimator.setAnimation(a);
- winAnimator.mAnimDw = w.mLastFrame.left - w.mFrame.left;
- winAnimator.mAnimDh = w.mLastFrame.top - w.mFrame.top;
- try {
- w.mClient.moved(w.mFrame.left, w.mFrame.top);
- } catch (RemoteException e) {
+ int repeats = 0;
+ do {
+ repeats++;
+ if (repeats > 6) {
+ Slog.w(TAG, "Animation repeat aborted after too many iterations");
+ displayContent.layoutNeeded = false;
+ break;
}
- }
- //Slog.i(TAG, "Window " + this + " clearing mContentChanged - done placing");
- w.mContentChanged = false;
+ if (DEBUG_LAYOUT_REPEATS) debugLayoutRepeats("On entry to LockedInner",
+ displayContent.pendingLayoutChanges);
- // Moved from updateWindowsAndWallpaperLocked().
- if (w.mHasSurface) {
- // Take care of the window being ready to display.
- if (winAnimator.commitFinishDrawingLocked(currentTime)) {
- if ((w.mAttrs.flags
- & WindowManager.LayoutParams.FLAG_SHOW_WALLPAPER) != 0) {
- if (WindowManagerService.DEBUG_WALLPAPER) Slog.v(TAG,
- "First draw done in potential wallpaper target " + w);
- mInnerFields.mWallpaperMayChange = true;
- mPendingLayoutChanges |= WindowManagerPolicy.FINISH_LAYOUT_REDO_WALLPAPER;
- if (WindowManagerService.DEBUG_LAYOUT_REPEATS) {
- debugLayoutRepeats("wallpaper and commitFinishDrawingLocked true",
- mPendingLayoutChanges);
- }
+ if (isDefaultDisplay && ((displayContent.pendingLayoutChanges
+ & WindowManagerPolicy.FINISH_LAYOUT_REDO_WALLPAPER) != 0)
+ && ((adjustWallpaperWindowsLocked()
+ & ADJUST_WALLPAPER_LAYERS_CHANGED) != 0)) {
+ assignLayersLocked(windows);
+ displayContent.layoutNeeded = true;
+ }
+
+ if (isDefaultDisplay && (displayContent.pendingLayoutChanges
+ & WindowManagerPolicy.FINISH_LAYOUT_REDO_CONFIG) != 0) {
+ if (DEBUG_LAYOUT) Slog.v(TAG, "Computing new config from layout");
+ if (updateOrientationFromAppTokensLocked(true)) {
+ displayContent.layoutNeeded = true;
+ mH.sendEmptyMessage(H.SEND_NEW_CONFIGURATION);
}
}
- winAnimator.setSurfaceBoundaries(recoveringMemory);
-
- final AppWindowToken atoken = w.mAppToken;
- if (DEBUG_STARTING_WINDOW && atoken != null && w == atoken.startingWindow) {
- Slog.d(TAG, "updateWindows: starting " + w + " isOnScreen="
- + w.isOnScreen() + " allDrawn=" + atoken.allDrawn
- + " freezingScreen=" + atoken.mAppAnimator.freezingScreen);
+ if ((displayContent.pendingLayoutChanges
+ & WindowManagerPolicy.FINISH_LAYOUT_REDO_LAYOUT) != 0) {
+ displayContent.layoutNeeded = true;
}
- if (atoken != null
- && (!atoken.allDrawn || atoken.mAppAnimator.freezingScreen)) {
- if (atoken.lastTransactionSequence != mTransactionSequence) {
- atoken.lastTransactionSequence = mTransactionSequence;
- atoken.numInterestingWindows = atoken.numDrawnWindows = 0;
- atoken.startingDisplayed = false;
+
+ // FIRST LOOP: Perform a layout, if needed.
+ if (repeats < 4) {
+ performLayoutLockedInner(displayContent, repeats == 1,
+ false /*updateInputWindows*/);
+ } else {
+ Slog.w(TAG, "Layout repeat skipped after too many iterations");
+ }
+
+ // FIRST AND ONE HALF LOOP: Make WindowManagerPolicy think
+ // it is animating.
+ displayContent.pendingLayoutChanges = 0;
+
+ if (DEBUG_LAYOUT_REPEATS) debugLayoutRepeats("loop number "
+ + mLayoutRepeatCount, displayContent.pendingLayoutChanges);
+
+ mPolicy.beginPostLayoutPolicyLw(dw, dh);
+ for (i = windows.size() - 1; i >= 0; i--) {
+ WindowState w = windows.get(i);
+ if (w.mHasSurface) {
+ mPolicy.applyPostLayoutPolicyLw(w, w.mAttrs);
}
- if ((w.isOnScreen() || winAnimator.mAttrType
- == WindowManager.LayoutParams.TYPE_BASE_APPLICATION)
- && !w.mExiting && !w.mDestroying) {
- if (WindowManagerService.DEBUG_VISIBILITY ||
- WindowManagerService.DEBUG_ORIENTATION) {
- Slog.v(TAG, "Eval win " + w + ": isDrawn=" + w.isDrawnLw()
- + ", isAnimating=" + winAnimator.isAnimating());
- if (!w.isDrawnLw()) {
- Slog.v(TAG, "Not displayed: s=" + winAnimator.mSurface
- + " pv=" + w.mPolicyVisibility
- + " mDrawState=" + winAnimator.mDrawState
- + " ah=" + w.mAttachedHidden
- + " th=" + atoken.hiddenRequested
- + " a=" + winAnimator.mAnimating);
+ }
+ displayContent.pendingLayoutChanges |= mPolicy.finishPostLayoutPolicyLw();
+ if (DEBUG_LAYOUT_REPEATS) debugLayoutRepeats("after finishPostLayoutPolicyLw",
+ displayContent.pendingLayoutChanges);
+ } while (displayContent.pendingLayoutChanges != 0);
+
+ mInnerFields.mObscured = false;
+ mInnerFields.mDimming = false;
+ mInnerFields.mSyswin = false;
+
+ // Only used if default window
+ final boolean someoneLosingFocus = !mLosingFocus.isEmpty();
+
+ final int N = windows.size();
+ for (i=N-1; i>=0; i--) {
+ WindowState w = windows.get(i);
+
+ final boolean obscuredChanged = w.mObscured != mInnerFields.mObscured;
+
+ // Update effect.
+ w.mObscured = mInnerFields.mObscured;
+ if (!mInnerFields.mObscured) {
+ handleNotObscuredLocked(w, currentTime, innerDw, innerDh);
+ }
+
+ if (isDefaultDisplay && obscuredChanged && (mWallpaperTarget == w)
+ && w.isVisibleLw()) {
+ // This is the wallpaper target and its obscured state
+ // changed... make sure the current wallaper's visibility
+ // has been updated accordingly.
+ updateWallpaperVisibilityLocked();
+ }
+
+ final WindowStateAnimator winAnimator = w.mWinAnimator;
+
+ // If the window has moved due to its containing
+ // content frame changing, then we'd like to animate
+ // it.
+ if (w.mHasSurface && w.shouldAnimateMove()) {
+ // Frame has moved, containing content frame
+ // has also moved, and we're not currently animating...
+ // let's do something.
+ Animation a = AnimationUtils.loadAnimation(mContext,
+ com.android.internal.R.anim.window_move_from_decor);
+ winAnimator.setAnimation(a);
+ winAnimator.mAnimDw = w.mLastFrame.left - w.mFrame.left;
+ winAnimator.mAnimDh = w.mLastFrame.top - w.mFrame.top;
+ try {
+ w.mClient.moved(w.mFrame.left, w.mFrame.top);
+ } catch (RemoteException e) {
+ }
+ }
+
+ //Slog.i(TAG, "Window " + this + " clearing mContentChanged - done placing");
+ w.mContentChanged = false;
+
+ // Moved from updateWindowsAndWallpaperLocked().
+ if (w.mHasSurface) {
+ // Take care of the window being ready to display.
+ if (isDefaultDisplay
+ && winAnimator.commitFinishDrawingLocked(currentTime)) {
+ if ((w.mAttrs.flags
+ & WindowManager.LayoutParams.FLAG_SHOW_WALLPAPER) != 0) {
+ if (WindowManagerService.DEBUG_WALLPAPER) Slog.v(TAG,
+ "First draw done in potential wallpaper target " + w);
+ mInnerFields.mWallpaperMayChange = true;
+ displayContent.pendingLayoutChanges |=
+ WindowManagerPolicy.FINISH_LAYOUT_REDO_WALLPAPER;
+ if (WindowManagerService.DEBUG_LAYOUT_REPEATS) {
+ debugLayoutRepeats(
+ "wallpaper and commitFinishDrawingLocked true",
+ displayContent.pendingLayoutChanges);
}
}
- if (w != atoken.startingWindow) {
- if (!atoken.mAppAnimator.freezingScreen || !w.mAppFreezing) {
- atoken.numInterestingWindows++;
- if (w.isDrawnLw()) {
- atoken.numDrawnWindows++;
- if (WindowManagerService.DEBUG_VISIBILITY ||
- WindowManagerService.DEBUG_ORIENTATION) Slog.v(TAG,
- "tokenMayBeDrawn: " + atoken
- + " freezingScreen=" + atoken.mAppAnimator.freezingScreen
- + " mAppFreezing=" + w.mAppFreezing);
- updateAllDrawn = true;
+ }
+
+ winAnimator.setSurfaceBoundaries(recoveringMemory);
+
+ final AppWindowToken atoken = w.mAppToken;
+ if (DEBUG_STARTING_WINDOW && atoken != null && w == atoken.startingWindow) {
+ Slog.d(TAG, "updateWindows: starting " + w + " isOnScreen="
+ + w.isOnScreen() + " allDrawn=" + atoken.allDrawn
+ + " freezingScreen=" + atoken.mAppAnimator.freezingScreen);
+ }
+ if (atoken != null
+ && (!atoken.allDrawn || atoken.mAppAnimator.freezingScreen)) {
+ if (atoken.lastTransactionSequence != mTransactionSequence) {
+ atoken.lastTransactionSequence = mTransactionSequence;
+ atoken.numInterestingWindows = atoken.numDrawnWindows = 0;
+ atoken.startingDisplayed = false;
+ }
+ if ((w.isOnScreen() || winAnimator.mAttrType
+ == WindowManager.LayoutParams.TYPE_BASE_APPLICATION)
+ && !w.mExiting && !w.mDestroying) {
+ if (WindowManagerService.DEBUG_VISIBILITY ||
+ WindowManagerService.DEBUG_ORIENTATION) {
+ Slog.v(TAG, "Eval win " + w + ": isDrawn=" + w.isDrawnLw()
+ + ", isAnimating=" + winAnimator.isAnimating());
+ if (!w.isDrawnLw()) {
+ Slog.v(TAG, "Not displayed: s=" + winAnimator.mSurface
+ + " pv=" + w.mPolicyVisibility
+ + " mDrawState=" + winAnimator.mDrawState
+ + " ah=" + w.mAttachedHidden
+ + " th=" + atoken.hiddenRequested
+ + " a=" + winAnimator.mAnimating);
}
}
- } else if (w.isDrawnLw()) {
- atoken.startingDisplayed = true;
+ if (w != atoken.startingWindow) {
+ if (!atoken.mAppAnimator.freezingScreen || !w.mAppFreezing) {
+ atoken.numInterestingWindows++;
+ if (w.isDrawnLw()) {
+ atoken.numDrawnWindows++;
+ if (WindowManagerService.DEBUG_VISIBILITY ||
+ WindowManagerService.DEBUG_ORIENTATION) Slog.v(TAG,
+ "tokenMayBeDrawn: " + atoken
+ + " freezingScreen=" + atoken.mAppAnimator.freezingScreen
+ + " mAppFreezing=" + w.mAppFreezing);
+ updateAllDrawn = true;
+ }
+ }
+ } else if (w.isDrawnLw()) {
+ atoken.startingDisplayed = true;
+ }
}
}
}
- }
- if (someoneLosingFocus && w == mCurrentFocus && w.isDisplayedLw()) {
- focusDisplayed = true;
- }
+ if (isDefaultDisplay && someoneLosingFocus && (w == mCurrentFocus)
+ && w.isDisplayedLw()) {
+ focusDisplayed = true;
+ }
- updateResizingWindows(w);
+ updateResizingWindows(w);
+ }
}
if (updateAllDrawn) {
@@ -8776,13 +8794,15 @@
Surface.closeTransaction();
}
+ final WindowList defaultWindows = defaultDisplay.getWindowList();
+
// If we are ready to perform an app transition, check through
// all of the app tokens to be shown and see if they are ready
// to go.
if (mAppTransitionReady) {
- mPendingLayoutChanges |= handleAppTransitionReadyLocked(windows);
+ defaultDisplay.pendingLayoutChanges |= handleAppTransitionReadyLocked(defaultWindows);
if (DEBUG_LAYOUT_REPEATS) debugLayoutRepeats("after handleAppTransitionReadyLocked",
- mPendingLayoutChanges);
+ defaultDisplay.pendingLayoutChanges);
}
mInnerFields.mAdjResult = 0;
@@ -8794,22 +8814,22 @@
// reflects the correct Z-order, but the window list may now
// be out of sync with it. So here we will just rebuild the
// entire app window list. Fun!
- mPendingLayoutChanges |= handleAnimatingStoppedAndTransitionLocked();
+ defaultDisplay.pendingLayoutChanges |= handleAnimatingStoppedAndTransitionLocked();
if (DEBUG_LAYOUT_REPEATS) debugLayoutRepeats("after handleAnimStopAndXitionLock",
- mPendingLayoutChanges);
+ defaultDisplay.pendingLayoutChanges);
}
- if (mInnerFields.mWallpaperForceHidingChanged && mPendingLayoutChanges == 0 &&
- !mAppTransitionReady) {
+ if (mInnerFields.mWallpaperForceHidingChanged && defaultDisplay.pendingLayoutChanges == 0
+ && !mAppTransitionReady) {
// At this point, there was a window with a wallpaper that
// was force hiding other windows behind it, but now it
// is going away. This may be simple -- just animate
// away the wallpaper and its window -- or it may be
// hard -- the wallpaper now needs to be shown behind
// something that was hidden.
- mPendingLayoutChanges |= animateAwayWallpaperLocked();
+ defaultDisplay.pendingLayoutChanges |= animateAwayWallpaperLocked();
if (DEBUG_LAYOUT_REPEATS) debugLayoutRepeats("after animateAwayWallpaperLocked",
- mPendingLayoutChanges);
+ defaultDisplay.pendingLayoutChanges);
}
mInnerFields.mWallpaperForceHidingChanged = false;
@@ -8822,26 +8842,27 @@
if ((mInnerFields.mAdjResult&ADJUST_WALLPAPER_LAYERS_CHANGED) != 0) {
if (DEBUG_WALLPAPER) Slog.v(TAG,
"Wallpaper layer changed: assigning layers + relayout");
- mPendingLayoutChanges |= PhoneWindowManager.FINISH_LAYOUT_REDO_LAYOUT;
- assignLayersLocked(windows);
+ defaultDisplay.pendingLayoutChanges |= WindowManagerPolicy.FINISH_LAYOUT_REDO_LAYOUT;
+ assignLayersLocked(defaultWindows);
} else if ((mInnerFields.mAdjResult&ADJUST_WALLPAPER_VISIBILITY_CHANGED) != 0) {
if (DEBUG_WALLPAPER) Slog.v(TAG,
"Wallpaper visibility changed: relayout");
- mPendingLayoutChanges |= PhoneWindowManager.FINISH_LAYOUT_REDO_LAYOUT;
+ defaultDisplay.pendingLayoutChanges |= WindowManagerPolicy.FINISH_LAYOUT_REDO_LAYOUT;
}
if (mFocusMayChange) {
mFocusMayChange = false;
if (updateFocusedWindowLocked(UPDATE_FOCUS_PLACING_SURFACES,
false /*updateInputWindows*/)) {
- mPendingLayoutChanges |= PhoneWindowManager.FINISH_LAYOUT_REDO_ANIM;
+ defaultDisplay.pendingLayoutChanges |= WindowManagerPolicy.FINISH_LAYOUT_REDO_ANIM;
mInnerFields.mAdjResult = 0;
}
}
- if (mLayoutNeeded) {
- mPendingLayoutChanges |= PhoneWindowManager.FINISH_LAYOUT_REDO_LAYOUT;
- if (DEBUG_LAYOUT_REPEATS) debugLayoutRepeats("mLayoutNeeded", mPendingLayoutChanges);
+ if (needsLayout()) {
+ defaultDisplay.pendingLayoutChanges |= WindowManagerPolicy.FINISH_LAYOUT_REDO_LAYOUT;
+ if (DEBUG_LAYOUT_REPEATS) debugLayoutRepeats("mLayoutNeeded",
+ defaultDisplay.pendingLayoutChanges);
}
if (!mResizingWindows.isEmpty()) {
@@ -8949,11 +8970,16 @@
mRelayoutWhileAnimating.clear();
}
- if (wallpaperDestroyed) {
- mLayoutNeeded |= adjustWallpaperWindowsLocked() != 0;
+ if (wallpaperDestroyed && (adjustWallpaperWindowsLocked() != 0)) {
+ getDefaultDisplayContent().layoutNeeded = true;
}
- if (mPendingLayoutChanges != 0) {
- mLayoutNeeded = true;
+
+ DisplayContentsIterator iterator = new DisplayContentsIterator();
+ while (iterator.hasNext()) {
+ DisplayContent displayContent = iterator.next();
+ if (displayContent.pendingLayoutChanges != 0) {
+ displayContent.layoutNeeded = true;
+ }
}
// Finally update all input windows now that the window changes have stabilized.
@@ -8990,11 +9016,33 @@
}
}
- if (mInnerFields.mOrientationChangeComplete && !mLayoutNeeded &&
- !mInnerFields.mUpdateRotation) {
+ if (mInnerFields.mOrientationChangeComplete && !defaultDisplay.layoutNeeded
+ && !mInnerFields.mUpdateRotation) {
checkDrawnWindowsLocked();
}
+ final int N = mPendingRemove.size();
+ if (N > 0) {
+ if (mPendingRemoveTmp.length < N) {
+ mPendingRemoveTmp = new WindowState[N+10];
+ }
+ mPendingRemove.toArray(mPendingRemoveTmp);
+ mPendingRemove.clear();
+ DisplayContentList displayList = new DisplayContentList();
+ for (i = 0; i < N; i++) {
+ WindowState w = mPendingRemoveTmp[i];
+ removeWindowInnerLocked(w.mSession, w);
+ if (!displayList.contains(w.mDisplayContent)) {
+ displayList.add(w.mDisplayContent);
+ }
+ }
+
+ for (DisplayContent displayContent : displayList) {
+ assignLayersLocked(displayContent.getWindowList());
+ displayContent.layoutNeeded = true;
+ }
+ }
+
// Check to see if we are now in a state where the screen should
// be enabled, because the window obscured flags have changed.
enableScreenIfNeededLocked();
@@ -9002,9 +9050,8 @@
updateLayoutToAnimationLocked();
if (DEBUG_WINDOW_TRACE) {
- Slog.e(TAG, "performLayoutAndPlaceSurfacesLockedInner exit: mPendingLayoutChanges="
- + Integer.toHexString(mPendingLayoutChanges) + " mLayoutNeeded=" + mLayoutNeeded
- + " animating=" + mAnimator.mAnimating);
+ Slog.e(TAG, "performLayoutAndPlaceSurfacesLockedInner exit: animating="
+ + mAnimator.mAnimating);
}
}
@@ -9077,6 +9124,12 @@
}
}
+ public void requestTraversal() {
+ synchronized (mWindowMap) {
+ requestTraversalLocked();
+ }
+ }
+
void requestTraversalLocked() {
if (!mTraversalScheduled) {
mTraversalScheduled = true;
@@ -9154,6 +9207,16 @@
setAnimDimParams(null);
}
+ private boolean needsLayout() {
+ DisplayContentsIterator iterator = new DisplayContentsIterator();
+ while (iterator.hasNext()) {
+ if (iterator.next().layoutNeeded) {
+ return true;
+ }
+ }
+ return false;
+ }
+
private boolean copyAnimToLayoutParamsLocked() {
boolean doRequest = false;
final WindowAnimator.AnimatorToLayoutParams animToLayout = mAnimator.mAnimToLayout;
@@ -9186,10 +9249,15 @@
mTurnOnScreen = true;
}
- mPendingLayoutChanges |= animToLayout.mPendingLayoutChanges;
- if (mPendingLayoutChanges != 0) {
+ SparseIntArray pendingLayouts = animToLayout.mPendingLayoutChanges;
+ final int count = pendingLayouts.size();
+ if (count > 0) {
doRequest = true;
}
+ for (int i = 0; i < count; ++i) {
+ final DisplayContent displayContent = getDisplayContent(pendingLayouts.keyAt(i));
+ displayContent.pendingLayoutChanges = pendingLayouts.valueAt(i);
+ }
mWindowDetachedWallpaper = animToLayout.mWindowDetachedWallpaper;
}
@@ -9325,7 +9393,7 @@
if (moveInputMethodWindowsIfNeededLocked(
mode != UPDATE_FOCUS_WILL_ASSIGN_LAYERS &&
mode != UPDATE_FOCUS_WILL_PLACE_SURFACES)) {
- mLayoutNeeded = true;
+ getDefaultDisplayContent().layoutNeeded = true;
}
if (mode == UPDATE_FOCUS_PLACING_SURFACES) {
performLayoutLockedInner(displayContent, true /*initial*/, updateInputWindows);
@@ -9337,9 +9405,9 @@
}
}
- if ((focusChanged&WindowManagerPolicy.FINISH_LAYOUT_REDO_LAYOUT) != 0) {
+ if ((focusChanged & WindowManagerPolicy.FINISH_LAYOUT_REDO_LAYOUT) != 0) {
// The change in focus caused us to need to do a layout. Okay.
- mLayoutNeeded = true;
+ getDefaultDisplayContent().layoutNeeded = true;
if (mode == UPDATE_FOCUS_PLACING_SURFACES) {
performLayoutLockedInner(displayContent, true /*initial*/, updateInputWindows);
}
@@ -9572,7 +9640,7 @@
return val;
}
- void createWatermark() {
+ void createWatermarkInTransaction() {
if (mWatermark != null) {
return;
}
@@ -10001,8 +10069,18 @@
}
pw.print(" mSystemBooted="); pw.print(mSystemBooted);
pw.print(" mDisplayEnabled="); pw.println(mDisplayEnabled);
- pw.print(" mLayoutNeeded="); pw.print(mLayoutNeeded);
- pw.print("mTransactionSequence="); pw.println(mTransactionSequence);
+ if (needsLayout()) {
+ pw.print(" layoutNeeded on displays=");
+ DisplayContentsIterator dcIterator = new DisplayContentsIterator();
+ while (dcIterator.hasNext()) {
+ final DisplayContent displayContent = dcIterator.next();
+ if (displayContent.layoutNeeded) {
+ pw.print(displayContent.getDisplayId());
+ }
+ }
+ pw.println();
+ }
+ pw.print("mTransactionSequence="); pw.println(mTransactionSequence);
pw.print(" mDisplayFrozen="); pw.print(mDisplayFrozen);
pw.print(" mWindowsFreezingScreen="); pw.print(mWindowsFreezingScreen);
pw.print(" mAppsFreezingScreen="); pw.print(mAppsFreezingScreen);
@@ -10382,4 +10460,7 @@
return getDefaultDisplayContent().getDisplayInfo();
}
+ public WindowList getWindowList(final Display display) {
+ return getDisplayContent(display.getDisplayId()).getWindowList();
+ }
}
diff --git a/services/java/com/android/server/wm/WindowState.java b/services/java/com/android/server/wm/WindowState.java
index a52e1d7..6711445 100644
--- a/services/java/com/android/server/wm/WindowState.java
+++ b/services/java/com/android/server/wm/WindowState.java
@@ -587,10 +587,12 @@
}
}
+ @Override
public int getSystemUiVisibility() {
return mSystemUiVisibility;
}
+ @Override
public int getSurfaceLayer() {
return mLayer;
}
@@ -598,7 +600,11 @@
public IApplicationToken getAppToken() {
return mAppToken != null ? mAppToken.appToken : null;
}
-
+
+ public int getDisplayId() {
+ return mDisplayContent.getDisplayId();
+ }
+
public long getInputDispatchingTimeoutNanos() {
return mAppToken != null
? mAppToken.inputDispatchingTimeoutNanos
diff --git a/services/java/com/android/server/wm/WindowStateAnimator.java b/services/java/com/android/server/wm/WindowStateAnimator.java
index 1bda22a..9bb7299 100644
--- a/services/java/com/android/server/wm/WindowStateAnimator.java
+++ b/services/java/com/android/server/wm/WindowStateAnimator.java
@@ -335,7 +335,7 @@
+ mWin.mPolicyVisibilityAfterAnim);
}
mWin.mPolicyVisibility = mWin.mPolicyVisibilityAfterAnim;
- mService.mLayoutNeeded = true;
+ mWin.mDisplayContent.layoutNeeded = true;
if (!mWin.mPolicyVisibility) {
if (mService.mCurrentFocus == mWin) {
mService.mFocusMayChange = true;
@@ -359,9 +359,10 @@
}
finishExit();
- mAnimator.mPendingLayoutChanges |= WindowManagerPolicy.FINISH_LAYOUT_REDO_ANIM;
+ final int displayId = mWin.mDisplayContent.getDisplayId();
+ mAnimator.setPendingLayoutChanges(displayId, WindowManagerPolicy.FINISH_LAYOUT_REDO_ANIM);
if (WindowManagerService.DEBUG_LAYOUT_REPEATS) mService.debugLayoutRepeats(
- "WindowStateAnimator", mAnimator.mPendingLayoutChanges);
+ "WindowStateAnimator", mAnimator.mPendingLayoutChanges.get(displayId));
if (mWin.mAppToken != null) {
mWin.mAppToken.updateReportedVisibilityLocked();
@@ -1106,8 +1107,9 @@
"SIZE " + width + "x" + height, null);
mSurfaceResized = true;
mSurface.setSize(width, height);
- mAnimator.mPendingLayoutChanges |=
- WindowManagerPolicy.FINISH_LAYOUT_REDO_WALLPAPER;
+ final int displayId = w.mDisplayContent.getDisplayId();
+ mAnimator.setPendingLayoutChanges(displayId,
+ WindowManagerPolicy.FINISH_LAYOUT_REDO_WALLPAPER);
if ((w.mAttrs.flags & LayoutParams.FLAG_DIM_BEHIND) != 0) {
final DisplayInfo displayInfo = mWin.mDisplayContent.getDisplayInfo();
mService.startDimming(this, w.mExiting ? 0 : w.mAttrs.dimAmount,
@@ -1360,7 +1362,7 @@
// do a layout. If called from within the transaction
// loop, this will cause it to restart with a new
// layout.
- mService.mLayoutNeeded = true;
+ c.mDisplayContent.layoutNeeded = true;
}
}
}
diff --git a/services/tests/servicestests/src/com/android/server/NetworkPolicyManagerServiceTest.java b/services/tests/servicestests/src/com/android/server/NetworkPolicyManagerServiceTest.java
index 3373fd4..a2104bb 100644
--- a/services/tests/servicestests/src/com/android/server/NetworkPolicyManagerServiceTest.java
+++ b/services/tests/servicestests/src/com/android/server/NetworkPolicyManagerServiceTest.java
@@ -880,7 +880,8 @@
private Future<Void> expectClearNotifications() throws Exception {
final FutureAnswer future = new FutureAnswer();
- mNotifManager.cancelNotificationWithTag(isA(String.class), isA(String.class), anyInt());
+ mNotifManager.cancelNotificationWithTag(isA(String.class), isA(String.class), anyInt(),
+ UserHandle.myUserId());
expectLastCall().andAnswer(future).anyTimes();
return future;
}
@@ -888,7 +889,7 @@
private Future<String> expectEnqueueNotification() throws Exception {
final FutureCapture<String> tag = new FutureCapture<String>();
mNotifManager.enqueueNotificationWithTag(isA(String.class), capture(tag.capture), anyInt(),
- isA(Notification.class), isA(int[].class));
+ isA(Notification.class), isA(int[].class), UserHandle.myUserId());
return tag;
}
diff --git a/test-runner/src/android/test/mock/MockContext.java b/test-runner/src/android/test/mock/MockContext.java
index eb1f2d6..5c9282e 100644
--- a/test-runner/src/android/test/mock/MockContext.java
+++ b/test-runner/src/android/test/mock/MockContext.java
@@ -40,6 +40,7 @@
import android.os.Looper;
import android.os.UserHandle;
import android.view.CompatibilityInfoHolder;
+import android.view.Display;
import java.io.File;
import java.io.FileInputStream;
@@ -369,6 +370,13 @@
throw new UnsupportedOperationException();
}
+ /** @hide */
+ @Override
+ public Intent registerReceiverAsUser(BroadcastReceiver receiver, UserHandle user,
+ IntentFilter filter, String broadcastPermission, Handler scheduler) {
+ throw new UnsupportedOperationException();
+ }
+
@Override
public void unregisterReceiver(BroadcastReceiver receiver) {
throw new UnsupportedOperationException();
@@ -521,13 +529,18 @@
}
@Override
+ public Context createDisplayContext(Display display) {
+ throw new UnsupportedOperationException();
+ }
+
+ @Override
public boolean isRestricted() {
throw new UnsupportedOperationException();
}
/** @hide */
@Override
- public CompatibilityInfoHolder getCompatibilityInfo() {
+ public CompatibilityInfoHolder getCompatibilityInfo(int displayId) {
throw new UnsupportedOperationException();
}
}
diff --git a/tests/HwAccelerationTest/AndroidManifest.xml b/tests/HwAccelerationTest/AndroidManifest.xml
index c783ad6..aebc594 100644
--- a/tests/HwAccelerationTest/AndroidManifest.xml
+++ b/tests/HwAccelerationTest/AndroidManifest.xml
@@ -33,6 +33,15 @@
<meta-data android:name="android.graphics.renderThread" android:value="true" />
<activity
+ android:name="PathOffsetActivity"
+ android:label="_PathOffset">
+ <intent-filter>
+ <action android:name="android.intent.action.MAIN" />
+ <category android:name="android.intent.category.LAUNCHER" />
+ </intent-filter>
+ </activity>
+
+ <activity
android:name="TextPathActivity"
android:label="_TextPath">
<intent-filter>
diff --git a/tests/HwAccelerationTest/src/com/android/test/hwui/PathOffsetActivity.java b/tests/HwAccelerationTest/src/com/android/test/hwui/PathOffsetActivity.java
new file mode 100644
index 0000000..fa73de1
--- /dev/null
+++ b/tests/HwAccelerationTest/src/com/android/test/hwui/PathOffsetActivity.java
@@ -0,0 +1,67 @@
+/*
+ * Copyright (C) 2010 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package com.android.test.hwui;
+
+import android.app.Activity;
+import android.content.Context;
+import android.graphics.Canvas;
+import android.graphics.Color;
+import android.graphics.Paint;
+import android.graphics.Path;
+import android.os.Bundle;
+import android.view.MotionEvent;
+import android.view.View;
+
+@SuppressWarnings({"UnusedDeclaration"})
+public class PathOffsetActivity extends Activity {
+ @Override
+ protected void onCreate(Bundle savedInstanceState) {
+ super.onCreate(savedInstanceState);
+ final PathsView view = new PathsView(this);
+ setContentView(view);
+ }
+
+ public class PathsView extends View {
+ private Path mPath;
+ private Paint mPaint;
+
+ public PathsView(Context context) {
+ super(context);
+
+ mPaint = new Paint();
+ mPaint.setStyle(Paint.Style.STROKE);
+ mPaint.setStrokeWidth(3);
+
+ mPath = new Path();
+ mPath.lineTo(100, 100);
+ mPath.lineTo(200, 300);
+ }
+
+ @Override
+ protected void onDraw(Canvas canvas) {
+ mPath.offset(1, 1);
+ mPaint.setColor(Color.RED);
+ canvas.drawPath(mPath, mPaint);
+ }
+
+ @Override
+ public boolean onTouchEvent(MotionEvent event) {
+ invalidate();
+ return super.onTouchEvent(event);
+ }
+ }
+}
diff --git a/tests/RenderScriptTests/ImageProcessing/src/com/android/rs/image/ColorMatrix.java b/tests/RenderScriptTests/ImageProcessing/src/com/android/rs/image/ColorMatrix.java
new file mode 100644
index 0000000..87a2de1
--- /dev/null
+++ b/tests/RenderScriptTests/ImageProcessing/src/com/android/rs/image/ColorMatrix.java
@@ -0,0 +1,64 @@
+/*
+ * 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.rs.image;
+
+import java.lang.Math;
+
+import android.renderscript.Allocation;
+import android.renderscript.Element;
+import android.renderscript.Matrix4f;
+import android.renderscript.RenderScript;
+import android.renderscript.Script;
+import android.renderscript.ScriptC;
+import android.renderscript.ScriptGroup;
+import android.renderscript.ScriptIntrinsicColorMatrix;
+import android.renderscript.Type;
+import android.util.Log;
+
+public class ColorMatrix extends TestBase {
+ private ScriptC_colormatrix mScript;
+ private ScriptIntrinsicColorMatrix mIntrinsic;
+ private boolean mUseIntrinsic;
+
+ public ColorMatrix(boolean useIntrinsic) {
+ mUseIntrinsic = useIntrinsic;
+ }
+
+ public void createTest(android.content.res.Resources res) {
+ Matrix4f m = new Matrix4f();
+ m.set(1, 0, 0.2f);
+ m.set(1, 1, 0.9f);
+ m.set(1, 2, 0.2f);
+
+ if (mUseIntrinsic) {
+ mIntrinsic = ScriptIntrinsicColorMatrix.create(mRS, Element.U8_4(mRS));
+ mIntrinsic.setColorMatrix(m);
+ } else {
+ mScript = new ScriptC_colormatrix(mRS, res, R.raw.colormatrix);
+ mScript.invoke_setMatrix(m);
+ }
+ }
+
+ public void runTest() {
+ if (mUseIntrinsic) {
+ mIntrinsic.forEach(mInPixelsAllocation, mOutPixelsAllocation);
+ } else {
+ mScript.forEach_root(mInPixelsAllocation, mOutPixelsAllocation);
+ }
+ }
+
+}
diff --git a/tests/RenderScriptTests/ImageProcessing/src/com/android/rs/image/Convolve3x3.java b/tests/RenderScriptTests/ImageProcessing/src/com/android/rs/image/Convolve3x3.java
new file mode 100644
index 0000000..51794db
--- /dev/null
+++ b/tests/RenderScriptTests/ImageProcessing/src/com/android/rs/image/Convolve3x3.java
@@ -0,0 +1,74 @@
+/*
+ * 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.rs.image;
+
+import java.lang.Math;
+
+import android.renderscript.Allocation;
+import android.renderscript.Element;
+import android.renderscript.Matrix4f;
+import android.renderscript.RenderScript;
+import android.renderscript.Script;
+import android.renderscript.ScriptC;
+import android.renderscript.ScriptGroup;
+import android.renderscript.ScriptIntrinsicConvolve3x3;
+import android.renderscript.Type;
+import android.util.Log;
+
+public class Convolve3x3 extends TestBase {
+ private ScriptC_convolve3x3 mScript;
+ private ScriptIntrinsicConvolve3x3 mIntrinsic;
+
+ private int mWidth;
+ private int mHeight;
+ private boolean mUseIntrinsic;
+
+ public Convolve3x3(boolean useIntrinsic) {
+ mUseIntrinsic = useIntrinsic;
+ }
+
+ public void createTest(android.content.res.Resources res) {
+ mWidth = mInPixelsAllocation.getType().getX();
+ mHeight = mInPixelsAllocation.getType().getY();
+
+ float f[] = new float[9];
+ f[0] = 0.f; f[1] = -1.f; f[2] = 0.f;
+ f[3] = -1.f; f[4] = 5.f; f[5] = -1.f;
+ f[6] = 0.f; f[7] = -1.f; f[8] = 0.f;
+
+ if (mUseIntrinsic) {
+ mIntrinsic = ScriptIntrinsicConvolve3x3.create(mRS, Element.U8_4(mRS));
+ mIntrinsic.setColorMatrix(f);
+ mIntrinsic.setInput(mInPixelsAllocation);
+ } else {
+ mScript = new ScriptC_convolve3x3(mRS, res, R.raw.convolve3x3);
+ mScript.set_gCoeffs(f);
+ mScript.set_gIn(mInPixelsAllocation);
+ mScript.set_gWidth(mWidth);
+ mScript.set_gHeight(mHeight);
+ }
+ }
+
+ public void runTest() {
+ if (mUseIntrinsic) {
+ mIntrinsic.forEach(mOutPixelsAllocation);
+ } else {
+ mScript.forEach_root(mOutPixelsAllocation);
+ }
+ }
+
+}
diff --git a/tests/RenderScriptTests/ImageProcessing/src/com/android/rs/image/Copy.java b/tests/RenderScriptTests/ImageProcessing/src/com/android/rs/image/Copy.java
new file mode 100644
index 0000000..efca0b5
--- /dev/null
+++ b/tests/RenderScriptTests/ImageProcessing/src/com/android/rs/image/Copy.java
@@ -0,0 +1,40 @@
+/*
+ * 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.rs.image;
+
+import java.lang.Math;
+
+import android.renderscript.Allocation;
+import android.renderscript.Element;
+import android.renderscript.RenderScript;
+import android.renderscript.Script;
+import android.renderscript.ScriptC;
+import android.renderscript.Type;
+import android.util.Log;
+
+public class Copy extends TestBase {
+ private ScriptC_copy mScript;
+
+ public void createTest(android.content.res.Resources res) {
+ mScript = new ScriptC_copy(mRS, res, R.raw.copy);
+ }
+
+ public void runTest() {
+ mScript.forEach_root(mInPixelsAllocation, mOutPixelsAllocation);
+ }
+
+}
diff --git a/tests/RenderScriptTests/ImageProcessing/src/com/android/rs/image/ImageProcessingActivity.java b/tests/RenderScriptTests/ImageProcessing/src/com/android/rs/image/ImageProcessingActivity.java
index 07626a3..001dea8 100644
--- a/tests/RenderScriptTests/ImageProcessing/src/com/android/rs/image/ImageProcessingActivity.java
+++ b/tests/RenderScriptTests/ImageProcessing/src/com/android/rs/image/ImageProcessingActivity.java
@@ -168,13 +168,25 @@
mTest = new Vignette(true, true);
break;
case 15:
- mTest = new GroupTest(true);
- break;
- case 16:
mTest = new GroupTest(false);
break;
+ case 16:
+ mTest = new GroupTest(true);
+ break;
case 17:
- mTest = new Intrinsics(0);
+ mTest = new Convolve3x3(false);
+ break;
+ case 18:
+ mTest = new Convolve3x3(true);
+ break;
+ case 19:
+ mTest = new ColorMatrix(false);
+ break;
+ case 20:
+ mTest = new ColorMatrix(true);
+ break;
+ case 21:
+ mTest = new Copy();
break;
}
@@ -188,7 +200,7 @@
}
void setupTests() {
- mTestNames = new String[18];
+ mTestNames = new String[22];
mTestNames[0] = "Levels Vec3 Relaxed";
mTestNames[1] = "Levels Vec4 Relaxed";
mTestNames[2] = "Levels Vec3 Full";
@@ -206,7 +218,11 @@
mTestNames[14] = "Vignette Approximate Relaxed";
mTestNames[15] = "Group Test (emulated)";
mTestNames[16] = "Group Test (native)";
- mTestNames[17] = "Intrinsics Convolve 3x3";
+ mTestNames[17] = "Convolve 3x3";
+ mTestNames[18] = "Intrinsics Convolve 3x3";
+ mTestNames[19] = "ColorMatrix";
+ mTestNames[20] = "Intrinsics ColorMatrix";
+ mTestNames[21] = "Copy";
mTestSpinner.setAdapter(new ArrayAdapter<String>(
this, R.layout.spinner_layout, mTestNames));
}
@@ -280,14 +296,14 @@
// button hook
public void benchmark(View v) {
- long t = getBenchmark();
+ float t = getBenchmark();
//long javaTime = javaFilter();
//mBenchmarkResult.setText("RS: " + t + " ms Java: " + javaTime + " ms");
mBenchmarkResult.setText("Result: " + t + " ms");
}
// For benchmark test
- public long getBenchmark() {
+ public float getBenchmark() {
mDoingBenchmark = true;
mTest.setupBenchmark();
@@ -303,14 +319,18 @@
Log.v(TAG, "Benchmarking");
t = java.lang.System.currentTimeMillis();
- mTest.runTest();
+ for (int i=0; i<10; i++) {
+ mTest.runTest();
+ }
mTest.finish();
t = java.lang.System.currentTimeMillis() - t;
+ float ft = (float)t;
+ ft /= 10;
- Log.v(TAG, "getBenchmark: Renderscript frame time core ms " + t);
+ Log.v(TAG, "getBenchmark: Renderscript frame time core ms " + ft);
mTest.exitBenchmark();
mDoingBenchmark = false;
- return t;
+ return ft;
}
}
diff --git a/tests/RenderScriptTests/ImageProcessing/src/com/android/rs/image/ImageProcessingTest.java b/tests/RenderScriptTests/ImageProcessing/src/com/android/rs/image/ImageProcessingTest.java
index 912d863..f995437 100644
--- a/tests/RenderScriptTests/ImageProcessing/src/com/android/rs/image/ImageProcessingTest.java
+++ b/tests/RenderScriptTests/ImageProcessing/src/com/android/rs/image/ImageProcessingTest.java
@@ -78,7 +78,7 @@
BufferedWriter rsWriter = new BufferedWriter(new FileWriter(resultFile));
Log.v(TAG, "Saved results in: " + resultFile.getAbsolutePath());
for (int i = 0; i < ITERATION; i++ ) {
- t = mAct.getBenchmark();
+ t = (long)mAct.getBenchmark();
sum += t;
rsWriter.write("Renderscript frame time core: " + t + " ms\n");
Log.v(TAG, "RenderScript framew time core: " + t + " ms");
diff --git a/tests/RenderScriptTests/ImageProcessing/src/com/android/rs/image/Intrinsics.java b/tests/RenderScriptTests/ImageProcessing/src/com/android/rs/image/Intrinsics.java
deleted file mode 100644
index dab8111..0000000
--- a/tests/RenderScriptTests/ImageProcessing/src/com/android/rs/image/Intrinsics.java
+++ /dev/null
@@ -1,63 +0,0 @@
-/*
- * 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.rs.image;
-
-import java.lang.Math;
-
-import android.renderscript.Allocation;
-import android.renderscript.Element;
-import android.renderscript.RenderScript;
-import android.renderscript.Script;
-import android.renderscript.ScriptIntrinsicConvolve3x3;
-import android.renderscript.Type;
-import android.util.Log;
-import android.widget.SeekBar;
-import android.widget.TextView;
-
-public class Intrinsics extends TestBase {
- private ScriptIntrinsicConvolve3x3 mScript;
-
- Intrinsics(int id) {
- }
-
- public boolean onBar1Setup(SeekBar b, TextView t) {
- t.setText("Strength");
- b.setProgress(50);
- return true;
- }
-
- public void onBar1Changed(int progress) {
- float s = progress / 100.0f;
- float v[] = new float[9];
- v[0] = 0.f; v[1] = -s; v[2] = 0.f;
- v[3] = -s; v[4] = s*4+1; v[5] = -s;
- v[6] = 0.f; v[7] = -s; v[8] = 0.f;
- mScript.setColorMatrix(v);
- }
-
-
- public void createTest(android.content.res.Resources res) {
- mScript = ScriptIntrinsicConvolve3x3.create(mRS, Element.RGBA_8888(mRS));
- }
-
- public void runTest() {
- mScript.setInput(mInPixelsAllocation);
- mScript.forEach(mOutPixelsAllocation);
- }
-
-}
-
diff --git a/tests/RenderScriptTests/ImageProcessing/src/com/android/rs/image/convolve3x3.rs b/tests/RenderScriptTests/ImageProcessing/src/com/android/rs/image/convolve3x3.rs
index b6b4a0f..455fcc2 100644
--- a/tests/RenderScriptTests/ImageProcessing/src/com/android/rs/image/convolve3x3.rs
+++ b/tests/RenderScriptTests/ImageProcessing/src/com/android/rs/image/convolve3x3.rs
@@ -25,9 +25,9 @@
float gCoeffs[9];
void root(uchar4 *out, uint32_t x, uint32_t y) {
- uint32_t x1 = min((int32_t)x+1, gWidth);
+ uint32_t x1 = min((int32_t)x+1, gWidth-1);
uint32_t x2 = max((int32_t)x-1, 0);
- uint32_t y1 = min((int32_t)y+1, gHeight);
+ uint32_t y1 = min((int32_t)y+1, gHeight-1);
uint32_t y2 = max((int32_t)y-1, 0);
float4 p00 = convert_float4(((uchar4 *)rsGetElementAt(gIn, x1, y1))[0]);
diff --git a/tests/RenderScriptTests/ImageProcessing/src/com/android/rs/image/copy.rs b/tests/RenderScriptTests/ImageProcessing/src/com/android/rs/image/copy.rs
new file mode 100644
index 0000000..9eb5d43
--- /dev/null
+++ b/tests/RenderScriptTests/ImageProcessing/src/com/android/rs/image/copy.rs
@@ -0,0 +1,24 @@
+/*
+ * 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.
+ */
+
+#pragma version(1)
+#pragma rs java_package_name(com.android.rs.image)
+
+void root(const uchar4 *v_in, uchar4 *v_out) {
+ *v_out = *v_in;
+}
+
+
diff --git a/tests/StatusBar/src/com/android/statusbartest/NotificationTestList.java b/tests/StatusBar/src/com/android/statusbartest/NotificationTestList.java
index 70e2aac..ec39aab 100644
--- a/tests/StatusBar/src/com/android/statusbartest/NotificationTestList.java
+++ b/tests/StatusBar/src/com/android/statusbartest/NotificationTestList.java
@@ -28,6 +28,7 @@
import android.os.Environment;
import android.os.Vibrator;
import android.os.Handler;
+import android.os.UserHandle;
import android.util.Log;
import android.net.Uri;
import android.os.SystemClock;
@@ -798,7 +799,8 @@
null,
100,
n,
- idOut);
+ idOut,
+ UserHandle.myUserId());
} catch (android.os.RemoteException ex) {
// oh well
}
@@ -822,7 +824,8 @@
null,
200,
n,
- idOut);
+ idOut,
+ UserHandle.myUserId());
} catch (android.os.RemoteException ex) {
// oh well
}
@@ -846,7 +849,8 @@
null,
1,
n,
- idOut);
+ idOut,
+ UserHandle.myUserId());
} catch (android.os.RemoteException ex) {
// oh well
}
diff --git a/tools/aapt/AaptAssets.cpp b/tools/aapt/AaptAssets.cpp
index 46b8a27..438a670 100644
--- a/tools/aapt/AaptAssets.cpp
+++ b/tools/aapt/AaptAssets.cpp
@@ -183,6 +183,13 @@
return 0;
}
+ // layout direction
+ if (getLayoutDirectionName(part.string(), &config)) {
+ *axis = AXIS_LAYOUTDIR;
+ *value = (config.screenLayout&ResTable_config::MASK_LAYOUTDIR);
+ return 0;
+ }
+
// smallest screen dp width
if (getSmallestScreenWidthDpName(part.string(), &config)) {
*axis = AXIS_SMALLESTSCREENWIDTHDP;
@@ -309,6 +316,8 @@
case AXIS_LANGUAGE:
return (((uint32_t)config.country[1]) << 24) | (((uint32_t)config.country[0]) << 16)
| (((uint32_t)config.language[1]) << 8) | (config.language[0]);
+ case AXIS_LAYOUTDIR:
+ return config.screenLayout&ResTable_config::MASK_LAYOUTDIR;
case AXIS_SCREENLAYOUTSIZE:
return config.screenLayout&ResTable_config::MASK_SCREENSIZE;
case AXIS_ORIENTATION:
@@ -364,7 +373,7 @@
Vector<String8> parts;
String8 mcc, mnc, loc, layoutsize, layoutlong, orient, den;
- String8 touch, key, keysHidden, nav, navHidden, size, vers;
+ String8 touch, key, keysHidden, nav, navHidden, size, layoutDir, vers;
String8 uiModeType, uiModeNight, smallestwidthdp, widthdp, heightdp;
const char *p = dir;
@@ -452,6 +461,18 @@
//printf("not region: %s\n", part.string());
}
+ if (getLayoutDirectionName(part.string())) {
+ layoutDir = part;
+
+ index++;
+ if (index == N) {
+ goto success;
+ }
+ part = parts[index];
+ } else {
+ //printf("not layout direction: %s\n", part.string());
+ }
+
if (getSmallestScreenWidthDpName(part.string())) {
smallestwidthdp = part;
@@ -674,6 +695,7 @@
this->navHidden = navHidden;
this->navigation = nav;
this->screenSize = size;
+ this->layoutDirection = layoutDir;
this->version = vers;
// what is this anyway?
@@ -691,6 +713,8 @@
s += ",";
s += this->locale;
s += ",";
+ s += layoutDirection;
+ s += ",";
s += smallestScreenWidthDp;
s += ",";
s += screenWidthDp;
@@ -747,6 +771,12 @@
}
s += locale;
}
+ if (this->layoutDirection != "") {
+ if (s.length() > 0) {
+ s += "-";
+ }
+ s += layoutDirection;
+ }
if (this->smallestScreenWidthDp != "") {
if (s.length() > 0) {
s += "-";
@@ -958,6 +988,28 @@
return false;
}
+bool AaptGroupEntry::getLayoutDirectionName(const char* name, ResTable_config* out)
+{
+ if (strcmp(name, kWildcardName) == 0) {
+ if (out) out->screenLayout =
+ (out->screenLayout&~ResTable_config::MASK_LAYOUTDIR)
+ | ResTable_config::LAYOUTDIR_ANY;
+ return true;
+ } else if (strcmp(name, "ltr") == 0) {
+ if (out) out->screenLayout =
+ (out->screenLayout&~ResTable_config::MASK_LAYOUTDIR)
+ | ResTable_config::LAYOUTDIR_LTR;
+ return true;
+ } else if (strcmp(name, "rtl") == 0) {
+ if (out) out->screenLayout =
+ (out->screenLayout&~ResTable_config::MASK_LAYOUTDIR)
+ | ResTable_config::LAYOUTDIR_RTL;
+ return true;
+ }
+
+ return false;
+}
+
bool AaptGroupEntry::getScreenLayoutSizeName(const char* name,
ResTable_config* out)
{
@@ -1415,6 +1467,7 @@
int v = mcc.compare(o.mcc);
if (v == 0) v = mnc.compare(o.mnc);
if (v == 0) v = locale.compare(o.locale);
+ if (v == 0) v = layoutDirection.compare(o.layoutDirection);
if (v == 0) v = vendor.compare(o.vendor);
if (v == 0) v = smallestScreenWidthDp.compare(o.smallestScreenWidthDp);
if (v == 0) v = screenWidthDp.compare(o.screenWidthDp);
@@ -1447,6 +1500,7 @@
getMccName(mcc.string(), ¶ms);
getMncName(mnc.string(), ¶ms);
getLocaleName(locale.string(), ¶ms);
+ getLayoutDirectionName(layoutDirection.string(), ¶ms);
getSmallestScreenWidthDpName(smallestScreenWidthDp.string(), ¶ms);
getScreenWidthDpName(screenWidthDp.string(), ¶ms);
getScreenHeightDpName(screenHeightDp.string(), ¶ms);
diff --git a/tools/aapt/AaptAssets.h b/tools/aapt/AaptAssets.h
index d5f296c..5cfa913 100644
--- a/tools/aapt/AaptAssets.h
+++ b/tools/aapt/AaptAssets.h
@@ -51,6 +51,7 @@
AXIS_SMALLESTSCREENWIDTHDP,
AXIS_SCREENWIDTHDP,
AXIS_SCREENHEIGHTDP,
+ AXIS_LAYOUTDIR,
AXIS_VERSION,
AXIS_START = AXIS_MCC,
@@ -95,6 +96,7 @@
static bool getSmallestScreenWidthDpName(const char* name, ResTable_config* out = NULL);
static bool getScreenWidthDpName(const char* name, ResTable_config* out = NULL);
static bool getScreenHeightDpName(const char* name, ResTable_config* out = NULL);
+ static bool getLayoutDirectionName(const char* name, ResTable_config* out = NULL);
static bool getVersionName(const char* name, ResTable_config* out = NULL);
int compare(const AaptGroupEntry& o) const;
@@ -133,6 +135,7 @@
String8 navHidden;
String8 navigation;
String8 screenSize;
+ String8 layoutDirection;
String8 version;
mutable bool mParamsChanged;
diff --git a/tools/aapt/ResourceTable.cpp b/tools/aapt/ResourceTable.cpp
index d98fe65..3d7b088 100644
--- a/tools/aapt/ResourceTable.cpp
+++ b/tools/aapt/ResourceTable.cpp
@@ -2811,7 +2811,7 @@
NOISY(printf("Writing config %d config: imsi:%d/%d lang:%c%c cnt:%c%c "
"orien:%d ui:%d touch:%d density:%d key:%d inp:%d nav:%d sz:%dx%d "
- "sw%ddp w%ddp h%ddp\n",
+ "sw%ddp w%ddp h%ddp dir:%d\n",
ti+1,
config.mcc, config.mnc,
config.language[0] ? config.language[0] : '-',
@@ -2829,7 +2829,8 @@
config.screenHeight,
config.smallestScreenWidthDp,
config.screenWidthDp,
- config.screenHeightDp));
+ config.screenHeightDp,
+ config.layoutDirection));
if (filterable && !filter.match(config)) {
continue;
@@ -2853,7 +2854,7 @@
tHeader->config = config;
NOISY(printf("Writing type %d config: imsi:%d/%d lang:%c%c cnt:%c%c "
"orien:%d ui:%d touch:%d density:%d key:%d inp:%d nav:%d sz:%dx%d "
- "sw%ddp w%ddp h%ddp\n",
+ "sw%ddp w%ddp h%ddp dir:%d\n",
ti+1,
tHeader->config.mcc, tHeader->config.mnc,
tHeader->config.language[0] ? tHeader->config.language[0] : '-',
@@ -2871,7 +2872,8 @@
tHeader->config.screenHeight,
tHeader->config.smallestScreenWidthDp,
tHeader->config.screenWidthDp,
- tHeader->config.screenHeightDp));
+ tHeader->config.screenHeightDp,
+ tHeader->config.layoutDirection));
tHeader->config.swapHtoD();
// Build the entries inside of this type.
@@ -3489,7 +3491,7 @@
if (config != NULL) {
NOISY(printf("New entry at %s:%d: imsi:%d/%d lang:%c%c cnt:%c%c "
"orien:%d touch:%d density:%d key:%d inp:%d nav:%d sz:%dx%d "
- "sw%ddp w%ddp h%ddp\n",
+ "sw%ddp w%ddp h%ddp dir:%d\n",
sourcePos.file.string(), sourcePos.line,
config->mcc, config->mnc,
config->language[0] ? config->language[0] : '-',
@@ -3506,7 +3508,8 @@
config->screenHeight,
config->smallestScreenWidthDp,
config->screenWidthDp,
- config->screenHeightDp));
+ config->screenHeightDp,
+ config->layoutDirection));
} else {
NOISY(printf("New entry at %s:%d: NULL config\n",
sourcePos.file.string(), sourcePos.line));
diff --git a/tools/layoutlib/bridge/src/android/os/SystemClock_Delegate.java b/tools/layoutlib/bridge/src/android/os/SystemClock_Delegate.java
index bd332a6..26cb97b 100644
--- a/tools/layoutlib/bridge/src/android/os/SystemClock_Delegate.java
+++ b/tools/layoutlib/bridge/src/android/os/SystemClock_Delegate.java
@@ -31,6 +31,7 @@
*/
public class SystemClock_Delegate {
private static long sBootTime = System.currentTimeMillis();
+ private static long sBootTimeNano = System.nanoTime();
@LayoutlibDelegate
/*package*/ static boolean setCurrentTimeMillis(long millis) {
@@ -60,6 +61,16 @@
}
/**
+ * Returns nanoseconds since boot, including time spent in sleep.
+ *
+ * @return elapsed nanoseconds since boot.
+ */
+ @LayoutlibDelegate
+ /*package*/ static long elapsedRealtimeNano() {
+ return System.nanoTime() - sBootTimeNano;
+ }
+
+ /**
* Returns milliseconds running in the current thread.
*
* @return elapsed milliseconds in the thread
diff --git a/tools/layoutlib/bridge/src/android/util/FloatMath_Delegate.java b/tools/layoutlib/bridge/src/android/util/FloatMath_Delegate.java
index 1df78c2..8b4c60b 100644
--- a/tools/layoutlib/bridge/src/android/util/FloatMath_Delegate.java
+++ b/tools/layoutlib/bridge/src/android/util/FloatMath_Delegate.java
@@ -91,4 +91,42 @@
/*package*/ static float sqrt(float value) {
return (float)Math.sqrt(value);
}
+
+ /**
+ * Returns the closest float approximation of the raising "e" to the power
+ * of the argument.
+ *
+ * @param value to compute the exponential of
+ * @return the exponential of value
+ */
+ @LayoutlibDelegate
+ /*package*/ static float exp(float value) {
+ return (float)Math.exp(value);
+ }
+
+ /**
+ * Returns the closest float approximation of the result of raising {@code
+ * x} to the power of {@code y}.
+ *
+ * @param x the base of the operation.
+ * @param y the exponent of the operation.
+ * @return {@code x} to the power of {@code y}.
+ */
+ @LayoutlibDelegate
+ /*package*/ static float pow(float x, float y) {
+ return (float)Math.pow(x, y);
+ }
+
+ /**
+ * Returns {@code sqrt(}<i>{@code x}</i><sup>{@code 2}</sup>{@code +} <i>
+ * {@code y}</i><sup>{@code 2}</sup>{@code )}.
+ *
+ * @param x a float number
+ * @param y a float number
+ * @return the hypotenuse
+ */
+ @LayoutlibDelegate
+ /*package*/ static float hypot(float x, float y) {
+ return (float)Math.sqrt(x*x + y*y);
+ }
}
diff --git a/tools/layoutlib/bridge/src/com/android/layoutlib/bridge/android/BridgeContext.java b/tools/layoutlib/bridge/src/com/android/layoutlib/bridge/android/BridgeContext.java
index 260ee3e..428c4c2 100644
--- a/tools/layoutlib/bridge/src/com/android/layoutlib/bridge/android/BridgeContext.java
+++ b/tools/layoutlib/bridge/src/com/android/layoutlib/bridge/android/BridgeContext.java
@@ -67,6 +67,7 @@
import android.util.TypedValue;
import android.view.BridgeInflater;
import android.view.CompatibilityInfoHolder;
+import android.view.Display;
import android.view.Surface;
import android.view.View;
import android.view.ViewGroup;
@@ -925,6 +926,12 @@
}
@Override
+ public Context createDisplayContext(Display display) {
+ // pass
+ return null;
+ }
+
+ @Override
public String[] databaseList() {
// pass
return null;
@@ -1157,6 +1164,13 @@
}
@Override
+ public Intent registerReceiverAsUser(BroadcastReceiver arg0, UserHandle arg0p5,
+ IntentFilter arg1, String arg2, Handler arg3) {
+ // pass
+ return null;
+ }
+
+ @Override
public void removeStickyBroadcast(Intent arg0) {
// pass
@@ -1357,7 +1371,7 @@
}
@Override
- public CompatibilityInfoHolder getCompatibilityInfo() {
+ public CompatibilityInfoHolder getCompatibilityInfo(int displayId) {
// pass
return null;
}
diff --git a/wifi/java/android/net/wifi/WifiNative.java b/wifi/java/android/net/wifi/WifiNative.java
index b9feb34..805faa6 100644
--- a/wifi/java/android/net/wifi/WifiNative.java
+++ b/wifi/java/android/net/wifi/WifiNative.java
@@ -500,6 +500,14 @@
}
}
+ public boolean setWfdEnable(boolean enable) {
+ return doBooleanCommand("SET wifi_display " + (enable ? "1" : "0"));
+ }
+
+ public boolean setWfdDeviceInfo(String hex) {
+ return doBooleanCommand("WFD_SUBELEM_SET 0 " + hex);
+ }
+
/**
* "sta" prioritizes STA connection over P2P and "p2p" prioritizes
* P2P connection over STA
diff --git a/wifi/java/android/net/wifi/p2p/WifiP2pDevice.java b/wifi/java/android/net/wifi/p2p/WifiP2pDevice.java
index c86ec8b..117af82 100644
--- a/wifi/java/android/net/wifi/p2p/WifiP2pDevice.java
+++ b/wifi/java/android/net/wifi/p2p/WifiP2pDevice.java
@@ -107,12 +107,14 @@
/** Device connection status */
public int status = UNAVAILABLE;
- /** Detailed device string pattern
+ /** @hide */
+ public WifiP2pWfdInfo wfdInfo;
+
+ /** Detailed device string pattern with WFD info
* Example:
- * P2P-DEVICE-FOUND fa:7b:7a:42:02:13 p2p_dev_addr=fa:7b:7a:42:02:13
- * pri_dev_type=1-0050F204-1 name='p2p-TEST1' config_methods=0x188 dev_capab=0x27
- * group_capab=0x0
- *
+ * P2P-DEVICE-FOUND 00:18:6b:de:a3:6e p2p_dev_addr=00:18:6b:de:a3:6e
+ * pri_dev_type=1-0050F204-1 name='DWD-300-DEA36E' config_methods=0x188
+ * dev_capab=0x21 group_capab=0x9
*/
private static final Pattern detailedDevicePattern = Pattern.compile(
"((?:[0-9a-f]{2}:){5}[0-9a-f]{2}) " +
@@ -273,6 +275,7 @@
sbuf.append("\n grpcapab: ").append(groupCapability);
sbuf.append("\n devcapab: ").append(deviceCapability);
sbuf.append("\n status: ").append(status);
+ sbuf.append("\n wfdInfo: ").append(wfdInfo);
return sbuf.toString();
}
@@ -292,6 +295,7 @@
deviceCapability = source.deviceCapability;
groupCapability = source.groupCapability;
status = source.status;
+ wfdInfo = source.wfdInfo;
}
}
@@ -305,6 +309,12 @@
dest.writeInt(deviceCapability);
dest.writeInt(groupCapability);
dest.writeInt(status);
+ if (wfdInfo != null) {
+ dest.writeInt(1);
+ wfdInfo.writeToParcel(dest, flags);
+ } else {
+ dest.writeInt(0);
+ }
}
/** Implement the Parcelable interface */
@@ -320,6 +330,9 @@
device.deviceCapability = in.readInt();
device.groupCapability = in.readInt();
device.status = in.readInt();
+ if (in.readInt() == 1) {
+ device.wfdInfo = WifiP2pWfdInfo.CREATOR.createFromParcel(in);
+ }
return device;
}
diff --git a/wifi/java/android/net/wifi/p2p/WifiP2pInfo.java b/wifi/java/android/net/wifi/p2p/WifiP2pInfo.java
index dce315a..8972b7e 100644
--- a/wifi/java/android/net/wifi/p2p/WifiP2pInfo.java
+++ b/wifi/java/android/net/wifi/p2p/WifiP2pInfo.java
@@ -44,8 +44,8 @@
public String toString() {
StringBuffer sbuf = new StringBuffer();
sbuf.append("groupFormed: ").append(groupFormed)
- .append("isGroupOwner: ").append(isGroupOwner)
- .append("groupOwnerAddress: ").append(groupOwnerAddress);
+ .append(" isGroupOwner: ").append(isGroupOwner)
+ .append(" groupOwnerAddress: ").append(groupOwnerAddress);
return sbuf.toString();
}
diff --git a/wifi/java/android/net/wifi/p2p/WifiP2pManager.java b/wifi/java/android/net/wifi/p2p/WifiP2pManager.java
index 96d3a7f..6edc232 100644
--- a/wifi/java/android/net/wifi/p2p/WifiP2pManager.java
+++ b/wifi/java/android/net/wifi/p2p/WifiP2pManager.java
@@ -455,6 +455,13 @@
/** @hide */
public static final int RESPONSE_PERSISTENT_GROUP_INFO = BASE + 63;
+ /** @hide */
+ public static final int SET_WFD_INFO = BASE + 64;
+ /** @hide */
+ public static final int SET_WFD_INFO_FAILED = BASE + 65;
+ /** @hide */
+ public static final int SET_WFD_INFO_SUCCEEDED = BASE + 66;
+
/**
* Create a new WifiP2pManager instance. Applications use
* {@link android.content.Context#getSystemService Context.getSystemService()} to retrieve
@@ -742,6 +749,7 @@
case WifiP2pManager.CLEAR_SERVICE_REQUESTS_FAILED:
case WifiP2pManager.SET_DEVICE_NAME_FAILED:
case WifiP2pManager.DELETE_PERSISTENT_GROUP_FAILED:
+ case WifiP2pManager.SET_WFD_INFO_FAILED:
if (listener != null) {
((ActionListener) listener).onFailure(message.arg1);
}
@@ -762,6 +770,7 @@
case WifiP2pManager.CLEAR_SERVICE_REQUESTS_SUCCEEDED:
case WifiP2pManager.SET_DEVICE_NAME_SUCCEEDED:
case WifiP2pManager.DELETE_PERSISTENT_GROUP_SUCCEEDED:
+ case WifiP2pManager.SET_WFD_INFO_SUCCEEDED:
if (listener != null) {
((ActionListener) listener).onSuccess();
}
@@ -1297,6 +1306,13 @@
c.mAsyncChannel.sendMessage(SET_DEVICE_NAME, 0, c.putListener(listener), d);
}
+ /** @hide */
+ public void setWFDInfo(
+ Channel c, WifiP2pWfdInfo wfdInfo,
+ ActionListener listener) {
+ checkChannel(c);
+ c.mAsyncChannel.sendMessage(SET_WFD_INFO, 0, c.putListener(listener), wfdInfo);
+ }
/**
* Set dialog listener to over-ride system dialogs on p2p events. This function
diff --git a/wifi/java/android/net/wifi/p2p/WifiP2pService.java b/wifi/java/android/net/wifi/p2p/WifiP2pService.java
index c34d70e..acb7e52 100644
--- a/wifi/java/android/net/wifi/p2p/WifiP2pService.java
+++ b/wifi/java/android/net/wifi/p2p/WifiP2pService.java
@@ -496,6 +496,10 @@
replyToMessage(message, WifiP2pManager.DELETE_PERSISTENT_GROUP,
WifiP2pManager.BUSY);
break;
+ case WifiP2pManager.SET_WFD_INFO:
+ replyToMessage(message, WifiP2pManager.SET_WFD_INFO_FAILED,
+ WifiP2pManager.BUSY);
+ break;
case WifiP2pManager.REQUEST_PEERS:
replyToMessage(message, WifiP2pManager.RESPONSE_PEERS, mPeers);
break;
@@ -629,6 +633,10 @@
replyToMessage(message, WifiP2pManager.DELETE_PERSISTENT_GROUP,
WifiP2pManager.P2P_UNSUPPORTED);
break;
+ case WifiP2pManager.SET_WFD_INFO:
+ replyToMessage(message, WifiP2pManager.SET_WFD_INFO_FAILED,
+ WifiP2pManager.P2P_UNSUPPORTED);
+ break;
default:
return NOT_HANDLED;
}
@@ -741,6 +749,7 @@
transitionTo(mP2pDisablingState);
break;
case WifiP2pManager.SET_DEVICE_NAME:
+ {
WifiP2pDevice d = (WifiP2pDevice) message.obj;
if (d != null && setAndPersistDeviceName(d.deviceName)) {
if (DBG) logd("set device name " + d.deviceName);
@@ -750,6 +759,18 @@
WifiP2pManager.ERROR);
}
break;
+ }
+ case WifiP2pManager.SET_WFD_INFO:
+ {
+ WifiP2pWfdInfo d = (WifiP2pWfdInfo) message.obj;
+ if (d != null && setWfdInfo(d)) {
+ replyToMessage(message, WifiP2pManager.SET_WFD_INFO_SUCCEEDED);
+ } else {
+ replyToMessage(message, WifiP2pManager.SET_WFD_INFO_FAILED,
+ WifiP2pManager.ERROR);
+ }
+ break;
+ }
case WifiP2pManager.DISCOVER_PEERS:
// do not send service discovery request while normal find operation.
clearSupplicantServiceRequest();
@@ -2018,6 +2039,27 @@
return true;
}
+ private boolean setWfdInfo(WifiP2pWfdInfo wfdInfo) {
+ boolean success;
+
+ if (!wfdInfo.isWfdEnabled()) {
+ success = mWifiNative.setWfdEnable(false);
+ } else {
+ success =
+ mWifiNative.setWfdEnable(true)
+ && mWifiNative.setWfdDeviceInfo(wfdInfo.getDeviceInfoHex());
+ }
+
+ if (!success) {
+ loge("Failed to set wfd properties");
+ return false;
+ }
+
+ mThisDevice.wfdInfo = wfdInfo;
+ sendThisDeviceChangedBroadcast();
+ return true;
+ }
+
private void initializeP2pSettings() {
mWifiNative.setPersistentReconnect(true);
mThisDevice.deviceName = getPersistedDeviceName();
diff --git a/wifi/java/android/net/wifi/p2p/WifiP2pWfdInfo.java b/wifi/java/android/net/wifi/p2p/WifiP2pWfdInfo.java
new file mode 100644
index 0000000..9dd3e4a
--- /dev/null
+++ b/wifi/java/android/net/wifi/p2p/WifiP2pWfdInfo.java
@@ -0,0 +1,185 @@
+/*
+ * 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 android.net.wifi.p2p;
+
+import android.os.Parcelable;
+import android.os.Parcel;
+
+/**
+ * A class representing Wifi Display information for a device
+ * @hide
+ */
+public class WifiP2pWfdInfo implements Parcelable {
+
+ private static final String TAG = "WifiP2pWfdInfo";
+
+ private boolean mWfdEnabled;
+
+ private int mDeviceInfo;
+
+ public static final int WFD_SOURCE = 0;
+ public static final int PRIMARY_SINK = 1;
+ public static final int SECONDARY_SINK = 2;
+ public static final int SOURCE_OR_PRIMARY_SINK = 3;
+
+ /* Device information bitmap */
+ /** One of {@link #WFD_SOURCE}, {@link #PRIMARY_SINK}, {@link #SECONDARY_SINK}
+ * or {@link #SOURCE_OR_PRIMARY_SINK}
+ */
+ private static final int DEVICE_TYPE = 0x3;
+ private static final int COUPLED_SINK_SUPPORT_AT_SOURCE = 0x4;
+ private static final int COUPLED_SINK_SUPPORT_AT_SINK = 0x8;
+ private static final int SESSION_AVAILABLE = 0x30;
+ private static final int SESSION_AVAILABLE_BIT1 = 0x10;
+ private static final int SESSION_AVAILABLE_BIT2 = 0x20;
+
+ private int mCtrlPort;
+
+ private int mMaxThroughput;
+
+ public WifiP2pWfdInfo() {
+ }
+
+ public boolean isWfdEnabled() {
+ return mWfdEnabled;
+ }
+
+ public void setWfdEnabled(boolean enabled) {
+ mWfdEnabled = enabled;
+ }
+
+ public int getDeviceType() {
+ return (mDeviceInfo & DEVICE_TYPE);
+ }
+
+ public boolean setDeviceType(int deviceType) {
+ if (deviceType >= WFD_SOURCE && deviceType <= SOURCE_OR_PRIMARY_SINK) {
+ mDeviceInfo |= deviceType;
+ return true;
+ }
+ return false;
+ }
+
+ public boolean isCoupledSinkSupportedAtSource() {
+ return (mDeviceInfo & COUPLED_SINK_SUPPORT_AT_SINK) != 0;
+ }
+
+ public void setCoupledSinkSupportAtSource(boolean enabled) {
+ if (enabled ) {
+ mDeviceInfo |= COUPLED_SINK_SUPPORT_AT_SINK;
+ } else {
+ mDeviceInfo &= ~COUPLED_SINK_SUPPORT_AT_SINK;
+ }
+ }
+
+ public boolean isCoupledSinkSupportedAtSink() {
+ return (mDeviceInfo & COUPLED_SINK_SUPPORT_AT_SINK) != 0;
+ }
+
+ public void setCoupledSinkSupportAtSink(boolean enabled) {
+ if (enabled ) {
+ mDeviceInfo |= COUPLED_SINK_SUPPORT_AT_SINK;
+ } else {
+ mDeviceInfo &= ~COUPLED_SINK_SUPPORT_AT_SINK;
+ }
+ }
+
+ public boolean isSessionAvailable() {
+ return (mDeviceInfo & SESSION_AVAILABLE) != 0;
+ }
+
+ public void setSessionAvailable(boolean enabled) {
+ if (enabled) {
+ mDeviceInfo |= SESSION_AVAILABLE_BIT1;
+ mDeviceInfo &= ~SESSION_AVAILABLE_BIT2;
+ } else {
+ mDeviceInfo &= ~SESSION_AVAILABLE;
+ }
+ }
+
+ public int getControlPort() {
+ return mCtrlPort;
+ }
+
+ public void setControlPort(int port) {
+ mCtrlPort = port;
+ }
+
+ public void setMaxThroughput(int maxThroughput) {
+ mMaxThroughput = maxThroughput;
+ }
+
+ public int getMaxThroughput() {
+ return mMaxThroughput;
+ }
+
+ public String getDeviceInfoHex() {
+ return String.format("%04x%04x%04x%04x", 6, mDeviceInfo, mCtrlPort, mMaxThroughput);
+ }
+
+ public String toString() {
+ StringBuffer sbuf = new StringBuffer();
+ sbuf.append("WFD enabled: ").append(mWfdEnabled);
+ sbuf.append("WFD DeviceInfo: ").append(mDeviceInfo);
+ sbuf.append("\n WFD CtrlPort: ").append(mCtrlPort);
+ sbuf.append("\n WFD MaxThroughput: ").append(mMaxThroughput);
+ return sbuf.toString();
+ }
+
+ /** Implement the Parcelable interface */
+ public int describeContents() {
+ return 0;
+ }
+
+ /** copy constructor */
+ public WifiP2pWfdInfo(WifiP2pWfdInfo source) {
+ if (source != null) {
+ mDeviceInfo = source.mDeviceInfo;
+ mCtrlPort = source.mCtrlPort;
+ mMaxThroughput = source.mMaxThroughput;
+ }
+ }
+
+ /** Implement the Parcelable interface */
+ public void writeToParcel(Parcel dest, int flags) {
+ dest.writeInt(mWfdEnabled ? 1 : 0);
+ dest.writeInt(mDeviceInfo);
+ dest.writeInt(mCtrlPort);
+ dest.writeInt(mMaxThroughput);
+ }
+
+ public void readFromParcel(Parcel in) {
+ mWfdEnabled = (in.readInt() == 1);
+ mDeviceInfo = in.readInt();
+ mCtrlPort = in.readInt();
+ mMaxThroughput = in.readInt();
+ }
+
+ /** Implement the Parcelable interface */
+ public static final Creator<WifiP2pWfdInfo> CREATOR =
+ new Creator<WifiP2pWfdInfo>() {
+ public WifiP2pWfdInfo createFromParcel(Parcel in) {
+ WifiP2pWfdInfo device = new WifiP2pWfdInfo();
+ device.readFromParcel(in);
+ return device;
+ }
+
+ public WifiP2pWfdInfo[] newArray(int size) {
+ return new WifiP2pWfdInfo[size];
+ }
+ };
+}